1. 程式人生 > >Opencv影象識別從零到精通(32)----直方圖對比,模版匹配,方向投影

Opencv影象識別從零到精通(32)----直方圖對比,模版匹配,方向投影

0、預備知識

歸一化就是要把需要處理的資料經過處理後(通過某種演算法)限制在你需要的一定範圍內。

函式原型:

<span style="font-size:18px;">void normalize(InputArray src,OutputArray dst, double alpha=1,doublebeta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() )
 </span>
<span style="font-size:18px;">該函式歸一化輸入陣列使它的範數或者數值範圍在一定的範圍內。
 
Parameters:
 src  輸入陣列
dst 輸出陣列,支援原地運算
alpha
range normalization模式的最小值
beta
range normalization模式的最大值,不用於norm normalization(範數歸一化)模式。
normType
 歸一化的型別,可以有以下的取值:
   NORM_MINMAX:陣列的數值被平移或縮放到一個指定的範圍,線性歸一化,一般較常用。
 
   NORM_INF: 此型別的定義沒有查到,根據OpenCV 1的對應項,可能是歸一化陣列的C-範數(絕對值的最大值)
 
   NORM_L1 :  歸一化陣列的L1-範數(絕對值的和)
 
   NORM_L2: 歸一化陣列的(歐幾里德)L2-範數
dtype
   dtype為負數時,輸出陣列的type與輸入陣列的type相同;
否則,輸出陣列與輸入陣列只是通道數相同,而tpye=CV_MAT_DEPTH(dtype).
mask
操作掩膜,用於指示函式是否僅僅對指定的元素進行操作</span>

歸一化公式:

1、線性函式轉換,表示式如下:(對應NORM_MINMAX

ifmask(i,j)!=0

   dst(i,j)=(src(i,j)-min(src))*(b‘-a‘)/(max(src)-min(src))+ a‘

else

    dst(i,j)=src(i,j)

其中b‘=MAX(a,b), a‘=MIN(a,b);

2. 當norm_type!=CV_MINMAX:

ifmask(i,j)!=0

   dst(i,j)=src(i,j)*a/norm (src,norm_type,mask)

else

   dst(i,j)=src(i,j)

其中,函式norm的功能是計算norm(範數)的絕對值

Thefunctions normcalculate an absolute norm of src1 (when there is no src2 ):


、將該數值儲存在新的影象中(BackProjection),也可以先歸一化hue直方圖數值到0-255範圍,這樣可以直接顯示BackProjection影象(單通道影象)。

mixChannels:   從輸入中拷貝某通道到輸出中特定的通道。

C++: voidmixChannels(const Mat*src, size_t nsrcs, Mat* dst, size_t ndsts, const int*fromTo, size_t npairs)
一、直方圖對比
double  compareHist(InputArray H1, //直方圖1 InputArray H2, //直方圖2    intmethod//對比方法 ); 

methodCV_COMP_CORREL CV_COMP_CHISQRCV_COMP_INTERSECTCV_COMP_BHATTACHARYYA四種方法,對應公式如下:


來個例項

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;

int main( )
{

	Mat srcImage_base, hsvImage_base;
	Mat srcImage_test1, hsvImage_test1;
	Mat srcImage_test2, hsvImage_test2;
	Mat hsvImage_halfDown;
	srcImage_base = imread( "1.jpg",1 );
	srcImage_test1 = imread( "2.jpg", 1 );
	srcImage_test2 = imread( "3.jpg", 1 );
	imshow("基準影象",srcImage_base);
	imshow("測試影象1",srcImage_test1);
	imshow("測試影象2",srcImage_test2);
	cvtColor( srcImage_base, hsvImage_base,  COLOR_BGR2HSV );
	cvtColor( srcImage_test1, hsvImage_test1, COLOR_BGR2HSV );
	cvtColor( srcImage_test2, hsvImage_test2, COLOR_BGR2HSV );
	hsvImage_halfDown = hsvImage_base( Range( hsvImage_base.rows/2, hsvImage_base.rows - 1 ), Range( 0, hsvImage_base.cols - 1 ) );
	int h_bins = 50; int s_bins = 60;
	int histSize[] = { h_bins, s_bins };
	float h_ranges[] = { 0, 256 };
	float s_ranges[] = { 0, 180 };
	const float* ranges[] = { h_ranges, s_ranges };
	int channels[] = { 0, 1 };
	MatND baseHist;
	MatND halfDownHist;
	MatND testHist1;
	MatND testHist2;
	calcHist( &hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges, true, false );
	normalize( baseHist, baseHist, 0, 1, NORM_MINMAX, -1, Mat() );
	calcHist( &hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, histSize, ranges, true, false );
	normalize( halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat() );
	calcHist( &hsvImage_test1, 1, channels, Mat(), testHist1, 2, histSize, ranges, true, false );
	normalize( testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat() );
	calcHist( &hsvImage_test2, 1, channels, Mat(), testHist2, 2, histSize, ranges, true, false );
	normalize( testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat() );
	for( int i = 0; i < 4; i++ )
	{ 
	
		int compare_method = i;
		double base_base = compareHist( baseHist, baseHist, compare_method );
		double base_half = compareHist( baseHist, halfDownHist, compare_method );
		double base_test1 = compareHist( baseHist, testHist1, compare_method );
		double base_test2 = compareHist( baseHist, testHist2, compare_method );
		printf( " 方法 [%d] 的匹配結果如下:\n\n 【基準圖 - 基準圖】:%f, 【基準圖 - 半身圖】:%f,【基準圖 - 測試圖1】: %f, 【基準圖 - 測試圖2】:%f \n-----------------------------------------------------------------\n", i, base_base, base_half , base_test1, base_test2 );
	}

	printf( "檢測結束。" );
	waitKey(0);
	return 0;
}


二、模版匹配

        模板匹配是一項在一幅影象中尋找與另一幅模板影象最匹配(相似)部分的技術. 模板匹配是一種用於在源影象S中尋找定位給定目標影象T(即模板影象)的技術。其原理很簡單,就是通過一些相似度準則來衡量兩個影象塊之間的相似度Similarity(S,T)。

     通過 模版滑動, 我們的意思是影象塊一次移動一個畫素 (從左往右,從上往下). 在每一個位置, 都進行一次度量計算來表明它是 “好” 或 “壞” 地與那個位置匹配 (或者說塊影象和原影象的特定區域有多麼相似).

    對於 T模版覆蓋在 I原圖 上的每個位置,你把度量值 儲存 到 結果影象矩陣 (R) 中. 在 R 中的每個位置 (x,y) 都包含匹配度量值: 最白的位置代表最高的匹配. 正如您所見, 紅色橢圓框住的位置很可能是結果影象矩陣中的最大數值, 所以這個區域 (以這個點為頂點,長寬和模板影象一樣大小的矩陣) 被認為是匹配的. 我們使用函式 minMaxLoc 來定位在矩陣 R 中的最大值點 (或者最小值, 根據函式輸入的匹配引數) .

目標匹配函式:

Void MatchTemplate( InputArray  image, InputArray  temp1, OutputArray  result,int method );
Image  待搜尋影象
Templ  模板影象
Result  匹配結果  用來存放通過以下方法計算出滑動視窗與模板的相似值
Method  計算匹配程度的方法
  關於匹配方法,使用不同的方法產生的結果的意義可能不太一樣,有些返回的值越大表示匹配程度越好,而有些方法返回的值越小表示匹配程度越好
關於引數 method:
TM_SQDIFF平方差匹配法:該方法採用平方差來進行匹配;最好的匹配值為0;匹配越差,匹配值越大。
TM_CCORR相關匹配法:該方法採用乘法操作;數值越大表明匹配程度越好。
TM_CCOEFF相關係數匹配法:1表示完美的匹配;-1表示最差的匹配。
TM_SQDIFF_NORMED歸一化平方差匹配法
CV_TM_CCORR_NORMED歸一化相關匹配法
CV_TM_CCOEFF_NORMED歸一化相關係數匹配法
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";
int match_method;
int max_Trackbar = 5;
void MatchingMethod( int, void* );
int main( int argc, char** argv )
{
  img = imread("1.jpg" , 1 );
  templ = imread( "2.jpg", 1 );
  namedWindow( image_window, CV_WINDOW_AUTOSIZE );
  namedWindow( result_window, CV_WINDOW_AUTOSIZE );
  char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
  createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
  MatchingMethod( 0, 0 );
  waitKey(0);
  return 0;
}

void MatchingMethod( int, void* )
{

  Mat img_display;
  img.copyTo( img_display );
  int result_cols =  img.cols - templ.cols + 1;
  int result_rows = img.rows - templ.rows + 1;
  result.create( result_cols, result_rows, CV_32FC1 );
  matchTemplate( img, templ, result, match_method );
  normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
  double minVal; double maxVal; Point minLoc; Point maxLoc;
  Point matchLoc;
  minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
  if( match_method  == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
    { matchLoc = minLoc; }
  else
    { matchLoc = maxLoc; }
  rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
  rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
  imshow( image_window, img_display );
  imshow( result_window, result );
  return;
}



三、反向投影

        什麼是反向投影直方圖呢?簡單的說在灰度影象的每個點(x,y),用它對應的直方圖的bin的值(就是有多少畫素落在bin內)來代替它。所以·如果這個bin的值比較大,那麼反向投影顯示的結果會比較亮,否則就比較暗。

從統計學的角度,反輸出影象象素點的值是觀測陣列在某個分佈(直方圖)下的的概率。

所以加入我們已經得到了一個物體的直方圖,我們可以計算它在另一幅影象中的反向投影,來判斷這幅影象中是否有該物體。

1.反向投影的作用是什麼?

反向投影用於在輸入影象(通常較大)中查詢特定影象(通常較小或者僅1個畫素,以下將其稱為模板影象)最匹配的點或者區域,也就是定位模板影象出現在輸入影象的位置。

2.反向投影如何查詢(工作)?

查詢的方式就是不斷的在輸入影象中切割跟模板影象大小一致的影象塊,並用直方圖對比的方式與模板影象進行比較。

3.反向投影的結果是什麼?

      反向投影的結果包含了:以每個輸入影象畫素點為起點的直方圖對比結果。可以把它看成是一個二維的浮點型陣列,二維矩陣,或者單通道的浮點型影象。backproject是直接取直方圖中的值,即以灰度為例,某種灰度值在整幅影象中所佔面積越大,其在直方圖中的值越大,backproject時,其對應的畫素的新值越大(越亮),反過來,某灰度值所佔面積越小,其新值就越小。

假設我們有一張100x100的輸入影象,有一張10x10的模板影象,查詢的過程是這樣的:

(1)從輸入影象的左上角(0,0)開始,切割一塊(0,0)至(10,10)的臨時影象;

(2)生成臨時影象的直方圖;

(3)用臨時影象的直方圖和模板影象的直方圖對比,對比結果記為c;

(4)直方圖對比結果c,就是結果影象(0,0)處的畫素值;

(5)切割輸入影象從(0,1)至(10,11)的臨時影象,對比直方圖,並記錄到結果影象;

(6)重複(1)~(5)步直到輸入影象的右下角。

     原圖中以某點為基準,摳出來作對比的部分也轉換為直方圖,兩個直方圖作匹配,匹配的結果作為此點的值。結果會是一張概率圖,概率越大的地方,代表此區域與模板的相似度越高。

利用Hue直方圖解釋反向投影原理:

1、獲取測試影象中每個畫素的hue資料 hi,j,並找到 hi,j 在hue直方圖中的bin的位置。

2、查詢hue直方圖中對應bin的數值。

3、將該數值儲存在新的影象中(BackProjection),也可以先歸一化hue直方圖數值到0-255範圍,這樣可以直接顯示BackProjection影象(單通道影象)。

4、通過對測試影象每個畫素採取以上步驟,可以得到最終的BackProjection影象。

void cvCalcBackProject( IplImage** image, CvArr* back_project, const CvHistogram* hist );
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
#define WINDOW_NAME1 "【原始圖】"        
Mat g_srcImage; Mat g_hsvImage; Mat g_hueImage;
int g_bins = 30;//直方圖組距
void on_BinChange(int, void* );
int main( )
{

	g_srcImage = imread( "1.jpg", 1 );
	if(!g_srcImage.data ) { printf("讀取圖片錯誤,請確定目錄下是否有imread函式指定圖片存在~! \n"); return false; } 
	cvtColor( g_srcImage, g_hsvImage, COLOR_BGR2HSV );
	g_hueImage.create( g_hsvImage.size(), g_hsvImage.depth() );
	int ch[ ] = { 0, 0 };
	mixChannels( &g_hsvImage, 1, &g_hueImage, 1, ch, 1 );
	namedWindow( WINDOW_NAME1 , WINDOW_AUTOSIZE );
	createTrackbar("色調組距 ", WINDOW_NAME1 , &g_bins, 180, on_BinChange );
	on_BinChange(0, 0);//進行一次初始化
	imshow( WINDOW_NAME1 , g_srcImage );
	waitKey(0);
	return 0;
}

void on_BinChange(int, void* )
{

	MatND hist;
	int histSize = MAX( g_bins, 2 );
	float hue_range[] = { 0, 180 };
	const float* ranges = { hue_range };
	calcHist( &g_hueImage, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
	normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
	MatND backproj;
	calcBackProject( &g_hueImage, 1, 0, hist, backproj, &ranges, 1, true );
	imshow( "反向投影圖", backproj );
	int w = 400; int h = 400;
	int bin_w = cvRound( (double) w / histSize );
	Mat histImg = Mat::zeros( w, h, CV_8UC3 );
	for( int i = 0; i < g_bins; i ++ )
	{ rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ),
           <span style="font-family: Arial, Helvetica, sans-serif;"> Scalar( 100, 123, 255 ), -1 ); }
</span><pre name="code" class="cpp" style="color: rgb(63, 63, 63);">	imshow( "直方圖", histImg );
}





影象識別演算法交流 QQ群:145076161,歡迎影象識別與影象演算法,共同學習與交流