opencv笔记--meanshift&camshift
meanshift 被应用于 object track 中,其主要思想如下:
如下图所示,对该点集应用 meanshift 算法可以定位到点集最稠密位置,而点集最稠密位置即为我们需要跟踪的物体位置。
1)为什么点集最稠密位置即为我们需要跟踪的物体位置呢?这一般情况下是使用 histogram backprojection 实现,
histogram backprojection 将原始图像转换为类似下图的点集,更准确的说是转换为是否为跟踪物体的概率图。
2)下图使用点集稠密程度模拟概率图,准确的方式应该使用灰度图像来表达每一点上属于物体的概率值。
3)meanshift 算法首先使用一个初始点c1_o,计算该点所在同心圆内点集重心c1_r,很显然从c1_r与c1_o不在同一点,
使用c1_r最为新的中心点,并重复以上步骤,直到cn_o与cn_r之间距离足够下,则认为找到物体中心点。

有了 meanshift 的大致工作原理后,接下来需要理解如何生成概率图。而概率图的生成一般基于 histogram backprojection,
使用函数 cv::calcBackProject(const Mat * images,int nimages,const int * channels,InputArray hist,OutputArray backProject,const float ** ranges,double scale = 1,bool uniform = true )
生成直方图反向投影,其基本思路如下:
1)计算图像直方图。
2)遍历原始图像,查找每一个点在直方图上对应的强度值,使用该强度值作为该点的概率值,即实现直方图反向投影。
到目前为止,我们得到了直方图反向投影,一般使用灰度图来可视化表达。那么反向投影图中包含了哪些信息呢?
在一幅图像中,灰度(或色彩)值会集中到某一个区间,即在特定区间出现的概率较大,反向投影图中值较大的点表示该点的灰度(或色彩)出现频率较高。
对一整幅图像实施直方图反向投影,我们只能得到以上信息。那么如何利用直方图方向投影实现图像分割与目标跟踪呢?
通过引入一个手动标记区域,在原始图像与目标区域图像上应用直方图反向投影,可实现图像分割与目标跟踪。方法如下:
1)手动选定需要分割物体的局部区域或者需要跟踪物体的局部区域,这里强调局部区域是因为当选定区域包含背景信息会影响算法准确性。
对于前景物体一致性较好的分割应用,通过选择一个较小的局部区域即可实现完美的分割。对于物体跟踪应用,尽可能的勾选被跟踪物体可以提升跟踪稳定性。
2)对于彩色图像,对原始图像与目标图像的颜色空间转换到HSV空间,这样可以避免光照变化产生的影响(物体具有色彩不变性)。
由于灰度图像没有色彩信息,所以只能使用灰度信息进行跟踪与分割,很显然,这会受到光照变化的影响,一种可能的解决方案是使用直方图均衡化来抑制光照影响。
3)使用 cv::calcHist 函数计算目标图像直方图,对于彩色图像,可选择H与S通道计算直方图,也可以值选择H通道计算直方图,我认为选择H和S通道计算直方图可提供更准确的分析结果。
4)使用 cv::normalize 函数归一化直方图,使得直方图概率和为1。
4)使用 cv::calcBackProject 函数实现直方图反向投影,其中,images为原始图像,hist为目标图像直方图。
该函数将目标图像上不同点的概率值映射到原始图像上,即得到原始图像上每个点属于目标图像的概率。
5)对于图像分割应用,使用一个合适的阈值作用于原始图像的反向投影图上即可初步分割出特定物体,然后应用一些形态学算法剔除噪声区域,利用轮廓提取算法实现物体分割。
6)对于物体跟踪应用,应用 cv::meanshift(InputArray probImage, Rect& window, TermCriteria criteria) 函数跟踪视频中每帧图像上目标物体位置,
其中,probImage为calcBackProject函数得到的概率图,window为meanshift算法的初始搜索区域,criteria为迭代停止条件。
到目前为止,我们理解了使用meanshift实现目标跟踪的所有流程,下面给出部分测试代码:
1 #include "opencv2/video/tracking.hpp"
2 #include "opencv2/imgproc/imgproc.hpp"
3 #include "opencv2/highgui/highgui.hpp"
4
5 using namespace cv;
6 using namespace std;
7
8 int main(int argc, const char** argv)
9 {
10 // 省略部分代码
11 ......
12
13 Mat image;
14 int trackObject = 0; // 非零时执行相关操作,小于零时标记目标,大于零时跟踪目标
15 int vmin = 10, vmax = 256, smin = 30; // 亮度,饱和度区间相关擦拭你
16
17 namedWindow("meanShift_camShift", 0);
18
19 for (;;)
20 {
21 cap >> image;
22 if (image.empty())
23 break;
24
25 // 使用HSV空间下hue分量进行跟踪
26 // 一个可能的选择是使用归一化RG色度图替换HSV, RG色度图计算量小于HSV
27 cvtColor(image, hsv, COLOR_BGR2HSV);
28
29 if (trackObject)
30 {
31 int _vmin = vmin, _vmax = vmax;
32
33 // 形成mask图像,scalar定义了HSV分量区间
34 inRange(hsv, Scalar(0, smin, MIN(_vmin, _vmax)),
35 Scalar(180, 256, MAX(_vmin, _vmax)), mask);
36 int ch[] = { 0, 0 };
37 hue.create(hsv.size(), hsv.depth());
38 // 提取hue分量
39 mixChannels(&hsv, 1, &hue, 1, ch, 1);
40
41 if (trackObject < 0)
42 {
43 // 计算目标区域hue分量直方图
44 Mat roi(hue, selection), maskroi(mask, selection);
45 calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
46 normalize(hist, hist, 0, 255, CV_MINMAX);
47
48 trackWindow = selection;
49 trackObject = 1;
50 }
51
52 // 将目标区域hue直方图方向投影到原始图像上,得到原始图像上目标区域概率图
53 calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
54 backproj &= mask;
55
56 // 使用 meanshift 或者 camshift 跟踪物体,camshift 是 meanshift 改进版本
57 meanShift(backproj, trackWindow,
58 TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));
59 /*RotatedRect trackBox = CamShift(backproj, trackWindow,
60 TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));*/
61 if (trackWindow.area() <= 1)
62 {
63 int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5) / 6;
64 trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,
65 trackWindow.x + r, trackWindow.y + r) &
66 Rect(0, 0, cols, rows);
67 }
68
69 rectangle(image, trackWindow, Scalar(0, 0, 255), 3, CV_AA);
70 //ellipse(image, trackBox, Scalar(0, 0, 255), 3, CV_AA);
71 }
72
73 imshow("meanShift_camShift", image);
74 }
75
76 return 0;
77 }
以上代码来自opencv示例代码,我仅截取了部分关键代码,完整代码可参考opencv\samples\cpp\camshiftdemo.cpp。
meanshift 跟踪使用一个固定的区域去迭代被跟踪物体中心,当视场大小发生改变时,meanshift 跟踪到物体尺寸没有发生改变,同时也会因为跟踪尺寸与物体成像尺寸不一致而产生跟踪误差,因此引入camshift。
Continuously Adaptive Meanshift 使用反向投影图的零阶矩作为视场大小评估依据,使用一阶矩与二阶矩获得被跟踪物体位置与朝向,从而得到一个更好的跟踪。
opencv 提供了 cv::Camshift 函数实现了更好的物体跟踪,与 meanshift 不同,其返回值使用一个旋转矩阵表示被跟踪物体的位置与形态。
参考资料:
OpenCV: Meanshift and Camshift
OpenCV: Histogram - 4 : Histogram Backprojection
Bradski98_camshift.pdf (kansai-u.ac.jp)
opencv笔记--meanshift&camshift的更多相关文章
- OpenCV笔记大集锦(转载)
整理了我所了解的有关OpenCV的学习笔记.原理分析.使用例程等相关的博文.排序不分先后,随机整理的.如果有好的资源,也欢迎介绍和分享. 1:OpenCV学习笔记 作者:CSDN数量:55篇博文网址: ...
- opencv笔记6:角点检测
time:2015年10月09日 星期五 23时11分58秒 # opencv笔记6:角点检测 update:从角点检测,学习图像的特征,这是后续图像跟踪.图像匹配的基础. 角点检测是什么鬼?前面一篇 ...
- opencv笔记5:频域和空域的一点理解
time:2015年10月06日 星期二 12时14分51秒 # opencv笔记5:频域和空域的一点理解 空间域和频率域 傅立叶变换是f(t)乘以正弦项的展开,正弦项的频率由u(其实是miu)的值决 ...
- opencv笔记4:模板运算和常见滤波操作
time:2015年10月04日 星期日 00时00分27秒 # opencv笔记4:模板运算和常见滤波操作 这一篇主要是学习模板运算,了解各种模板运算的运算过程和分类,理论方面主要参考<图像工 ...
- opencv笔记3:trackbar简单使用
time:2015年 10月 03日 星期六 13:54:17 CST # opencv笔记3:trackbar简单使用 当需要测试某变量的一系列取值取值会产生什么结果时,适合用trackbar.看起 ...
- opencv笔记2:图像ROI
time:2015年 10月 03日 星期六 12:03:45 CST # opencv笔记2:图像ROI ROI ROI意思是Region Of Interests,感兴趣区域,是一个图中的一个子区 ...
- opencv笔记1:opencv的基本模块,以及环境搭建
opencv笔记1:opencv的基本模块,以及环境搭建 安装系统 使用fedora22-workstation-x86_64 安装opencv sudo dnf install opencv-dev ...
- Opencv目标跟踪—CamShift算法
CamShift算法全称是"Continuously Adaptive Mean-Shift"(连续的自适应MeanShift算法),是对MeanShift算法的改进算法,可以在跟 ...
- OpenCV基本架构[OpenCV 笔记0]
最近正在系统学习OpenCV,将不定期发布笔记,主要按照毛星云的<OpenCV3编程入门>的顺序学习,会参考官方教程和文档.学习工具是Xcode+CMake,会对书中一部分内容更正,并加入 ...
随机推荐
- linux安全篇:禁止频繁访问的ip访问nginx
实验环境 版本:redhat6.5ip:172.16.1.100,172.16.10软件:nginx 172.16.1.10部署nginx [root@localhost tools]# lsngin ...
- MapReduce和Hive学习文档链接学习顺序
1.<CentOS6.5下安装Hadoop-2.7.3(图解教程)> https://www.toutiao.com/i6627365258090512909/ 2.<CentOS6 ...
- 机器学习|线性回归算法详解 (Python 语言描述)
原文地址 ? 传送门 线性回归 线性回归是一种较为简单,但十分重要的机器学习方法.掌握线性的原理及求解方法,是深入了解线性回归的基本要求.除此之外,线性回归也是监督学习回归部分的基石. 线性回归介绍 ...
- Solon 开发,一、注入或手动获取配置
Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...
- Solon 开发,三、构建一个Bean的三种方式
Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...
- vue3代码setup中this为什么无效
结论:setup并没有通过各种方式去绑定this 在vue2中,我们可以在optionsApi中调用this来指向当前组件的实例,但是在vue3的setup中并不能这样做,因为setup位于组件创建成 ...
- 【Android】安卓四大组件之Activity(一)
[Android]安卓四大组件之Activity(一) 前言 Activity是Android学习中的一个重要组件,想要对其进行系统的了解可以分为几块内容,这一大章节的内容是有关于activity之间 ...
- JavaCV推流实战(MP4文件)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- mysql表设计原则
0.三大范式及反范式 ◆ 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列. ◆ 第二范式(2NF):首先是 1NF,另外包含两部分内容,一是表必须有一个主键:二是没有包含在主键中的 ...
- http 的get 与 post 的区别
1.原理区别 一般在浏览器中输入网址访问资源都是通过GET方式:在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交 Http定义了与服务器交互的不同方法,最基本的 ...