如何在Python zlib模块中设置参数
我想写一个Python程序来生成PNG文件。我的大问题是如何生成CRC和IDAT块中的数据。Python 2.6.4有一个zlib模块,但需要一些额外的设置。PNG的规范要求IDAT数据必须使用zlib的deflate方法进行压缩,并且窗口大小必须是32768字节,但我找不到如何在Python的zlib模块中设置这些参数。
至于每个块的CRC,zlib模块的文档说明它包含一个CRC函数。我相信调用这个CRC函数,像这样crc32(data,-1),就能生成我需要的CRC,当然如果有必要,我可以把PNG规范中给出的C代码翻译过来。
需要注意的是,我可以生成PNG文件的其他部分,以及要为IDAT块压缩的数据,只是我不知道在实现初步过滤步骤后,如何正确压缩IDAT块的图像数据。
编辑:
PyPNG的问题在于它无法写入tEXt块。一个小烦恼是,处理图像时必须使用(R, G, B)数据;我更希望直接操作像素的调色板值,然后定义调色板值和颜色数据之间的关联。我也不确定PyPNG是否利用了图像数据中使用1位、2位和4位调色板值的“压缩”特性,以便在一个字节中放入多个像素。
6 个回答
你是不是不想自己动手生成PNG图片,而是想用一些现成的软件呢?那你可以试试PyPNG这个工具哦!
即使你不能使用PyPNG来处理tEXt块的原因,你也可以使用它的代码!(它是MIT许可证的)。下面是一个块是如何写的:
def write_chunk(outfile, tag, data=''): """ Write a PNG chunk to the output file, including length and checksum. """ # http://www.w3.org/TR/PNG/#5Chunk-layout outfile.write(struct.pack("!I", len(data))) outfile.write(tag) outfile.write(data) checksum = zlib.crc32(tag) checksum = zlib.crc32(data, checksum) outfile.write(struct.pack("!i", checksum))
注意使用zlib.crc32来生成CRC校验和,同时也要注意校验和是如何同时作用于标签和数据的。
对于压缩IDAT块,你基本上只需要使用zlib。正如其他人提到的,adler校验和和默认窗口大小都是可以的(顺便说一下,PNG规范并不要求窗口大小必须是32768,它只要求窗口最大为32768字节;这有点奇怪,因为无论如何,32768是当前版本zlib规范允许的最大窗口大小)。
在PyPNG中实现这一点的代码并不是特别好,看看write_passes()函数。实际上压缩数据并写入块的部分是:
compressor = zlib.compressobj() compressed = compressor.compress(tostring(data)) if len(compressed): # print >> sys.stderr, len(data), len(compressed) write_chunk(outfile, 'IDAT', compressed)
PyPNG从来不使用扫描行过滤。这部分是因为在Python中这样做会非常慢,部分原因是我还没有写这段代码。如果你有Python代码可以做过滤,那将是对PyPNG非常欢迎的贡献。:)
简短回答: (1) “deflate”和“32Kb窗口”是默认设置 (2) 使用的是adler32而不是crc32
详细回答:
""" PNG规范要求IDAT数据使用zlib的deflate方法进行压缩,窗口大小为32768字节,但我找不到如何在Python的zlib模块中设置这些参数。 """
其实你不需要设置这些参数。这些都是默认的。
如果你真的想指定一些不同于默认的参数,可以使用zlib.compressobj() ... 它有几个参数在Python文档中没有详细说明。可以参考一下:
来源: Python的gzip.py(看看它是如何调用zlib.compressobj的)
来源: Python的zlibmodule.c(查看它的默认设置)
相关问题: 这个问题(看看MizardX和我自己的回答,以及每个回答的评论)
文档: zlib网站上的手册
"""关于每个数据块的CRC,zlib模块的文档说明它包含一个CRC函数。我相信调用这个CRC函数,像这样crc32(data,-1),会生成我需要的CRC,不过如果有必要,我可以把PNG规范中给出的C代码翻译过来。"""
请查看 zlib规范,也就是RFC 1950 ... 它说明使用的校验和是adler32
zlib的compress或compressobj输出会包含适当的CRC;你为什么认为你需要自己来做呢?
编辑 所以你确实需要一个CRC-32。好消息是:zlib.crc32()可以完成这个任务:
代码:
import zlib
crc_table = None
def make_crc_table():
global crc_table
crc_table = [0] * 256
for n in xrange(256):
c = n
for k in xrange(8):
if c & 1:
c = 0xedb88320L ^ (c >> 1)
else:
c = c >> 1
crc_table[n] = c
make_crc_table()
"""
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
should be initialized to all 1's, and the transmitted value
is the 1's complement of the final running CRC (see the
crc() routine below)). */
"""
def update_crc(crc, buf):
c = crc
for byte in buf:
c = crc_table[int((c ^ ord(byte)) & 0xff)] ^ (c >> 8)
return c
# /* Return the CRC of the bytes buf[0..len-1]. */
def crc(buf):
return update_crc(0xffffffffL, buf) ^ 0xffffffffL
if __name__ == "__main__":
tests = [
"",
"\x00",
"\x01",
"Twas brillig and the slithy toves did gyre and gimble in the wabe",
]
for test in tests:
model = crc(test) & 0xFFFFFFFFL
zlib_result = zlib.crc32(test) & 0xFFFFFFFFL
print (model, zlib_result, model == zlib_result)
下面是Python 2.7的输出。也在Python 2.1到2.6之间进行了测试,包括1.5.2 JFTHOI。
(0L, 0L, True)
(3523407757L, 3523407757L, True)
(2768625435L, 2768625435L, True)
(4186783197L, 4186783197L, True)