Python+OpenCVでサイゼの間違い探しをしてみた話

#はじめに
はじめまして、[ぱそきいろ](https://twitter.com/takacpu55)と申します。
初めて技術ブログを書くので生暖かい目で見守ってください。
「いいね」いただけるとモチベーションに繋がるので、良いと思っていただけた方はお願いします。

こちらでブログもやってますので、よかったらご覧になってください。
[ぱそきいろのIT日記](https://www.takacpu55.xyz/entry/2024/01/01/000000)

#間違い探しをしてみた
みなさん、サイゼリヤの間違い探しが難し過ぎると度々話題になるのは知っていますか？
子供が解くレベルを軽く超えてると思いますよね。
これをプログラミングを使って解こうと思ったのがきっかけです。
（ただ、全く同じ技術で先にやっている人がいるんですよね。。。[リンク参照](http://kawalabo.blogspot.com/2014/11/blog-post.html)）


まぁ見た目が違うんで何かの参考になればと思います。
![body.jpg](https://qiita-image-store.s3.amazonaws.com/0/148852/95d30f74-255d-9b76-491a-89d0bfc11632.jpeg)
![result.jpg](https://qiita-image-store.s3.amazonaws.com/0/148852/b89f0dec-a508-3539-3401-0bbeaa93ae01.jpeg)


#使った技術
MacBook Air
Python 3.6
OpenCV 3.4.3.18
Matplotlib 2.1.2

#やったこと
具体的にやったことの流れとしては
1.pythonで間違い探しの画像を読み込む
2.OpenCVで画像の差分をとる
3.領域を切り分けて差分が多い部分を抽出
4.Matplotlibを使ってヒートマップを作成、画像にまとめて出力

##1.pythonで画像を読み込む
まずは[ここ](https://www.saizeriya.co.jp/entertainment/)から間違い探しの画像を持ってくる。
この画像は2枚が1セットになっているので真ん中で分ける必要があります。
ただ、中心が画像によって微妙に異なっているのでここは手動で分ける必要がありました。
非常にダサい。。。
どこかで、画像差分が一番小さくなるような中心を選ぶコードを書いた方が良いと思います。。。

```python
def separate():
    body=cv2.imread("body.jpg")
    body1=np.empty([570,570,3])
    body2=np.empty([570,570,3])

    for i in range(560-1):
        for j in range(570):
            body1[j][i]=body[j][i+38]#ここの38は手動です。。。

    for i in range(560-1):
        for j in range(570):
            body2[j][i]=body[j][i+600]

    cv2.imwrite("body1.jpg",body1)
    cv2.imwrite("body2.jpg",body2)
```
##2.OpenCVで画像の差分をとる
これはOpenCVを使えば一発ですよね。

```python
 diff = cv2.absdiff(img0, img1)
```
これでできた差分の画像が以下です。
もうなんとなく間違いが分かりそうな気がしますが。。。
![diff2.jpg](https://qiita-image-store.s3.amazonaws.com/0/148852/e34fbcf4-4126-910a-766f-d43728fb3c57.jpeg)
##3.領域を切り分けて差分が多い部分を抽出
ここら辺がポイントですかね。
画像を10×10の領域に分けて各領域である一定の明るさ以下のところは切り捨てます。
ある程度差分があるところだけを残して、それらの和を取ります。
この配列を元にヒートマップを作成し、解いていきます。

```python
def mkheatdata(img):
    N=10
    M=10
    y=int(np.shape(img)[1]/N)
    x=int(np.shape(img)[0]/M)
    array=np.zeros((N,M))
    for i in range(np.shape(img)[0]):
        for j in range(np.shape(img)[1]):
            if img[i][j]<70:
                img[i][j]=0
    for n in range(N):
        for m in range(M):
            array[n][m]=np.sum(img[x*n:x*(n+1)-1,y*m:y*(m+1)-1])
    array=array[::-1][::1]
    return array
```
##4.Matplotlibを使ってヒートマップを作成、画像にまとめて出力
この配列の値が大きい上位10個を間違いとしています。
上位10個の抽出は割愛します。
ヒートマップの出力のところだけ。
ポイントは、画像を重ねるために軸や余白を無くしたところですね。

```python
def heatmap(heatdata):
    fig,ax=plt.subplots()
    fig=plt.figure(figsize=(57,57),dpi=10)
    ax.tick_params(labelbottom="off", bottom="off")  # x軸の削除
    ax.tick_params(labelleft="off", left="off")  # y軸の削除
    ax.set_xticklabels([])
    box("off")  # 枠線の削除
    fig.subplots_adjust(left=0,bottom=0,right= 1,top=1)

    plt.pcolor(heatdata,cmap=plt.cm.Reds)
    plt.savefig('heatmap.png')
```
これでできた画像が以下です。
これを重ねると正解ができます。
![heatmap.png](https://qiita-image-store.s3.amazonaws.com/0/148852/105676e0-f50b-4bba-e7c1-3602f15d82c6.png)
![result.jpg](https://qiita-image-store.s3.amazonaws.com/0/148852/971ddd87-c1f9-009c-4035-bf6cfd9e654a.jpeg)
#まとめ

![サイゼ.jpg](https://qiita-image-store.s3.amazonaws.com/0/148852/14a35ca8-0a18-19e6-5c7a-667fe2f3f475.jpeg)

結局正解が6/10
全部当てることはできなかったのですよね。。。
画像差分をとるから、位置がずれる、形が変わるには強いけど、点や線が増えるという間違いには弱いみたいです。。。
まぁ、研究に活かせそうですし、良い暇つぶしになったのかなと思います。

初投稿、読んでいただきありがとうございました。