現在圖像處理髮展迅猛異常,檢測相似性,方法不勝枚舉。。然而。。簡單易懂容易實現的方法,就只有幾個了。。
首先最廣泛應用的是直方圖相似度檢測。這個方法大多數openCV的教材都有,思路大概就是 數一數 各種顏色有多少個像素點,統計起來,記錄成爲直方圖,然後比較兩個圖的直方圖有什麼差別。
給出一個函數來看看吧
{
Mat src1,src2;
//灰度直方圖
Mat hist1,hist2;
//rgb直方圖
Mat hist1_r,hist1_g,hist1_b;
Mat hist2_r,hist2_g,hist2_b;
//記錄結果
double result1,result2,result3,result4;
//bin數目,取值範圍
int binNumber = 16;
float range[] = {0, 255};
const float *ranges[] = {range};
const int channels = 0;
/// 裝載圖像
src1 = imread("/圖片的路徑。。/1.png");
src2 = imread("/圖片的路徑。。/2.png");
//分割圖像的矩陣,成爲RGB三個通道,分別存於vector1和vector2中
vector<<span class="s1">Mat> vec1;
split(src1, vec1);
vector<<span class="s1">Mat> vec2;
split(src2, vec2);
calcHist(&src1, 1, &channels, Mat(), hist1, 1, &binNumber, ranges, true, false);
calcHist(&vec1[0], 1, &channels, Mat(), hist1_r, 1, &binNumber, ranges, true, false);
calcHist(&vec1[1], 1, &channels, Mat(), hist1_g, 1, &binNumber, ranges, true, false);
calcHist(&vec1[2], 1, &channels, Mat(), hist1_b, 1, &binNumber, ranges, true, false);
calcHist(&src2, 1, &channels, Mat(), hist2, 1, &binNumber, ranges, true, false);
calcHist(&vec2[0], 1, &channels, Mat(), hist2_r, 1, &binNumber, ranges, true, false);
calcHist(&vec2[1], 1, &channels, Mat(), hist2_g, 1, &binNumber, ranges, true, false);
calcHist(&vec2[2], 1, &channels, Mat(), hist2_b, 1, &binNumber, ranges, true, false);
//歸一化
normalize(hist1,hist1);
normalize(hist1_r,hist1_r);
normalize(hist1_g,hist1_g);
normalize(hist1_b,hist1_b);
normalize(hist2,hist2);
normalize(hist2_r,hist2_r);
normalize(hist2_g,hist2_g);
normalize(hist2_b,hist2_b);
//相關係數:(絕對值)=1完全相同,>0.8高度相關,<0.3低度相關,其他爲中度相關
//巴氏:完全匹配爲0,完全不匹配爲1
result1 = compareHist(hist1, hist2, CV_COMP_BHATTACHARYYA);
result2 = compareHist(hist1_r, hist2_r, CV_COMP_BHATTACHARYYA);
result3 = compareHist(hist1_g, hist2_g, CV_COMP_BHATTACHARYYA);
result4 = compareHist(hist1_b, hist2_b, CV_COMP_BHATTACHARYYA);
//通過彈出窗口顯示
NSString *str= [NSString stringWithFormat:@"相似度: %f (%f %f %f) !"
,result1,result2,result3,result4];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"(巴氏距離,越小越好)!" message:str delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil];
[alert show];
}
另外一種方法叫hash。。。其實我忘記叫什麼了。。。
思路是縮小圖片至某個size,然後統一成爲灰度圖,再計算一下所有點的灰度值的平均,然後大於平均的點記錄爲1,小得記錄爲0..這時候,圖片就變成了一個size * size(比如8*8),裝滿了0和1的矩陣。這個矩陣,聽說是叫做圖片的指紋(fingerprint,外國人起的名字,,,)然後比較一下兩個指紋區別多大,就可以知道兩個圖片區別多大。一般用漢明距離來計算相似度,比如A指紋的左上角是0,而B指紋的左上角是1,那麼漢明距離就+1,以此類推循環一遍。
另外有更高級的pHash方法,在縮小矩陣,轉化灰度之後,做一個離散餘弦變換(不懂。。我猜大概就是。。把圖片中高頻的成分(變化大的地方,比如黑白交接的地方)往右下角挪動,把低頻成分(變化小的地方)往左上角挪動)。然後取出低頻的成分,計算兩圖的距離
同樣奉上一段代碼
- (void)DoSomething
{
Mat src1,src2;
src1 = imread("/圖片的路徑/1.png");
src2 = imread("/圖片的路徑/2.png");
string str1 = avgHashValue(src1);
string str2 = avgHashValue(src2);
printf("結果:%d\n",HamingDistance(str1,str2));
}
//均值Hash算法
string avgHashValue(Mat &src)
{
string rst(64,'\0');
Mat img,dst;
if(src.channels()==3){
cvtColor(src,src,CV_BGR2GRAY);
img=Mat_<<span class="s1">double>(src);
}
else
img=Mat_<<span class="s1">double>(src);
//第一步,縮小尺寸。將圖片縮小到8x8的尺寸,總共64個像素,去除圖片的細節
resize(img,img,cv::Size(8,8));
// 第二步,簡化色彩(Color Reduce)。將縮小後的圖片,轉爲64級灰度。
uchar *pData;
for(int i=0;irows;i++)
{
pData = img.ptr<<span class="s4">uchar>(i);
for(int j=0;jcols;j++)
{
pData[j]=pData[j]/4;
}
}
// 第三步,計算平均值。計算所有64個像素的灰度平均值。
int average = mean(img).val[0];
//第四步,比較像素的灰度。將每個像素的灰度,與平均值進行比較。大於或等於平均值記爲1,小於平均值記爲0
Mat mask= (img>=(uchar)average);
// 第五步,計算哈希值。
int index = 0;
for(int i=0;irows;i++)
{
pData = mask.ptr<<span class="s4">uchar>(i);
for(int j=0;jcols;j++)
{
if(pData[j]==0)
rst[index++]='0';
else
rst[index++]='1';
}
}
return rst;
}
//pHash算法
string pHashValue(Mat &src)
{
string rst(64,'\0');
Mat img,dst;
double dIdex[64];
double avg = 0.0;
int k = 0;
if(src.channels()==3){
cvtColor(src,src,CV_BGR2GRAY);
img=Mat_<<span class="s1">double>(src);
}
else
img=Mat_<<span class="s1">double>(src);
//第一步,縮小尺寸。將圖片縮小到8x8的尺寸,總共64個像素,去除圖片的細節
resize(img,img,cv::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<<span class="s1">double>(i, j);
avg += dst.at<<span class="s1">double>(i, j)/64;
++k;
}
}
// 第四步,計算哈希值。
for (int i =0;i<64;++i)
{
if (dIdex[i]>=avg)
{
rst[i]='1';
}
else
{
rst[i]='0';
}
}
return rst;
}
//漢明距離計算
int HamingDistance(string &str1,string &str2)
{
if((str1.size()!=256)||(str2.size()!=256))
return -1;
int difference = 0;
for(int i=0;i<256;i++)
{
if(str1[i]!=str2[i])
difference++;
}
return difference;
}
就寫這麼多吧~