読者です 読者をやめる 読者になる 読者になる

PILで高画質なjpegを出力する

PIL(Python Imaging Library)を利用して以下のようなコードを書くと、モアレが出てしまって画質の粗悪なjpgが出力されてしまいます。

import Image

img = Image.open('input.png')
img.save('output.jpg')

そこでjpgの画質をコントロールできないものかと /site-packages/PIL/JpegImagePlugin.py を見ていると、こんなコードになっていました。

def _save(im, fp, filename):

    try:
        rawmode = RAWMODE[im.mode]
    except KeyError:
        raise IOError("cannot write mode %s as JPEG" % im.mode)

    info = im.encoderinfo

    dpi = info.get("dpi", (0, 0))

    # get keyword arguments
    im.encoderconfig = (
        info.get("quality", 0),
        # "progressive" is the official name, but older documentation
        # says "progression"
        # FIXME: issue a warning if the wrong form is used (post-1.1.5)
        info.has_key("progressive") or info.has_key("progression"),
        info.get("smooth", 0),
        info.has_key("optimize"),
        info.get("streamtype", 0),
        dpi[0], dpi[1]
        )

    ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)])

こいつがどうも怪しいですね。

        info.get("quality", 0),

そこで、あとはqualityに指定できる整数の範囲を調べようと思い、こんなコードを書きました。おそらく0〜100だろうとは思っていたのですが、最高画質が0xffだったりする可能性もありそうだったので念のため。

import os
import Image

img = Image.open('input.png')

quality = 1
while True:
    f1 = 'output-%03d.jpg' % quality
    f2 = 'output-%03d.jpg' % (quality - 1)
    
    img.save(f1, quality=quality)
    img.save(f2, quality=quality-1)
    
    f1_data = open(f1, 'rb').read()
    f2_data = open(f2, 'rb').read()
    os.remove(f1)
    os.remove(f2)
    
    if f1_data == f2_data:
        print '%d and %d are completely same.' % (quality, quality - 1)
        break
    
    quality += 1

すると出力は

101 and 100 are completely same.

となり、qualityには 0〜100 の値を指定できそうだとわかりました。(手抜きの検証ではありますが^^;)


ちなみに上記コードで一時的に生成したjpgファイルに対して

f0 = 'output-000.jpg'
f0_data = open(f0, 'rb').read()
for i in xrange(1, 101):
    f1 = 'output-%03d.jpg' % i
    f1_data = open(f1, 'rb').read()
    
    if f0_data == f1_data:
        print '%d and %d are completely same.' % (0, i)

というコードを実行すると出力結果は

0 and 75 are completely same.

となり、qualityのデフォルト値が75であることがわかります。