"感知哈希算法"(Perceptual hash algorithm),它的作用是對每張圖片生成一個"指紋"(fingerprint)字符串,然後比較不同圖片的指紋。結果越接近,就說明圖片越相似。
一般步驟:
- 縮小圖片:32 * 32是一個較好的大小,這樣方便DCT計算
- 轉化爲灰度圖:把縮放後的圖片轉化爲256階的灰度圖。(具體算法見平均哈希算法步驟)
- 計算DCT:DCT把圖片分離成分率的集合
- 縮小DCT:DCT計算後的矩陣是32 * 32,保留左上角的8 * 8,這些代表的圖片的最低頻率
- 計算平均值:計算縮小DCT後的所有像素點的平均值。
- 進一步減小DCT:大於平均值記錄爲1,反之記錄爲0.
- 得到信息指紋:組合64個信息位,順序隨意保持一致性。
- 最後比對兩張圖片的指紋,獲得漢明距離即可。
實現代碼:
import cv2
import numpy as np
from itertools import chain
class PHash(object):
@staticmethod
def pHash(img_name):
"""
get image pHash value
"""
# 加載並調整圖片爲32x32灰度圖片
img = cv2.imread(img_name, 0)
img = cv2.resize(img, (64, 64), interpolation=cv2.INTER_CUBIC)
# 創建二維列表
h, w = img.shape[:2]
vis0 = np.zeros((h, w), np.float32)
vis0[:h, :w] = img # 填充數據
# 二維Dct變換
vis1 = cv2.dct(cv2.dct(vis0))
# cv.SaveImage('a.jpg',cv.fromarray(vis0)) #保存圖片
vis1.resize((32, 32), refcheck=False)
# 把二維list變成一維list
img_list = list(chain.from_iterable(vis1))
# 計算均值
avg = sum(img_list) * 1. / len(img_list)
avg_list = ['0' if i < avg else '1' for i in img_list]
# 得到哈希值
return ''.join(['%x' % int(''.join(avg_list[x:x + 4]), 2) for x in range(0, 32*32, 4)])
@staticmethod
def hammingDist(s1, s2):
"""
計算兩張圖片的漢明距離
"""
assert len(s1) == len(s2)
return sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)])
if __name__ == '__main__':
HASH1 = PHash.pHash('001.jpg')
HASH2 = PHash.pHash('002.jpg')
distance = PHash.hammingDist(HASH1, HASH2)
print(distance)
out_score = 1 - distance * 1. / (32 * 32 / 4)
print(out_score)