間違い探しを秒で解いてくれるプログラムを作ってみた

[富士通システムズウェブテクノロジー Advent Calendar 2019](https://qiita.com/advent-calendar/2019/fsweb) 16日目の投稿です。
記事の内容は全て個人の見解であり、執筆内容は執筆者自身の責任です。所属する組織は関係ありません。


# はじめに
16日目の今回は、~~サイゼリア~~ **サイゼリヤ** の間違い探しを自力でクリアしたことがないでお馴染みの私が

**二枚の画像から差分をとって相違点をぱっとみでわかりやすく表示できるプログラム**

を作る方法を、拙筆ながら書かせていただきたいと思いますまる

# おおまかなイメージ図
<img width="624" alt="スクリーンショット 2019-12-13 21.04.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529160/2ec63332-b50d-de86-b817-6ecd4a6501a3.png">
<img width="624" alt="スクリーンショット 2019-12-13 21.04.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529160/510e87a7-12a2-1229-95e1-f0cab1a5aa2f.png">

# 実装環境
* OS
    * MacOS Mojave10.14.5
* 言語
    * python 3.7.4
* ライブラリ
    * openCV 3.4.2
    * NumPy 1.17.4
    * matplotlib 3.1.1
* その他
    * jupyter notebook 6.0.1
    * anaconda navigator 

# 事前準備
（なんの保証もないですがpythonと各種ライブラリが使えれば普通に動作すると思うので流し見程度でお願いします）

* anaconda navigatorとかいうguiでわかりやすくanacondaを扱えるツールを入れます。
* 並行して（別に並行してなくてもいい）pythonのインストールとかもしとけばいいとおもいます。
* anaconda navigator使ってjupyterとか各種ライブラリとかをよしなに入れます。

（詳しい導入方法は '[anaconda navigator jupyter インストール](https://www.google.com/search?q=anaconda+navigator+jupyter+%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)'とかでぐぐれば色々出てくると思います。）

# ソース解説

ではここからが本番、ソースの内容についてです。

パーツとしては大きく分けて3つ

* 差分のマスク画像を作成
* マスク画像のマスク部に色をのせる
* マスク画像の透過度を変更し、合成する

といった感じになりマウス
### importとか変数設定とか

```python

import cv2, matplotlib
import numpy as np
from IPython.display import Image, display_png

# 元画像１
img_01 = "sample1.png"
# 元画像2
img_02 = "sample2.png"
# 出力後の画像につけたい名前を入力してください
outImg = "output.png"
```

### 差分のマスク画像を作成

```python

#　元画像１をカラーで読み込み
img_src01 = cv2.imread(img_01, 1)
 
#　元画像2をカラーで読み込み
img_src02 = cv2.imread(img_02, 1)
 
#　混合正規分布(Gaussian Mixture)を基にした前景・背景の領域分割アルゴリズムらしいです。
bg = cv2.bgsegm.createBackgroundSubtractorMOG()
 
# マスク画像を生成
mask = bg.apply(img_src01)
mask = bg.apply(img_src02)

#　マスク画像を出力
cv2.imwrite("output/machigai_mask.png", mask)

print("元画像１")
display_png(Image(img_01))
print("元画像2")
display_png(Image(img_02))
print("マスク画像")
display_png(Image("output/machigai_mask.png"))
```
ここでの出力結果は以下のようになりました。
![スクリーンショット 2019-12-13 22.21.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529160/7be1e3bf-3560-7f70-bc43-4c7d48456a9d.png)

### マスク画像のマスク部に色をのせる

```python

mask_img = cv2.imread("output/machigai_mask.png", 1)

# 画像の白い部分を赤に置き換える
red = [240, 20, 20]
white = [255, 255, 255]
mask_img[np.where((mask_img == white).all(axis=2))] = red

# BGR to RGB
mask_img = cv2.cvtColor(mask_img, cv2.COLOR_BGR2RGB)

# 結果を保存する。
cv2.imwrite("output/color_mask_img.png", mask_img)

color_mask_img = cv2.imread("output/color_mask_img.png", 1)
display_png(Image("output/color_mask_img.png"))
```
ここでの出力結果は以下のように。
![スクリーンショット 2019-12-14 0.05.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529160/601e05e1-7c9a-be61-946f-aa9df0d02226.png)

### マスク画像の透過度を変更し、合成する

最後にこれができれば完成ですね。

```python

# src1 = 元画像1、src2 = マスク画像、alpha(beta) = あわせて１になるくらいの比率で調整するのが良さそう、gamma = 0だといい感じになった
diff_image = cv2.addWeighted(src1=img_src01,alpha=0.3,src2=color_mask_img,beta=0.7,gamma=0)
cv2.imwrite(outImg, diff_image)

display_png(Image(outImg))
```

はい、完成です
![スクリーンショット 2019-12-14 0.11.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529160/e901f8fd-31cc-025e-c195-f9836f64f553.png)


# 間違い探しっぽい画像でやってみた

このために間違い探し作ってみました。
まずはご自身の力でやってみてください（間違いは6箇所あります）

![machigai_1_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529160/1867d733-c9ac-ec25-cf89-5973750dee9c.png)
　　
　　
　　
　　
　　
　　
　　
　　
　　
　　
　　
　　
　　


さあ、間違いを探し出すことはできたでしょうか。

続いては全自動~~卵割り機~~間違い探し機に解いてもらった結果をご覧入れましょう

　　
　　
　　
こちらです
![diff_image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529160/56f8f4b2-ea49-73df-1e29-c3ad8d0c888e.png)

どうでしょう。
すごい正答率ですね。

ただまぁ**先生のかつらがスイカに変わっていた**ことの検出精度はあまりよくないですね。

# 背景差分法について

そもそもこの背景差分法というものなんですが、
動画像上での移動物を検出するための方法なのでございます。

要するに

定点カメラで
**あー人が通りましたねー**
みたいなのを判定する方法なんです

この背景差分の方法、間違い探しに使うとなると大きな問題点が２つございます

* 差分を取る画像のサイズが同じでないといけない
* ちょっとでも画素がずれてると全部違う判定される

なので、汎用的に使えるものを作ろうとすると、

* ちょっとのズレは補正してくれて
* あと、傾きも補正してくれて
* 大きさも補正してくれて
* けど認識精度はある程度保証できる

そんなソフトを作らなきゃいけない気がしてきますね（）

# 最後に

* ちょっとのズレは補正してくれて
* あと、傾きも補正してくれて
* 大きさも補正してくれて
* けど認識精度はある程度保証できる

そんなソフトを、次は頑張って作ってみようかな（白目）



# 参考

- [背景差分法](https://qiita.com/goodboy_max/items/71a3b804d14f961c0d91)
- [openCV基礎知識](https://postd.cc/image-processing-101/)
- [画像合成・透過](https://code-graffiti.com/blending-images-with-opencv-in-python/)
- [画像塗りつぶし](https://www.pynote.info/entry/opencv-flood-fill)