類似画像検索 - 知覚ハッシュ-BMB

類似画像検索について、いくつか。

BMBハッシュ((PDF) Block Mean Value Based Image Perceptual Hashing | Bian Yang - Academia.edu)。概要を知覚ハッシュによる類似画像検索(PDF)から引用しておく。

  1. 対象となる画像をグレースケール化し、あらかじめ決められた大きさに正規化する。
  2. ハッシュの長さをNと決める。画像を重複しない N 個のブロックに分ける\{I_1,I_2,\cdots,I_N\}
  3. 分けたブロックごとに画素値の平均値を計算する\{M_1,M_2,\cdots,M_N\}。平均値の集合から中央値を計算する\{M_d\}
  4. ハッシュの値h(i)を以下のように決める。

h(i)={\displaystyle
 \begin{eqnarray}
  \left\{
    \begin{array}{l}
      0,M_i  < M_d \\
      1,M_i \geq M_d
     \end{array}
  \right.
\end{eqnarray}
}


ブロックサイズを1x1、中央値の代わりに平均値を使えば「Average Hash」と同等のものになる(はず)。
Average HashについてはLooks Like It - The Hacker Factor Blogを参照。

以下、コード。ハッシュ長は144(=12x12)にした。
画像データはCaltech101から。カテゴリーが101個あるが、そのうちBACKGROUND_Googleは外してある。

import numpy as np
import cv2
import glob


def hdiff(a, b):
    return (a != b).astype('int').sum()   # astype()はなくても可、好みで
    
def mk_hash(img, size=12):
    size2 = size*size
    img = cv2.resize(img, (size2, size2))
    s = [np.mean(img[i:i+size, j:j+size])
        for i in range(0, size2, size)
            for j in range(0, size2, size)]
    s = np.array(s)
    m = np.median(s)

    return (s >= m)


q_img = cv2.imread("101_ObjectCategories/airplanes/image_0001.jpg",  0)
q_hash = mk_hash(q_img)

for file in sorted(glob.glob("101_ObjectCategories/*/*.jpg")):
    t_img = cv2.imread(file,  0)
    t_hash = mk_hash(t_img)
    d = hdiff(q_hash, t_hash)
    print(file, d)


クエリーも含めた上位10件を載せておく。

画像 距離
airplanes/image_0001.jpg 0
airplanes/image_0115.jpg 10
Motorbikes/image_0149.jpg 12
airplanes/image_0019.jpg 12
airplanes/image_0086.jpg 12
airplanes/image_0244.jpg 12
airplanes/image_0010.jpg 14
airplanes/image_0046.jpg 14
airplanes/image_0067.jpg 14
airplanes/image_0082.jpg 14

左上がクエリー画像
https://kakasi.skr.jp/images/bmb-hash.jpg