Как установить параметры в модуле Python zlib - PullRequest
3 голосов
/ 06 января 2011

Я хочу написать программу на Python, которая создает файлы PNG.Моя большая проблема заключается в создании CRC и данных в блоке IDAT.В Python 2.6.4 есть модуль zlib, но необходимы дополнительные настройки.Спецификация PNG ТРЕБУЕТ, чтобы данные IDAT были сжаты с помощью метода deflate zlib с размером окна 32768 байт, но я не могу найти, как установить эти параметры в модуле Python zlib.

Что касается CRC дляВ документации каждого блока в модуле zlib указано, что он содержит функцию CRC.Я полагаю , что вызов этой функции CRC как crc32 (data, -1) сгенерирует CRC, который мне нужен, хотя при необходимости я могу перевести код C, указанный в спецификации PNG.

Обратите внимание, что я могу сгенерировать остальную часть файла PNG и данные, которые должны быть сжаты для блока IDAT, я просто не знаю, как правильно сжать данные изображения дляблок IDAT после выполнения начального шага фильтрации.

РЕДАКТИРОВАНИЕ:

Проблема с PyPNG заключается в том, что он не будет записывать фрагменты tEXt.Небольшое раздражение заключается в том, что нужно манипулировать изображением как (R, G, B) данными;Я бы предпочел напрямую манипулировать значениями палитры пикселей, а затем определять ассоциации между значениями палитры и данными цвета.Я также не уверен, использует ли PyPNG «сжатие», допускаемое с использованием 1-, 2- и 4-битных значений палитры в данных изображения для размещения более одного пикселя в байте.

Ответы [ 6 ]

1 голос
/ 16 марта 2011

Даже если вы не можете использовать 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))

Обратите внимание на использование 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 голосов
/ 14 сентября 2014

zlib.crc32 работает нормально, а компрессор zlib имеет правильные значения по умолчанию для генерации png.

Для случайного читателя, который ищет генерацию png из кода Python, вот полный пример, который вы можете использовать какстартер для вашего собственного кода генератора 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]]))
0 голосов
/ 07 января 2011

Краткий ответ: (1) по умолчанию "deflate" и "окно 32Kb" (2) использует adler32, а не crc32

Длинный ответ:

""«Спецификация PNG ТРЕБУЕТ, чтобы данные IDAT были сжаты с помощью метода deflate zlib с размером окна 32768 байт, но я не могу найти, как установить эти параметры в модуле Python zlib.» »«

Выне нужно устанавливать их.Это значения по умолчанию.

Если вы действительно хотите указать аргументы не по умолчанию для zlib, вы можете использовать zlib.compressobj () ... у него есть несколько аргументов, которые не описаны в документации по Python.Материал для чтения:

источник: Python's gzip.py (посмотрите, как он вызывает zlib.compressobj)

source: Python's zlibmodule.c (см.по умолчанию)

SO: Этот вопрос (см. ответы MizardX и меня и комментарии к каждому)

документы: руководство на zlib site

"" "Что касается CRC для каждого чанка, документация модуля zlib указывает, что он содержит функцию CRC. Я считаю, что вызов этой функции CRC как crc32 (data, -1) сгенерирует нужный мне CRC, хотя при необходимости я могу перевести код C, указанный в спецификации PNG. "" "

Пожалуйста, ознакомьтесь с спецификацией zlib aka RFC 1950 ... он говорит, что используется контрольная сумма adler32

Вывод zlib compress или compressobj будет включать в себя соответствующий CRC;почему вы думаете, что вам нужно будет сделать это самостоятельно?

Edit Итак, вам нужен 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)
0 голосов
/ 06 января 2011

Похоже, вам придется прибегать к вызову zlib "вручную" с помощью ctypes - это не так сложно:

>>> 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

0 голосов
/ 06 января 2011

Существуют библиотеки, которые могут записывать PNG-файлы для вас, например PIL .Это будет проще и быстрее, а в качестве дополнительного бонуса вы можете читать и писать множество форматов.

0 голосов
/ 06 января 2011

Разве вы не хотите использовать существующее программное обеспечение для генерации ваших PNG?Как насчет PyPNG ?

...