3

PNG ファイルを作成する Python プログラムを作成したいと考えています。私の大きな問題は、CRC と IDAT チャンク内のデータを生成することです。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 バイトに複数のピクセルを収めることができる「圧縮」を利用しているかどうかもわかりません。

4

6 に答える 6

1

tEXtチャンクの理由でPyPNGを使用できない場合でも、そのコードを使用できます。(MITライセンスです)。チャンクの記述方法は次のとおりです。

def write_chunk(outfile、tag、data =''):
    "" "
    長さと長さを含むPNGチャンクを出力ファイルに書き込みます
    チェックサム。
    "" "

    #http://www.w3.org/TR/PNG/#5Chunk-layout
    outfile.write(struct.pack( "!I"、len(data)))
    outfile.write(tag)
    outfile.write(data)
    チェックサム=zlib.crc32(tag)
    チェックサム=zlib.crc32(データ、チェックサム)
    outfile.write(struct.pack( "!i"、チェックサム))

zlib.crc32を使用してCRCチェックサムを作成していることに注意してください。また、チェックサムがタグとデータの両方でどのように実行されるかにも注意してください。

IDATチャンクを圧縮するには、基本的にzlibを使用します。他の人が指摘しているように、アドラーチェックサムとデフォルトのウィンドウサイズはすべて問題ありません(ちなみに、PNG仕様では32768のウィンドウサイズは必要ありませんが、ウィンドウは最大32768バイトである必要があります。これはすべて少し奇妙です。いずれの場合も、32768は現在のバージョンのzlib仕様で許可されている最大ウィンドウサイズです)。

PyPNGでこれを行うためのコードは、特に優れているわけではありません。write_passes()関数を参照してください。実際にデータを圧縮してチャンクを書き込むビットは次のとおりです。

                コンプレッサー=zlib.compressobj()
                圧縮=compressor.compress(tostring(data))
                len(圧縮)の場合:
                    #印刷>> sys.stderr、len(data)、len(compressed)
                    write_chunk(outfile、'IDAT'、圧縮)

PyPNGはスキャンラインフィルタリングを使用しません。これは、Pythonでは非常に遅いため、コードを記述していないためです。フィルタリングを行うPythonコードがある場合は、PyPNGへの貢献が最も歓迎されます。:)

于 2011-03-16T10:22:46.017 に答える
0

簡単な答え:(1)「deflate」と「32Kbwindow」がデフォルトです(2)crc32ではなくadler32を使用します

長い答え:

"" "PNG仕様では、32768バイトのウィンドウサイズでzlibのdeflateメソッドを使用してIDATデータを圧縮する必要がありますが、Pythonzlibモジュールでこれらのパラメーターを設定する方法が見つかりません。"""

それらを設定する必要はありません。これらがデフォルトです。

本当にzlibにデフォルト以外の引数を指定したい場合は、zlib.compressobj()を使用できます...Pythonドキュメントに記載されていないいくつかの引数があります。読み物:

ソース:Pythonのgzip.py(zlib.compressobjの呼び出し方法を参照)

ソース:Pythonのzlibmodule.c(デフォルトを参照)

SO:この質問(MizardXと私自身による回答、およびそれぞれについてのコメントを参照してください)

docs: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)

Python2.7からの出力は以下のとおりです。Python 2.1〜2.6および1.5.2JFTHOIでもテストされています。

(0L, 0L, True)
(3523407757L, 3523407757L, True)
(2768625435L, 2768625435L, True)
(4186783197L, 4186783197L, True)
于 2011-01-06T22:13:58.770 に答える
0

既存のソフトウェアを使用して PNG を生成したくありませんか? PyPNGはどうですか?

于 2011-01-06T05:53:26.487 に答える
0

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]]))
于 2014-09-14T16:13:31.923 に答える
0

PILなど、PNG ファイルを書き込めるライブラリがあります。その方が簡単かつ高速で、追加のボーナスとして、大量の形式を読み書きできます。

于 2011-01-06T06:53:59.050 に答える
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

于 2011-01-06T09:19:51.970 に答える