OpenCV | CAMShift - 指定对象跟踪

一、原理介绍

CAMShift是基于MeanShift算法的基础上而来的,所以CAMShift又叫连续的自适应的MeanShift算法。

MeanShift 算法
如下图所示:
在这里插入图片描述

  1. 首先在图中选取一个点作为中心点,然后得到一个窗口。
  2. 然后计算窗口内所有点的均值,计算完得到一个点(图中蓝色的),然后将这个蓝色的点作为中心点又得到一个窗口。
  3. 迭代若干次数后,中心的距离之差为0,就说明了找打了密度最大的点。

CAMShift 算法
如下图所示:
在这里插入图片描述

  1. 选择特征模型;
  2. 用MeanShift算出密度空间的特征,选择转换为特征空间;
  3. 然后在密度窗口内,算出直方图,也就是特征空间PDF模型数据。
  4. 有了模板特征直方图数据,然后在图中搜索有类似的特征直方图,说明就是那个选择对象,即就跟踪到了。注意直方图的 bin 要相等。
二、代码示例
#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;
const char* CAMShift_WIN = "CAMShift Tracking";

int smin = 40; 
int vmin = 40; 
int vmax = 256;
int bins = 16;

int main()
{
	VideoCapture capture;
	capture.open("D:/source/images/duan3.mp4");
	if (!capture.isOpened())
	{
		puts("dont open video.");
		system("pause");
		return -1;
	}
	bool firstRead = true;
	Rect selection;
	Mat frame, hsv, hue, mask, hist, backProject;
	float hrange[] = {0, 180};
	const float* hranges = hrange;
	namedWindow(CAMShift_WIN, WINDOW_AUTOSIZE);
	Mat drawImg = Mat::zeros(300, 300, CV_8UC3);

	while (capture.read(frame))
	{
		if (firstRead)
		{
			Rect2d first = selectROI(CAMShift_WIN, frame);
			// 需要int类型的所以用中间变量转了一下
			selection.x = first.x;
			selection.y = first.y;
			selection.width = first.width;
			selection.height = first.height;
			printf("ROI.x = %d, ROI.y = %d, width = %d, height = %d\n", selection.x, selection.y, \
				selection.width, selection.height);
		}


		// convert to HSV
		cvtColor(frame, hsv, COLOR_BGR2HSV);
		
		// 通过目标对象的颜色给定范围确定mask,如果不加这个效果没那么好
		inRange(hsv, Scalar(0, smin, vmin), Scalar(180, vmax, vmax), mask);
		
		// 获取色调通道信息
		hue = Mat(hsv.size(), hsv.depth());
		int channels[] = {0, 0};
		mixChannels(&hsv, 1, &hue, 1, channels, 1);

		if (firstRead)
		{
			// calculate histogram
			Mat roi(hue, selection);	// 提取色调ROI
			Mat maskRoi(mask, selection);

			calcHist(&roi, 1, 0, maskRoi, hist, 1, &bins, &hranges);
			normalize(hist, hist, 0, 255, NORM_MINMAX);

			// show histogram
			int binw = drawImg.cols / bins;
			Mat colorIndex = Mat(1, bins, CV_8UC3);
			for (int i = 0; i < bins; i++)
			{
				// H 映射到BGR的时候最大的是180
				colorIndex.at<Vec3b>(0, i) = Vec3b(saturate_cast<uchar>(i*180 / bins), 255, 255);
			}
			cvtColor(colorIndex, colorIndex, COLOR_HSV2BGR);
			
			for (int i = 0; i < bins; i++)
			{
				int val = saturate_cast<int>(hist.at<float>(i) * drawImg.rows / 255);
				rectangle(drawImg, Point(i * binw, drawImg.rows), Point((i + 1)*binw, drawImg.rows - val), \
					Scalar(colorIndex.at<Vec3b>(0, i)), -1, 8, 0);

			}
		}

		// back projection
		calcBackProject(&hue, 1, 0, hist, backProject, &hranges);
		// CAMShift tracking
		backProject &= mask;
		RotatedRect trackBox = CamShift(backProject, selection, \
			TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 10, 1));

		// draw location on frame
		ellipse(frame, trackBox, Scalar(0, 0, 255), 3, 8);

		if (firstRead)
			firstRead = false;
		imshow("draw hisgram", drawImg);
		imshow(CAMShift_WIN, frame);
		if (27 == waitKey(50)) break;
	}

	capture.release();
	
	waitKey(0);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值