Pythonで画像の余白を削除する(Pillow編)

PythonのPillowモジュールを使って、画像の余白部分を削除(クロップ、トリミング)します。

背景差分法と2値化の2種類について述べます。画像によって適切な方法を選択してください。

目次

  1. サンプル画像
  2. Pillowで背景差分法によるクロップを行う
  3. Pillowで2値化によるクロップを行う
  4. 主なメソッド

サンプル画像

本稿でクロップする画像はこちらです。

元画像

プロ生ちゃんこと暮井慧さんです。

Pillowで背景差分法によるクロップを行う

背景差分法というのは、クロップ(トリミング)したい画像と背景画像の差分からクロップする範囲を求める方法です。

大凡のな手順は下記です。

  1. 背景部分の色を求める。

  2. 背景色で元画像と同じ大きさの画像を作成する。

  3. 元画像と背景色画像の差を計算する。(差分画像の生成)

  4. 差分画像のゼロでない領域のボックスを計算する。

  5. 求めたボックスで元画像をクロップする。

これらの計算は、Pillowモジュールの機能でできてしまいます。

背景差分法のコード例

from PIL import Image, ImageChops

img = Image.open('test.jpg')

# 背景色の抽出
bg_img = Image.new('RGB', img.size, img.getpixel((0,0)))

# 差分画像の生成
diff_img = ImageChops.difference(img, bg_img)

# クロップ範囲の計算
crop_range = diff_img.convert('RGB').getbbox()

# クロップの実行と出力
crop_img = img.crop(crop_range)
crop_img.save('out.jpg')

背景差分法での実施例

元画像は347x347のJpegファイルで、(0,0)画素の色は白(255,255,255)です。ですから、背景画像は347x347の白色の画像になります。

背景画像と元画像の差分は、このような画像になります。

差分画像

背景(余白)部分が黒になっています。

get_bbox()メソッドで差分画像を計算すると(32, 0, 336, 347)という結果が得られます。画素が0(黒色)でない範囲が計算されるわけです。

この範囲を使って元画像をクロップすると、下記の画像が得られます。

背景差分法でクロップした例

う~ん、まだ少し余白が残ってますね。

背景差分法は背景色が白色でなくても同じ手順が使えることがメリットです。

ただし背景色が1色しか指定できないので、見た目は白色だけれども数値的には微妙に異なる画素があったりすると背景とは見なされなくなるのかもしれません。

Pillowで2値化によるクロップを行う

背景が白に近い色であるならば、2値化してクロップの範囲を決めてしまいましょう。

大凡の手順はこうです。

  1. 元画像を2値化する。

  2. 2値化した画像を、白黒反転する。

  3. 白黒反転画像の、画素がゼロでない領域のボックスを計算する。

  4. 計算したボックスで元画像をクロップする。

2値化の場合のコード例

from PIL import Image, ImageChops

img = Image.open('test.jpg')

# 2値化画像の生成
bw_img = img.convert(mode='1', dither=None)

# 白黒反転
bw_inv_img = ImageChops.invert(bw_img)

# クロップ範囲の計算とクロップの実行
crop_range = bw_inv_img.convert('RGB').getbbox()
crop_img = img.crop(crop_range)

crop_img.save('out.jpg')

2値化での実施例

元画像をconvert()メソッドで2値化すると、このようになります。

2値化の結果

この画像を白黒反転すると、このようになります。

白黒反転の結果

余白部分が黒(0)になっていますね。

この反転画像に対してgetbbox()メソッドで範囲を求めると(40, 9, 323, 347)という結果が得られました。

この範囲で元画像をクロップすると、このような画像が得られます。

2値化でのクロップ結果

書類をスキャンした画像など、余白が単一の白色ではなくややばらつきのある白色の場合は、こちらの方法が良さそうです。

主なメソッド

ImageChops.difference

from PIL import ImageChops

out = ImageChops.difference(image1, image2)

変数

内容

image1

Image

比較する画像1

image2

Image

比較する画像2

out

Image

比較の結果画像

2つの画像のピクセルごとの差分画像を生成します。

ImageChops.invert

from PIL import ImageChops

out = ImageChops.invert(image)

変数

内容

image

Image

元の画像

out

Image

色反転した画像

画像のピクセルの色(チャンネル)を反転します。

Image.getbbox

from PIL import Image

ret = img.getbbox()

変数

内容

img

Image

元の画像

ret

tuple

ボックスを表すタプル(左、上、右、下)

画像の画素が0でない領域(ボックス)を計算します。戻り値は、ボックスの左上の座標と右下の座標を示すタプルです。タプルの要素の型はintです。

Image.crop

from PIL import Image

ret = img.crop(box)

変数

内容

img

Image

元の画像

box

tuple

ボックスを表すタプル(左、上、右、下)

ret

Image

クロップされた画像

元画像の指定された領域(ボックス)を返します。つまり、指定したボックスでクロップした画像を返します。

公開日

広告