OpenCV4学习笔记(37)——CAMShift(Continuously Adaptive MeanShift)算法

在上次的笔记《OpenCV4学习笔记(36)——基于均值迁移(MeanShift)算法和直方图反向投影的目标移动跟踪》中,整理记录了一种针对目标的移动跟踪算法,主要是基于均值迁移和直方图反向投影来实现的。而今天要记录的笔记内容,依然是一种针对目标的移动跟踪算法——CAMShift目标移动跟踪算法,这种算法是基于MeanShift目标移动跟踪算法的改进。

CAMShift算法其实就是连续自适应的MeanShift算法,是对MeanShift 算法的改进,它能够自动调节搜索窗口大小来适应目标的大小,可以跟踪视频中尺寸变化的目标。

CAMShift算法相比起MeanShift算法主要具有两个改进方向:
一是CAMShift算法会根据跟踪对象的大小变化来自动调整搜索窗口大小;
二是返回的检测目标信息更加完整,不仅包含位置信息,同时还包含了角度信息,也即返回的是目标所在的最小外接旋转矩形。

在OpenCV中提供了CamShift()这个API来实现CAMShift算法,它的返回值为一个包含位置和角度的旋转矩形,其参数列表如下:

第一个参数probImage:输入的概率分布图像,也就是反向投影图像;

第二个参数window:目标位置的初始窗口;

第三个参数criteria:迁移终止条件,TermCriteria::EPS为迁移距离小于阈值时停止迁移,TermCriteria::COUNT为迁移迭代次数达到设定的最大值时停止迁移,可以两者同时使用。

下面看一下具体的代码演示:

	VideoCapture capture;
	capture.open("D:\\opencv_c++\\opencv_tutorial\\data\\images\\balltest.mp4");
	//capture.open(0);
	if (!capture.isOpened())
	{
		return 0;
	}
	//提取第一帧图像中ROI区域并将其转化为HSV图像
	Mat first_image, first_hsv, roi_image, roi_hsv;
	capture.read(first_image);
	Rect roi_rect = selectROI("first_image", first_image, false, false);
	roi_image = first_image(roi_rect).clone();
	cvtColor(roi_image, roi_hsv, COLOR_BGR2HSV);
	//计算ROI图像的直方图
	Mat roi_hist;
	int dims = 2;
	int channels[2] = { 0,1 };
	int histSize[2] = { 180,255 };
	float ranges_h[2] = { 0,180 };
	float ranges_s[2] = { 0,255 };
	const float* histRanges[2] = { ranges_h, ranges_s };
	calcHist(&roi_hsv, 1, channels, Mat(), roi_hist, dims, histSize, histRanges);
	normalize(roi_hist, roi_hist, 0, 255, NORM_MINMAX);

	Mat frame, frame_hsv;
	vector<Point2f> centers;
	while (capture.read(frame))
	{
		flip(frame, frame, 1);
		cvtColor(frame, frame_hsv, COLOR_BGR2HSV);
		//对每一帧图像进行反向投影
		Mat backProject;
		calcBackProject(&frame_hsv, 1, channels, roi_hist, backProject, histRanges);
		//进行CamShift算法,不断更新目标窗口位置;返回值为一个包含位置和角度的旋转矩形
		RotatedRect new_rect = CamShift(backProject, roi_rect, TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 1));
		// //参数probImage:输入的概率分布图像,也就是反向投影图像;
		//	//参数window:目标位置的初始窗口;
		//	//参数criteria:迁移终止条件;TermCriteria::EPS为迁移距离小于阈值时停止迁移,TermCriteria::COUNT为迁移迭代次数达到设定的最大值时停止迁移,可以两者同时使用;
		ellipse(frame, new_rect, Scalar(0, 255, 0), 1, 8);			//绘制目标区域位置

		//获取当前目标中心座标,并存入点集中;不绘制完整目标移动轨迹,如果点集中的点数量达到规定个数,就清空点集;也就是每次只绘制最近的移动轨迹
		Point2f center = new_rect.center;	
		if (centers.size() % 20 == 0)
		{
			centers.clear();
		}
		centers.push_back(center);

		if (centers.size() > 2)		//只有当目标出现移动时,也就是目标中心座标至少有两个点时才进行绘制
		{
			for (size_t k = 1; k < centers.size(); k++)
			{
				float  flag = float(k) / float(centers.size());			//标记当前点位于当前绘制的轨迹中的位置
				//通过标记位置的不同,来绘制不同粗细的曲线,从而使绘制出的轨迹曲线有逐渐消失的效果,类似于彗星尾巴
				if (flag < 0.2)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 1, LINE_AA, 0);
				}
				else if(flag >= 0.2 && flag <= 0.4)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 2, LINE_AA, 0);
				}
				else if (flag >= 0.4 && flag <= 0.6)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 3, LINE_AA, 0);
				}
				else if(flag >= 0.6 && flag <= 0.8)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 4, LINE_AA, 0);
				}
				else if (flag >= 0.8 && flag <= 1)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 5, LINE_AA, 0);
				}
			}
		}
		imshow("frame", frame);

		char ch = waitKey(20);
		if (ch == 27)
		{
			break;
		}

	}
	capture.release();

在上述代码中,首先读取视频流,然后提取第一帧图像中我们需要的ROI区域图像并将其转化到HSV色彩空间,并且计算该ROI图像的直方图。

接着读取视频流的每一帧图像,对每一帧图像都转化到HSV色彩空间进行反向投影,得到反向投影图。并对获取到的反向投影图进行CAMShift算法计算,得到在每帧图像中检测到的目标位置,以一个旋转矩形来表示。

在进行跟踪显示的时候,我们做一点视觉上的优化处理。我们不绘制移动目标的完整移动轨迹,也就是每次只绘制最近若干帧图像中的移动轨迹,并且对于轨迹的颜色是从深到浅变化,粗细程度是从粗到细变化、直至消失。这样,就把移动轨迹变成了类似于彗星拖尾的感觉。

下面看一下演示效果截图:
在这里插入图片描述
这是通过鼠标来进行ROI区域的提取。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面4张图像是视频播放过程的截图。可以看到,随着画面中球体目标的不断运动,用于标记目标位置的椭圆也不断随之运动,而且尺寸、形状也是不断变化,并且后续的运动轨迹也是符合我们的视觉要求,形成一条类似的”彗尾”。
这样我们就实现了对视频流中目标物体的移动跟踪,并进行视觉优化显示。

好了,今天的笔记内容就整理到此,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

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