【Python】OpenCVとNumPyで2つの画像を比較(完全一致、部分一致の比率)

時計 2020.11.13 / 時計

【Python】OpenCVとNumPyで2つの画像を比較(完全一致、部分一致の比率)

記事ではPythonを用いて2つの画像比較して、それらが完全一致しているか、または一致している比率(部分一致)を判定する方法を解説します。

使用するパッケージはOpenCVとNumPyの2つです。OpenCVは画像処理で利用されるライブラリ、NumPyは数値計算を高速に行うライブラリです。

2つの画像が完全一致しているかや、一致している割合がわかるようになれば、作成できるアプリケーションの幅が大きく広がります。

私の場合

アップロードした請求書がどの請求書なのか、一致した比率で割り出すアプリケーションに使用しています

このように大きな可能性を秘めた技術なので、ぜひ本記事を通して2つの画像を比較する方法について理解を深めてください。

使用するライブラリ

本記事では以下に記す2つのライブラリを使用して画像の比較を行います。

  • OpenCV
  • NumPy

OpenCV

OpenCVとは画像処理や動画処理、画像解析に利用されるライブラリです。

OpenCVはインテル社が開発しており、オープンソースとして無料で配布されています。商用の利用であっても無料で使うことができるため、幅広く利用されています。

OpenCVは標準ではインストールされていないため、pipコマンドでインストールする必要があります。

pip install opencv-python

本記事で使用しているOpenCVの関数は以下になります。

関数 説明
imread() Pythonで画像読み込みに使用。返り値はndarray配列
resize() 画像をリサイズする
calcHist() 画像からヒストグラム計算をする
compareHist() 2つのヒストグラムを比較し一致率を返す

OpenCVについて詳しく知りたい方は以下記事をご参照ください。

NumPy

NumpyはPythonで数値計算を行う際に使用する有名なライブラリです。オープンソースであり、誰でも私用/商用を問わず使用可能です。

NumPyを用いることで、簡単に配列を操作したり、多次元数値配列を扱うことができ、数値計算を効率的にできるようになります。

AI(人工知能)の分野である機械学習においては、NumPyによる計算が多く用いられています。

NumPyは標準ではインストールされていないため、pipコマンドでインストールする必要があります。

pip install numpy

プログラムで使用するには、プログラムの先頭でインポートを忘れずに行ってください。

import numpy

本記事で使用しているNumPyの関数は以下になります。

関数 説明
array_equal(image1, image2) 2つの画像のndarray配列が一致しているか判定
count_nonzero(判定式) 判定式でTrueになった総数を返す

画像の完全一致

2つの画像が完全に一致しているか判定するには、以下2つの方法があります。

  • array_equal関数による完全一致の判定
  • ヒストグラム比較による判定

array_equal関数による完全一致

NumPyのarray_equal関数を使用して、2つの画像が完全一致しているか判定できます。

これはOpenCVが画像をNumPy配列のndarrayとして扱うため、array_equal関数で2つの画像のndarray配列が一致しているかを判定している、ということです。

array_equal関数はboolean型で比較結果が返されます。
戻り値がTrueであれば完全一致、Falseであれば不一致ということになります。

以下サンプルプログラムでは、「office54.png」と「office54-eng.png」の画像を使用しております。

office54.png

office54.pngの画像

office54-eng.png

office54-eng.pngの画像
import cv2
import numpy as np

image1 = cv2.imread('office54.png')
image2 = cv2.imread('office54.png')
image3 = cv2.imread('office54-eng.png')

print(np.array_equal(image1, image2))
print(np.array_equal(image1, image3))
# True
# False

同じ画像同士ではもちろんTrueが返され、異なる画像ではFalseが返ってきていることがわかります。

ヒストグラム比較

画像の比較方法の一つに、画像の色味を加味して比較できるヒストグラム比較というのがあります。

ヒストグラムとは、「ピクセル」の色の明るさをレベル別に分布したグラフです。
このヒストグラムは、画像処理や画像解析では大事な指標です。

ヒストグラムの計算にはcv2.calcHist()関数を使用します。

cv2.calcHist([image], [channel], [mask], [hist size], [range])
引数 説明
image 入力画像を指定します
channel 画像のチェンネルインデックスを指定します。グレースケールは[0]、カラー画像は色相(B, G, R)に対応する[0],[1],[2]のどれか値を指定します
mask マスク画像画像の指定です。全画素を計算するなら[None]を指定してマスク無しにします
hist size ビンの数を指定します。全画素対象であれば[256]を指定します
range 計算範囲を指定します。基本は[0, 256]です

2つのヒストグラムの比較にはcompareHist()関数を使用します。

cs2.compareHist(image1_hist, image2_hist, int method)

以下にサンプルプログラムを示します。

# 例
image = cv2.imread('office54.png')
image1_hist = cv2.calcHist([image1], [0], None, [256], [0, 256])

以下に2つの画像をヒストグラム比較するプログラムを記します。

import cv2

image1 = cv2.imread('office54.png')
image2 = cv2.imread('office54.png')
image3 = cv2.imread('office54-eng.png')

img_size = (100, 100)

# 画像をリサイズする
image1 = cv2.resize(image1, img_size)
image2 = cv2.resize(image2, img_size)
image3 = cv2.resize(image3, img_size)

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

# ヒストグラムした画像を比較
print(cv2.compareHist(image1_hist, image2_hist, 0))
print(cv2.compareHist(image1_hist, image3_hist, 0))
# 1.0
# 0.8882466100801328

同じ画像同士であれば、1.0が返されることがわかります。
1.0が返されるということは完全一致しているということです。

画像の部分一致の比率

画像が完全に一致していなくても部分的に一致している比率を計算したい場合、以下の方法があります。

  • ヒストグラム比較
  • NamPy配列ndarrayの一致している要素数の比率

ヒストグラム比較

ヒストグラム比較についてはすでに解説していますが、この方法で完全一致だけでなく、部分一致の比率も求めることができます。

print(cv2.compareHist(image1_hist, image3_hist, 0))
# 0.8882466100801328

NamPy配列ndarrayの一致している要素数の比率

こちらは2つの画像のNamPy配列ndarrayの要素(画素値)を比較し、一致している要素(画素値)数の比率で、画像の一致率を割り出していく方法です。

ndarray配列の全要素数を取得するために、size属性を用います。
また要素(画素値)が一致しているかの判定には、NumPyのcount_nonzero()関数を用います。

count_nonzero()関数で得た要素の一致している総数を、全要素数で割ることで一致率が導かれます。

import cv2
import numpy as np

image1 = cv2.imread('office54.png')
image2 = cv2.imread('office54.png')
image3 = cv2.imread('office54-eng.png')

img_size = (100, 100)

# 画像をリサイズする
image1 = cv2.resize(image1, img_size)
image2 = cv2.resize(image2, img_size)
image3 = cv2.resize(image3, img_size)

print(np.count_nonzero(image1 == image2) / image1.size)
print(np.count_nonzero(image1 == image3) / image1.size)
# 1.0
# 0.46696666666666664

上記サンプルでは、同じ画像同士だと一致率が100%、少し似ている画像同士だと一致率46.6%という結果になりました。

まとめ

OpenCVとNumPyで2つの画像を比較する方法はいかがでしたか?

今回紹介した技術とTkinterを使って、GUIアプリを作ってみても面白そうですね。

今度その記事も作ってみたいと思います。