CamShift算法是对MeanShift算法的改进算法,可以在跟踪的过程中随着目标大小的变化实时调整搜索窗口大小,对于视频序列中的每一帧还是采用MeanShift来寻找最优迭代结果。
下面的代码是这个博客中来的,自己添加了一些注释
Mat image;
Mat rectImage;
Mat imageCopy; // 绘制矩形框时用来拷贝原图的图像
bool leftButtonDownFlag=false; // 左键单击后视频暂停播放的标志位
Point originalPoint; // 矩形框起点
Point processPoint; // 矩形框终点
Mat targetImageHSV;
int histSize=200;
float histR[]={0, 255};
const float * histRange=histR;
int channels[]={0,1};
Mat dstHist;
Rect rect;
vector<Point> pt; // 保存目标轨迹
void onMouse(int event, int x, int y, int flags, void * ustc); // 鼠标回调函数
int main(int argc, char*argv[])
{
VideoCapture cap(0);
double delay = (double)1000/30;
namedWindow("跟踪木头人",WINDOW_AUTOSIZE);
setMouseCallback("跟踪木头人", onMouse);
while (true)
{
if(!leftButtonDownFlag) // 判定鼠标左键没有按下去,采取播放视频,否则暂停
{
cap >> image;
}
if(!image.data||waitKey(delay)=='q')
break;
if(originalPoint!=processPoint && !leftButtonDownFlag)
{
Mat imageHSV;
Mat calcBackImage;
cvtColor(image, imageHSV, COLOR_BGR2HSV);
// // 用回调函数中计算得到的目标直方图,在图像中计算存在的特征概率分布(反向投影)
calcBackProject(&imageHSV,2, channels,dstHist,calcBackImage,&histRange);
// // 运行camshift 算法
TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 100, 0.001);
CamShift(calcBackImage, /*输入图像直方图的反向投影图*/
rect, /*要跟踪目标的初始位置矩形框*/ /*也是实时跟踪目标后的位置*/
criteria); /*s算法结束条件*/
// camShift更新后的目标,计算目标直反图,用于下一帧分析
Mat imageROI = imageHSV(rect);
targetImageHSV = imageHSV(rect);
calcHist(&imageROI,2, channels, Mat(),dstHist,1, &histSize, &histRange);
normalize(dstHist, dstHist, 0.0, 1.0, NORM_MINMAX); // 归一化
rectangle(image, rect, Scalar(255,0,0), 3); // 目标绘制
// 绘制目标移动轨迹
pt.push_back(Point(rect.x + rect.width/2, rect.y+rect.height/2));
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 == EVENT_LBUTTONDOWN) // 左键按下
{
leftButtonDownFlag=true; // 标志位
originalPoint=Point(x,y); // 设置左键按下点的矩形起点
processPoint=originalPoint;
if (pt.size() > 0) pt.erase(pt.begin(), pt.end()); // 清空轨迹
}
if (event == 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 == EVENT_LBUTTONUP) // 左键抬起
{
leftButtonDownFlag=false;
// 获得目标框区域
rect = Rect(originalPoint, processPoint);
rectImage=image(rect);
imshow("sub Image", rectImage);
// 目标框区域转HSV
cvtColor(rectImage, targetImageHSV, COLOR_BGR2HSV);
imshow("targetImageHSV", targetImageHSV);
// 获得目标框直方图(用于后续分析)
calcHist(&targetImageHSV, 2, channels, Mat(), dstHist, 1, &histSize, &histRange);
normalize(dstHist, dstHist, 0, 255, NORM_MINMAX); // 归一化
// imshow("dstHist", dstHist);
}
}