ユーニックス総合研究所

  • home
  • archives
  • python-pillow-image-crop

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 高さ600pxPNG画像です。

🦝 < 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引数として適当なものを答えよ

  1. (0, 0, 100, 100)
  2. (100, 20, 150, 100)
  3. (100, 200)

Q2: Image.crop()の返り値として適当なものを答えよ

  1. list
  2. dict
  3. Image

Q3: Image.crop()で画像の範囲外を指定したときの挙動を答えよ

  1. エラーになる
  2. 警告が出力される
  3. 範囲外の領域が0埋めされる

問題の正解はこちら↓

Q1: 1, 2
Q2: 3
Q3: 3