opencv筆記(3)——模板匹配實現目標識別與跟蹤
阿新 • • 發佈:2018-12-27
1 知識補充
1.1 回撥函式
在影象處理時,如果我們需要實現實時的改變值,並重新開始程式,就需要我們自己實現回撥函式,其中,對於滑鼠事件的回撥,需要我們重寫滑鼠回撥函式void onMouse(int event, int x, int y, int flags, void* ustc); //滑鼠回撥函式
函式中的主要引數:
- event對應於滑鼠事件
- x,y滑鼠的位置引數
- flags標誌位
- ustc(不常用)
其中,int型event事件分別有:
- EVENT_MOUSEMOVE 滑動
- EVENT_LBUTTONDOWN 左擊
- EVENT_RBUTTONDOWN 右擊
- EVENT_MBUTTONDOWN 中鍵點選
- EVENT_LBUTTONUP 左鍵放開
- EVENT_RBUTTONUP 右鍵放開
- EVENT_MBUTTONUP 中鍵放開
- EVENT_LBUTTONDBLCLK 左鍵雙擊
- EVENT_RBUTTONDBLCLK 右鍵雙擊
- EVENT_MBUTTONDBLCLK 中鍵雙擊
重寫回調函式後,我們只要使用setMouseCallback函式開啟呼叫即可使用
minMaxLoc()函式找最大最小值
函式原型:void minMaxLoc(InputArray src,double * minVal=0,double* maxVal=0,Point* minLoc=0,Point* maxLoc=0,InputArray mask=noArray())
引數說明:
- InputArray型的src,是單通道的輸入陣列
- double * 型的minVal,返回最小值的指標,無返回則是NULL
- double * 型的maxVal,返回最大值的指標,無返回則是NULL
- Point * 型的minLoc,返回最小值的指標(二維),無返回則是NULL
- Point * 型的miaxLoc,返回最大值的指標(二維),無返回則是NULL
- InputArray型的mask,選擇可選掩膜
模板匹配函式matchTemplate()
模板匹配是從一張圖片裡找到與另一模板影象最匹配的部分
函式原型:void matchTemplate(InputArray image,InputArray templ,OutputArray result,int method)
引數說明:
- 第一個引數,是待搜尋的圖片,為8位或32位浮點圖
- 第二個引數,是帶匹配的圖片,格式應與源圖相同
- 第三個引數,輸出結果圖片,單通道,32位浮點型
- 第四個引數,匹配方式,共六種
六種匹配方式及引數:
- 平方差匹配 method=TM_SQDIFF
- 歸一化平方差匹配 method=TM_SQDIFF_NORMED
- 相關匹配 method=TM_CCORR
- 歸一化相關匹配method=TM_CCORR_NORMED
- 係數匹配 method=TM_CCOEFF
- 化相關係數匹配method=TM_CCOEFF_NORMED
注意:
對於1和2兩種匹配方式,最小值為最好匹配,其餘的最大值為最好匹配
程式碼思路
首先讀取視訊,並且提取幀影象,使用回撥函式選中模板影象, 開始在下一幀中匹配,每次匹配完,都擷取相應位置的圖,作為下一次匹配的模板圖,並且用框框框選,輸出到輸出檔案中
可能有幫助的解釋
對於matchTemplate函式中result大小的解釋:
比如源圖尺寸為WH,待匹配圖為wh,那麼輸出結果大小就是(W-w+1)(H-h+1),原因是匹配是拿著小圖在大圖上移動來計算值的,所以移動的範圍就是(W-w+1)(H-h+1),計算的結果就儲存在這個結果矩陣中
總體程式碼及註釋
#include <core/core.hpp>
#include <highgui/highgui.hpp>
#include <imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
Mat image; //當前幀影象
Mat imageCopy; //用於拷貝的當前幀影象
Mat rectImage; //子影象
bool leftButtonDownFlag = false; //左鍵單擊後視訊暫停標誌位
Point beginPoint; //矩形框起點
Point endPoint; //矩形框終點
int resultRows; //模板匹配result的行
int resultcols; //模板匹配result的列
Mat ImageResult; //模板匹配result
double minValue; //模板匹配result最小值
double maxValude; //模板匹配result最大值
Point minPoint; //模板匹配result最小值位置
Point maxPoint; //模板匹配result最大值位置
int frameCount = 0; //幀數統計
void onMouse(int event, int x, int y, int flags, void* ustc); //滑鼠回撥函式
void Tips() {
cout << "請選擇要使用的匹配規則:" << endl;
cout << " 1.平方差匹配" << endl;
cout << " 2.歸一化平方差匹配" << endl;
cout << " 3.相關匹配" << endl;
cout << " 4.歸一化相關匹配" << endl;
cout << " 5.係數匹配" << endl;
cout << " 6.化相關係數匹配" << endl;
cout << " 0.退出" << endl;
}
int main(int argc, char*argv[])
{
VideoCapture capture("C:\\Users\\14527\\Desktop\\Video\\cut.AVI");
int capture_fps = capture.get(CV_CAP_PROP_FPS); //獲取視訊幀率
int capture_count = capture.get(CV_CAP_PROP_FRAME_COUNT);
int capture_width = capture.get(CV_CAP_PROP_FRAME_WIDTH);
int capture_height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
cout << "視訊幀率:" << capture_fps<<endl;
cout << "視訊幀數:" << capture_count << endl;
cout << "視訊寬度:" << capture_width << endl;
cout << "視訊高度:" << capture_height << endl;
int pauseTime = 1000 / capture_fps; //兩幅畫面中間間隔
VideoWriter writer("C:\\Users\\14527\\Desktop\\Video\\out.avi", CV_FOURCC('X', 'V', 'I', 'D'), capture_fps, Size(capture_width, capture_height));
Tips();
int choice = 0;
cin >> choice;
namedWindow("Video");
setMouseCallback("Video", onMouse); //設定滑鼠回撥函式
while (choice)
{
if (!leftButtonDownFlag) //滑鼠左鍵按下繪製矩形時,視訊暫停播放
{
capture >> image;
frameCount++; //幀數
}
if (!image.data || waitKey(pauseTime + 30) == 27) //影象為空或Esc鍵按下退出播放
{
break;
}
if (rectImage.data)
{
ImageResult = Mat::zeros(resultRows, resultcols, CV_32FC1);//建立結果矩陣,注意是單通道的32位浮點型
switch (choice) { //根據一開始的選擇使用對應的匹配模式
case 1:
matchTemplate(image, rectImage, ImageResult, TM_SQDIFF);
break;
case 2:
matchTemplate(image, rectImage, ImageResult, TM_SQDIFF_NORMED);
break;
case 3:
matchTemplate(image, rectImage, ImageResult, TM_CCORR);
break;
case 4:
matchTemplate(image, rectImage, ImageResult, TM_CCORR_NORMED);
break;
case 5:
matchTemplate(image, rectImage, ImageResult, TM_CCOEFF);
break;
case 6:
matchTemplate(image, rectImage, ImageResult, TM_CCOEFF_NORMED);
break;
}
minMaxLoc(ImageResult, &minValue, &maxValude, &minPoint, &maxPoint, Mat()); //最小值最大值獲取
Point point;
switch (choice) {//為了統一處理,所以先把Point取出來
case 1:
point = minPoint;
break;
case 2:
point = minPoint;
break;
case 3:
point = maxPoint;
break;
case 4:
point = maxPoint;
break;
case 5:
point = maxPoint;
break;
case 6:
point = maxPoint;
break;
}
rectangle(image, point, Point(point.x + rectImage.cols, point.y + rectImage.rows), Scalar(0, 0, 255), 2); //繪製
//更新當前模板匹配的模板
Mat resultImage = image(Rect(point, Point(point.x + rectImage.cols, point.y + rectImage.rows)));
rectImage = resultImage.clone();
//當前幀數輸出到視訊流
writer << image;
}
imshow("Video", image);
}
return 0;
}
//滑鼠回撥函式
void onMouse(int event, int x, int y, int flags, void *ustc)
{
if (event == CV_EVENT_LBUTTONDOWN) //檢測到左鍵按下時
{
leftButtonDownFlag = true; //標誌位為true,也就是停止讀取下一幀影象
beginPoint = Point(x, y); //設定左鍵按下點的矩形起點
endPoint = beginPoint;
}
if (event == CV_EVENT_MOUSEMOVE && leftButtonDownFlag)
{ //當滑鼠移動且之前左鍵有按下的話
imageCopy = image.clone();
endPoint = Point(x, y);
if (beginPoint != endPoint)
{
//在複製的影象上繪製矩形
rectangle(imageCopy, beginPoint, endPoint, Scalar(0, 0, 255), 2);
}
//imshow("Video", imageCopy);
}
if (event == CV_EVENT_LBUTTONUP) //左鍵放開時,開始匹配
{
leftButtonDownFlag = false;
Mat subImage = image(Rect(beginPoint, endPoint)); //擷取影象
rectImage = subImage.clone(); //給全域性的待匹配影象
resultRows = image.rows - rectImage.rows + 1; //輸出結果影象的行數及列數
resultcols = image.cols - rectImage.rows + 1;
//imshow("Sub Image", rectImage);
}
}