Python Imaging Library (PIL) で、パレット形式のPNGを扱う

PIL のパレットは色々と問題がある。256色あることしか考えてないとか。
んで、PngImagePlugin も、 PNG の "tRNS" チャンクのうちαが0のところを見つけて、そのインデックスを info['transparency'] に突っ込むだけしかしてくれない。PNGのパレット形式はパレットそれぞれにα値を設定できるのに。

まずは PNG のパレットについて復習。
PNGの中は、チャンクという要素が並んでいる。パレットは 'PLTE' というチャンクに入っている。 "RGBRGBRGB..." という感じで、3バイト×色数(256以下) だけ並んでいる。
パレット形式のPNGでも、α値を利用できる。その場合は、 'tRNS' というチャンクに "AAAA..." という感じでα値が並んでいる。最初のα値は、パレットの最初の色のα値に相当する。 tRNS のサイズは PLTE の色数以下になっていて、 tRNS の足りない部分のα値は 255 で補完される。

ということで、この2種類のチャンクを保存するように PngImagePlugin を改造してみる。

from PIL import PngImagePlugin

PngStream = PngImagePlugin.PngStream

class PngStreamA(PngStream):

   def chunk_PLTE(self, pos, len):
       s = PngStream.chunk_PLTE(self, pos, len)
       self.im_info["PNG_PLTE"] = s
       return s

   def chunk_tRNS(self, pos, len):
       s = PngStream.chunk_PLTE(self, pos, len)
       self.im_info["PNG_tRNS"] = s
       return s

PngImagePlugin.PngStream = PngStreamA

これで、 img = Image.open('foobar.png') したあと、 img.info.get('PNG_tRNS') とか img.info.get('PNG_PLTE') とかできるようになる。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。