PILチュートリアル
Imageクラスを使う
PILにおいて最も重要なクラスはImageクラスです。これは同じ名前のモジュールの中で定義されています。あなたは例えばファイルから画像を読み込んだり、他の画像を処理したり、スクラッチから画像を生成したりすることによってこのクラスのインスタンスを生成することができます。
ファイルを画像から読み込むためには、Imageモジュールのopen関数を使います。
>>> import Image
>>> im = Image.open("lena.ppm")
openに成功すると、open関数はImageオブジェクトを返します。そうすればあなたはファイルの中身を調べるためにアトリビュートを使うことができるようになります。
>>> print im.format, im.size, im.mode
PPM (512, 512) RGB
formatアトリビュートは画像のソースタイプを特定します。ただし画像がファイルから読み込まれていない場合には、この属性にはNoneがセットされます。sizeアトリビュートは幅と高さ(単位はpixel)を含んだタプルです。またmodeアトリビュートは画像の帯域名を特定します。グレイスケール画像にはL、トゥルーカラー画像にはRGB、プリプレス画像にはCMYKが割り当てられます。
ファイルを開くことができない場合には、IOError例外が引き起こされます。
あなたがImageクラスのインスタンスを手にしたならば、画像を処理したり操作したりするためにこのクラスで定義されたいくつかのメソッドを使うことができます。例えば先ほど読み込んだ画像を表示してみましょう。
>>> im.show()
(標準のshowメソッドは、画像を一時ファイルとして保存しそれをxvユーティリティによって表示するため、あまり効率がよくありません。またxvをインストールしていない場合は動きません。しかしxvがあって動く場合には手軽にデバッグやテストを行うことができます。)
以下のセクションではこのライブラリによって提供される関数を概観していきます。
画像の読み込みと保存
PILはさまざまな画像ファイルフォーマットをサポートしています。ファイルを読むにはImageモジュールのopen関数を使います。ファイルのフォーマットを知っている必要はありません。このライブラリはファイルの内容から自動的にそのフォーマットを判別するからです。
ファイルを保存するためにはImageクラスのsaveメソッドを使います。ファイルを保存するときは名前が大切になります。フォーマットを指定しない限り、ライブラリはどのフォーマットで保存するかを決定するためにファイルの拡張子を用いるからです。
ファイルをJPEGに変換する
import os, sys
import Image
for infile in sys.argv[1:]:
f, e = os.path.splitext(infile)
outfile = f + ".jpg"
if infile != outfile:
try:
Image.open(infile).save(outfile)
except IOError:
print "cannot convert", infile
2番目の引数にはどのファイルフォーマットで保存するかを指定します。非標準の拡張子を使う場合には常にそのフォーマットを指定してやる必要があります。
JPEGのサムネイルを生成する
import os, sys
import Image
size = 128, 128
for infile in sys.argv[1:]:
outfile = os.path.splitext(infile)[0] + ".thumbnail"
if infile != outfile:
try:
im = Image.open(infile)
im.thumbnail(size)
im.save(outfile, "JPEG")
except IOError:
print "cannot create thumbnail for", infile
このライブラリは(本当にしなければならない場合を除いて)ラスタデータをデコードしたり読み込んだりしないということに注意してください。ファイルをopenするとファイルフォーマットを決定するためにファイルのヘッダが読み込まれ、このファイルをデコードするために必要なモードやサイズ、その他のプロパティが取り出されますが、残りの部分はすぐには処理されません。
これは画像ファイルをopenするという動作が、ファイルのサイズや圧縮タイプに関係なく素早く行われる、ということを意味しています。複数の画像ファイルを素早く特定するスクリプトは以下のようになります。
Identify Image Files
import sys
import Image
for infile in sys.argv[1:]:
try:
im = Image.open(infile)
print infile, im.format, "%dx%d" % im.size, im.mode
except IOError:
pass
画像をカット、ペースト、マージする
Imageクラスは画像内のある領域を操作するためのメソッドを持っています。画像から矩形を取り出すにはcropメソッドを使います。
画像から矩形をコピーする
box = (100, 100, 400, 400)
region = im.crop(box)
領域は4つの要素を持つタプルから成ります。これらは(左, 上, 右, 下)に対応しています。PILは(0, 0)を左上の隅に対応させる座標を使っています。この位置はピクセル座標になることにも注意してください。すなわち上記の例の領域は300*300ピクセルになります。
この領域は特定のやり方で処理され、ペーストされます。
矩形を処理し、ペーストする
region = region.transpose(Image.ROTATE_180)
im.paste(region, box)
領域をペーストするときには、領域のサイズはペーストする領域のサイズと一致していなければ成りません。またこの領域は画像の外まで拡張することはできません。しかしながら元画像のモードとその領域のモードは一致している必要はありません。モードが異なっている場合には、領域はペーストされる前に自動的に変換されます(詳しくは口述する「色の変換」のセクションを参照してください)。
もう少し例を見てみましょう。
画像を回転する
def roll(image, delta):
"Roll an image sideways"
xsize, ysize = image.size
delta = delta % xsize
if delta == 0: return image
part1 = image.crop((0, 0, delta, ysize))
part2 = image.crop((delta, 0, xsize, ysize))
image.paste(part2, (0, 0, xsize-delta, ysize))
image.paste(part1, (xsize-delta, 0, xsize, ysize))
return image
より高度な操作のために、pasteメソッドは透過マスクをオプション引数として取ることもできます。このマスクでは255という値だと完全に不透明になります(すなわちペーストされた画像がそのままペーストされる)。値が0のときはペーストされた画像は完全に透明になります。この間の値ではその値に応じた透過レベルになります。
PILはRGBのような複数帯域の個々の帯域も扱うことができます。splitメソッドは、元の複数帯域の画像の中にある1つの帯域を持つような新しい画像セットを生成します。またmerge関数はモードと、画像セットが入ったタプルを引数にとって新しい一つの画像へマージします。以下のサンプルはRGB画像の3つの帯域を入れ替えます。
帯域を分割し、マージする
r, g, b = im.split()
im = Image.merge("RGB", (b, g, r))
幾何学変換
Imageクラスは画像のサイズを変更したり回転したりするメソッドを持っています。前者は引数として新しいサイズのタプルを取り、後者は引数として反時計回りの角度を取ります。
シンプルな幾何学変換
out = im.resize((128, 128))
out = im.rotate(45) # degrees counter-clockwise
画像を90度回転させるためには、rotetaメソッドやtransposeメソッドを利用できます。後者は画像を水平方向や垂直方向へ反転させることにも使えます。
画像の置換
out = im.transpose(Image.FLIP_LEFT_RIGHT)
out = im.transpose(Image.FLIP_TOP_BOTTOM)
out = im.transpose(Image.ROTATE_90)
out = im.transpose(Image.ROTATE_180)
out = im.transpose(Image.ROTATE_270)
transpose(ROTATE)とrotateはパフォーマンスや結果がすべて同じです。
より一般的な形の画像置換はtransformメソッドによって行うことができます。詳しくはリファレンスセクションを参照してください。
色の置換
PILのconvert関数を用いれば異なるモードの画像を変換することができます。
モードを変換する
im = Image.open("lena.ppm").convert("L")
PILはサポートするモードと「L」モード、「RGB」モードの間での相互変換をサポートしています。他のモード間で変換する場合には一度画像を仲介モード(通常RGB)にしなければならないでしょう。
画像効果
PILには画像に効果を与えるために用いられるたくさんのメソッドやモジュールがあります。
フィルタ
ImageFilterモジュールはfilterメソッドで使うことができるたくさんの効果フィルタを持っています。
フィルタを適用する
import ImageFilter
out = im.filter(ImageFilter.DETAIL)
点の操作
pointメソッドは画像のピクセル値を変換する(コントラストを操作する)ために用いられます。たいていの場合、関数オブジェクトがこのメソッドへ渡され、それぞれのピクセルはこの関数に従って処理されます。
点を変換する
# multiply each pixel by 1.2
out = im.point(lambda i: i * 1.2)
上記のテクニックを使うと、画像に対して簡単に特定の関数表現を適用することが可能です。また画像を選択的に修正するために各点をまとめてペーストすることができます。
個々の帯域を処理する
# split the image into individual bands
source = im.split()
R, G, B = 0, 1, 2
# select regions where red is less than 100
mask = source[R].point(lambda i: i < 100 and 255)
# process the green band
out = source[G].point(lambda i: i * 0.7)
# paste the processed band back, but only where red was < 100
source[G].paste(out, None, mask)
# build a new multiband image
im = Image.merge(im.mode, source)
マスクを生成するのに使われているシンタクスに注意してみてください。
imout = im.point(lambda i: expression and 255)
Pythonは結果を決定するためにexpressionの一部だけを評価し、expressionの結果と255が評価されて返されます。すなわちexpressionがfalse(0)のときはPythonは2番目のオペランドを見ません。falseでなければ255を返します。
効果
より高度な画像効果を施すためにはImageEnhanceモジュール内のクラスを用います。画像からenhancementオブジェクトが生成されると、異なる設定ですばやくいろいろな効果を試すことができます。
下記の要領でコントラスト、明度、カラーバランスやシャープ効果を調整することができます。
画像効果を施す
import ImageEnhance
enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")
画像シークエンス
PILはいわゆるアニメーションフォーマットである画像シークエンスを基本的にサポートしています。サポートされる画像シークエンスは FLI/FLCとGIF、その他いくつかの実験的なフォーマットです。TIFFファイルも複数フレームを扱えます。
シークエンスファイルを開くとPILは自動的にシークエンスの最初のフレームを読み込みます。異なるフレーム間の移動にはseekメソッドやtellメソッドを使います。
シークエンスを読み込む
import Image
im = Image.open("animation.gif")
im.seek(1) # skip to the second frame
try:
while 1:
im.seek(im.tell()+1)
# do something to im
except EOFError:
pass # end of sequence
この例からわかるように、シークエンスの最後ではEOFErrorを受け取ります。
またライブラリの現在のバージョンのドライバでは上記のように次のフレームしか探すことができないことに注意してください。前に戻るためにはファイルを再度開かなければなりません。
以下のようにイテレータクラスを作ればシークエンスに対してforループを使うことができるでしょう。
シークエンスイテレータクラス
class ImageSequence:
def __init__(self, im):
self.im = im
def __getitem__(self, ix):
try:
if ix:
self.im.seek(ix)
return self.im
except EOFError:
raise IndexError # end of sequence
for frame in ImageSequence(im):
# ...do something to frame...
Postscriptの出力
PILは画像やテキスト、グラフィックをPostscriptプリンタへ出力するための関数を含んでいます。簡単な例を以下に挙げます。
Postscriptの描画
import Image
import PSDraw
im = Image.open("lena.ppm")
title = "lena"
box = (1*72, 2*72, 7*72, 10*72) # in points
ps = PSDraw.PSDraw() # default is sys.stdout
ps.begin_document(title)
# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)
# draw centered title
ps.setfont("HelveticaNarrow-Bold", 36)
w, h, b = ps.textsize(title)
ps.text((4*72-w/2, 1*72-h), title)
ps.end_document()
画像の読み込みに関するあれこれ
前述したように、Imageモジュールのopen関数は画像ファイルを開くのに使われます。ほとんどの場合はファイル名を引数に渡すでしょう。
im = Image.open("lena.ppm")うまくいけば結果としてImageオブジェクトが得られます。そうでなければIOErrorが引き起こされます。
しかしファイル名の代わりにファイル様のオブジェクトを使うこともできます。このオブジェクトはread、seek、tellメソッドを実装し、バイナリモードでopenされます。
openしたファイルから読み込む
fp = open("lena.ppm", "rb")
im = Image.open(fp)文字列データから画像を読み込む場合には、StringIOクラスを使います。
文字列から読み込む
import StringIO
im = Image.open(StringIO.StringIO(buffer))
ライブラリは画像のヘッダを読み込む前にファイルを一度巻き戻す(seek(0)する)ことに注意してください。またseekメソッドは画像データが読み込まれる際にloadメソッドによっても使用されます。画像ファイルがtarファイルのような大きなファイルに組み込まれているならContainerIOかTarIOモジュールを使うことができます。
tarファイルから読み込む
import TarIO
fp = TarIO.TarIO("Imaging.tar", "Imaging/test/lena.ppm")
im = Image.open(fp)
デコーダをコントロールする
いくつかのデコーダを使えばファイルから読み込んだ画像を操作することができます。これはデコードをスピードアップするために、サムネイルを生成するとき(品質よりスピードが重要な場合)や、モノクロレーザプリンタで印刷するとき(グレースケール画像だけが求められる場合)によく使われます。
draftメソッドは「openされているけれどまだ画像が読み込まれていないようなオブジェクト」に対してモードやサイズを与えて操作します。これはデコーダを再調整することによって行われています。
ドラフトモードで読み込む
im = Image.open(file)
print "original =", im.mode, im.size
im.draft("L", (100, 100))
print "draft =", im.mode, im.size
これは以下のような結果になります。
original = RGB (512, 512)
draft = L (128, 128)
結果として出力される画像は与えられたモードとサイズに完全に一致しているわけではないということに注意してください。画像が与えられたサイズよりも大きくないということを確認するためにはthumbnailメソッドを代わりに使ってください。

