Python, Pillowで二枚の画像をマスク画像に従って合成
Pythonの画像処理ライブラリPillow(PIL)のImageモジュールに、二枚の画像をマスク画像に従って合成する関数composite()が用意されている。
ここでは以下の内容について説明する。
Image.composite()の引数Image.composite()のコード例- 全面を一律の割合で合成
- 図形描画でマスク画像を作成
- 既存の画像をマスク画像として使用
Pillow(PIL)のインストール、基本的な使い方などは以下の記事参照。
なお、composite()は同じサイズの2枚の画像を合成する関数。サイズの異なる画像を合成する場合はpaste()メソッドを使う。小さい画像をマスクして大きい画像の任意の場所に貼り付けることができる。
PillowではなくOpenCVやNumPyでも合成処理が可能。以下の記事を参照。
Image.composite()の引数
composite()の引数は3つ。3つともImageオブジェクトですべて同じサイズでなければならない。
image1, image2
合成する二枚の画像。
mask
マスク画像。
modeは以下の三種類のいずれか。
1: 1bit画像(二値画像)L: 8bitグレースケール画像RGBA: アルファチャンネルを持った画像
maskの値によって画像1, 2が以下のようにアルファブレンドされる。
# 1bit(0 or 1)の場合
result = mask * image1 + (1 - mask) * image2
# 8bit(0 - 255)の場合
result = mask / 255 * image1 + (1 - mask / 255 ) * image2
Image.composite()のコード例
PILからImageをインポートして画像を2枚読み込む。
ImageDrawとImageFilterは図形を描画してマスク画像を作成する際に使用する。画像ファイルを読み込んでマスク画像とする場合は省略してOK。
from PIL import Image, ImageDraw, ImageFilter
im1 = Image.open('data/src/lena.jpg')
im2 = Image.open('data/src/rocket.jpg').resize(im1.size)


今回はサイズを合わせるために2枚目の画像をresize()で縮小している。画像の一部を切り出してサイズを合わせる場合はcrop()を使えばよい。以下の記事参照。
全面を一律の割合で合成
マスク画像としてベタ画像を使うと、画像全面が一律の割合で合成される。
例として、Image.new()で階調が128のベタ画像を作成してマスク画像とする。
mask = Image.new("L", im1.size, 128)
im = Image.composite(im1, im2, mask)
# im = Image.blend(im1, im2, 0.5)

全面を一律の割合で合成する場合、blend()メソッドも使える。引数maskの代わりに0.0〜1.0の定数を引数alphaとして指定する。
図形描画でマスク画像を作成
円や四角など単純な形状でマスクして合成したい場合は、ImageDrawモジュールでの図形描画が便利。図形描画についての詳細は以下の記事参照。面倒だが多角形とかも描画できる。
黒背景に白い円を描画してマスク画像とする。
mask = Image.new("L", im1.size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((140, 50, 260, 170), fill=255)
im = Image.composite(im1, im2, mask)


さらに、ImageFilterでマスク画像をぼかすと境界を滑らかに合成できる。
mask_blur = mask.filter(ImageFilter.GaussianBlur(10))
im = Image.composite(im1, im2, mask_blur)


既存の画像をマスク画像として使用
既存の画像を読み込んでマスク画像とすることも可能。ImageDrawモジュールでは難しい複雑な形状で合成できる。
白黒の馬の形の画像(scikit-imageのサンプル: skimage.data.horse())を使ってみる。

open()で画像を読み込んだあと、convert()でモードを'L'(グレースケール)に変換し、resize()で画像のサイズを合わせる。
mask = Image.open('data/src/horse.png').convert('L').resize(im1.size)
im = Image.composite(im1, im2, mask)

白黒を逆転したい場合はImageOpsモジュールのinvert()を使えばよい。
違う例として、グラデーション画像を使って空間的に徐々に変化していくように合成。グラデーション画像はPillowのImageDrawだと難しいのでNumPyを使用して生成した。
mask = Image.open('data/src/gradation_h.jpg').convert('L').resize(im1.size)
im = Image.composite(im1, im2, mask)

