Pillowとは

Pillow は、Python の画像処理ライブラリで、Python Imaging Library (PIL)の fork プロジェクトです。

PIL は開発が停滞し Python 2.7 までの対応にとどまっていますが、Pillow は Python 3.3 に対応しています。

2016-04-14 追記:Python 3.5.1 に対応しています。

環境

pip インストール可能です。

$ pip install pillow

Windows の場合、error: Unable to find vcvarsall.bat というエラーが出て、これは Visual C++ のなにがしを導入すると解消するらしいのですが、僕の環境で2008やら2012やらを入れても解消できなかったので諦めて Mac でやりました。

2013-05-07 追記:Windows7 に入りました。pip ではなくて easy_install を使ったらインストールできた。。何故だ。

2016-04-14 追記:Windows 10 64bit + Python 3.5.1 で pip install 可能なことを確認しました。

libjpeg や libpng に依存しているので入っていない場合は入れます。Mac OS X Portsで手に入る .dmg から入れるのが手っ取り早そう。Linux 系なら yum なりなんなりで。この手順は割愛します。

インストールすると pilconvert などのコマンドラインツールが併せて導入されます。

使い方

前述の通り PIL の fork のため基本的な使い方で困ったことがあったら本家PILのドキュメントを参照します。ただし、メソッド自体の解説はあるが引数についての説明が割愛されている(?)ため見ただけではよくわからないので、詳細な動きはソースを読んだほうが良さそうです。

2016-04-14 追記:既に Pillow の公式ドキュメントが充実しているようです。

Pillowの利用の流れとしては次の通りです。

  1. 既存画像なら Image.open() で開き、新規画像なら Image.new() でcanvasを作成する。
  2. ImageDraw() などで画像オブジェクトを処理する。
  3. Image.save() で保存。

実際に使ったあたりで機能を列挙すると、

  • リサイズ
  • 回転
  • 色調操作
  • フォーマット変換
  • 合成(レイヤの重ね)
  • テキストの画像出力

などがあって、各々割とお手軽に行えます。他にも色々機能がありますが今回は基本的な使い方を書いていきます。

リサイズ

さっそくリサイズから。

from PIL import Image

# 既存ファイルを readモードで読み込み
img = Image.open('original_img.jpg', 'r')

# リサイズ。サイズは幅と高さをtupleで指定
resize_img = img.resize((100, 100))

# リサイズ後の画像を保存
resize_img.save('resize_img.jpg', 'JPEG', quality=100, optimize=True)

save() については見ての通り保存するだけですが詳細は後述。

テキストの画像出力

リサイズでは Image だけ読み込みましたが今度は ImageDrawImageFont も読み込みます。

from PIL import Image, ImageDraw, ImageFont

# 画像オブジェクトを作成。サイズと背景色を指定する。背景色はRBGの各々をtupleにして与える。
text_canvas = Image.new('RGB', (80, 40), (255, 255, 255))
draw = ImageDraw.Draw(text_canvas)

# フォントの種類とサイズを指定
font = ImageFont.truetype('/Library/Fonts/ipaexp.ttf', 15)

# テキストを書き込み。引数は順に、書き込み座標(tuple)、テキスト、テキストのフォント、テキストのカラー
draw.text((10, 10), 'hogehoge', font=font, fill='#000')

# 保存
text_canvas.save('text_img.jpg', 'JPEG', quality=100, optimize=True)

draw.text() で指定する fill には16進数のカラーコードが使えます。この例だと、作成した縦40, 幅80の画像の座標(10, 10)を始点にしてテキスト「hogehoge」が書き込まれます。カラーは黒。

複数の画像を並べて1枚の画像にする

100 x 100 の画像 a.jpg と、100 x 100 の画像 b.jpg を、縦にマージして 200 x 100 の画像 c.jpg を作成したいとします。

from PIL import Image

# 既存画像を読み込み
a_jpg = Image.open('a.jpg', 'r')
b_jpg = Image.open('b.jpg', 'r')

# マージに利用する下地画像を作成する
canvas = Image.new('RGB', (100, 200), (255, 255, 255))

# pasteで、座標(0, 0)と(0, 100)に既存画像を乗せる。
canvas.paste(a_jpg, (0, 0))
canvas.paste(b_jpg, (0, 100))

# 保存
canvas.save('c.jpg', 'JPEG', quality=100, optimize=True)

既存画像の読み込みの他に、土台となる画像を一枚作成しておくのがミソ。あとはコメントの通り。(これが一番シンプルぽいのですが他にやり方あったら教えてくだしあ)

また、例ではべた書きしていますが、実際に利用する場合は、 canvas = Image.new() で指定するサイズはマージしたい画像から取得して合計した値を与える、、とかしておいたほうが便利と思います。

画像の品質について

ImageMagicのほうが加工後の画質が良いという意見も見かけましたが比較を見た/したわけではないので分かりません。が試行錯誤した結果画質をそんなに劣化させずに出来そうなポイントが2つほどありました。

save() では quality を明示する

例でも記載した save() で JPEG画像を保存する際、quality にその名の通り品質を指定できます。これを明示しないとデフォルトの quality = 75 が適応されてしまう。100にしたらJPEGのクオンタイズが無効になるからアカンよ、みたいなことが書かれているがやってみると実害は特に感じられなかったので前述の例では100にしてみました。

未指定(=75)と100では一寸見れば違いが分かる、くらいには差がでました。

縦横比維持の縮小目的ならresizeよりもthumbnailのほうが良い(かも知れない)

150pxくらいの画像を100pxに縮小したらジャギが目立つようになってこれはアカンかも。。とげんなりしていたところ、サムネイル作成用の関数を利用しても縮小ができるようだったので試してみました。

from PIL import Image

# 既存ファイルを readモードで読み込み
img = Image.open('original_img.jpg', 'r')

# resizeではなくthumbnailを利用して縮小
img.thumbnail((100, 100), Image.ANTIALIAS)

# リサイズ後の画像を保存
img.save('thumbnail_img.jpg', 'JPEG', quality=100, optimize=True)

resize() は戻り値としてリサイズ後の画像オブジェクトを返すのに対して、thumbnail() は画像オブジェクト自体を書き換えます。

実際にやった例だとこっちのほうが圧倒的に画質が良いことが分かりました。当社比で言うと120%程度(適当)。 thumbnail() の引数には、サイズとフィルタを指定できますが、フィルタに Image.ANTIALIAS を指定するとアンチエイリアスが効いて、あとは妖精さんが宜しくやってくれているようです。

optimize=True はやってもやらなくてもファイルサイズがちょっと増減するだけで見た目には影響しませんでした。元画像のサイズ、品質、加工後のサイズ、縮小するのか拡大するのか、等々によって最終的に得られる画像の品質は異なると思われ、指定するパラメータの最適値も変わってくると思います。