PNG形式の画像をRGBモードからパレットモードに変換する
なにも考えずに変換
RGBモードの画像をパレットモードに変換したい
from PIL import Image img = Image.open('./image.png') img.convert('P') img.save('./image_p.png')
パレットモード変換するだけならこれで終わるのだが、これだと色が微妙に変わってしまう上パレットも意図したものとはならない。
これを何とかする。
なんとかする
とりあえず完成はこんな感じ。
from PIL import Image import numpy as np def convert_pmode(img_path, palette): img = Image.open(img_path).convert('P', dither=Image.NONE, palette=Image.ADAPTIVE) ori_palette = list(zip(*[iter(img.getpalette())]*3)) np_img = np.asarray(img) for num, col in enumerate(zip(*[iter(palette)]*3)): try: np_img = np.where(np_img == ori_palette.index(tuple(col)), num+256, np_img) except: pass p_img = Image.fromarray(np.uint8(np_img-256), mode='P') p_img.putpalette(palette) return p_img if __name__ == '__main__': img_path = './image.png' palette = [0,0,0,0,0,255.....] p_img = convert_pmode(img_path, palette) p_img.save('./image_p.png')
やってることとしては画像をいったんパレットモードに変換して読み込む。そこから自分が用意したパレットに合うように画像を操作していく感じ。
上から順に説明していく。
convert()
の引数でdither=Image.NONE
、palette=Image.ADAPTIVE
にしておく。
dither
は色の変化を滑らかにしてくれる処理。今回は必要ないので処理しない設定に。
palette
は正直よくわからない。Image.ADAPTIVE
にしておくと色が変わることなく変換できたので設定している。
ADAPTIVEの意味が「適応できる」とかだったから画像に応じてパレットを作るみたいなことかな?
変換した画像からパレットを取得してRGBの順になるように3つずつのlistに格納する。zip(*[iter(img.getpalette())]*3)
の書き方についてはここ とか見てください。
んで、画像をndarrayに変換してインデックスの値をいじっていく。
用意したパレットの色が今のパレットでは何番目になっているかをindex()
で調べてnp.where()
で置換。変換に支障が出ないようにインデックスの値+256で置換しておく。
あとはImage.fromarray()
でndarrayを変換して、putpalette()
でパレットを置き換えて終わり。
おわり
ずいぶん苦労して変換している気がする。もっと簡単な方法ありそう。