感知哈希算法

1、感知哈希算法

一、基於低頻的均值哈希

一張圖片就是一個二維信號,它包含了不一樣頻率的成分。以下圖所示,亮度變化小的區域是低頻成分,它描述大範圍的信息。而亮度變化劇烈的區域(好比物體的邊緣)就是高頻的成分,它描述具體的細節。或者說高頻能夠提供圖片詳細的信息,而低頻能夠提供一個框架。 ios

而一張大的,詳細的圖片有很高的頻率,而小圖片缺少圖像細節,因此都是低頻的。因此咱們平時的下采樣,也就是縮小圖片的過程,其實是損失高頻信息的過程。 算法

均值哈希算法主要是利用圖片的低頻信息,其工做過程以下:框架

  1. 縮小尺寸:去除高頻和細節的最快方法是縮小圖片,將圖片縮小到8x8的尺寸,總共64個像素。不要保持縱橫比,只需將其變成8*8的正方形。這樣就能夠比較任意大小的圖片,摒棄不一樣尺寸、比例帶來的圖片差別。
  2. 簡化色彩:將8*8的小圖片轉換成灰度圖像。
  3. 計算平均值:計算全部64個像素的灰度平均值。
  4. 比較像素的灰度:將每一個像素的灰度,與平均值進行比較。大於或等於平均值,記爲1;小於平均值,記爲0。
  5. 計算hash值:將上一步的比較結果,組合在一塊兒,就構成了一個64位的整數,這就是這張圖片的指紋。組合的次序並不重要,只要保證全部圖片都採用一樣次序就好了。(我設置的是從左到右,從上到下用二進制保存)。

計算一個圖片的hash指紋的過程就是這麼簡單。剛開始的時候以爲這樣就損失了圖片的不少信息了,竟然還能有效。簡單的算法也許存在另外一種美。若是圖片放大或縮小,或改變縱橫比,結果值也不會改變。增長或減小亮度或對比度,或改變顏色,對hash值都不會太大的影響。最大的優勢:計算速度快!ui

這時候,比較兩個圖片的類似性,就是先計算這兩張圖片的hash指紋,也就是64位0或1值,而後計算不一樣位的個數(漢明距離)。若是這個值爲0,則表示這兩張圖片很是類似,若是漢明距離小於5,則表示有些不一樣,但比較相近,若是漢明距離大於10則代表徹底不一樣的圖片。spa

二、加強版:pHash

均值哈希雖然簡單,但受均值的影響很是大。例如對圖像進行伽馬校訂或直方圖均衡就會影響均值,從而影響最終的hash值。存在一個更健壯的算法叫pHash。它將均值的方法發揮到極致。使用離散餘弦變換(DCT)來獲取圖片的低頻成分。code

離散餘弦變換(DCT)是種圖像壓縮算法,它將圖像從像素域變換到頻率域。而後通常圖像都存在不少冗餘和相關性的,因此轉換到頻率域以後,只有不多的一部分頻率份量的係數纔不爲0,大部分系數都爲0(或者說接近於0)。下圖的右圖是對lena圖進行離散餘弦變換(DCT)獲得的係數矩陣圖。從左上角依次到右下角,頻率愈來愈高,由圖能夠看到,左上角的值比較大,到右下角的值就很小很小了。換句話說,圖像的能量幾乎都集中在左上角這個地方的低頻係數上面了。 blog

** pHash的工做過程以下:**圖片

  1. 縮小尺寸:pHash以小圖片開始,但圖片大於8*832*32是最好的。這樣作的目的是簡化了DCT的計算,而不是減少頻率。string

  2. 簡化色彩:將圖片轉化成灰度圖像,進一步簡化計算量。hash

  3. 計算DCT:計算圖片的DCT變換,獲得32*32的DCT係數矩陣。

  4. 縮小DCT:雖然DCT的結果是32*32大小的矩陣,但咱們只要保留左上角的8*8的矩陣,這部分呈現了圖片中的最低頻率。

  5. 計算平均值:如同均值哈希同樣,計算DCT的均值。

  6. 計算hash值:這是最主要的一步,根據8*8的DCT矩陣,設置0或1的64位的hash值,大於等於DCT均值的設爲」1」,小於DCT均值的設爲「0」。組合在一塊兒,就構成了一個64位的整數,這就是這張圖片的指紋。

結果並不能告訴咱們真實性的低頻率,只能粗略地告訴咱們相對於平均值頻率的相對比例。只要圖片的總體結構保持不變,hash結果值就不變。可以避免伽馬校訂或顏色直方圖被調整帶來的影響。

與均值哈希同樣,pHash一樣能夠用漢明距離來進行比較。(只須要比較每一位對應的位置並算計不一樣的位的個數)

#include<iostream>
#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"  

using namespace cv;  
using namespace std;  

//pHash算法  
string pHashValue(Mat &src)  
{  
	Mat img ,dst;  
	string rst(64,'\0');  
	double dIdex[64];  
	double mean = 0.0;  
	int k = 0;  
	if(src.channels()==3)  
	{  
		cvtColor(src,src,CV_BGR2GRAY);  
		img = Mat_<double>(src);  
	}     
	else  
	{  
		img = Mat_<double>(src);  
	}       

	/* 第一步,縮放尺寸*/  
	resize(img, img, Size(8,8));  

	/* 第二步,離散餘弦變換,DCT係數求取*/  
	dct(img, dst);   

	/* 第三步,求取DCT係數均值(左上角8*8區塊的DCT係數)*/  
	for (int i = 0; i < 8; ++i) {  
		for (int j = 0; j < 8; ++j)   
		{  
			dIdex[k] = dst.at<double>(i, j);  
			mean += dst.at<double>(i, j)/64;  
			++k;  
		}  
	}  

	/* 第四步,計算哈希值。*/  
	for (int i =0;i<64;++i)  
	{  
		if (dIdex[i]>=mean)  
		{  
			rst[i]='1';  
		}  
		else  
		{  
			rst[i]='0';  
		}  
	}  
	return rst;  
}  

//漢明距離計算  
int HanmingDistance(string &str1,string &str2)  
{  
	if((str1.size()!=64)||(str2.size()!=64))  
		return -1;  
	int difference = 0;  
	for(int i=0;i<64;i++)  
	{  
		if(str1[i]!=str2[i])  
			difference++;  
	}  
	return difference;  
} 

int main() 
{ 
	Mat src1 = imread("F:\\My_Test\\2018-06-19\\test\\1_CaptureStudio20180619091831_0_1.jpg"); 
	Mat src2 = imread("F:\\My_Test\\2018-06-19\\test\\1_CaptureStudio20180619091831_1_2.jpg"); 

	string sHash1 =  pHashValue(src1); 

	string sHash2 =  pHashValue(src2); 

	int nRet = HanmingDistance(sHash1,sHash2);
	
	printf("definition: %f\n\n",nRet);

	cvWaitKey(0);
	return 0;
}
相關文章
相關標籤/搜索