柴犬がビーチに！画像合成のマスタークラス

## はじめに

この記事では、柴犬の写真とビーチの写真を組み合わせて、柴犬がビーチにいるかのような新しい画像を作成するプロセスをご紹介します。この合成プロセスには、マスク処理を利用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/2f16c578-2804-6ac3-cf3e-408bf929e8ae.png)

## 合成プロセス

### 1. マスク画像の作成

まず、柴犬の写真からマスク画像を生成します。このマスクは、柴犬の形を表すもので、他の画像との合成時に重要な役割を果たします。マスクの作成方法の詳細は、以下の参考記事をご覧ください。

https://qiita.com/kotai2003/items/2cddf1b3e17c728439b0


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/5af36396-f8cb-e538-f9ad-8784906f09ce.png)


### 2. 反転マスク画像の作成
次に、作成したマスクを白黒反転して、反転マスク画像を作成します。これにより、ビーチの画像から柴犬の形を除外する準備が整います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/4fcf856b-3079-ba12-4d32-82cc4b0dfe35.png)


### 3. 切り出し画像１の作成
柴犬のマスク画像と元の柴犬の画像をAND演算で組み合わせることで、背景を除去した切り出し画像１を生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/20d79d66-3cc1-9f95-6415-d086707979f0.png)


### 4. 切り出し画像2の作成
ビーチの写真と反転マスク画像をAND演算で組み合わせ、柴犬の形が除外されたビーチの背景だけの切り出し画像2を生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/38c8d88a-8f86-537e-9630-6116d13001ce.png)


### 5. 合成画像の作成
最後に、切り出し画像1（柴犬）と切り出し画像2（ビーチ背景）をOR演算で組み合わせ、最終的な合成画像を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/cc860123-9267-5621-13f6-b213759ba19e.png)

## 結果
記の手順を経て、柴犬がビーチにいるように見える合成画像を作成することができました。このプロセスは少し手間がかかりますが、マスク処理を利用することで精度の高い画像合成が可能です。

![final_gousei.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/358bb479-b3c9-3f39-6fe1-14bf45cc5ecd.png)

## コード

```python:
import cv2
from rembg import remove
from PIL import Image
import numpy as np

def load_image(image_path):
    try:
        return np.array(Image.open(image_path))
    except IOError:
        print(f"Error: Cannot open {image_path}")
        return None

def save_image(image, image_path):
    Image.fromarray(image).save(image_path)

def remove_background(input_image_path, output_image_path):
    input_image = load_image(input_image_path)
    if input_image is None:
        return

    output_image = remove(Image.fromarray(input_image))
    output_image.save(output_image_path)

def apply_mask_to_background(masked_image_path):
    rgba_image = cv2.imread(masked_image_path, cv2.IMREAD_UNCHANGED)
    if rgba_image is None:
        print(f"Error: Cannot open {masked_image_path}")
        return None, None

    alpha_channel = rgba_image[:, :, 3]
    background = np.ones((rgba_image.shape[0], rgba_image.shape[1], 1), dtype=np.uint8) * 255
    background_mask = cv2.bitwise_and(background, background, mask=alpha_channel)
    inverted_mask = cv2.bitwise_not(background_mask)

    return background_mask, inverted_mask

def create_masked_images(object_image, new_bg_image, mask, inverted_mask):
    mask_color = cv2.merge((mask, mask, mask))
    inverted_mask_color = cv2.merge((inverted_mask, inverted_mask, inverted_mask))
    kiridashi_1 = cv2.bitwise_and(object_image, mask_color)
    kiridashi_2 = cv2.bitwise_and(new_bg_image, inverted_mask_color)

    return kiridashi_1, kiridashi_2

def combine_images(image1, image2):
    return cv2.bitwise_or(image1, image2)

if __name__ == "__main__":
    input_path_object = './images/shiba-inu.jpg'
    input_path_new_bg = "./images/beach.jpg"
    output_path_rembg = './images/shiba-inu-remove.png'
    output_path_gousei = "./images/final_gousei.png"

    img_object = load_image(input_path_object)
    img_new_bg = load_image(input_path_new_bg)
    if img_object is None or img_new_bg is None:
        exit()

    remove_background(input_path_object, output_path_rembg)

    mask_image, inverted_mask_image = apply_mask_to_background(output_path_rembg)
    if mask_image is None or inverted_mask_image is None:
        exit()

    kiridashi_1, kiridashi_2 = create_masked_images(img_object, img_new_bg, mask_image, inverted_mask_image)

    save_image(kiridashi_1, "./images/kiridashi_01.jpg")
    save_image(kiridashi_2, "./images/kiridashi_02.jpg")

    img_dst = combine_images(kiridashi_1, kiridashi_2)
    save_image(img_dst, output_path_gousei)

```

## 参考資料

https://qiita.com/kotai2003/items/2cddf1b3e17c728439b0