pythonで２つの画像を比較してその類似度を算出するプログラムを作ってみた


pythonで２つの画像を比較してその類似度を算出するプログラムを作ってみました。
ちなみに、ディレクトリ名を日本語にしたかったので、日本語のパスにも対応するように作ってます。

ソースコードはこちら↓ の「1_類似度の算出」のプログラムです。

https://github.com/jun-higuche/python-ImageCompare

## 背景
これを作った背景ですが、現在、私が、スマフォアプリの提供APIを変更するというエンハンスプロジェクトを担当しておりまして、その新アプリでは、新しいAPIを使った場合と現在のAPIを使った場合とで、全く同じ動作をさせなければなりません。
その比較のために、[以前、作成したAppium＋Pythonで作った自動テストツール](https://qiita.com/jun_higuche/items/7c6a23ed4f9cda53b8a0)を新アプリと現在のアプリで同じアカウントで動作させて、そのアプリのキャプチャ画像を撮っていき、新と旧で比較するっという事をしようとしてました。
その画像比較では、時計などその時々に変わってしまう値は異なるために全く画像が一致するわけではありませんので、類似度という形で参考数値を出すことで、あまりにその類似度が違う場合には気を付けてみることができるかなっと思って、算出することにしたのです。


## 01_２画像のヒストグラム比較による類似度の算出

ヒストグラムというのは、画像の特徴量を示す数値です。
それを比較することで類似度を算出するプログラムを作ってみました。

```python:main1_01.py
"""
２画像のヒストグラム比較による類似度の算出
"""
import cv2, os
from opencv_japanese import imread

dirname =  os.path.dirname(__file__)

image1 = imread(dirname + '\\1_1.png')
image2 = imread(dirname + '\\1_2.png')
image3 = imread(dirname + '\\2.png')
image4 = imread(dirname + '\\3.png')

height = image1.shape[0]
width = image1.shape[1]

img_size = (int(width), int(height))

# 比較するために、同じサイズにリサイズしておく
image1 = cv2.resize(image1, img_size)
image2 = cv2.resize(image2, img_size)
image3 = cv2.resize(image3, img_size)
image4 = cv2.resize(image4, img_size)

# 画像をヒストグラム化する
image1_hist = cv2.calcHist([image1], [2], None, [256], [0, 256])
image2_hist = cv2.calcHist([image2], [2], None, [256], [0, 256])
image3_hist = cv2.calcHist([image3], [2], None, [256], [0, 256])
image4_hist = cv2.calcHist([image4], [2], None, [256], [0, 256])

# ヒストグラムした画像を比較
print("「1_1.png」と「1_2.png」の類似度：" + str(cv2.compareHist(image1_hist, image2_hist, 0)))
print("「1_1.png」と「2.png」の類似度：" + str(cv2.compareHist(image1_hist, image3_hist, 0)))
print("「1_1.png」と「3.png」の類似度：" + str(cv2.compareHist(image1_hist, image4_hist, 0)))
```


## 02_２画像の画素値を比較し類似度を算出

こちらは画素が同じものの割合から類似度を算出するプログラムを作ってみました。

```python:main1_02.py
"""
２画像の画素値を比較し類似度を算出
"""

import cv2, os
import numpy as np
from opencv_japanese import imread

dirname =  os.path.dirname(__file__)

image1 = imread(dirname + '\\1_1.png')
image2 = imread(dirname + '\\1_2.png')
image3 = imread(dirname + '\\2.png')
image4 = imread(dirname + '\\3.png')

height = image1.shape[0]
width = image1.shape[1]

img_size = (int(width), int(height))

# 比較するために、同じサイズにリサイズしておく
image1 = cv2.resize(image1, img_size)
image2 = cv2.resize(image2, img_size)
image3 = cv2.resize(image3, img_size)
image4 = cv2.resize(image4, img_size)

#画素数が一致している割合を計算
print("「1_1.png」と「1_2.png」の類似度：" + str(np.count_nonzero(image1 == image2) / image2.size))
print("「1_1.png」と「2.png」の類似度：" + str(np.count_nonzero(image1 == image3) / image3.size))
print("「1_1.png」と「3.png」の類似度：" + str(np.count_nonzero(image1 == image4) / image4.size))
```

## ２つのやり方で類似度を算出してその特徴を比較してみた。

Android端末で、適当なスクリーンショットを撮ってサンプル画像として使ってみました。

### サンプルの画像：

| 1_1.jpg | 1_2.jpg | 2.jpg | 3.jpg |
|:-----------|:------------|:------------|:------------|
| ![1_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448092/a360ed40-dba8-e2b4-07c2-6622b07d4b64.jpeg)| ![1_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448092/6fbf643d-edbd-d136-4df4-ecc6ebbf2c94.jpeg)|![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448092/8dc5bdfb-b903-d623-a7e9-6e7d19581a0a.jpeg)|![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448092/f9bfd5d8-e1f5-8e17-8ae8-c3c5aa34374d.jpeg)|

### 比較結果


#### 01_２画像のヒストグラム比較による類似度の算出

```
> python main1_01.py
「1_1.png」と「1_2.png」の類似度：0.9986494780757148
「1_1.png」と「2.png」の類似度：0.7874921596233533
「1_1.png」と「3.png」の類似度：0.2099863461799079
```
※「1」が完全一致です。1に近いほど類似度が高いことを示します。

#### 02_２画像の画素値を比較し類似度を算出

```
> python main1_02.py
「1_1.png」と「1_2.png」の類似度：0.9868415637860082
「1_1.png」と「2.png」の類似度：0.01643464370803552
「1_1.png」と「3.png」の類似度：0.02162280701754386
```
※「1」が完全一致です。1に近いほど類似度が高いことを示します。

### まとめ

比較結果から、『「1_1.png」と「2.png」』の比較が違いとして顕著に出てますね。
「1_1.png」と「2.png」って、一見、背景が同じように見えますが、よく見るとズレています。
ヒストグラムでの比較の方は、特徴量での比較になるため、より１に近いになるのですが、画素値での比較の場合では、そのズレの箇所は差異として扱われるために「0」に近い値になってしまっているのですね。

この結果から、類似度の計算をするうえで、『多少のズレは許容して同じとして扱いたい』ということであれば、ヒストグラムでの算出を利用する。『ズレは全て差異として扱いたい』のであれば、画素値による算出を行えばよいのではないかと思います。

他にも類似度の計算の方法がありそうなので、必要があればまたやるかもしれません。
いったん、今回は、この結果だけでも大丈夫そうなので、このいずれかを使って続きのプログラムを作ろうと思います。

次は、２つの画像を比較させてその差異を示す別画像を生成するプログラムの紹介をする予定です。