概述
一般情況下, 圖片相似算法分為三種
- 均值Hash算法
- 差異值hash算法
- 感知hash算法
其實三個算法差不多. 我們以均值hash算法為例, 算法的步驟如下:
- 縮放為n*n的圖片
- 去色, 獲取灰度圖
- 得到hash指紋
- 比較hash指紋的漢明距離, 得到相似值
上面算法的不同, 主要是得到hash指紋的算法不同
均值hash算法
我們舉例說明. 原圖如下:
縮放為8*8的圖片
代碼:
img = cv2.resize(img, (8, 8), interpolation=cv2.INTER_CUBIC) 復制代碼
縮放結果
去色
代碼
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 復制代碼
結果:
計算灰度平均值
# 計算灰度平均值 for i in range(8): for j in range(8): s = s + gray_img[i, j] avg = s / 64 復制代碼
計算結果為176
計算Hash指紋
計算方法為, 如果該點灰度值大于平均灰度值, 這為1, 否則為0, 代碼如下:
# 如果值大于灰度值, 則為1, 否則為0. 生成hash指紋 for i in range(8): for j in range(8): if gray_img[i, j] > avg: hash_str = hash_str + '1' else: hash_str = hash_str + '0' 復制代碼
計算漢明距離
漢明距離: 在信息論中,兩個等長字符串之間的漢明距離(英語:Hamming distance)是兩個字符串對應位置的不同字符的個數。 代碼:
def cmp_hash(hash_1, hash_2): # 計算漢明距離 n = 0 if len(hash_1) != len(hash_2): return -1 for i in range(len(hash_1)): if hash_1[i] != hash_2[i]: n = n + 1 return n 復制代碼
差異值hash算法
前面縮放, 置灰, 計算漢明距離等和平均值hash算法一樣, 區別在于計算hash指紋的算法不同. 該處算法為: 如果前一個像素大于后一個像素的灰度值, 則為1, 否則為0. 完整的算法如下:
# 差值感應hash def b_hash(img): # 縮放到8*8的圖片 img = cv2.resize(img, (9, 8), interpolation=cv2.INTER_CUBIC) # 得到灰度圖 gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) hash_str = '' # 如果前一個像素比后一個大, 則為1, 否則為0. 生成hash指紋 for i in range(8): for j in range(8): if gray_img[i, j] > gray_img[i, j + 1]: hash_str = hash_str + '1' else: hash_str = hash_str + '0' return hash_str 復制代碼
感知Hash算法
前面兩種比較圖片相似的算法很好理解. 感應Hash算法的思想與之不同. 我們換一種角度來看待一張圖片. 一個圖片其實就是二維的信息圖譜, 有各種頻率的變化, 頻率高的地方代表顏色的變換比較大, 例如輪廓部分. 我們可以利用DCT變換(離散余弦變換)得到頻域圖. 比較低頻部分(為什么要比較低頻部分?) 是否相似就可以了.
原圖還是上面的原圖. 我們先進行縮放, 縮放為32*32的部分
灰度處理
進行DCT變換
我們拿到左上角的8*8的區域, 根據平均值hash算法比較這個灰度圖的漢明距離
完整的代碼如下:
# 感知hash def p_hash(img): # 縮放到32 * 32 img = cv2.resize(img, (32, 32), interpolation=cv2.INTER_CUBIC) cv2.imwrite("bb.jpg", img) # 得到灰度圖 gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray_img = gray_img.astype(np.float32) cv2.imwrite("cc.jpg", gray_img) # 進行離散余弦變換, 目的是將低頻部分放到左上角 img = cv2.dct(gray_img) cv2.imwrite("dd.jpg", img) # 得到左上角8*8 的頻率圖 img = img[0:8, 0:8] avg = 0 # 與得到低頻圖的平均灰度 hash_str = '' for i in range(8): for j in range(8): avg += img[i, j] avg = avg / 64 # 如果低頻圖的像素值大于平均灰度, 則為1, 否則為0. 依次生成hash指紋 for i in range(8): for j in range(8): if img[i, j] > avg: hash_str = hash_str + '1' else: hash_str = hash_str + '0' return hash_str 復制代碼
參考文獻:K碼農-http://kmanong.top/kmn/qxw/form/home?top_cate=28