要点初见:OpenCV3中基于模板匹配的目标跟踪思路

     前文链接:要点初见:从旅行青蛙开始的OpenCV3中模板匹配的探索


     图像处理中有着目标识别与目标跟踪两种概念,后者也被常被成为Tracking。网上大部分的目标捕捉教程都是“目标识别”,譬如特征提取、光流法等等。然而将目标识别与目标跟踪结合使用,能稳定捕捉频率提高性能


     先谈谈为什么单纯使用目标识别不能“稳定捕捉频率”“提高性能”:

     1、不能稳定捕捉频率

     因为目标识别常需要通过提取特征、判断诸多条件来实现,而如果每一帧都进行识别操作,会导致目标的捕捉与否与识别率直接挂钩。因某些特定帧会出现无法识别的情况,这将导致目标捕捉的时断时续,且断续的频率不稳定。这会使电控、机械端较难使用图像捕捉的结果进行控制操作。比如你通过串口给主控板传输时断时续的图像识别结果,主控板中定要对这些数据进行整合,而这将导致实际运动的明显延迟。

     2、不能提高性能

     性能最直接的反馈之一是帧率,因所用方法的区别,大部分的目标识别消耗的时间都比目标跟踪消耗的时间多。因此即便通过降低目标识别的“门槛”(对特征的筛选条件等),来弥补单纯使用目标识别而导致的如上1中的问题,也无法弥补性能方面识别与跟踪的差距。

     而将目标识别与目标跟踪结合使用,就能较好地解决以上两个问题:对于对识别准确率要求不是特别高的目标,在未识别到目标的帧中使用目标跟踪能在兼顾性能的同时,稳定捕捉频率。


      本文主要就是想讨论如何实现“在未识别到目标的帧中使用目标跟踪”,简述其思路。

      OpenCV中提供了实现目标跟踪Tracking的诸多方法,博主暂时对这些现成的方法没有多少研究。本文中用的目标跟踪方法是模板匹配,即OpenCV3中的matchTemplate()函数与minMaxLoc()函数,模板匹配及这两个函数的使用方法可见要点初见:从旅行青蛙开始的OpenCV3中模板匹配的探索 一文。


     如何在未识别到目标的帧中使用目标跟踪呢?最重要的有五点:

      (1)目标识别成功时,将目标保存为模板匹配所需的模板,以供下一帧的目标跟踪使用;

      (2)对连续识别到目标的帧数进行计数,若超过一定帧数连续识别到目标,则允许接下来的若干帧可以在识别不到目标的情况下进行模板匹配(即目标跟踪);

      (3)限制跟踪窗口大小!即不对全图,而是对识别或跟踪到的目标的附近用模板比对,可以大大降低运算量;

      (4)对模板匹配到的目标进行简单的特征判断,尽可能排除假匹配。因为模板匹配一定会找到一个最相似区域,无论是否目标是否存在;

      (5)对连续未识别到目标的帧数进行计数,超过一定帧数未识别到,则中断模板匹配(即目标跟踪)。这样可以避免目标消失后仍在强行跟踪过长时间。


     以上五点我们将在如下代码片段中进行分析:

vector<Point> track_points;
int result_cols = 0;
int result_rows = 0;

if(convert_dirct.z == 1)//如果识别到目标
{
	Is_recognizing = 1;//更新识别状态为正在识别
	Is_tracking = 0;//更新跟踪状态为不在跟踪
	TargetGet_times++;//统计识别到的帧数
	TargetLost_times = 0;//更新未识别到的帧数为0

	//当识别到目标时,更新目标跟踪的模板为识别到的目标
	for(int i = 0;i < 4;i++)
	{
		if(rectpoint[i].x > imagehsv.cols)//rectpoint[i]为识别到的矩形目标的四个角点
			rectpoint[i].x = imagehsv.cols-1;//如果没有-1,数据可能会超过摄像头分辨率的行数或列数,从而导致程序中断,下方的-1与1同此
		if(rectpoint[i].y > imagehsv.rows)
			rectpoint[i].y = imagehsv.rows-1;
		if(rectpoint[i].x < 0)
			rectpoint[i].x = 1;
		if(rectpoint[i].y < 0)
			rectpoint[i].y = 1;
		track_points.push_back(rectpoint[i]);//需以这种方式导入四个角点
	}

	Rect track_window = boundingRect(track_points);//创建目标跟踪的窗口为一个ROI区域(这里非常容易因超出行数列数而导致程序中断)

	//下方的1与-1是为了避免目标跟踪的窗口超出分辨率边缘
	if(track_window.x <= 0)
		track_window.x = 1;
	if(track_window.x + track_window.width >= imagehsv.cols)//imagehsv为当前待测图片
		track_window.x = imagehsv.cols - track_window.width - 1;

	if(track_window.y <= 0)
		track_window.y = 1;
	if(track_window.y + track_window.height >= imagehsv.rows)
		track_window.y = imagehsv.rows - track_window.height - 1;

	track_HSV = imagehsv(track_window);//此处把跟踪窗口转成hsv是为了与hsv格式的原图匹配
	result_cols = imagehsv.cols - track_HSV.cols + 1;
	result_rows = imagehsv.rows - track_HSV.rows + 1;
	//cout << result_cols << " result " << result_cols << endl;//用于调试中断是否因为ROI超出边缘
}
else//如果未识别到目标(注意此处未设置跟踪状态)
{
	Is_recognizing = 0;//更新识别状态为不在识别
	TargetGet_times = 0;//更新识别到的帧数为0
	TargetLost_times++;//统计未识别到的帧数
}
Mat result(result_cols,result_rows,CV_32FC1);

//何时目标跟踪,何时停止目标跟踪
if(TargetGet_times > 2)//当连续识别到目标超过2帧时
{
	track_object = 1;//目标跟踪flag置1
}
if(TargetLost_times > 300)//当连续未识别到目标超过300帧时
{
	track_object = 0;//停止目标跟踪(即300帧内都允许跟踪)
	Is_tracking = 0;//更新跟踪状态为不在跟踪
}

Mat image_roix;
int side_x = 0;
int side_y = 0;

int trackArea_x = 300;//跟踪窗口大小长300
int trackArea_y = 300;//跟踪窗口大小宽300

if(track_object && (track_HSV.cols <= 300) && (track_HSV.rows <= 300))//目标跟踪flag要求为1,且限制跟踪窗口不能超过300*300大小(因目标在视野中过大时,易出现目标仅有一部分在视野中)
{
	if(convert_dirct.z == 0)//如果未识别到目标
	{
		Is_tracking = 1;//更新跟踪状态为正在跟踪

		//使目标跟踪的ROI窗口在靠近分辨率边缘时,能不超出边缘(否则程序会崩溃)

		if((front_x + trackArea_x/2 < imagehsv.cols) && (front_y + trackArea_y/2 < imagehsv.rows) && (front_x - trackArea_x/2 > 0) && (front_y - trackArea_y/2 > 0))//当跟踪窗口不靠近分辨率边缘时(front_x和front_y是上一帧目标中心点的横纵坐标(左上坐标系))
		{
			image_roix = imagehsv(Rect(front_x-trackArea_x/2,front_y-trackArea_y/2,trackArea_x,trackArea_y));//重新设定ROI避免超出边缘;300为跟踪的长宽,修改跟踪窗口大小时需连着修改
			side_x = 1;//确定窗口中心位置时x的系数
			side_y = 1;//确定窗口中心位置时y的系数
		}

		if((front_x + trackArea_x/2 >= imagehsv.cols) && (front_y + trackArea_y/2 < imagehsv.rows) && (front_y - trackArea_y/2 > 0))//当跟踪窗口在分辨率右边缘时
		{
			image_roix = imagehsv(Rect(front_x-trackArea_x/2,front_y-trackArea_y/2,imagehsv.cols-(front_x-trackArea_x/2),trackArea_y));
			side_x = 1;
			side_y = 1;
		}
		if((front_y + trackArea_y/2 >= imagehsv.rows) && (front_x + trackArea_x/2 < imagehsv.cols) && (front_x - trackArea_x/2 > 0))//当跟踪窗口在分辨率下边缘时
		{
			image_roix = imagehsv(Rect(front_x-trackArea_x/2,front_y-trackArea_y/2,trackArea_x,imagehsv.rows-(front_y-trackArea_y/2)));
			side_x = 1;
			side_y = 1;
		}
		if((front_x + trackArea_x/2 >= imagehsv.cols) && (front_y + trackArea_y/2 >= imagehsv.rows))//当跟踪窗口在分辨率右下边缘时
		{
			image_roix = imagehsv(Rect(front_x-trackArea_x/2,front_y-trackArea_y/2,imagehsv.cols-(front_x-trackArea_x/2),imagehsv.rows-(front_y-trackArea_y/2)));
			side_x = 1;
			side_y = 1;
		}

		if((front_y + trackArea_y/2 < imagehsv.rows) && (front_x - trackArea_x/2 <= 0) && (front_y - trackArea_y/2 > 0))//当跟踪窗口在分辨率左边缘时
		{
			image_roix = imagehsv(Rect(0,front_y-trackArea_y/2,trackArea_x,trackArea_y));
			side_x = 0;
			side_y = 1;
		}
		if((front_x + trackArea_x/2 < imagehsv.cols) && (front_x - trackArea_x/2 > 0) && (front_y - trackArea_y/2 <= 0))//当跟踪窗口在分辨率上边缘时
		{
			image_roix = imagehsv(Rect(front_x-trackArea_x/2,0,trackArea_x,trackArea_y));
			side_x = 1;
			side_y = 0;
		}
		if((front_x - trackArea_x/2 <= 0) && (front_y - trackArea_y/2 <= 0))//当跟踪窗口在分辨率左上边缘时
		{
			image_roix = imagehsv(Rect(0,0,trackArea_x,trackArea_y));
			side_x = 0;
			side_y = 0;
		}

		if((front_y + trackArea_y/2 >= imagehsv.rows) && (front_x - trackArea_x/2 <= 0))//当跟踪窗口在分辨率左下边缘时
		{
			image_roix = imagehsv(Rect(0,front_y-trackArea_y/2,trackArea_x,imagehsv.rows-(front_y-trackArea_y/2)));
			side_x = 0;
			side_y = 1;
		}
		if((front_x + trackArea_x/2 >= imagehsv.cols) && (front_y - trackArea_y/2 <= 0))//当跟踪窗口在分辨率右上边缘时
		{
			image_roix = imagehsv(Rect(front_x-trackArea_x/2,0,imagehsv.cols-(front_x-trackArea_x/2),trackArea_y));
			side_x = 1;
			side_y = 0;
		}

		matchTemplate(image_roix,track_HSV,result,CV_TM_SQDIFF_NORMED);//对跟踪区域,用跟踪模板进行匹配
		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());
		matchLoc = minLoc;//等于minLoc还是maxLoc受上方CV_TM_SQDIFF_NORMED的影响

		//此处为对模板匹配到的目标进行简单的特征判断,尽可能排除假匹配
		......

		}
	}
}
     这段目标跟踪代码跟踪到的目标中心的横坐标为

(front_x - trackArea_x/2)*side_x + (matchLoc.x + matchLoc.x + track_HSV.cols)/2

     纵坐标为

(front_y - trackArea_y/2)*side_y + (matchLoc.y + matchLoc.y + track_HSV.rows)/2


     欢迎讨论!


猜你喜欢

转载自blog.csdn.net/m0_37857300/article/details/79255632