如何在Python zlib模块中设置参数

3 投票
6 回答
4096 浏览
提问于 2025-04-16 09:29

我想写一个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 个回答

0

你是不是不想自己动手生成PNG图片,而是想用一些现成的软件呢?那你可以试试PyPNG这个工具哦!

1

即使你不能使用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非常欢迎的贡献。:)

0

简短回答: (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)

撰写回答