PythonのPillowのImage.cropで画像の矩形領域を切り取る【トリミング】
- 作成日: 2020-10-07
- 更新日: 2023-12-24
- カテゴリ: Python
PillowのImage.cropの使い方
Pythonの画像処理ライブラリであるPillownには画像を管理するImage
モジュールがあります。
このImage
モジュールは、画像の指定の矩形(四角形)領域を切り抜き、新しい画像を生成する関数crop()
を持っています。
この関数を使えば、プログラミングで画像の指定領域の切り抜き(トリミング, クロッピング)を自動化することが出来ます。
大きすぎる画像をImage.crop()
で切り抜き、一部だけを画像として保存したい時や、画像の比率を指定の比率に合わせたピクセルにしたいときなどに使えます。
また、集合写真から顔認識処理で顔を認識し、特定の顔だけを切り抜きたいなどにも使えます。
トリミング?それともクロッピング?
画像の一部を切り出すことを日本では「トリミング」と表現します。
しかしこれは英語圏では違うらしくて、英語では「クロッピング」と言うらしいです。
そのためPillowのメソッドもcrop
になっているわけですね。
この記事では「トリミング」で解説させていただきます。
🐭 < 色々な呼び方があるのね
Image.crop()の構造
Image.crop()
は↓のような構造を持っています。
Image.crop(box=None)
Image.crop()
は1つの引数を取ります。
また、返り値を1つ返します。
box(第1引数)
第1引数のbox
にはトリミングする領域をタプルで指定します。
このタプルは4要素のタプルで、先頭から「左、上、右、下」の領域を表します。
これはつまり(left, upper, right, lower)
ということになります。
タプルの要素の値はピクセルです。
たとえば左上0px
, 右下100px
の領域を指定したい時は
(0, 0, 100, 100)
のタプルを指定します。
返り値
返り値はトリミングされた画像(Image
オブジェクト)です。
使用する画像
今回使用する画像はこちらです。
RGB
の横幅600px x 高さ600px
のPNG
画像です。
🦝 < SFチック
画像をトリミングする
先ほどの画像をImage.crop()
を使ってトリミングしてみます。
ためしに左上0px
, 右下100px
の領域を指定してトリミングします。
Image.open()
に画像のパスを指定して画像を開き変数(im
)に保存します。
そしてそのim
からImage.crop()
を呼び出し、先ほどの座標を引数に指定します。
Image.crop()
の返り値をcropped
変数に保存し、Image.save()
に保存する画像のパスを指定して画像を保存します。
from PIL import Image
im = Image.open('img/a.png') # 画像を開く
cropped = im.crop((0, 0, 100, 100)) # 左上0px, 右下100pxでトリミング
cropped.save('dst/out0.png') # トリミングした画像を保存
保存されたdst/out0.png
を開くと↓のように表示されます。
画像の中心を基点にトリミングする
先ほどの画像の中心座標を基点に横幅200px
, 高さ200px
でトリミングしたいとします。
そういう場合は↓のようにします。
from PIL import Image
l = 200 # トリミングしたい大きさ
l2 = l // 2 # トリミングしたい大きさの半分
im = Image.open('img/a.png') # 画像を開く
w, h = im.size # 画像の横幅と高さ
w2 = w // 2 # 横幅の半分
h2 = h // 2 # 高さの半分
cropped = im.crop((w2 - l2, h2 - l2, w2 + l2, h2 + l2)) # 左上0px, 右下100pxでトリミング
cropped.save('dst/out1.png') # トリミングした画像を保存
出力結果。
範囲外を指定したときはどうなるか?
Image.crop()
の引数に画像の範囲外の領域を指定したらどうなるのでしょうか?
先ほどトリミングしたこちらの画像を使って実験してみたいと思います。
この画像は横幅200px
で高さが200px
です。
つまり(0, 0, 400, 400)
のような領域は画像からはみ出ることになります。
では↓のコードで実行してみましょう。
from PIL import Image
im = Image.open('img/b.png') # 画像を開く
cropped = im.crop((0, 0, 400, 400)) # 左上0px, 右下400pxでトリミング
cropped.save('dst/out2.png') # トリミングした画像を保存
結果は↓のような画像になりました。
エラーにはなりませんでした。
範囲外の領域は0
埋めされて作成されるようです。
ソースコードの解析
Image.crop()
は最終的にC言語で書かれたCrop.c
モジュール内のImagingCrop()
に辿り着きます
コードを見るとトリミングの処理では内部的にImagingPaste()
を使って実現しているのがわかります。
やってることは足し算と引き算、単純な論理演算のみなので、比較的に単純な処理に見えます。
問題
Q1: Image.crop()
の第1引数として適当なものを答えよ
- (0, 0, 100, 100)
- (100, 20, 150, 100)
- (100, 200)
Q2: Image.crop()
の返り値として適当なものを答えよ
- list
- dict
- Image
Q3: Image.crop()
で画像の範囲外を指定したときの挙動を答えよ
- エラーになる
- 警告が出力される
- 範囲外の領域が0埋めされる
問題の正解はこちら↓
Q1: 1, 2
Q2: 3
Q3: 3