PHA是一類比較哈希方法的統稱。圖片所包含的特徵被用來生成一組指紋(不過它不是唯一的),而這些指紋是可以進行比較的。
PHA與加密哈希方法(以下簡稱CHA),如MD5、SHA1等,是不同的概念。CHA的哈希值是隨機的。用來生成哈希的數據的行爲就像隨機種子,所以相同的數據產生相同的結果,反之亦然。讀者可以如下做ruby測試:
require 'digest/sha2'
......
#生成加密後的散列值
def self.encrypt(string)
return Digest::SHA256.hexdigest(string)
end
我對這方面未做深層研究,如有錯誤,不吝賜教。此文的重點不是CHA,恕草草帶過。
如果想要深入理解下文內容的話,不妨讀讀我在ftp裏的fourier相關文檔,會有所收穫。當然,不讀也不會造成大的影響。(ftp具體在文末有提及)
對於圖片來說,高頻得到細節,低頻得到輪廓。所以小圖缺少細節,是低頻的。我先敘述一個阮一峯先生提及的最簡單PHA,在此表示感謝。大家可一睹爲快。
第一步,縮小尺寸。
最快速的去除高頻和細節,只保留結構明暗的方法就是縮小尺寸。
將圖片縮小到8x8的尺寸,總共64個像素。摒棄不同尺寸、比例帶來的圖片差異。
第二步,簡化色彩。
將縮小後的圖片,轉爲64級灰度。也就是說,所有像素點總共只有64種顏色。
第三步,計算平均值。
計算所有64個像素的灰度平均值。
第四步,比較像素的灰度。
算法的精髓,簡單、有趣,又充滿深意。
將每個像素的灰度,與平均值進行比較。大於或等於平均值,記爲1;小於平均值,記爲0。
第五步,計算哈希值。
將上一步的比較結果,組合在一起,就構成了一個64位的整數,這就是這張圖片的指紋。組合的次序並不重要,只要保證所有圖片都採用同樣次序就行了(例如,自左到右、自頂向下、big-endian)。
= 8f373714acfcf4d0
得到指紋以後,就可以對比不同的圖片,看看64位中有多少位是不一樣的。在理論上,這等同於計算Hammingdistance)。如果不相同的數據位不超過5,就說明兩張圖片很相似;如果大於10,就說明這是兩張不同的圖片。
這個算法非常好,無論你改變圖片的高寬、亮度甚至顏色,都不會改變哈希值。最關鍵的是速度極快!cool!
我在ftp裏放了這個算法的python版本(使用了PIL庫),如果讀者會ruby的話,不妨使用RMagick庫,同樣強大(文檔:http://www.imagemagick.org/RMagick/doc/項目:http://rubyforge.org/projects/rmagick/)
有沒有更好的方法來判斷相似度呢?有,pHash。這種方法稍複雜寫,運用了DCT來降低頻度,比起平均值方法更精確了。DCT參見http://en.wikipedia.org/wiki/Discrete_cosine_transform
下面來看它的過程。
第一步,縮小尺寸。
與平均值方法類似,不過要比8X8大些,32X32是個好尺寸,這樣做是爲了簡化DCT的計算,而不是降頻。
第二步,簡化色彩。
改成灰度圖,也是爲了減少DCT計算量。
第三步,計算DCT。
DCT將圖片分成了頻度和純量的集合。當JPEG使用8X8的DCT時,算法就使用32X32的DCT。
第四步,降低DCT。
當DCT是32X32時,只保留頂左的8X8,這部分代表了圖片的最低頻。這是神奇的一步。
第五步,計算平均值。
只用8X8的DCT低頻值,因爲DC係數和其他值相差很大,會破壞平均值。
第六步,進一步縮小DCT
將每個像素的灰度(64比特),與平均值進行比較。大於或等於平均值,記爲1;小於平均值,記爲0。圖片結構不變,結果就不會變。這完成了關鍵的一步。
第七步,構造哈希。