参考博客
1.opencv3中camshift详解(一)camshiftdemo代码详解
https://blog.csdn.net/qq_37775990/article/details/82558110
2.目标跟踪学习笔记_1(opencv中meanshift和camshift例子的应用)
https://www.cnblogs.com/tornadomeet/archive/2012/03/15/2398769.html
3.OpenCV自带的CamShift算法解读
https://www.baidu.com/link?url=w-xSSIYB7vdZOayE2pSl-PflMYadtvXr_A8PVipFnuq_ySiwhq0TbuKCWapwobJJHUSRmw91qe9gkFpqlIkhIHjSjPbfHMHFbteoI_0MgjW&wd=&eqid=c48098ba0004bcc9000000035cd148fc
我具体参考的博客就是以上三个。其他大神的注释网上也都能搜到,也很全面,就不一一列举了,还请见谅。
程序标志位及变量汇总
camshiftdemo程序思路
定义鼠标操作
void onMouse(int event, int x, int y, int, void*)
从鼠标左键按下、拖拽选择区域到鼠标左键松开,该函数得到selection(选择的跟踪区域)及trackObject = -1这一标志位。而后者将决定下边对选中区域H直方图的计算。
打开摄像头,如不成功则提示
CommandLineParser parser(argc, argv, keys);
int camNum = parser.get<int>("1");
cap.open(camNum);
OpenCV中CommandLineParse类主要是命令行解析类。
初始化窗口、鼠标操作及滑动条
namedWindow( "Histogram", 0 );
namedWindow( "CamShift Demo", 0 );
setMouseCallback( "CamShift Demo", onMouse, 0 );
createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 );
createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 );
createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 );
由于HSV模型的缺陷,故而要限制过低的饱和度(saturation)和亮度(value)
将hsv图像中hue值在0到180,saturation值在smin到256,value值在_vmin到_vmax之间
int vmin = 10, vmax = 256, smin = 30;
for循环
当trackObject = -1,即目标区域选择完成
1.满足
if (trackObject)`
在for循环中将当前帧转换为HSV颜色模型并提取Hue(色调)分量。
2.满足
if (trackObject < 0)
计算Hue分量的直方图,并归一化到[0,255]
并trackObject =1,表示目标区域的直方图只计算一次,除非再次重新选择跟踪区域。此后,for循环只计算当前帧的H分量,即只满足
if (trackObject)`
将目标特征直方图绘制于histimg中
rectangle(histimg, Point(i*binW, histimg.rows),
Point((i + 1)*binW, histimg.rows - val),
Scalar(buf.at<Vec3b>(i)), -1, 8);
注:
值得注意的是矩阵的坐标系以左上角为原点,y轴是向下的,而需要展示给人看的直方图图案是左下角为原点,y轴向上的。
对当前帧H分量反投影,并进行CAMShift跟踪
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
backproj &= mask;
RotatedRect trackBox = CamShift(backproj, trackWindow,
TermCriteria( TermCriteria::EPS | TermCriteria::COUNT, 10, 1 ));
返回的跟踪结果在trackBox中,该椭圆框代表了目标在当前帧上的位置和大小。
if( trackWindow.area() <= 1 )
{
int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;
trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,
trackWindow.x + r, trackWindow.y + r) &
Rect(0, 0, cols, rows);
}
如果跟踪窗口的面积小于1,说明搜索窗口的长和宽都小于等于1个像素点了,强制调整窗口的大小。
ellipse(image, trackBox, Scalar(0, 0, 255), 3, CV_AA);
将trackBox显示在当前帧上。
if( selectObject && selection.width > 0 && selection.height > 0 )
{
Mat roi(image, selection);
bitwise_not(roi, roi);
}
imshow( "CamShift Demo", image );
只有在选定目标时才会被执行。在按下鼠标左键和抬起鼠标左键之间的这段时间,selectObject为真,selection会随着鼠标的移动不断变化,直到抬起鼠标左键后,
selectObject为假,selection就是选中的目标矩形框。显示选择的目标区域。
热键解释
Esc- 退出程序
c - 清零跟踪目标对象
b - 反向投影模型交替
h - 显示/隐藏直方图
p - 启动/暂停
camshiftdemo程序修改及仿真
将调用摄像头改为调用工程路径下名为"vtest.avi"的视频文件
VideoCapture capture("vtest.avi");
并将cap替换为capture.
将
CommandLineParser parser(argc, argv, keys);
相关内容屏蔽掉。
仿真图如下:
但当背景较为复杂,或者有许多与目标颜色相似像素干扰的情况下,会导致跟踪失败。
仿真失败的图就不放上了。
致谢
至此,VS环境下的CAMShift算法理解告一段落。至于MATLAB下的CAMShift算法,如果毕业前时间允许,会继续补充~
可算把导师指定的四种基本算法大致过了一遍,接下来就是毕设的攻坚战了!!!
菜鸡解读必有不妥之处,欢迎各位大神批评指正!
同时,感谢浅墨大神在《OpenCV3 编程入门》中给出的源码camshiftdemo.cpp,同时感谢各个博客平台上各位大神给出的思路及注释,感激不尽~