目标跟踪--模板匹配

  • 模板匹配原理

  • 算法步骤

  • opencv调用函数

  1. matchTemplate
  2. minMaxLoc
  3. cvSetMouseCallback
  4. Rect
  5. 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;
}

发布了176 篇原创文章 · 获赞 84 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/try_again_later/article/details/103408004