Изменение палитры 8-битных изображений .png с помощью python PIL

Я ищу быстрый способ применить новую палитру к существующему 8-битному изображению .png. Как я могу это сделать? Перекодируется ли .png при сохранении изображения? (Собственный ответ: вроде так)

Что я пробовал (отредактировал):

import Image, ImagePalette
output = StringIO.StringIO()
palette = (.....) #long palette of 768 items
im = Image.open('test_palette.png') #8 bit image
im.putpalette(palette) 
im.save(output, format='PNG')

По моим наблюдениям, функция сохранения занимает около 65 миллисекунд. Моя мысль: без декодирования и кодирования это может быть намного быстрее ??


person Jack Ha    schedule 21.07.2009    source источник
comment
Примеры: stackoverflow.com/questions/236692/   -  person alex vasi    schedule 21.07.2009


Ответы (3)


Если вы хотите изменить только палитру, то PIL просто помешает вам. К счастью, формат файла PNG был разработан таким образом, чтобы с ним было легко работать, когда вас интересуют только некоторые фрагменты данных. Формат блока PLTE представляет собой просто массив троек RGB с CRC в конце. Чтобы изменить палитру в файле на месте без чтения или записи всего файла:

import struct
from zlib import crc32
import os

# PNG file format signature
pngsig = '\x89PNG\r\n\x1a\n'

def swap_palette(filename):
    # open in read+write mode
    with open(filename, 'r+b') as f:
        f.seek(0)
        # verify that we have a PNG file
        if f.read(len(pngsig)) != pngsig:
            raise RuntimeError('not a png file!')

        while True:
            chunkstr = f.read(8)
            if len(chunkstr) != 8:
                # end of file
                break

            # decode the chunk header
            length, chtype = struct.unpack('>L4s', chunkstr)
            # we only care about palette chunks
            if chtype == 'PLTE':
                curpos = f.tell()
                paldata = f.read(length)
                # change the 3rd palette entry to cyan
                paldata = paldata[:6] + '\x00\xff\xde' + paldata[9:]

                # go back and write the modified palette in-place
                f.seek(curpos)
                f.write(paldata)
                f.write(struct.pack('>L', crc32(chtype+paldata)&0xffffffff))
            else:
                # skip over non-palette chunks
                f.seek(length+4, os.SEEK_CUR)

if __name__ == '__main__':
    import shutil
    shutil.copyfile('redghost.png', 'blueghost.png')
    swap_palette('blueghost.png')

Этот код копирует redghost.png в blueghost.png и изменяет палитру blueghost.png на месте.

красный призрак->  синий призрак

person Theran    schedule 31.07.2009
comment
Спасибо! Именно то, что я искал! - person Jack Ha; 04.08.2009

im.palette не вызывается - это экземпляр класса ImagePalette в режиме P, иначе None. im.putpalette(...) - метод, поэтому вызываемый: аргумент должен быть последовательностью из 768 целых чисел, дающей значение R, G и B для каждого индекса.

person Alex Martelli    schedule 21.07.2009

Изменение палитры без декодирования и (пере) кодирования не представляется возможным. Метод в вопросе кажется лучшим (на данный момент). Если производительность важна, кодирование в GIF кажется намного быстрее.

person Jack Ha    schedule 29.07.2009