Mask R-CNNで物体切り抜きしてみた(人物切り抜き)

#概要
友人ととあるアプリを作ることになり、そのアプリの機能の中に送られてきた画像から人物を切り出す処理が必要になったため試してみました。
今回作成したものはお試しで短時間でとりあえず作ったもののため、かなり処理後の画像が荒くなってしまっているのはご了承ください。（おそらくMask R-CNNの本来の使い方ではない）

#この記事の対象
+ 画像内から物体を認識して切り出したいかた
+ とりあえず短時間で手軽に実装してみたいかた

#今回使用するもの
Mask R-CNN
COCOデータセット
Google colaboratory（Python実行環境であればなんでも）

#使用するものの説明
##Mask R-CNN
下記の引用の通り画像内から物体を検出してそれにマスク（色付け）して分割します。
>Mask R-CNN とは ICCV 2017 Best Paper に選出された手法で、物体検出やセグメンテーションを実現するための手法です。
[引用元](https://qiita.com/shtamura/items/4283c851bc3d9721ed96)

##COCOデータセット
Microsoftが提供している画像のデータセットです。
今回は自分で画像を用意するのではなくこちらのデータセットを用います。
[githubリンク](https://github.com/waleedka/coco)
>セマンティックセグメンテーション情報(いわゆるアノテーション／ラベリングよりも詳しい、画素レベルでの物体認識情報)が付加されたデータセットです。
[引用元](https://qiita.com/leetmikeal/items/7c0d23e39bf38ab8be23)

##Google colaboratory
Googleアカウントさえあれば無料で使うことができるPython実行環境です。
環境構築もいらなくweb上で使用できるため端末依存もなくそのまま使うことが出来ます。
その上GPU環境も最大12時間使用することが出来るため時間短縮も出来ます。
今回はこちらの環境を用いて行いますが、もちろん自分の環境で行うこともできるのでここは自由に選んでいただいて問題ないです。
[公式概要リンク](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)

#実装
ここからはGoogle colaboratoryでの操作になります。
##必要なものをインストール
まずは必要なものをインストールしていきます。
※cdなどのコマンドはGoogle colaboratory独自の記述法になっております。

**1 Mask R-CNN**

```shell

!git clone https://github.com/matterport/Mask_RCNN.git
%cd Mask_RCNN
!pip install -r requirements.txt
%run -i setup.py install
# 学習済みの重みの取得
!wget https://github.com/matterport/Mask_RCNN/releases/download/v2.0/mask_rcnn_coco.h5
```
**2 COCOデータセット**

```shell

!git clone https://github.com/waleedka/coco.git
%cd Mask_RCNN/coco/PythonAPI
%run -i setup.py build_ext --inplace
%run -i setup.py build_ext install
```

**3 その他必要なもの**

```shell

!pip install keras
!pip install -U -q PyDrive
!pip install -U -q mrcnn
!pip install cython 
```

##画像読み込み
今回はGoocle colaboratoryと同じディレクトリにある画像を用いたいと思うのでそちらの読み込みを行います。

**1 必要なもののインポート**

```python

from google.colab import files
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from google.colab import auth
```

**2 googleドライブの認証**

```python

auth.authenticate_user()
gauth = GoogleAuth()
gauth.credentials = GoogleCredentials.get_application_default()
drive = GoogleDrive(gauth)
```

**読み込み**
画像を読み込むにあたって対象の画像の共有IDが必要になります。
Googleドライブ上の画像で右クリックで共有リンクを取得で得られるURLのid=より後ろの値を持ってきてください。
もしわからない場合はこちらを参照してください [参考リンク](https://qiita.com/uni-3/items/201aaa2708260cc790b8)

```python

id = '共有リンクで取得した id= より後の部分'
downloaded = drive.CreateFile({'id': id})
downloaded.GetContentFile('sample_abe.jpg')
img = cv2.imread('sample_abe.jpg')
```

**表示**
試しにちゃんと読み込めているか確認のため表示してみます。
私は今回安倍さんの画像をお借りしています。[画像参照元](http://trump.teamblog.jp/archives/10138485.html)

```python

#カラー変換
color_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 

import matplotlib.pyplot as plt
fig, axes = plt.subplots(figsize=(6, 6))
axes.imshow(base_img)
axes.axis('off')
plt.show()
```
**結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406495/6feb3cbb-b0dd-2723-b314-9afbf964c603.png)

##学習モデルの用意
**ライブラリのインポート定数定義**
この後使うライブラリと定数の定義を行っていきます。
この辺りは参考にさせていただいた記事のものをそのまま使わせていただいております。

```python

import os
import sys
import random
import math
import numpy as np
import skimage.io
import matplotlib.pyplot as plt
from PIL import Image

# Root directory of the project
ROOT_DIR = os.path.abspath("/content/Mask_RCNN")

# Import Mask RCNN
sys.path.append(ROOT_DIR)  # To find local version of the library
from mrcnn import utils
import mrcnn.model as modellib
from mrcnn import visualize
# Import COCO config
sys.path.append(os.path.join(ROOT_DIR, "samples/coco/"))  # To find local version
import coco

# Directory to save logs and trained model
MODEL_DIR = os.path.join(ROOT_DIR, "logs")

# Local path to trained weights file
COCO_MODEL_PATH = os.path.join(ROOT_DIR, "mask_rcnn_coco.h5")
# Download COCO trained weights from Releases if needed
if not os.path.exists(COCO_MODEL_PATH):
    utils.download_trained_weights(COCO_MODEL_PATH)
```

**学習モデル定義**

```python

class InferenceConfig(coco.CocoConfig):
    # Set batch size to 1 since we'll be running inference on
    # one image at a time. Batch size = GPU_COUNT * IMAGES_PER_GPU
    GPU_COUNT = 1
    IMAGES_PER_GPU = 1

config = InferenceConfig()
# config.display()

# Create model object in inference mode.
model = modellib.MaskRCNN(mode="inference", model_dir=MODEL_DIR, config=config)

# Load weights trained on MS-COCO
model.load_weights(COCO_MODEL_PATH, by_name=True)

# COCO Class names
# Index of the class in the list is its ID. For example, to get ID of
# the teddy bear class, use: class_names.index('teddy bear')
class_names = ['BG', 'person', 'bicycle', 'car', 'motorcycle', 'airplane',
               'bus', 'train', 'truck', 'boat', 'traffic light',
               'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird',
               'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear',
               'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie',
               'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
               'kite', 'baseball bat', 'baseball glove', 'skateboard',
               'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup',
               'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
               'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza',
               'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed',
               'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote',
               'keyboard', 'cell phone', 'microwave', 'oven', 'toaster',
               'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors',
               'teddy bear', 'hair drier', 'toothbrush']
```

**画像から輪郭を取得する**

```python

# 画像を学習モデルに流して結果を取得する
results = model.detect([img], verbose=1)
r = results[0]

# 結果からマスク情報を抜き出す
mask = r['masks'][:, :, 0]

# 結果表示用の画像を用意
result_image = color_img.copy()

# マスクの色を固定させる
color = (1.0, 0.0, 0.0) #Red

# 画像に輪郭を描く
result_image = visualize.apply_mask(result_image, mask, color)
skimage.io.imshow(result_image)
plt.show()
```

**出力画像**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406495/4beb596a-a6b3-8300-053b-6f1a9e1a2f7c.png)

##物体の切り出し
最後に先ほど取得したマスク情報を用いて検出した物体を切り出していきます。

**マスク画像の作成**

```python

# 真っ黒な画像情報を用意
mask_base = np.zeros((img.shape[0],img.shape[1],img.shape[2]),np.uint8)
after_mask_img = img.copy()
color = (10, 10, 10) #white
# 真っ黒な画像に白色でマスクを表示させる
mask_img = visualize.apply_mask(mask_base, mask, color)
skimage.io.imshow(mask_img)
plt.show()
```

**出力**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406495/c770afda-bfb8-48e3-c45b-4ec3818ca3de.png)

**マスク画像を合成**
作成したマスク画像をオリジナルの画像と合成させます。

```python

after_img = cv2.bitwise_and(color_img, mask_img)
skimage.io.imshow(after_img)
plt.show()
```

**出力**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406495/44616928-6d2f-21ea-a62b-65fe0b0a5ad4.png)

#まとめ
最終的な出力結果はあまり綺麗とは言えないものになってしまいました。
今回は他の方の記事をとても参考にして作成したため、自分に合ったものが出来ていないのかなと感じました。
人物切り抜きであれば[remove.bg](https://www.remove.bg/)というものがかなり綺麗に切り取れておすすめですがAPIとしてがっつり使う場合はお金もかかってしまうため出来れば自分で作成したいものです。
今回はGoogle colaboratoryも機械学習もほぼ初めて触ったためこれで合っているのかなとかなり不安になりながらの作成でした。
また何かご指摘やアドバイス等ありましたら遠慮なくお願いします!!

#参考URL
[https://jyuko49.hatenablog.com/entry/2018/11/14/083226](https://jyuko49.hatenablog.com/entry/2018/11/14/083226)
[https://qiita.com/nanako_ut/items/cb28d403fcaf013b2396](https://qiita.com/nanako_ut/items/cb28d403fcaf013b2396)