2009-07-21 2 views
2

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

То, что я пробовал (редактировать):

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

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

+0

Примеры: http://stackoverflow.com/questions/236692/how-do-i-convert-any-image-to-a-4-color-paletted-image-using -the-python-imaging-l –

ответ

4

Если вы хотите изменить только палитру, тогда PIL будет просто мешать вам. К счастью, формат PNG-файла был разработан, чтобы с ним было легко справиться, когда вас интересуют только некоторые части данных. Формат PLTE chunk - это всего лишь массив триггеров 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 на месте.

red ghost ->blue ghost

+0

Thx! Именно то, что я искал! –

1

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

0

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