meanshift原理:
meanshift算法思想其实很简单:利用概率密度的梯度爬升来寻找局部最优。它要做的就是输入一个在图像的范围,然后一直迭代(朝着重心迭代)直到满足你的要求为止。但是他是怎么用于做图像跟踪的呢?这是我自从学习meanshift以来,一直的困惑。而且网上也没有合理的解释。经过这几天的思考,和对反向投影的理解使得我对它的原理有了大致的认识。
在opencv中,进行meanshift其实很简单,输入一张图像(imgProb),再输入一个开始迭代的方框(windowIn)和一个迭代条件(criteria),输出的是迭代完成的位置(comp )。
这是函数原型:
int cvMeanShift( const void* imgProb, CvRect windowIn,
CvTermCriteria criteria, CvConnectedComp* comp )
但是当它用于跟踪时,这张输入的图像就必须是反向投影图了。
为什么必须是反向投影图呢?首先我们要理解什么是反向投影图。
简单理解它其实实际上是一张概率密度图。经过反向投影时的输入是一个目标图像的直方图(也可以认为是目标图像),还一个输入是当前图像就是你要跟踪的全图,输出大小与全图一样大,它上像素点表征着一种概率,就是全图上这个点是目标图像一部分的概率。如果这个点越亮,就说明这个点属于物体的概率越大。现在我们明白了这原来是一张概率图了。当用meanshift跟踪时,输入的原来是这样一幅图像,那也不难怪它可以进行跟踪了。
半自动跟踪思路:输入视频,用画笔圈出要跟踪的目标,然后对物体跟踪。
用过opencv的都知道,这其实是camshiftdemo的工作过程。
第一步:选中物体,记录你输入的方框和物体。
第二步:求出视频中有关物体的反向投影图。
第三步:根据反向投影图和输入的方框进行meanshift迭代,由于它是向重心移动,即向反向投影图中概率大的地方移动,所以始终会移动到目标上。
第四步:然后下一帧图像时用上一帧输出的方框来迭代即可。
全自动跟踪思路:输入视频,对运动物体进行跟踪。
第一步:运用运动检测算法将运动的物体与背景分割开来。
第二步:提取运动物体的轮廓,并从原图中获取运动图像的信息。
第三步:对这个信息进行反向投影,获取反向投影图。
第四步:根据反向投影图和物体的轮廓(也就是输入的方框)进行meanshift迭代,由于它是向重心移 动,即向反向投影图中概率大的地方移动,所以始终会移动到物体上。
第五步:然后下一帧图像时用上一帧输出的方框来迭代即可。
总结:用meanshift进行跟踪最重要的一点是输入图像的把握,也就是要让它的迭代能越来越迭代到目标上。这种图像也不一定就是反向投影图,只要是一幅反映当前图像中每个像素点含有目标概率图就可以了,其实反向投影图就是这样的一幅图而已。
转载:http://www.cnblogs.com/cfantaisie/archive/2011/06/10/2077190.html
Camshift原理
CamShift算法的全称是"Continuously Adaptive Mean-SHIFT",即:连续自适应的MeanShift算法。其基本思想是对视频序列的所有图像帧都作MeanShift运算,并将上一帧的结果(即搜索窗口的中心位置和窗口大小)作为下一帧MeanShift算法的搜索窗口的初始值,如此迭代下去。简单点说,meanShift是针对单张图片寻找最优迭代结果,而camShift则是针对视频序列来处理,并对该序列中的每一帧图片都调用meanShift来寻找最优迭代结果。正是由于camShift针对一个视频序列进行处理,从而保证其可以不断调整窗口的大小,如此一来,当目标的大小发生变化的时候,该算法就可以自适应地调整目标区域继续跟踪。
在OpenCV自带的camShift的例子当中,是通过计算目标在HSV空间下的H分量直方图,通过直方图反向投影得到目标像素的概率分布,然后通过调用OpenCV的CAMSHIFT算法,自动跟踪并调整目标窗口的中心位置与大小。该算法对于简单背景下的单目标跟踪效果较好,但如果被跟踪目标与背景颜色或周围其它目标颜色比较接近,则跟踪效果较差。另外,由于采用颜色特征,所以它对被跟踪目标的形状变化有一定的抵抗能力。
http://blog.csdn.net/carson2005/article/details/7439125
分为三个部分:
1--色彩投影图(反向投影):
(1).RGB颜色空间对光照亮度变化较为敏感,为了减少此变化对跟踪效果的影响,首先将图像从RGB空间转换到HSV空间。(2).然后对其中的H分量(色调)作直方图,在直方图中代表了不同H分量值出现的概率或者像素个数,就是说可以查找出H分量大小为h的概率或者像素个数,即得到了颜色概率查找表。(3).将图像中每个像素的值用其颜色出现的概率对替换,就得到了颜色概率分布图。这个过程就叫反向投影,颜色概率分布图是一个灰度图像。
2--meanshift
meanshift算法是一种密度函数梯度估计的非参数方法,通过迭代寻优找到概率分布的极值来定位目标。
算法过程为:
(1).在颜色概率分布图中选取搜索窗W
(2).计算零阶距:
计算一阶距:
计算搜索窗的质心:
(3).调整搜索窗大小
宽度为;长度为1.2s;
(4).移动搜索窗的中心到质心,如果移动距离大于预设的固定阈值,则重复2)3)4),直到搜索窗的中心与质心间的移动距离小于预设的固定阈值,或者循环运算的次数达到某一最大值,停止计算。关于meanshift的收敛性证明可以google相关文献。
3--camshift
将meanshift算法扩展到连续图像序列,就是camshift算法。它将视频的所有帧做meanshift运算,并将上一帧的结果,即搜索窗的大小和中心,作为下一帧meanshift算法搜索窗的初始值。如此迭代下去,就可以实现对目标的跟踪。
算法过程为:
(1).初始化搜索窗
(2).计算搜索窗的颜色概率分布(反向投影)
(3).运行meanshift算法,获得搜索窗新的大小和位置。
(4).在下一帧视频图像中用(3)中的值重新初始化搜索窗的大小和位置,再跳转到(2)继续进行。
camshift能有效解决目标变形和遮挡的问题,对系统资源要求不高,时间复杂度低,在简单背景下能够取得良好的跟踪效果。但当背景较为复杂,或者有许多与目标颜色相似像素干扰的情况下,会导致跟踪失败。因为它单纯的考虑颜色直方图,忽略了目标的空间分布特性,所以这种情况下需加入对跟踪目标的预测算法。
__________________________________________________________________________________________________________________________________________
半自动跟踪思路:输入视频,用画笔圈出要跟踪的目标,然后对物体跟踪。
用过opencv的都知道,这其实是camshiftdemo的工作过程。
第一步:选中物体,记录你输入的方框和物体。
第二步:求出视频中有关物体的反向投影图。
第三步:根据反向投影图和输入的方框进行meanshift迭代,由于它是向重心移动,即向反向投影图中概率大的地方移动,所以始终会移动到目标上。
第四步:然后下一帧图像时用上一帧输出的方框来迭代即可。
全自动跟踪思路:输入视频,对运动物体进行跟踪。
第一步:运用运动检测算法将运动的物体与背景分割开来。
第二步:提取运动物体的轮廓,并从原图中获取运动图像的信息。
第三步:对这个信息进行反向投影,获取反向投影图。
第四步:根据反向投影图和物体的轮廓(也就是输入的方框)进行meanshift迭代,由于它是向重心移动,即向反向投影图中概率大的地方移动,所以始终会移动到物体上。
第五步:然后下一帧图像时用上一帧输出的方框来迭代即可。
http://www.cnblogs.com/cfantaisie/archive/2011/06/10/2077190.html
下面是一个全自动跟踪的例子
代码是copy来的,从哪来的忘了……
- #include
- #include
-
- using namespace std;
- using namespace cv;
-
-
- bool biggerSort(vector v1, vector v2)
- {
- return contourArea(v1)>contourArea(v2);
- }
-
- int main()
- {
-
- VideoCapture cap("3.AVI");
- if(cap.isOpened()==false)
- return 0;
-
-
- int i;
-
- Mat frame;
- Mat foreground;
- Mat bw;
- Mat se;
-
-
- BackgroundSubtractorMOG mog;
- for(i=0;i<10;++i)
- {
- cout<<"正在训练背景:"<
- cap>>frame;
- if(frame.empty()==true)
- {
- cout<<"视频帧太少,无法训练背景"<
- getchar();
- return 0;
- }
- mog(frame,foreground,0.01);
- }
-
-
- Rect rt;
- se=getStructuringElement(MORPH_RECT,Size(5,5));
-
-
- vector vecImg;
- vector<int> vecChannel;
- vector<int> vecHistSize;
- vector<float> vecRange;
- Mat mask(frame.rows,frame.cols,DataType::type);
-
- vecChannel.push_back(0);
- vecHistSize.push_back(32);
- vecRange.push_back(0);
- vecRange.push_back(180);
-
- Mat hsv;
- MatND hist;
- double maxVal;
- Mat backP;
- Mat result;
-
-
- while(1)
- {
-
- cap>>frame;
- if(frame.empty()==true)
- break;
-
-
- frame.copyTo(result);
-
-
- mog(frame,foreground,0.01);
- imshow("混合高斯检测前景",foreground);
- moveWindow("混合高斯检测前景",400,0);
-
- medianBlur(foreground,foreground,5);
- imshow("中值滤波",foreground);
- moveWindow("中值滤波",800,0);
- morphologyEx(foreground,foreground,MORPH_DILATE,se);
-
-
- foreground.copyTo(bw);
- vector> contours;
- findContours(bw,contours,RETR_EXTERNAL,CHAIN_APPROX_NONE);
- if(contours.size()<1)
- continue;
-
- std::sort(contours.begin(),contours.end(),biggerSort);
-
-
-
-
-
- cvtColor(frame,hsv,COLOR_BGR2HSV);
- vecImg.clear();
- vecImg.push_back(hsv);
- for(int k=0;k
- {
-
- if(contourArea(contours[k])
- break;
- rt=boundingRect(contours[k]);
- mask=0;
- mask(rt)=255;
-
-
- calcHist(vecImg,vecChannel,mask,hist,vecHistSize,vecRange);
- minMaxLoc(hist,0,&maxVal);
- hist=hist*255/maxVal;
-
- calcBackProject(vecImg,vecChannel,hist,backP,vecRange,1);
-
- Rect search=rt;
- RotatedRect rrt=CamShift(backP,search,TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,10,1));
- Rect rt2=rrt.boundingRect();
- rt&=rt2;
-
-
- rectangle(result,rt,Scalar(0,255,0),2);
- }
-
-
- imshow("原图",frame);
- moveWindow("原图",0,0);
-
- imshow("膨胀运算",foreground);
- moveWindow("膨胀运算",0,350);
-
- imshow("反向投影",backP);
- moveWindow("反向投影",400,350);
-
- imshow("跟踪效果",result);
- moveWindow("跟踪效果",800,350);
- waitKey(30);
- }
-
- getchar();
- return 0;
- }
3.avi是我写的一个目标运动仿真,用来做这个实验
BackgroundSubtractorMOG
http://blog.csdn.net/xiaowei_cqu/article/details/23689189
- int main()
- {
- VideoCapture video("3.avi");
- Mat frame,mask,thresholdImage, output;
- BackgroundSubtractorMOG bgSubtractor;
- while(true)
- {
- video>>frame;
- if (frame.empty())
- break;
- bgSubtractor(frame,mask,0.1);
- imshow("mask",mask);
- waitKey(10);
- }
- return 0;
- }