-
模板匹配原理
-
算法步骤
-
opencv调用函数
- matchTemplate
- minMaxLoc
- cvSetMouseCallback
- Rect
- rectangle
-
代码
模板匹配原理
根据已知模板图像T在被搜索图S中平移,寻找与模板相似度最大的区域,并记录坐标。
其中,子图S(i,j)与模板有相同的尺寸、方向。
算法步骤
(1)首先取得待跟踪的目标图像,该目标图像常较小,称该图像为模板,以T 表示;
(2)定义匹配公式以备在移动模板时得到匹配度;
(3)把模板T 在待检测的图像(往往是视频中的一帧帧图像)中移动,在模板覆盖下那块搜索图叫做子图,每移动到一个位置就按定义的匹配公式计算匹配度,直至移动完整幅图像为止;
(4)按照匹配度的大小,选择匹配度最大(即最匹配)的位置,此位置即为最佳匹配位置,以此点为起始点,范围的大小就以模板的大小为准,即可锁定目标;
(5)针对视频中的每一帧图像执行步骤(3)~(4),这样就达到了跟踪的目的。
核心在于计算模板T与子图S(i,j)的相似程度。
opencv调用函数
Image为源图像,temp为模板图像,result为各个位置相似度的结果矩阵,method为相似度评价准则。
----------------------越小的值表示越匹配--------------------------
CV_TM_SQDIFF 平方差匹配法,最好的匹配为0,值越大匹配越差;Sumof Squared Difference (SSD) 差值的平方和
CV_TM_SQDIFF_NORMED 归一化平方差匹配法;
--------------------------值越大越匹配----------------------------
CV_TM_CCORR 相关匹配法,采用乘法操作,数值越大表明匹配越好;
CV_TM_CCORR_NORMED 归一化相关匹配法;
CV_TM_CCOEFF 相关系数匹配法,最好的匹配为1,-1表示最差的匹配;
CV_TM_CCOEFF_NORMED 归一化相关系数匹配法;
寻找矩阵中最大最小值以及对应坐标。
第一个参数为窗户名,第二个为回调函数,鼠标消息处理函数,第三个为传给鼠标消息的参数。
回调函数:
有些库函数要求应用先传给它一个函数,好在合适的时候调用完成任务。这个被传入的、后被调用的函数成为回调函数。
例子:你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数。
Rect(x,y,width,height),x, y 为左上角坐标, width, height 则为长和宽。
Rect的成员变量有:
rect.x 、rect.y、rect.width、rect.height,分别为左上角点的坐标和矩形的宽和高
rect.area();// 面积
rect.size();// 尺寸
Rect的操作:
// 求两个矩形的交集和并集
rect = rect1 & rect2;
rect = rect1 | rect2;
// 平移和缩放
rect = rect + Point(-100, 100); //平移,也就是左上顶点的x坐标-100,y坐标+100
rect = rect + Size(-100, 100); //缩放,左上顶点不变,宽度-100,高度+100
//OpenCV貌似也没有获取矩形中心点的功能,还是自己写一个
Point getCenterPoint(Rect rect)
{
Point cpt;
cpt.x = rect.x + cvRound(rect.width/2.0);
cpt.y = rect.y + cvRound(rect.height/2.0);
return cpt;
}
代码
对于视频来说,运行会先显示第一帧,然后我们用鼠标框选要跟踪的目标,然后跟踪器开始跟踪每一帧。
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
// Global variables
Rect box;
bool drawing_box = false;
bool gotBB = false;
// bounding box mouse callback
void mouseHandler(int event, int x, int y, int flags, void *param){
switch (event){
case CV_EVENT_MOUSEMOVE:
if (drawing_box){
box.width = x - box.x;
box.height = y - box.y;
}
break;
case CV_EVENT_LBUTTONDOWN:
drawing_box = true;
box = Rect(x, y, 0, 0);
break;
case CV_EVENT_LBUTTONUP:
drawing_box = false;
if (box.width < 0){
box.x += box.width;
box.width *= -1;
}
if (box.height < 0){
box.y += box.height;
box.height *= -1;
}
gotBB = true;
break;
}
}
// tracker: get search patches around the last tracking box,
// and find the most similar one
void tracking(Mat frame, Mat &model, Rect &trackBox)
{
Mat gray;
cvtColor(frame, gray, CV_RGB2GRAY);
Rect searchWindow;// 原搜索框附近扩大一些
searchWindow.width = trackBox.width * 3;
searchWindow.height = trackBox.height * 3;
searchWindow.x = trackBox.x + trackBox.width * 0.5 - searchWindow.width * 0.5;// 顶点分别向左向上移增长部分的一般
searchWindow.y = trackBox.y + trackBox.height * 0.5 - searchWindow.height * 0.5;
searchWindow &= Rect(0, 0, frame.cols, frame.rows);// 和原图进行交集
Mat similarity;// 输出Mat矩阵每个点表示相似度
matchTemplate(gray(searchWindow), model, similarity, CV_TM_CCOEFF_NORMED);// 只搜索扩大的搜索框,两个都是Mat类型
double mag_r;// 相似度最大值
Point point;// 最大相似度的坐标(相对于搜索框的)
minMaxLoc(similarity, 0, &mag_r, 0, &point);
trackBox.x = point.x + searchWindow.x;
trackBox.y = point.y + searchWindow.y;
//model = gray(trackBox); // 更新追踪框
}
int main(int argc, char * argv[])
{
// 打开视频
VideoCapture capture;
capture.open("./out.avi");
bool fromfile = true;
//Init camera
if (!capture.isOpened())
{
cout << "capture device failed to open!" << endl;
return -1;
}
// 鼠标操作Register mouse callback to draw the bounding box
cvNamedWindow("Tracker", CV_WINDOW_AUTOSIZE);
cvSetMouseCallback("Tracker", mouseHandler, NULL);
// 帧,模板
Mat frame, model;
capture >> frame;
while (!gotBB)// 没有鼠标点击
{
if (!fromfile)
capture >> frame;
imshow("Tracker", frame);
if (cvWaitKey(20) == 'q')
return 1;
}
//Remove callback
cvSetMouseCallback("Tracker", NULL, NULL);
Mat gray;
cvtColor(frame, gray, CV_RGB2GRAY);
model = gray(box);// 框
int frameCount = 0;
while (1)
{
capture >> frame;
if (frame.empty())
return -1;
double t = (double)cvGetTickCount();
frameCount++;
// 主函数 tracking
tracking(frame, model, box);// 原图像,框的图像,Rect框(会更新)
// show
stringstream buf;
buf << frameCount;
string num = buf.str();
putText(frame, num, Point(20, 20), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 3);
rectangle(frame, box, Scalar(0, 0, 255), 3);
imshow("Tracker", frame);
t = (double)cvGetTickCount() - t;
cout << "cost time: " << t / ((double)cvGetTickFrequency()*1000.) << endl;
if (cvWaitKey(1) == 27)
break;
}
return 0;
}