剛上研一,本身想對嵌入式進行一番學習,結果實習的時候用opencv搞圖像識別,有點讓人失望。不過畢竟是AI的電腦視覺方面的東西,所以也就報有很大熱情學的,業餘還得鞏固基礎C、C++的書(等把C++Primer再看一遍後進軍SDK了,得等多久到MFC啊)。
CSDN開了博客,寫了些,後來才發現有學生大本營,決定在這安家落戶,畢竟還是學生嘛。
經過前面學習(圖形結構體數組相關操作,opencv自帶GUI控件,圖片的色彩變換以及座標變換),終於可以進入主題——圖像識別了。
圖像識別基礎是直方圖,在我看來好像一切都是重它開始的,比如說圖像匹配其實是源圖像直方圖和特徵圖像直方圖進行數學比較得到的。
啥是直方圖?
在我看來就是一張統計圖,統計各個像素在一定範圍內的數量。舉個例子:大家都知道一般RGB圖像有3個通道分別是R,G,B。就拿紅色通道R來說,它的範圍有0-255,直方圖就是把它分爲N個,每個塊是255/N(平均分),統計色彩在0-255/N的像素點有多少個,255/N-255/N*2的像素點,255/N*2-255/N*3的像素點...
瞭解直方圖的結構和操作可以通過下面我對比兩張圖片相識度來了解:
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
int main(int argc, char** argv)
{
IplImage *src = cvLoadImage("./lena.jpg");
IplImage *hsv = cvCreateImage(cvGetSize(src),8,3);
cvCvtColor(src,hsv,CV_BGR2HSV);// change RGB of src to HSV of hsv
IplImage *h_plane = cvCreateImage(cvGetSize(src),8,1);
IplImage *s_plane = cvCreateImage(cvGetSize(src),8,1);
IplImage *v_plane = cvCreateImage(cvGetSize(src),8,1);
IplImage *planes[]={h_plane,s_plane};
cvSplit(hsv,h_plane,s_plane,v_plane,0);//split HSV to 3 channal
//求得直方圖
int h_bins=30,s_bins=32;
CvHistogram *hist1,*hist2;
int size[]={h_bins,s_bins};
float h_ranges[]={0,180};
float s_ranges[]={0,255};
float *ranges[]={h_ranges,s_ranges};
hist1=cvCreateHist(2,size,CV_HIST_ARRAY,ranges,1);
cvCalcHist(planes,hist1,0,0);//只能一個通道一個通道的寫入直方圖,所以上面分成H、S、V
cvNormalizeHist(hist1,1.0);//歸一化直方圖,使所有塊的值加起來爲1
hist2=cvCreateHist(2,size,CV_HIST_ARRAY,ranges,1);
cvCalcHist(planes,hist2,0,0);
cvNormalizeHist(hist2,1.0);
//求得signature用於比較直方圖
CvMat *sig1,*sig2;
int numrows=h_bins*s_bins;
sig1=cvCreateMat(numrows,3,CV_32FC1);//由於是2維圖像直方圖,所以只需保存每個點的(值,橫座標,縱座標)三個數,一共numrows個點
sig2=cvCreateMat(numrows,3,CV_32FC1);
for(int h=0;h<h_bins;h++)
{
for(int s=0;s<s_bins;s++)
{
float bin_val=cvQueryHistValue_2D(hist1,h,s);
cvSet2D(sig1,h*s_bins+s,0,cvScalar(bin_val));
cvSet2D(sig1,h*s_bins+s,1,cvScalar(h));
cvSet2D(sig1,h*s_bins+s,2,cvScalar(s));
bin_val=cvQueryHistValue_2D(hist2,h,s);
cvSet2D(sig2,h*s_bins+s,0,cvScalar(bin_val));
cvSet2D(sig2,h*s_bins+s,1,cvScalar(h));
cvSet2D(sig2,h*s_bins+s,2,cvScalar(s));
}
}
float emd=cvCalcEMD2(sig1,sig2,CV_DIST_L2);
printf("%d/n",emd);//可以清楚的看出因爲我對比同一個圖像直方圖,所以得數0是對的,完全一樣
return 0;
}
程序不難,主要對於直方圖和rignature結構的理解!
當然opencv有封裝號的可以比較的函數——反向投影,模板匹配。
用直方圖比較來定位圖像中的模板
void cvCalcBackProjectPatch( IplImage** image, CvArr* dst,
CvSize patch_size, CvHistogram* hist,
int method, float factor );
image
輸入圖像 (可以傳遞 CvMat** )
dst
輸出圖像.
patch_size
掃描輸入圖像的補丁尺寸
hist
直方圖
method
比較方法,傳遞給 cvCompareHist (見該函數的描述).
factor
直方圖的歸一化因子,將影響輸出圖像的歸一化縮放。如果爲 1,則不定。
函數 cvCalcBackProjectPatch 通過輸入圖像補丁的直方圖和給定直方圖的比較,來計算反向投影。提取圖像在 ROI 中每一個位置的某種測量結果產生了數組 image. 這些結果可以是色調, x 差分, y 差分, Laplacian 濾波器, 有方向 Gabor 濾波器等中 的一個或多個。每種測量輸出都被劃歸爲它自己的單獨圖像。 image 圖像數組是這些測量圖像的集合。一個**直方圖 hist 從這些圖像數組中被採樣創建。最後直方圖被歸一化。直方圖 hist 的維數通常很大等於圖像數組 image 的元素個數。
在選擇的 ROI 中,每一個新的圖像被測量並且轉換爲一個圖像數組。在以錨點爲“補丁”中心的圖像 image 區域中計算直方圖 (如下圖所示)。用參數 norm_factor 來歸一化直方圖,使得它可以與 hist 互相比較。計算出的直方圖與直方圖模型互相比較, (hist 使用函數 cvCompareHist ,比較方法是 method=method). 輸出結果被放置到概率圖像 dst 補丁錨點的對應位置上。這個過程隨着補丁滑過整個 ROI 而重複進行。迭代直方圖的更新可以通過在原直方圖中減除“補丁”已覆蓋的尾象素點或者加上新覆蓋的象素點來實現,這種更新方式可以節省大量的操作,儘管目前在函數體中還沒有實現。
void cvMatchTemplate( const CvArr* image, const CvArr* templ,
CvArr* result, int method );
image
欲搜索的圖像。它應該是單通道、8-比特或32-比特 浮點數圖像
templ
搜索模板,不能大於輸入圖像,且與輸入圖像具有一樣的數據類型
result
比較結果的映射圖像。單通道、32-比特浮點數. 如果圖像是 W×H 而 templ 是 w×h ,則 result 一定是 (W-w+1)×(H-h+1).
method
指定匹配方法:
函數 cvMatchTemplate 與函數 cvCalcBackProjectPatch 類似。它滑動過整個圖像 image, 用指定方法比較 templ 與圖像尺寸爲 w×h 的重疊區域,並且將比較結果存到 result 中。 下面是不同的比較方法,可以使用其中的一種
method=CV_TM_SQDIFF:
method=CV_TM_SQDIFF_NORMED:
method=CV_TM_CCORR:
method=CV_TM_CCORR_NORMED:
method=CV_TM_CCOEFF:
給後面的MatchTempate加個例子把:
int main( int argc, char** argv ) {
IplImage *src, *templ,*ftmp[6]; //ftmp is what to display on
int i;
if( argc == 3){
//Read in the template to be used for matching:
if((templ=cvLoadImage(argv[1], 1))== 0) {
printf("Error on reading template %s/n",argv[2]); help();
return(-1);
}
//Read in the source image to be searched:
if((src=cvLoadImage(argv[2], 1))== 0) {
printf("Error on reading src image %s/n",argv[i]); help();
return(-1);
}
int patchx = templ->width;
int patchy = templ->height;
int iwidth = src->width - patchx + 1;
int iheight = src->height - patchy + 1;
for(i=0; i<6; ++i){
ftmp[i] = cvCreateImage( cvSize(iwidth,iheight),32,1);
}
//DO THE MATCHING OF THE TEMPLATE WITH THE IMAGE
for(i=0; i<6; ++i){
cvMatchTemplate( src, templ, ftmp[i], i);
// double min,max;
// cvMinMaxLoc(ftmp,&min,&max);
cvNormalize(ftmp[i],ftmp[i],1,0,CV_MINMAX);
}
//DISPLAY
cvNamedWindow( "Template", 0 );
cvShowImage( "Template", templ );
cvNamedWindow( "Image", 0 );
cvShowImage( "Image", src );
cvNamedWindow( "SQDIFF", 0 );
cvShowImage( "SQDIFF", ftmp[0] );
cvNamedWindow( "SQDIFF_NORMED", 0 );
cvShowImage( "SQDIFF_NORMED", ftmp[1] );
cvNamedWindow( "CCORR", 0 );
cvShowImage( "CCORR", ftmp[2] );
cvNamedWindow( "CCORR_NORMED", 0 );
cvShowImage( "CCORR_NORMED", ftmp[3] );
cvNamedWindow( "CCOEFF", 0 );
cvShowImage( "CCOEFF", ftmp[4] );
cvNamedWindow( "CCOEFF_NORMED", 0 );
cvShowImage( "CCOEFF_NORMED", ftmp[5] );
//LET USER VIEW RESULTS:
cvWaitKey(0);
}
return 0;
}