【Python】 Pillowを使用して画像を円形にマスク処理

# はじめに
Pythonの**Pillow**を使用して、画像の中心部分以外を円形にマスク処理するコードを紹介します。

**マスク処理後の画像例**
<img width="400" alt="maksed" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585927/fb6de906-28d0-20d5-ada6-dc3999dbb587.jpeg">


# 使用する画像
この馬の顔部分以外をマスク処理で円形に黒く塗りつぶします。[馬の画像]
(https://pixabay.com/ja/photos/%E9%A6%AC-%E5%8B%95%E7%89%A9-%E4%B9%97%E3%82%8B-%E3%83%AC%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%9B%E3%83%95-4847088/)
<img width="400" alt="Horse" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585927/5a33f690-7acf-062f-04b2-a8952be55b27.jpeg">

マスクに使用する画像はこの画像です。[黒のマスク画像](https://illustimage.com/?dl=1701)
<img width="400" alt="Horse" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585927/33f589d1-ea38-3971-2348-20c3e92073ef.jpeg">

# マスク処理の流れ
1. マスク部分（馬の顔以外）が白色で、マスク処理を適用しない部分（馬の顔部分）を黒色にしたパネルを生成する。画像の枠が白色で見えませんが、馬の画像と同じ画像サイズになっています。
<img width="200" alt="panel" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585927/456692fa-8710-9480-c295-278d8822e718.jpeg">

2. 馬の画像に、パネルでくり抜いたマスク画像を貼り付ける。

# コード
プログラムの説明を簡単に行っていきます。

**全体のプログラムは最後に記載しています。**

## マスク画像の読み込み
馬の画像の大きさに合わせて、マスク画像も読み込みます。

```Python:画像を読み込む
from PIL import Image,ImageDraw

# 馬の画像を読み込む
img = Image.open("horse.jpg")

# 馬の画像に合わせてマスク画像を読み込む
mask_width = img.size[0]
mask_height = img.size[1]
black_mask=Image.open("black.jpg").resize((mask_width,mask_height))
```

## パネル作成

黒のマスク画像と同じサイズのパネル（circle_mask）作成し、そこに円を描画します。
パネルの作成時には、"L"：8bitグレースケール画像を使用します。

> マスク画像として使用できるのは、貼り付け画像と同じサイズでmodeが以下の三種類の場合。
>1：1bit画像（二値画像）
L：8bitグレースケール画像
RGBA：アルファチャンネルを持った画像

> マスク画像が8bitグレースケール（mode='L'）の場合、0（黒）ではベース画像が100%、255（白）では貼り付け画像が100%、中間値では2つの画像が値に応じてブレンドされる。


```Python:パネル作成

# パネルに円を描画
circle_mask = Image.new("L", black_mask.size, 255)
draw = ImageDraw.Draw(circle_mask)
```

## 円を描画
円はellipseメソッドを使用して描画しますが、バウンディングボックスの座標（左下と右下）を渡すことに注意してください。

```Python:円の描画
# 円のバウンディングボックスの座標
start_x = 600
start_y = 200
width = 900
height = 900 # 正円にするならwidthの同値
end_x = start_x + width
end_y = start_y + height

# 描画
draw.ellipse(((start_x, start_y),(end_x, end_y)), fill=0)
```

## 画像をくり抜いてマスク処理

```Python:マスク処理
# 円パネル(circle_mask)をマスクとし、黒のマスク画像(black_mask)を馬画像にペースト
img.paste(black_mask, (0, 0), circle_mask)

# 保存
img.save("horse_masked.jpg")
```
<img width="400" alt="maksed" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585927/fb6de906-28d0-20d5-ada6-dc3999dbb587.jpeg">

# 全体コード
```Python
from PIL import Image,ImageDraw

# 馬の画像を読み込む
img = Image.open("horse.jpg")

# 馬の画像に合わせてマスク画像を読み込む
mask_width = img.size[0]
mask_height = img.size[1]
black_mask=Image.open("black.jpg").resize((mask_width,mask_height))

# パネルに円を描画
circle_mask = Image.new("L", black_mask.size, 255)
draw = ImageDraw.Draw(circle_mask)

# 円のバウンディングボックスの座標
start_x = 600
start_y = 200
width = 900
height = 900 # 正円にするならwidthの同値
end_x = start_x + width
end_y = start_y + height

# 描画
draw.ellipse(((start_x, start_y),(end_x, end_y)), fill=0)

# 円パネル(circle_mask)をマスクとし、黒のマスク画像(black_mask)を馬画像にペースト
img.paste(black_mask, (0, 0), circle_mask)

# 保存
img.save("horse_masked.jpg")

```

# 参考URL
詳しい引数の説明が紹介されています。
[note.nkmk.me](https://note.nkmk.me/python-pillow-paste/)