gpt4 book ai didi

python - 如何在 python 中修改现有文件的压缩 itxt 记录?

转载 作者:可可西里 更新时间:2023-11-01 11:48:32 26 4
gpt4 key购买 nike

我知道这看起来太简单了,但我找不到直接的解决方案。

保存后,itxt 应该再次压缩。

最佳答案

它并不像您目测的那么简单。如果是,您可能已经发现没有直接的解决方案。

让我们从基础开始。

PyPNG 可以读取所有 block 吗?

一个重要的问题,因为修改现有的 PNG 文件是一项艰巨的任务。阅读它的文档,开始时并不顺利:

PNG: Chunk by Chunk

Ancillary Chunks

..iTXt
Ignored when reading. Not generated.

( https://pythonhosted.org/pypng/chunk.html )

但在那一页的下方,救恩!

Non-standard Chunks
Generally it is not possible to generate PNG images with any other chunk types. When reading a PNG image, processing it using the chunk interface, png.Reader.chunks, will allow any chunk to be processed (by user code).

所以我所要做的就是编写这个“用户代码”,PyPNG 可以完成剩下的工作。 (糟糕。)

iTXt block 呢?

让我们看看您感兴趣的内容。

4.2.3.3. iTXt International textual data

.. the textual data is in the UTF-8 encoding of the Unicode character set instead of Latin-1. This chunk contains:

Keyword:             1-79 bytes (character string)
Null separator: 1 byte
Compression flag: 1 byte
Compression method: 1 byte
Language tag: 0 or more bytes (character string)
Null separator: 1 byte
Translated keyword: 0 or more bytes
Null separator: 1 byte
Text: 0 or more bytes

( http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.iTXt )

我看起来很清楚。可选的压缩应该不是问题,因为

.. [t]he only value presently defined for the compression method byte is 0, meaning zlib ..

而且我非常有信心,Python 已经存在可以为我做这件事的东西。

然后回到 PyPNG 的 block 处理。

我们能看到 block 数据吗?

PyPNG 提供了一个迭代器,因此检查 PNG 是否包含 iTXt block 确实很容易:

chunks()
Return an iterator that will yield each chunk as a (chunktype, content) pair.

( https://pythonhosted.org/pypng/png.html?#png.Reader.chunks )

那么让我们以交互模式编写一些代码并检查一下。我从 http://pmt.sourceforge.net/itxt/ 得到了一张样本图片, 为方便起见在此重复。 (如果此处未保存iTXt数据,请下载并使用原始数据。)

itxt sample image

>>> import png
>>> imageFile = png.Reader("itxt.png")
>>> print imageFile
<png.Reader instance at 0x10ae1cfc8>
>>> for c in imageFile.chunks():
... print c[0],len(c[1])
...
IHDR 13
gAMA 4
sBIT 4
pCAL 44
tIME 7
bKGD 6
pHYs 9
tEXt 9
iTXt 39
IDAT 4000
IDAT 831
zTXt 202
iTXt 111
IEND 0

成功!

回信呢?好吧,PyPNG 通常用于创建完整的图像,但幸运的是,它还提供了一种从自定义 block 显式创建图像的方法:

png.write_chunks(out, chunks)
Create a PNG file by writing out the chunks.

因此我们可以遍历 block ,更改所需的 block ,然后写回修改后的 PNG。

解包和打包iTXt数据

这本身就是一项任务。数据格式描述的很好,但是不适合Python原生的unpackpack方法。所以我们必须自己发明一些东西。

文本字符串以 ASCIIZ 格式存储:以零字节结尾的字符串。我们需要一个小函数来拆分第一个 0:

def cutASCIIZ(str):
end = str.find(chr(0))
if end >= 0:
result = str[:end]
return [str[:end],str[end+1:]]
return ['',str]

这个快速而简单的函数返回一个 [before, after] 对的数组,并丢弃零本身。

为了尽可能透明地处理 iTXt 数据,我将其设为一个类:

class Chunk_iTXt:
def __init__(self, chunk_data):
tmp = cutASCIIZ(chunk_data)
self.keyword = tmp[0]
if len(tmp[1]):
self.compressed = ord(tmp[1][0])
else:
self.compressed = 0
if len(tmp[1]) > 1:
self.compressionMethod = ord(tmp[1][1])
else:
self.compressionMethod = 0
tmp = tmp[1][2:]
tmp = cutASCIIZ(tmp)
self.languageTag = tmp[0]
tmp = tmp[1]
tmp = cutASCIIZ(tmp)
self.languageTagTrans = tmp[0]
if self.compressed:
if self.compressionMethod != 0:
raise TypeError("Unknown compression method")
self.text = zlib.decompress(tmp[1])
else:
self.text = tmp[1]

def pack (self):
result = self.keyword+chr(0)
result += chr(self.compressed)
result += chr(self.compressionMethod)
result += self.languageTag+chr(0)
result += self.languageTagTrans+chr(0)
if self.compressed:
if self.compressionMethod != 0:
raise TypeError("Unknown compression method")
result += zlib.compress(self.text)
else:
result += self.text
return result

def show (self):
print 'iTXt chunk contents:'
print ' keyword: "'+self.keyword+'"'
print ' compressed: '+str(self.compressed)
print ' compression method: '+str(self.compressionMethod)
print ' language: "'+self.languageTag+'"'
print ' tag translation: "'+self.languageTagTrans+'"'
print ' text: "'+self.text+'"'

因为这使用了 zlib,所以它需要在程序的顶部有一个 import zlib

类构造函数接受“太短”的字符串,在这种情况下它将对所有未定义的内容使用默认值。

show 方法列出用于调试目的的数据。

使用我的自定义类

有了所有这些,现在检查、修改和添加 iTXt block 终于简单了:

import png
import zlib

# insert helper and class here

sourceImage = png.Reader("itxt.png")
chunkList = []
for chunk in sourceImage.chunks():
if chunk[0] == 'iTXt':
itxt = Chunk_iTXt(chunk[1])
itxt.show()
# modify existing data
if itxt.keyword == 'Author':
itxt.text = 'Rad Lexus'
itxt.compressed = 1
chunk = [chunk[0], itxt.pack()]
chunkList.append (chunk)

# append new data
newData = Chunk_iTXt('')
newData.keyword = 'Custom'
newData.languageTag = 'nl'
newData.languageTagTrans = 'Aangepast'
newData.text = 'Dat was leuk.'
chunkList.insert (-1, ['iTXt', newData.pack()])

with open("foo.png", "wb") as file:
png.write_chunks(file, chunkList)

当添加一个全新的 block 时,注意不要append它,因为那样它会出现所需的最后一个 IEND block ,这是一个错误。我没有尝试,但您也不应该将它插入到所需的第一个 IHDR block 之前,或者(正如 Glenn Randers-Pehrson 评论的那样)插入连续的 IDAT block 之间。

请注意,根据规范,iTXt 中的所有文本都应采用 UTF8 编码。

关于python - 如何在 python 中修改现有文件的压缩 itxt 记录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37068414/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com