在重複圖識別領域,對於識別肉眼相同圖片,PHash是頗有用的,並且算法複雜度很低。它抓住了 」 人眼對於細節信息不是很敏感 「 的特性,利用DCT變換把高頻信息去掉,再加上合適&簡單的二值化方式,使得算法效果比較魯棒。html
def phash(image, hash_size=8, highfreq_factor=4): import scipy.fftpack img_size = hash_size * highfreq_factor image = image.convert("L").resize((img_size, img_size), Image.ANTIALIAS)// 一、【預處理】轉灰度圖,resize pixels = numpy.asarray(image) dct = scipy.fftpack.dct(scipy.fftpack.dct(pixels, axis=0), axis=1) //DCT變換 dctlowfreq = dct[:hash_size, :hash_size] //二、只留下直流&&低頻變量 med = numpy.median(dctlowfreq) //取中值 diff = dctlowfreq > med //三、【二值化】大於中值爲1,小於等於中值爲0 return diff
PHash算法其實很簡單,主要就3步:python
其中圖片預處理很簡單,這裏就不詳細講解了。下面主要給你們直觀介紹下DCT變換到底是什麼,還有這裏是怎麼二值化的。算法
DCT變換,全稱是Discrete Cosine Transform,也就是離散餘弦變換,具體的公式跟原理這裏就不詳述了,具體能夠看DCT變換公式&原理函數
其中頻譜圖中,左上角屬於低頻變量,右下角屬於高頻變量,而後比較特殊的一點是左上角a[0][0]這點屬於直流變量。因爲人眼對於細節信息不是很敏感,因此咱們在識別肉眼相同級別重複圖的時候,只用頻譜圖中的低頻信息就足夠了,因此這就是phash中只取DCT低頻信息的緣由。ui
在頻譜圖中,咱們知道左上角那些是低頻信息,右下角是高頻信息。那麼在一張圖片中,哪些信息是低頻,哪些信息是高頻呢?code
因爲DCT是可逆變換,那麼咱們能夠只用頻譜圖中某一塊進行DCT逆變換,那麼就能夠直觀看到頻譜圖中這一塊表明什麼信息?orm
接下來,咱們利用DCT逆變換生成兩列圖片(以下所示):htm
從上能夠得出結論:blog
附上代碼,方便你們理解圖片
import cv2 import copy import numpy as np import matplotlib.pyplot as plt #展現圖片 def show_img(img): plt.imshow(img, cmap='Greys_r') plt.show() #左上角低頻矩陣,進行DCT逆變換 def low_frequency_idct(dct,dct_size): #非左上角N*N區域置0 dct[dct_size+1:,:] = 0 dct[:,dct_size+1:] = 0 #逆DCT變換 img = cv2.idct(dct) #展現圖片 show_img(img) #把左上角信息清除後,進行DCT逆變換 def hight_frequency_idct(dct,dct_size): #左上角N*N區域置0 dct[0:dct_size,0:dct_size]=0 #逆DCT變換 img = cv2.idct(dct) #展現圖片 show_img(img) #主函數 def work(image_name, img_size, dct_size): #圖片預處理 img = cv2.imread(image_name,0) show_img(img) img = cv2.resize(img,(img_size,img_size),interpolation=cv2.INTER_CUBIC) show_img(img) img = np.float32(img) #DCT變換 dct = cv2.dct(img) #用左上角,進行逆dct變換 low_frequency_idct(copy.deepcopy(dct),dct_size) #左上角置0,進行逆dct變換 hight_frequency_idct(copy.deepcopy(dct),dct_size) image_name = '11.png' img_size = 1000 dct_size = 30 work(image_name,img_size,dct_size)
目前咱們獲取到了肉眼最敏感的信息,這裏應該怎麼二值化呢?
首先咱們須要選取一個基準值,而後大於基準值的置1,小於等於基準值的置0。
那麼問題來了,怎麼選擇這個基準值呢?這裏有兩種方式:
因爲頻譜圖左上角那一點(直流變量),就是用原圖全部像素點加起來獲得的,因此這個點會很大,徹底偏離整體的值。
而後這裏基準值若是用均值的話,會致使phash值中1的個數會偏少,並且左上角那邊大機率是1,右下角那邊大概爲0。這就會致使phash中0,1的分佈不均勻。那麼其實對於phash值的特徵空間就有必定的縮小不少了。(如上圖所示,1個數不多)
PS: 改進策略:去除頻譜圖中第一行&&第一列的元素。
這樣能把一些很離譜的偏離點刪除,可是未必偏離點就在第一行&第一列,只是大機率在這裏。其實這樣還不如直接用中值更加直接。
改進以後效果好不少,可是並無中值魯棒。
利用中值來當基準值,效果會好不少。phash值中,0,1分佈機率同樣,而且特徵空間比均值大不少。