🐍

[Python] OpenCVによる2次元ノルムを使った画像比較について

2022/05/15に公開

はじめに

OpenCVで画像比較をしていました。
大した知識がないのでインターネットで調べて、2次元ノルムを使った方法を採用してみました。
しかし、目につくコードがどうやら間違っているようなので正しいと思われるコードを乗せることにしました。

環境

python
Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)]
OpenCv
>>> cv2.__version__
'4.5.5'

正しいと思われるコード

以下は、どの程度に通っているかパーセントで取得できます。

python
import numpy as np
import cv2

def getMaxL2(height, width, dtype=np.uint8):
  info = np.iinfo(dtype)
  white = np.ones((height, width, 3), dtype) * info.min
  black = np.ones((height, width, 3), dtype) * info.max
  return cv2.norm(white, black, cv2.NORM_L2)

def getSimilarity(image1, image2, maxL2=None):
  if image1.shape != image2.shape:
    return 0.0
  if maxL2 is None:
    maxL2 = getMaxL2(image1.shape[0], image1.shape[1])
  l2 = cv2.norm(image1, image2, cv2.NORM_L2)
  similarity = 1 - l2 / maxL2
  print(f"Similarity = {similarity}, L2 = {l2}")
  return similarity

目につく間違い

インターネットで調べてみると、以下のようなコードが紹介されています。
一致している場合は、1.0を返して正しいように思えます。
しかし、一致していない場合は、マイナスの値が帰ってきてどの程度に通っているか判断が付きません。

python
def getSimilarityNG(image1, image2):
  if image1.shape != image2.shape:
    return 0.0
  l2 = cv2.norm(image1, image2, cv2.NORM_L2)
  similarity = 1 - l2 / (image1.shape[0] * image1.shape[1])
  print(f"Similarity = {similarity}, L2 = {l2}")
  return similarity

テストコード

OpenCVは、RGBではなくてBGRだったので名前の付け方がおかしいですが見なかったことにしてください。

python
white = np.ones((3, 3, 3), np.uint8) * 0
gray = np.ones((3, 3, 3), np.uint8) * 127
black = np.ones((3, 3, 3), np.uint8) * 255
red = np.ones((3, 3, 3), np.uint8) * np.array((255, 0, 0), dtype=np.uint8)
green = np.ones((3, 3, 3), np.uint8) * np.array((0, 255, 0), dtype=np.uint8)
blue = np.ones((3, 3, 3), np.uint8) * np.array((0, 0, 255), dtype=np.uint8)
yellow = np.ones((3, 3, 3), np.uint8) * np.array((255, 255, 0), dtype=np.uint8)
magenta = np.ones((3, 3, 3), np.uint8) * np.array((255, 0, 255), dtype=np.uint8)
cyan = np.ones((3, 3, 3), np.uint8) * np.array((0, 255, 255), dtype=np.uint8)

print("OK")
getSimilarity(white, black)
getSimilarity(black, black)
getSimilarity(white, gray)
getSimilarity(black, gray)
getSimilarity(red, white)
getSimilarity(red, black)
getSimilarity(red, green)
getSimilarity(red, magenta)

print("NG")
getSimilarityNG(white, black)
getSimilarityNG(black, black)
getSimilarityNG(white, gray)
getSimilarityNG(black, gray)
getSimilarityNG(red, white)
getSimilarityNG(red, black)
getSimilarityNG(red, green)
getSimilarityNG(red, magenta)

実行結果
OK
Similarity = 0.0, L2 = 1325.018867790191
Similarity = 1.0, L2 = 0.0
Similarity = 0.5019607843137255, L2 = 659.9113576837423
Similarity = 0.4980392156862745, L2 = 665.1075101064489
Similarity = 0.42264973081037416, L2 = 765.0
Similarity = 0.18350341907227385, L2 = 1081.8733752154178
Similarity = 0.18350341907227385, L2 = 1081.8733752154178
Similarity = 0.42264973081037416, L2 = 765.0
NG
Similarity = -146.22431864335456, L2 = 1325.018867790191
Similarity = 1.0, L2 = 0.0
Similarity = -72.32348418708247, L2 = 659.9113576837423
Similarity = -72.9008344562721, L2 = 665.1075101064489
Similarity = -84.0, L2 = 765.0
Similarity = -119.20815280171308, L2 = 1081.8733752154178
Similarity = -119.20815280171308, L2 = 1081.8733752154178
Similarity = -84.0, L2 = 765.0

Discussion