Pillow(PIL)で生成するgifの画質を上げる


単刀直入

im.quantize() するだけ

はじめに

Pythonの画像処理ライブラリのPillowを使えば、簡単にgifアニメーションを作ることができます。
詳しいことはみんな大好きnote.nkmk.meに譲りますが、適当に画像を数枚開いてオプションを決めるだけです。

makegif.py
from PIL import Image

im1 = Image.open("hoge.png")
im2 = Image.open("huga.png")
images = [im1, im2]
images[0].save('test.gif', save_all=True,
               append_images=images[1:], optimize=False, duration=500, loop=0)

たのしいアニメができました。

写真の場合

では、いらすとやのようなシンプルなイラストではなく、写真でも試してみましょう

監視カメラを通して見たような酷い画質になってしまいました。
何故……。

*追記
スマホから見るとそんなに画質が悪くないように見えるかもしれません。
PCからだと結構酷さがわかります。(追記終わり)

gifの制限と回避

2色モノクロから16777216色 (= 224) 中256色 (= 28) までの色サポート

そうだったんですね。昔から私を楽しませてくれていたgifたちは256色の制限の中で戦っていたらしいです。
しかしこれでは困ります。というかガビガビじゃない写真のgifも見たことある気がします。

調べてみると以下のような記述を見つけました。(Deepl訳に少し追記)

もうひとつの方法は、「量子化(the quantize method)」です。現在、マルチフレームGIFを作成する際、PillowはWebパレットを使って画像を変換しています。量子化を使って画像を最初にPに戻した方が、使われている正確な色をよりよく表現できるように思えます。
https://github.com/python-pillow/Pillow/issues/3660

いまいちピンと来ませんが、Pillowで開いたイメージに.quantize()することでいい感じになるらしいです。
やってみましょう。

makegif.py
from PIL import Image

im1 = Image.open("gohan1.jpg").quantize()
im2 = Image.open("gohan2.jpg").quantize()
im3 = Image.open("gohan3.jpg").quantize()
images = [im1, im2, im3]
images[0].save('test2.gif', save_all=True,
               append_images=images[1:], optimize=False, duration=800, loop=0)


!!!
美味しそうです!これなら食えますね!!

なにが起こったの?

Pillowのquantizeによって、飯の画像を256色までいい感じに減色しました。

今回はデフォルトのままなので、メジアンカット(option=0)という方法で減色されたようです。
他にもmaximum coverage, fast octreeの方法が選べるようです。(libimagequantという方法もあるようですが、実行できませんでした)

それぞれを比較してみましょう。

maximum coverageの劣化が酷いですね。ほか2つはかなり似ていますが、手前の白飯を見ると若干fast octreeのほうが上手く減色できている気がします。

まとめ

Pillowでgifがボケたときはとりあえずquantize!