目的:
直方圖在cv領域到處可見,因為其功能在cv算法的實現中必不可少。Opencv庫中也集成了關於直方圖的不少函數,比如直方圖的計算,均衡,歸一化,相似度比較等等。
為了體驗這些函數,做了個小實驗,功能是:打開攝像頭,鼠標選定一個框,框內圖像作為標准圖像,計算出其直方圖並顯示出來;然后繼續鼠標選定框,該框內的圖像的直方
圖與標准圖像的進行相似度計算,計算結果在終端輸出,數值越大表示相似度越大。
工程環境:opencv2.3.1+vs2010。
工程代碼:
1 // hist_test.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <opencv2/core/core.hpp> 6 #include <opencv2/imgproc/imgproc.hpp> 7 #include <opencv2/highgui/highgui.hpp> 8 #include <iostream> 9 #include <stdio.h> 10 11 using namespace cv; 12 using namespace std; 13 14 int nFrame_num=0; 15 bool pause=false; 16 bool tracking=false; 17 Rect preselectROI,selectROI;//用於存放手選的矩形 18 bool comp=true; 19 20 Mat rhist,ghist,bhist; 21 int channels[]={0,1,2}; 22 //const int histsize[]={256,256,256}; 23 const int histsize[]={16,16,16}; 24 const int histsize1=16; 25 float rranges[]={0,255}; 26 float granges[]={0,255}; 27 float branges[]={0,255}; 28 float range[]={0,255}; 29 const float *ranges1={range};//這里的ranges就相當於一個雙指針了 30 const float *ranges[]={rranges,granges,branges};//ranges是個雙指針,且前面一定要用const,即不可改變常量,提高程序的可讀性和穩健性 31 //const float *ranges[]={{0,255},{0,255},{0,255}}; 32 33 void onMouse(int event,int x,int y,int,void *) 34 { 35 if(event==CV_EVENT_LBUTTONDOWN) 36 { 37 selectROI.x=x; 38 selectROI.y=y; 39 tracking=false; 40 } 41 else if(event==CV_EVENT_LBUTTONUP) 42 { 43 selectROI.width=x-selectROI.x; 44 selectROI.height=y-selectROI.y; 45 tracking=true; 46 comp=true; 47 nFrame_num++;//選定后才算真正意義上的第一幀 48 if(nFrame_num>=10)nFrame_num=10;//防止nFrame_num溢出 49 } 50 } 51 52 int main(int argc, const char* argv[]) 53 { 54 Mat frame,img; 55 Mat staRoiHist; 56 MatND RoiHist; 57 int DRAW_H=400,DRAW_W=400; 58 Mat draw(DRAW_W,DRAW_H,CV_8UC3,Scalar(0,0,0));//建立一個顯示直方圖的圖片,背景為純黑色 59 int DRAW_BIN_W=cvRound(DRAW_W/histsize1); 60 61 /****打開攝像頭****/ 62 VideoCapture cam(0); 63 if(!cam.isOpened()) 64 return -1; 65 66 /****鼠標捕捉****/ 67 namedWindow("camera",1); 68 namedWindow("rgb_hist",1); 69 setMouseCallback("camera",onMouse,0);//這里用的是面向對象的思想,只要有鼠標動作就會調用鼠標響應函數 70 71 while(1) 72 { 73 if(!pause)//暫停按鈕只需控制視頻的讀取 74 { 75 cam>>frame; 76 if(frame.empty()) 77 break;//break此處跳出的是while語句,一般是跳出for或while語句,不要理解為跳出if語句 78 } 79 /* if(1==nFrame_num) 80 { 81 82 }*/ 83 if(tracking) 84 { 85 Mat RoiImage(frame,selectROI); 86 87 /*************************************************************************************************************************/ 88 /**** calcHist():計算圖像塊的直方圖矩陣 ****/ 89 /****calcHist(),第1個參數為原數組區域列表;第二個參數為有計算幾個原數組;參數3為需要統計的通道索引數;參數4為操作掩碼****/ 90 /****第5個參數為存放目標直方圖矩陣;參數6為需要計算的直方圖的維數;參數7為每一維的bin的個數;參數8為每一維數值的取值范圍****/ 91 /****參數10為每個bin的大小是否相同的標志,默認為1,即bin的大小都相同;參數11為直方圖建立時清除內存痕跡標志,默認為0,即清除****/ 92 /*************************************************************************************************************************/ 93 calcHist(&RoiImage,1,channels,Mat(),RoiHist,3,histsize,ranges);//原數組區域RoiImage,1個源,需要統計的通道索引為{0,1,2}, 94 //目標直方圖RoiHist,3維,每一維的bin數histsize,取值范圍為 95 //ranges,實際上計算出的目標矩陣類似一維矩陣。 96 97 98 /*************************************************************************************************************************/ 99 /**** normalize():根據某種范數或者數值范圍歸一化數組 ****/ 100 /**** normalize(),參數1表示需要歸一化的數組;參數2為歸一化后的目的數組;參數3表示輸出數值的最小值/最大值或者輸出數值的范數;****/ 101 /**** 參數4表示輸出數值的最小值/最大值;參數5表示歸一化數組使用的歸一化類型,默認值為使用L2范數;參數6為對應元素的掩膜矩陣 ****/ 102 /**** 默認值為空,即不采用掩膜操作 ****/ 103 /*************************************************************************************************************************/ 104 normalize(RoiHist,RoiHist);//使用L2范數將RoiHist直方圖原地歸一化 105 106 vector<Mat> rgb_planes;//這里的vector為向量,向量的數據類型為Mat結構體,向量的長度為3 107 split(RoiImage,rgb_planes);//將rgb圖分解到rgb_planes各個分量中 108 calcHist(&rgb_planes[0],1,0,Mat(),rhist,1,&histsize1,&ranges1); 109 normalize(rhist,rhist,0,DRAW_H,NORM_MINMAX);//進行最大最小值歸一化 110 calcHist(&rgb_planes[1],1,0,Mat(),ghist,1,&histsize1,&ranges1); 111 normalize(ghist,ghist,0,DRAW_H,NORM_MINMAX); 112 calcHist(&rgb_planes[2],1,0,Mat(),bhist,1,&histsize1,&ranges1); 113 normalize(bhist,bhist,0,DRAW_H,NORM_MINMAX); 114 if(nFrame_num==1) 115 { 116 // preselectROI=selectROI; 117 preselectROI.x=selectROI.x; 118 preselectROI.y=selectROI.y; 119 preselectROI.width=selectROI.width; 120 preselectROI.height=selectROI.height; 121 staRoiHist=RoiHist.clone();//第一次選定目標,作為標准模板目標 122 } 123 else if(nFrame_num>1&&comp==true) 124 { 125 /*************************************************************************************************************************/ 126 /**** compareHist():比較2個直方圖的相似度 ****/ 127 /**** compareHist(),參數1為比較相似度的直方圖1;參數2為比較相似度的直方圖2;參數3為相似度的計算方式。有四種, ****/ 128 /**** 分別為CV_COMP_CORREL,CV_COMP_CHISQR,CV_COMP_INTERSECT,CV_COMP_BHATTACHARYYA ****/ 129 /*************************************************************************************************************************/ 130 double distence=compareHist(staRoiHist,RoiHist,CV_COMP_INTERSECT);//計算后面選定的與這次選定的相似度,使用INTERSECT,值越大越相似 131 printf("與第1次選定的圖像區域相似度為:%f\n",distence);//數組越大,相似度越大 132 133 //顯示直方圖 134 for(int i=1;i<histsize1;i++) 135 { 136 //畫直線中要注意2點,因為圖片的原點在左上角,而直方圖坐標系的原點在左下角,所以高度值都需要被直方圖圖紙高度減掉,另外取一維直方圖時只能用at運算符 137 line(draw,Point(DRAW_BIN_W*(i-1),DRAW_H-cvRound(rhist.at<float>((i-1)))),Point(DRAW_BIN_W*(i),DRAW_H-cvRound(rhist.at<float>(i))),Scalar(255,0,0),2,8,0); 138 line(draw,Point(DRAW_BIN_W*(i-1),DRAW_H-cvRound(ghist.at<float>((i-1)))),Point(DRAW_BIN_W*(i),DRAW_H-cvRound(ghist.at<float>(i))),Scalar(0,255,0),2,8,0); 139 line(draw,Point(DRAW_BIN_W*(i-1),DRAW_H-cvRound(bhist.at<float>((i-1)))),Point(DRAW_BIN_W*(i),DRAW_H-cvRound(bhist.at<float>(i))),Scalar(0,0,255),2,8,0); 140 } 141 imshow("rgb_hist",draw); 142 draw=Mat::zeros(DRAW_W,DRAW_H,CV_8UC3);//每畫完一次直方圖后都進行一次清0操作 143 comp=false; 144 } 145 rectangle(frame,selectROI,Scalar(0,255,0),2,8);//手動選定一次就顯示一次 146 }//end tracking 147 rectangle(frame,preselectROI,Scalar(0,0,255),5,8);//初始的選定目標一直不變 148 imshow("camera",frame); 149 150 //鍵盤響應 151 char c = (char)waitKey(10); 152 if( c == 27 ) 153 break; 154 switch(c) 155 { 156 case 'p'://暫停鍵 157 pause = !pause; 158 break; 159 default: 160 ; 161 } 162 }//end while; 163 return 0; 164 }
實驗結果:
選定框內的模板用紅色框標出,其他待比較的模板用綠色框標出。
模板圖像塊的簡單直方圖(rgb分開畫的)顯示如下:
第一次比較結果圖:
第二次比較結果圖:
第三次比較結果圖(2個框選定的基本重合):
三次比較相似度結果:
可以看出,第三次的框與標准框內圖像(即第1次選定的圖像區域)的相似度值最大。