🐾

【Python】OpenCV img_hashモジュールで画像ハッシュ計算・比較

2021/07/02に公開

いつのまにかPython版のOpenCV(contrib)でimg_hashモジュールが使えるようになっていたのでメモです(もしかして結構前から使えるようになってた、、、🦔?

バージョン

opencv-contrib-python 4.5.2.54で試しています。

リポジトリ

サンプルソースは以下のリポジトリで公開しています。
https://github.com/Kazuhito00/OpenCV-img_hash-Sample

サンプル画像

サンプルとしてストリートバスケの画像を使います。

import cv2

sample_image01 = cv2.imread('OpenCV-img_hash-Sample-Private/01_images/000005.jpg')
sample_image02 = cv2.imread('OpenCV-img_hash-Sample-Private/01_images/000004.jpg')
sample_image03 = cv2.imread('OpenCV-img_hash-Sample-Private/01_images/000015.jpg')
sample_image04 = cv2.imread('OpenCV-img_hash-Sample-Private/01_images/000025.jpg')

ハッシュ計算

ハッシュ計算は以下のように簡単に実施できます。
img_hash.XXXX_create()でインスタンスを作り、compute()でハッシュ値を計算できます。

下記例は「Average Hash」アルゴリズムの例ですが、
2021/07/02時点で6個のアルゴリズムが実装されています。

Average Hash

# Average Hash (also called Different hash)
hash_func = cv2.img_hash.AverageHash_create()
hash_func.compute(sample_image01)
array([[126, 124, 126, 126, 110, 126, 116,   0]], dtype=uint8)

Block Mean Hash

# Block Mean Hash
hash_func = cv2.img_hash.BlockMeanHash_create()
hash_func.compute(sample_image01)
array([[252,  63, 252,  63, 248,  63, 252,  63, 252,  63, 252,  63, 252,
         63, 252,  62, 252,  62, 252,  63, 252,  63, 240,  63, 224,  63,
        224,  35,   0,   7,   0,   3]], dtype=uint8)

Color Moment Hash

# Color Moment Hash (this is the one and only hash algorithm resist to rotation attack(-90~90 degree))
hash_func = cv2.img_hash.ColorMomentHash_create()
hash_func.compute(sample_image01)
array([[ 5.14092446e-03,  1.92133498e-06,  2.41530964e-09,
         1.04915002e-09,  1.65954156e-18,  1.44657153e-12,
         1.87509796e-19,  5.64878912e-03,  8.40300102e-07,
         2.39878731e-08,  1.19881960e-08,  1.99745105e-16,
         1.03596766e-11, -3.78250751e-17,  1.12674525e-03,
         8.41700440e-08,  1.82524094e-11,  3.54288408e-12,
         2.53729795e-23,  9.97188988e-16, -1.29577573e-23,
         1.14495802e-03,  8.32214753e-08,  2.19804361e-11,
         4.44136302e-12,  3.97670401e-23,  1.23232301e-15,
        -1.85544324e-23,  1.29366224e-03,  2.53985499e-11,
         5.13882746e-14,  6.30625052e-14, -6.46305139e-29,
         2.20295026e-19,  3.58937187e-27,  1.36153071e-03,
         1.15752215e-10,  9.30512955e-15,  3.43450058e-14,
         1.48443469e-28, -3.12655174e-19,  5.95768500e-28]])

Marr Hildreth Hash

# Marr Hildreth Hash
hash_func = cv2.img_hash.MarrHildrethHash_create()
hash_func.compute(sample_image01)
array([[101, 205, 130, 201,  32,  59, 109, 108,  77, 224, 118,  60, 156,
         14,   7,  35, 177, 248, 242, 126,  62,  28, 158,  71,  99, 225,
        216, 147, 193, 252, 157, 142,   7,  37, 182, 182,  19, 153,  60,
         29, 129, 192, 219, 166, 151, 242, 104,   6, 227, 103, 183,  13,
        178,  15,  19, 137, 198, 225, 249, 168,  60, 151, 248,  31, 143,
        199, 227, 241, 248, 252, 126,  63]], dtype=uint8)

Perceptual hash

# PHash (also called Perceptual hash)
hash_func = cv2.img_hash.PHash_create()
hash_func.compute(sample_image01)
array([[169,  11, 254, 107, 214, 253, 155, 255]], dtype=uint8)

Radial Variance Hash

# Radial Variance Hash
hash_func = cv2.img_hash.RadialVarianceHash_create()
hash_func.compute(sample_image01)
array([[ 78,   0, 255, 207,  78,  21, 106,  75,  30, 109,  42,  71,  91,
         54, 127,  85,  74,  90,  47,  71,  62,  78, 105,  76,  87,  85,
         63,  75,  74,  78,  88,  71,  75,  80,  69,  82,  86,  78,  85,
         75]], dtype=uint8)

性能

各種攻撃に対する性能の比較表がOpenCVドキュメントに公開されています。

※OpenCVドキュメント Performance under different attacksより引用

ハッシュ生成速度は以下の通り。

※OpenCVドキュメント Hash Computation chartより引用

ハッシュ比較速度は以下の通り。

※OpenCVドキュメント Hash comparison chartより引用

使用アルゴリズムは各種耐性や処理速度を元に決めればよいですが、回転耐性も考慮するとColor Moment Hashが良いです。

ハッシュ比較

ハッシュ同士はcompare()を使用することで比較できます。

hash_func = cv2.img_hash.ColorMomentHash_create()

hash01 = hash_func.compute(sample_image01)
hash02 = hash_func.compute(sample_image02)
hash03 = hash_func.compute(sample_image03)
hash04 = hash_func.compute(sample_image04)
result_01 = hash_func.compare(hash01, hash01)
result_02 = hash_func.compare(hash01, hash02)
result_03 = hash_func.compare(hash01, hash03)
result_04 = hash_func.compare(hash01, hash04)

print('compare(hash01, hash01)', result_01)
print('compare(hash01, hash02)', result_02)
print('compare(hash01, hash03)', result_03)
print('compare(hash01, hash04)', result_04)

0に近いほど類似度が高くなります。

compare(hash01, hash01) 0.0
compare(hash01, hash02) 0.5421972228454053
compare(hash01, hash03) 17.16120600183815
compare(hash01, hash04) 21.10215832598905

同じ画像では0となり、似ている画像ほど比較結果が小さくなる傾向が出ます。

以上。

Discussion