Opencv目标跟踪—CamShift和meanshift算法

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分量直方图,通过直方图反向投影得到目标像素的概率分布,然后通过调用OpenCVCAMSHIFT算法,自动跟踪并调整目标窗口的中心位置与大小。该算法对于简单背景下的单目标跟踪效果较好,但如果被跟踪目标与背景颜色或周围其它目标颜色比较接近,则跟踪效果较差。另外,由于采用颜色特征,所以它对被跟踪目标的形状变化有一定的抵抗能力。

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来的,从哪来的忘了……

[cpp]  view plain  copy
  1. #include <iostream>  
  2. #include <opencv2/opencv.hpp>  
  3.   
  4. using namespace std;  
  5. using namespace cv;  
  6.   
  7. //对轮廓按面积降序排列  
  8. bool biggerSort(vector<Point> v1, vector<Point> v2)  
  9. {  
  10.     return contourArea(v1)>contourArea(v2);  
  11. }  
  12.   
  13. int main()  
  14. {  
  15.     //视频不存在,就返回  
  16.     VideoCapture cap("3.AVI");  
  17.     if(cap.isOpened()==false)  
  18.         return 0;  
  19.   
  20.     //定义变量  
  21.     int i;  
  22.   
  23.     Mat frame;          //当前帧  
  24.     Mat foreground;     //前景  
  25.     Mat bw;             //中间二值变量  
  26.     Mat se;             //形态学结构元素  
  27.   
  28.     //用混合高斯模型训练背景图像  
  29.     BackgroundSubtractorMOG mog;      
  30.     for(i=0;i<10;++i)  
  31.     {  
  32.         cout<<"正在训练背景:"<<i<<endl;  
  33.         cap>>frame;  
  34.         if(frame.empty()==true)  
  35.         {  
  36.             cout<<"视频帧太少,无法训练背景"<<endl;  
  37.             getchar();  
  38.             return 0;  
  39.         }  
  40.         mog(frame,foreground,0.01);   
  41.     }  
  42.       
  43.     //目标外接框、生成结构元素(用于连接断开的小目标)  
  44.     Rect rt;  
  45.     se=getStructuringElement(MORPH_RECT,Size(5,5));  
  46.   
  47.     //统计目标直方图时使用到的变量  
  48.     vector<Mat> vecImg;  
  49.     vector<int> vecChannel;  
  50.     vector<int> vecHistSize;  
  51.     vector<float> vecRange;  
  52.     Mat mask(frame.rows,frame.cols,DataType<uchar>::type);  
  53.     //变量初始化  
  54.     vecChannel.push_back(0);  
  55.     vecHistSize.push_back(32);  
  56.     vecRange.push_back(0);  
  57.     vecRange.push_back(180);  
  58.           
  59.     Mat hsv;        //HSV颜色空间,在色调H上跟踪目标(camshift是基于颜色直方图的算法)  
  60.     MatND hist;     //直方图数组  
  61.     double maxVal;      //直方图最大值,为了便于投影图显示,需要将直方图规一化到[0 255]区间上  
  62.     Mat backP;      //反射投影图  
  63.     Mat result;     //跟踪结果  
  64.       
  65.     //视频处理流程  
  66.     while(1)  
  67.     {  
  68.         //读视频  
  69.         cap>>frame;  
  70.         if(frame.empty()==true)  
  71.             break;        
  72.       
  73.         //生成结果图  
  74.         frame.copyTo(result);  
  75.   
  76.         //检测目标(其实是边训练边检测)  
  77.         mog(frame,foreground,0.01);  
  78.         imshow("混合高斯检测前景",foreground);  
  79.         moveWindow("混合高斯检测前景",400,0);  
  80.         //对前景进行中值滤波、形态学膨胀操作,以去除伪目标和接连断开的小目标       
  81.         medianBlur(foreground,foreground,5);  
  82.         imshow("中值滤波",foreground);  
  83.         moveWindow("中值滤波",800,0);  
  84.         morphologyEx(foreground,foreground,MORPH_DILATE,se);  
  85.   
  86.         //检索前景中各个连通分量的轮廓  
  87.         foreground.copyTo(bw);  
  88.         vector<vector<Point>> contours;  
  89.         findContours(bw,contours,RETR_EXTERNAL,CHAIN_APPROX_NONE);  
  90.         if(contours.size()<1)  
  91.             continue;  
  92.         //对连通分量进行排序  
  93.         std::sort(contours.begin(),contours.end(),biggerSort);  
  94.   
  95.         //结合camshift更新跟踪位置(由于camshift算法在单一背景下,跟踪效果非常好;  
  96.         //但是在监控视频中,由于分辨率太低、视频质量太差、目标太大、目标颜色不够显著  
  97.         //等各种因素,导致跟踪效果非常差。  因此,需要边跟踪、边检测,如果跟踪不够好,  
  98.         //就用检测位置修改  
  99.         cvtColor(frame,hsv,COLOR_BGR2HSV);  
  100.         vecImg.clear();  
  101.         vecImg.push_back(hsv);  
  102.         for(int k=0;k<contours.size();++k)  
  103.         {  
  104.             //第k个连通分量的外接矩形框  
  105.             if(contourArea(contours[k])<contourArea(contours[0])/5)  
  106.                 break;  
  107.             rt=boundingRect(contours[k]);                 
  108.             mask=0;  
  109.             mask(rt)=255;  
  110.   
  111.             //统计直方图  
  112.             calcHist(vecImg,vecChannel,mask,hist,vecHistSize,vecRange);               
  113.             minMaxLoc(hist,0,&maxVal);  
  114.             hist=hist*255/maxVal;  
  115.             //计算反向投影图  
  116.             calcBackProject(vecImg,vecChannel,hist,backP,vecRange,1);  
  117.             //camshift跟踪位置  
  118.             Rect search=rt;  
  119.             RotatedRect rrt=CamShift(backP,search,TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,10,1));  
  120.             Rect rt2=rrt.boundingRect();  
  121.             rt&=rt2;  
  122.   
  123.             //跟踪框画到视频上  
  124.             rectangle(result,rt,Scalar(0,255,0),2);           
  125.         }  
  126.   
  127.         //结果显示  
  128.         imshow("原图",frame);  
  129.         moveWindow("原图",0,0);  
  130.   
  131.         imshow("膨胀运算",foreground);  
  132.         moveWindow("膨胀运算",0,350);  
  133.   
  134.         imshow("反向投影",backP);  
  135.         moveWindow("反向投影",400,350);  
  136.   
  137.         imshow("跟踪效果",result);  
  138.         moveWindow("跟踪效果",800,350);  
  139.         waitKey(30);      
  140.     }  
  141.           
  142.     getchar();  
  143.     return 0;  
  144. }  

3.avi是我写的一个目标运动仿真,用来做这个实验

BackgroundSubtractorMOG 

http://blog.csdn.net/xiaowei_cqu/article/details/23689189

[cpp]  view plain  copy
  1. int main()  
  2. {    
  3.     VideoCapture video("3.avi");    
  4.     Mat frame,mask,thresholdImage, output;    
  5.     BackgroundSubtractorMOG bgSubtractor;    
  6.     while(true)  
  7.     {   
  8.         video>>frame;    
  9.         if (frame.empty())  
  10.             break;  
  11.         bgSubtractor(frame,mask,0.1);    
  12.         imshow("mask",mask);    
  13.         waitKey(10);    
  14.     }    
  15.     return 0;    
  16. }    
  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
自定义Meanshift跟踪算法的实现步骤如下: 1. 获取视频帧,并选择要跟踪的目标区域。 2. 将目标区域转换为HSV色彩空间,并计算出该区域的直方图。 3. 对于每个后续帧,首先将其转换为HSV色彩空间,然后使用反向投影算法将其与目标直方图进行比较。 4. 对于每个像素,计算该像素的概率,即它属于目标区域的概率。 5. 使用MeanShift算法来计算下一个目标位置。在此算法中,计算出目标区域的质心,并将其用作新的目标位置。重复该过程,直到质心不再移动。 6. 将新的目标位置用矩形框标记在视频帧上,并将其显示出来。 下面是C++代码实现: ```c++ #include <iostream> #include <opencv2/opencv.hpp> using namespace cv; using namespace std; int main(int argc, char** argv) { VideoCapture cap(0); if (!cap.isOpened()) { cout << "Error opening video stream or file" << endl; return -1; } // 选择目标区域 Rect trackWindow(0, 0, 0, 0); bool init = false; Mat frame, hsv, mask, hist, backproj; // 设置终止条件 TermCriteria termcrit(TermCriteria::EPS | TermCriteria::COUNT, 10, 1); // 设置HSV颜色范围 int hmin = 0, smin = 0, vmin = 0; int hmax = 180, smax = 255, vmax = 255; while (true) { cap >> frame; if (frame.empty()) break; // 将帧转换为HSV颜色空间 cvtColor(frame, hsv, COLOR_BGR2HSV); // 如果已经选择了初始目标区域,则执行跟踪 if (init) { // 计算反向投影 calcBackProject(&hsv, 1, 0, hist, backproj, &ranges); // 应用CAMShift算法来计算新的目标位置 meanShift(backproj, trackWindow, termcrit); // 绘制矩形框 rectangle(frame, trackWindow, Scalar(0, 0, 255), 3); } // 显示图像 imshow("Frame", frame); // 按下空格键来选择目标区域 if (waitKey(1) == ' ') { init = false; trackWindow = selectROI("Frame", frame, false, false); if (trackWindow.area() > 0) { Mat roi(hsv, trackWindow); calcHist(&roi, 1, 0, mask, hist, 1, &histSize, &ranges); normalize(hist, hist, 0, 255, NORM_MINMAX); init = true; } } } cap.release(); destroyAllWindows(); return 0; } ``` 需要注意的是,这个算法的效果可能不如OpenCV中的MeanShift算法,因为OpenCV中的算法使用了更复杂的技术来提高跟踪的准确性。但是,通过自定义算法,可以更好地理解MeanShift算法的原理和实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值