Python zlibモジュールでパラメータを設定する方法
質問
PNG ファイルを作成する Python プログラムを作成したいと考えています。私の大きな問題は、IDAT チャンク内の CRC とデータの生成です。Python 2.6.4 には zlib モジュールがありますが、追加の設定が必要です。PNG 仕様では、ウィンドウ サイズ 32768 バイトの zlib の deflate メソッドで IDAT データを圧縮する必要がありますが、Python zlib モジュールでこれらのパラメータを設定する方法が見つかりません。
各チャンクの CRC については、zlib モジュールのドキュメントに CRC 関数が含まれていることが示されています。私 信じる その CRC 関数を crc32(data,-1) として呼び出すと、必要な CRC が生成されますが、必要に応じて、PNG 仕様で指定された C コードを変換できます。
注意してください、私は できる 残りの PNG ファイルとデータを生成します。 圧縮される IDAT チャンクについては、最初のフィルタリング手順を実装した後に、IDAT チャンクの画像データを適切に圧縮する方法がわかりません。
編集済み:
PyPNG の問題は、tEXt チャンクを書き込めないことです。ちょっと面倒なのは、画像を (R, G, B) データとして操作しなければならないことです。ピクセルのパレット値を直接操作して、パレット値とカラーデータの間の関連付けを定義したいと考えています。また、PyPNG が、画像データの 1、2、および 4 ビットのパレット値を使用して 1 バイトに複数のピクセルを収めることによって可能になる「圧縮」を利用しているかどうかもわかりません。
解決
短い答え:(1) "デフレート" と "32Kbのウィンドウには、" デフォルト(2)の用途はCRC32 の
ではないAdler32チェックされていますロング答えます:
「」「PNGの仕様では、32768バイトのウィンドウサイズではzlibのデフレート方法で圧縮することがIDATデータが必要ですが、私は、Pythonのzlibモジュールでこれらのパラメータを設定する方法を見つけることができません。 『』」
あなたはそれらを設定する必要はありません。それらはデフォルトです。
あなたが本当にZLIBにデフォルト以外の引数を指定したい場合は、(zlib.compressobj使用することができます)...それは、Pythonのドキュメントに記載されていないいくつかの引数があります。素材を読み込みます:
ソース: Pythonのgzip.py (それはzlib.compressobj呼び出す方法を参照)。
ソース: Pythonのzlibmodule.c (デフォルトを参照)。
SO:この質問する(MizardXと自分で答え、とのコメントを見ます各)
文書:
「」 "各チャンクのCRCについては、zlibのモジュールのドキュメントは、それがCRC機能が含まれていることを示しています。私は信じているCRC32としてCRC機能(データは、-1)私は、けれども必要があるとCRCを生成することを呼び出します必要であれば、私はPNG仕様で与えられたCコードを変換することができます。「」 "
RFC 1950 の別名をチェックアウトしてください zlibの仕様...それがいることを言います使用されるチェックサムがあるの Adler32チェックの
ZLIBの圧縮または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から出力は以下の通りです。また、包括2.6および1.5.2 JFTHOIにはPython 2.1でテストされます。
(0L, 0L, True)
(3523407757L, 3523407757L, True)
(2768625435L, 2768625435L, True)
(4186783197L, 4186783197L, True)
他のヒント
tEXt チャンクの理由で PyPNG を使用できない場合でも、そのコードを使用できます。(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))
CRC チェックサムを作成するために zlib.crc32 が使用されていることに注意してください。また、チェックサムがタグとデータの両方でどのように実行されるかにも注意してください。
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 では非常に遅くなることが原因の 1 つであり、コードを書いていないことも原因の 1 つです。フィルタリングを行うための Python コードをお持ちの場合は、PyPNG への貢献を大歓迎します。:)
あなたはPNG画像を生成するために、いくつかの既存のソフトウェアを使用したくないですか?どの程度 PyPNG の?
など PIL のようにあなたのためのPNGファイルを書き込むことができライブラリがあります。それは、簡単かつ迅速になり、追加ボーナスとして、あなたはフォーマットのトンを読み書きすることができます。
に見えます - それは難しいことではありません。
>>> import ctypes
>>> z = ctypes.cdll.LoadLibrary("libzip.so.1")
>>> z.zlibVersion.restype=ctypes.c_char_p
>>> z.zlibVersion()
'1.2.3'
あなたがここにzlibライブラリのdocmentationを確認することができます: http://zlib.net/manual.htmlする
zlib.crc32罰金を動作し、ZLIB圧縮機はPNG生成のための正しいデフォルト値を有している。
はPythonコードから生成をpngを探しカジュアルな読者のために、ここにあなたがあなた自身のPNG・ジェネレータ・コードのためのスターターとして使用することができます完全な例です - あなたが必要とするすべては、標準のzlibモジュールであり、いくつかのバイトをコードします
#! /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]]))