圖像相似性檢測入門

前文提到,本爺接到一個小任務,是要做圖像相似性檢測。

現在圖像處理髮展迅猛異常,檢測相似性,方法不勝枚舉。。然而。。簡單易懂容易實現的方法,就只有幾個了。。

首先最廣泛應用的是直方圖相似度檢測。這個方法大多數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;
}


 


就寫這麼多吧~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章