opencv视觉跟踪——CAMshift(meanshift均值漂移)

关于CAMshift的资料很多,如下链接写的都很不错,我就说说自己对CAMshift的理解

https://blog.csdn.net/li_dongxuan/article/details/70667170

https://blog.csdn.net/leixiaohua1020/article/details/12236091

总结:

一:HSV对光照影响小,RGB空间对光照影响大。
二:把ROI区域的直方图求出来,然后以ROI区域直方图为依据,对原图像反向投影, 这样使得反向投影后的ROI区域的像素点都有值并且都很大,使得原图像中与ROI区域颜色不一样的像素点的值很小甚至接近0。这样就形成了一副密度图,利用Meanshft均值漂移,使圆形窗口的中心向窗口内稠密度大的质心移动。注意:均值偏移会在上一个ROI区域附近为起点开始
搜索稠密度最大的质心,而不是对整幅图像全部搜索,所以上一个ROI区域搜索到的稠密度最大的质心,只是局部最优答案,
不是整幅图像稠密度最大的质心。
三:由于ROI区域会变大变小,为了使算法具有鲁棒性,添加预测算法。预测算法:
先设定一个最大、最小阈值。当搜索到ROI区域时,如果原图像中ROI目标变小了,则圆形窗口内的值会变大,当大于阈值时,则使圆形窗口变小,精确追踪ROI目标。如果原图像中ROI目标变大了,则圆形窗口内的值会变小,当小于阈值时,则使圆形窗口变大,精确追踪ROI目标

四:可以看出颜色对该算法影响很大

 

 

 

 

#include<opencv2/opencv.hpp> 
#include<iostream>      
using namespace cv;
using namespace std;

Mat image;
   
bool leftButtonDownFlag = false; //左键单击后视频暂停播放的标志位,ture暂停
Point originalPoint; //矩形框起点    
Point processPoint; //矩形框终点    

//定义绘制直方图的参数
int histSize = 200;//直方图的bin值
float histR[] = { 0,255 };//每个bin值的范围
const float *histRange = histR;
int channels[] = { 0,1 };//因为输入图像个数为2,所以不是 int channels=0;
Mat dstHist;//直方图

Rect rect;//ROI矩形区域
Mat rectImage;//ROI矩形图像
Mat targetImageHSV;//ROI区域的HSV空间图像

Mat imageCopy; //绘制矩形框时用来拷贝原图的图像 

vector<Point> pt; //保存目标轨迹  

//鼠标回调函数,相当于中断调用  
void onMouse(int event, int x, int y, int flags, void* ustc);   

int main(int argc, char*argv[])
{
	VideoCapture video;
	Mat frame;
	video.open(0);
	namedWindow("跟踪木头人", CV_WINDOW_NORMAL);
	//调用鼠标
	setMouseCallback("跟踪木头人", onMouse);

	while (video.read(frame))
	{
		if (!leftButtonDownFlag) //判定鼠标左键有没有按下,没有则采取播放视频,则此时!leftButtonDownFlag为ture
		{
			image= frame;
		}
		if (!image.data || waitKey(100) == 27)  //图像为空或Esc键按下退出播放    
		{
			break;
		}
		if (originalPoint != processPoint && !leftButtonDownFlag)//如果矩阵框起点不等于终点以及鼠标左键没有按下
			//左键没有按下分为一直没有按下和选择ROI后的松开左键。  当左键一直没有按下使,originalPoint与processPoint为空,就不想等,则跳过if
			//当是选择了ROI区域的松开左键,此时originalPoint与processPoint是有具体数值的。并且选择ROI区域后,这两个值会一直不相等
		{
			Mat imageHSV;//整幅图像的HSV空间
			Mat calcBackImage;//反向投影
			cvtColor(image, imageHSV, CV_RGB2HSV);//原图像转换成HSV空间
			calcBackProject(&imageHSV, 2, channels, dstHist, calcBackImage, &histRange);  //反向投影 
			//dstHist输入直方图,calcBackImage输出图像

			TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.001);
			//这个类是作为迭代算法的终止条件的.该类变量需要3个参数,一个是类型,第二个参数为迭代的最大次数,最后一个是特定的阈值。
			//类型有CV_TERMCRIT_ITER、CV_TERMCRIT_EPS、CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,
			//分别代表着迭代终止条件为达到最大迭代次数终止,迭代到阈值终止,或者两者都作为迭代终止条件。
			//以上的宏对应的c++的版本分别为TermCriteria::COUNT、TermCriteria::EPS,这里的COUNT也可以写成MAX_ITER。
			CamShift(calcBackImage, rect, criteria);
			//rect为输入和输出的ROI窗口,窗口的尺寸会自动调整

			Mat imageROI = imageHSV(rect);   //更新HSV空间下的ROI             
			calcHist(&imageROI, 2, channels, Mat(), dstHist, 1, &histSize, &histRange);//更新ROI的直方图
			normalize(dstHist, dstHist, 0.0, 1.0, NORM_MINMAX);   //归一化  
			rectangle(image, rect, Scalar(255, 0, 0), 3);    //绘制新的ROI矩形    
			pt.push_back(Point(rect.x + rect.width / 2, rect.y + rect.height / 2));
			//C++中容器中的push_back用的时候,容器的大小不能给定。空号里的点座标附加给容器pt结尾,而容器pt会自动调整容器容量
			//pt.insert(a),也能把点座标附加给容器pt结尾,但是容器pt的容量不会自动改变,需要自己编程序
			for (int i = 0; i<pt.size() - 1; i++)
			{
				line(image, pt[i], pt[i + 1], Scalar(0, 255, 0), 2.5);//画质心线
			}
		}
		imshow("跟踪木头人", image);
		waitKey(100);
	}
	return 0;
}

//*******************************************************************//      
//鼠标回调函数      
void onMouse(int event, int x, int y, int flags, void *ustc)
{
	if (event == CV_EVENT_LBUTTONDOWN)//如果按下鼠标左键
	{
		leftButtonDownFlag = true; //标志位    
		originalPoint = Point(x, y);  //设置左键按下点的矩形起点    
		processPoint = originalPoint;//此时的起点跟终点座标一样
	}
	if (event == CV_EVENT_MOUSEMOVE && leftButtonDownFlag)//如果鼠标左键按下并且移动
	{
		imageCopy = image.clone();
		processPoint = Point(x, y);//把移动的座标赋给矩形终点
		if (originalPoint != processPoint)//如果矩形起点不等于终点
		{
			//在复制的图像上绘制矩形    
			rectangle(imageCopy, originalPoint, processPoint, Scalar(255, 0, 0), 2);
		}
		imshow("跟踪木头人", imageCopy);
	}
	if (event == CV_EVENT_LBUTTONUP)//如果鼠标左键放开
	{
		leftButtonDownFlag = false;//改变鼠标是否按下的标志位
		rect = Rect(originalPoint, processPoint);//ROI区域
		rectImage = image(rect); //ROI图像显示    
		imshow("ROI", rectImage);

		cvtColor(rectImage, targetImageHSV, CV_RGB2HSV);//ROI区域转换成HSV空间
		imshow("ROI HSV", targetImageHSV);

		calcHist(&targetImageHSV, 2, channels, Mat(), dstHist, 1, &histSize, &histRange, true, false);//直方图
		normalize(dstHist, dstHist, 0.0, 1.0, CV_MINMAX);//归一化
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章