我想写一个制作PNG文件的Python程序。我最大的问题是在IDAT块中生成CRC和数据。 Python 2.6.4确实有一个zlib模块,但还需要额外的设置。 PNG规范要求使用zlib的deflate方法压缩IDAT数据,窗口大小为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位调色板值所允许的“压缩”,以适应一个字节中的多个像素。
答案 0 :(得分:1)
即使您不能将PyPNG用于tEXt块原因,您也可以使用其代码! (这是麻省理工学院的许可)。以下是块的编写方式:
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中执行此操作的代码并不是特别好,请参阅the write_passes() function。实际压缩数据并写入块的位是:
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 :(得分:0)
您不想使用某些现有软件来生成PNG吗? PyPNG怎么样?
答案 2 :(得分:0)
有些库可以为您编写PNG文件,例如PIL。这将更容易,更快,作为一个额外的奖励,你可以阅读和写大量的格式。
答案 3 :(得分:0)
看起来你将不得不求助于使用ctypes“手动”调用zlib - 这并不难:
>>> import ctypes
>>> z = ctypes.cdll.LoadLibrary("libzip.so.1")
>>> z.zlibVersion.restype=ctypes.c_char_p
>>> z.zlibVersion()
'1.2.3'
您可以在此处查看zlib库文档:http://zlib.net/manual.html
答案 4 :(得分:0)
简答:(1)“deflate”和“32Kb window”是默认值(2)使用adler32而不是crc32
答案很长:
“”“PNG规范要求使用zlib的deflate方法压缩IDAT数据,窗口大小为32768字节,但我找不到如何在Python zlib模块中设置这些参数。”“”
您无需设置它们。这些都是默认值。
如果你真的想为zlib指定非默认参数,你可以使用zlib.compressobj()...它有几个未在Python文档中记录的args。阅读材料:
来源:Python's gzip.py(请参阅它如何调用zlib.compressobj)
来源:Python's zlibmodule.c(参见默认值)
SO:This question(参见MizardX和我自己的回答,以及对每个的评论)
docs:manual
上的zlib site“”“对于每个块的CRC,zlib模块文档表明它包含CRC函数。我相信将CRC函数称为crc32(data,-1)将生成我需要的CRC,尽管如果有必要,我可以翻译PNG规范中给出的C代码。“”“
请查看the zlib specification aka 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)
答案 5 :(得分:0)
zlib.crc32工作正常,zlib压缩器具有正确的png生成默认值。
对于从Python代码中寻找png生成的随意读者,这里有一个完整的示例,您可以将其用作您自己的png生成器代码的启动器 - 您只需要标准的zlib模块和一些字节编码: / p>
#! /usr/bin/python
""" Converts a list of list into gray-scale PNG image. """
__copyright__ = "Copyright (C) 2014 Guido Draheim"
__licence__ = "Public Domain"
import zlib
import struct
def makeGrayPNG(data, height = None, width = None):
def I1(value):
return struct.pack("!B", value & (2**8-1))
def I4(value):
return struct.pack("!I", value & (2**32-1))
# compute width&height from data if not explicit
if height is None:
height = len(data) # rows
if width is None:
width = 0
for row in data:
if width < len(row):
width = len(row)
# generate these chunks depending on image type
makeIHDR = True
makeIDAT = True
makeIEND = True
png = b"\x89" + "PNG\r\n\x1A\n".encode('ascii')
if makeIHDR:
colortype = 0 # true gray image (no palette)
bitdepth = 8 # with one byte per pixel (0..255)
compression = 0 # zlib (no choice here)
filtertype = 0 # adaptive (each scanline seperately)
interlaced = 0 # no
IHDR = I4(width) + I4(height) + I1(bitdepth)
IHDR += I1(colortype) + I1(compression)
IHDR += I1(filtertype) + I1(interlaced)
block = "IHDR".encode('ascii') + IHDR
png += I4(len(IHDR)) + block + I4(zlib.crc32(block))
if makeIDAT:
raw = b""
for y in xrange(height):
raw += b"\0" # no filter for this scanline
for x in xrange(width):
c = b"\0" # default black pixel
if y < len(data) and x < len(data[y]):
c = I1(data[y][x])
raw += c
compressor = zlib.compressobj()
compressed = compressor.compress(raw)
compressed += compressor.flush() #!!
block = "IDAT".encode('ascii') + compressed
png += I4(len(compressed)) + block + I4(zlib.crc32(block))
if makeIEND:
block = "IEND".encode('ascii')
png += I4(0) + block + I4(zlib.crc32(block))
return png
def _example():
with open("cross3x3.png","wb") as f:
f.write(makeGrayPNG([[0,255,0],[255,255,255],[0,255,0]]))