今天要给大家分享的是CamShift算法的实现,这是OpenCV自带的一个demo,源文件在...\OpenCV245\samples\cpp\camshiftdemo.cpp.地方。在这里把我的学习心得给大家分享一下。

如果要了解该算法的原理,请看我的博文:CamShift算法的原理解读

需要说明的是,由于OpenCV的demo只是简单地调用了函数CamShift(...)而隐藏了该函数的实现细节,所以我把CamShift(...)函数的实现代码也拿了出来放在main()函数所在的文件中。CamShift(...)函数内部调用了OpenCV的C语言版本的cvCamShift(...)函数,所以我把它也拿了出来放在主cpp文件中,然后cvCamShift(...)函数又紧接着调用了OpenCV的C语言版本的cvMeanShit(...)函数,所以我把它也拿出来了。一连串的函数调用就到此为止了,可以看出虽然CamShift(...)函数是OpenCV2.x以上版本的,但是底层实现是1.x的版本,用的数据结构也是1.x版本的。

下面请看源代码吧:(猪似的很详细)

// CamShift1.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
#include 
   
    
#include "opencv2/opencv.hpp"
#include "opencv2/core/internal.hpp"
//#include "opencv2/imgproc/imgproc.hpp"
//#include "opencv2/video/tracking.hpp"
//#include "opencv2/highgui/highgui.hpp"
 
using namespace std;
using namespace cv;
 
const char* keys =
{
    "{1|  | 0 | camera number}"
};
 
Mat image;
 
bool backprojMode = true; // 显示结果的图像是原始图还是概率图的选项,按键盘'b'切换
bool selectObject = false; //selectObject的初始值为false,
int trackObject = 0;  //trackObject的初值是0
bool showHist = true;
Point origin;
Rect selection;
 
//亮度的最小值和最大值,饱和度的最小值
int vmin = 10, vmax = 256, smin = 30;
 
static void onMouse( int event, int x, int y, int, void* )
{
    if( selectObject ) //鼠标左键被按下后,该段语句开始执行
    {                  //按住左键拖动鼠标的时候,该鼠标响应函数
                       //会被不断的触发,不断计算目标矩形的窗口
        selection.x = MIN(x, origin.x);
        selection.y = MIN(y, origin.y);
        selection.width = std::abs(x - origin.x);
        selection.height = std::abs(y - origin.y);
 
        selection &= Rect(0, 0, image.cols, image.rows);
    }
 
    switch( event )
    {
    case CV_EVENT_LBUTTONDOWN:
        origin = Point(x,y);
        selection = Rect(x,y,0,0);
        selectObject = true;  //当在第一帧按下鼠标左键后,被置为true,拖动鼠标,开始选择目标的矩形区域
        break;
    case CV_EVENT_LBUTTONUP:
        selectObject = false;//直到鼠标左键抬起,标志着目标区域选择完毕。selectObject被置为false
        if( selection.width > 0 && selection.height > 0 )
            trackObject = -1; //当在第一帧用鼠标选定了合适的目标跟踪窗口后,trackObject的值置为 -1
        break;
    }
}
 
 
static void help()
{
    cout << "\n这是一个基于 Mean-Shift 的目标跟踪算法\n"
            "你用鼠标选择一个彩色目标(比如你的脸),然后算法就开始跟踪他.\n"
            "程序从摄像头读取是叛徒像序列(0 默认摄像头, 或者指定其他摄像头\n"
            "用法: \n"
            "   ./camshiftdemo [camera number]\n";
 
    cout << "\n\n快捷键: \n"
            "\tESC - 退出程序\n"
            "\tc - 停止跟踪\n"
            "\tb - 切换到反向投影图像\n"
            "\th - 显示目标的特征直方图\n"
            "\tp - 暂停视频\n"
            "初始化跟踪,用鼠标选择一个目标\n";
}
 
 extern "C" int CvCamShift( const void* imgProb, CvRect windowIn,CvTermCriteria criteria,
                        CvConnectedComp* _comp, CvBox2D* box );
RotatedRect MyCamShift( InputArray _probImage, Rect& window,TermCriteria criteria );
 
extern "C" int
CvMeanShift( const void* imgProb, CvRect windowIn,
             CvTermCriteria criteria, CvConnectedComp* comp );
 
int MyMeanShift( InputArray _probImage, Rect& window, TermCriteria criteria );
 
int _tmain( int argc, const char** argv)
{
    help(); //提示信息
 
    VideoCapture cap;  //声明一个读取视频流的类VideoCapture的对象
    Rect trackWindow;  //跟踪窗口
    int hsize = 16;    //直方图的特征子区间bins的数目
    float hranges[] = {0,180};//特征区间的取值范围,这里只有一个特征:色度 hue
    const float* phranges = hranges;
    CommandLineParser parser(argc, argv, keys); //传递命令行参数,用于指定使用哪个摄像头
    int camNum = parser.get
    
     ("1");
 
    cap.open(camNum);  //调用VideoCapture的对象的open函数打开摄像头
 
    if( !cap.isOpened() )//判断相机是否被正常打开
    {
        help();
        cout << "***无法初始化视频读入流...***\n";
        cout << "当前命令行参数值: \n";
        parser.printParams(); //输出当前命令行参数
        return -1;
    }
 
    namedWindow( "Histogram", 0 ); //创建显示目标直方图的窗口
    namedWindow( "CamShift Demo", 0 );//创建显示跟踪过程的窗口
    setMouseCallback( "CamShift Demo", onMouse, 0 );//为 CamShift Demo窗口添加鼠标响应函数,用于选择跟踪目标
    createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 );// 在Camshift Demo窗口上添加调节参数的进度条
    createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 );// 在Camshift Demo窗口上添加调节参数的进度条
    createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 );// 在Camshift Demo窗口上添加调节参数的进度条
     //在大循环外预先声明重复使用的变量
    Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj; 
    bool paused = false;  //暂停标志
 
    /*进入循环,处理视频图像序列,跟踪目标*/
    for(;;)
    {
        if( !paused ) //如果没有暂停,则继续读入下一帧图像
        {
            cap >> frame;  //cap对象将从摄像头读入的原始图像保存在frame中
            if( frame.empty() ) //判断读入的图像数据是否为空
                break;
        }
 
        frame.copyTo(image); //将读入的图像拷贝到image中,深度拷贝
 
        if(!paused) //判断是否暂停处理
        {
            cvtColor(image,hsv,CV_BGR2HSV); //将image中保存的RGB图像转换为HSV格式的图像
 
            if( trackObject )//直到选择了目标,trackObject才不为0,该if语句体才会执行
            {
                int _vmin = vmin, _vmax = vmax; //将TrackerBar上的参数读取到参数变量中
                //由于HSV模型的缺陷,故而要限制过低的饱和度和亮度
                //该函数的作用是将hsv图像中hue值在0到180且saturation值在smin到256且value值在_vmin到_vmax之间
                //的像素点的值置为255,而将不在规定范围内的像素点的值置为0,最后将结果保存在mask矩阵中
                inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
                        Scalar(180, 256, MAX(_vmin, _vmax)), mask);  
                int ch[] = {0, 0}; //通道序号,hsv图像的第0个通道对应hue的第0个通道
                hue.create(hsv.size(), hsv.depth()); //创建色调hue的图像
                mixChannels(&hsv, 1, &hue, 1, ch, 1);//将hsv中的h通道放到hue中去
 
                if( trackObject < 0 )  //如果trackObject小于0,就表示刚刚选定目标,
                {                       //该if语句段就是在鼠标给出的矩形框中提取目标的特征直方图,显示直方图
                    Mat roi(hue, selection), maskroi(mask, selection);  //取出hue图像和mask图像中的感兴趣区域
                    //计算ROI区域的直方图,只有在maskroi中对应位置非0的roi元素才会被统计进入直方图
                    calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
                    normalize(hist, hist, 0, 255, CV_MINMAX);//归一化直方图
 
                    trackWindow = selection;  //在第一帧中,跟踪窗口就是有鼠标指定的selection矩形区域
                    trackObject = 1;          //跟踪标志置1,所以下一次循环,该段if语句就不会执行了
 
                     histimg = Scalar::all(0);//histimg是用来绘制目标特征直方图的一个图像,这里把它清零
                    int binW = histimg.cols / hsize; //直方图的一个矩形柱子的宽度 bin width
                    Mat buf(1, hsize, CV_8UC3); //buf有hsize个三元组,每个三元组中对应着该位置上的颜色值
                    for( int i = 0; i < hsize; i++ )
                        buf.at
     
      (i) = Vec3b(saturate_cast
      
       (i*180./hsize), 255, 255);
                    cvtColor(buf, buf, CV_HSV2BGR);
 
                    for( int i = 0; i < hsize; i++ )
                    {
                        //取出直方图的第i个bin中的值,根据histimg图像的高换算成0到histimg.rows的值
                        int val = saturate_cast
       
        (hist.at
        
         (i)*histimg.rows/255); //在histimg图像上画出第i个直方图bin的矩形框 rectangle( histimg, Point(i*binW,histimg.rows),//左下角 Point((i+1)*binW,histimg.rows - val),//右上角 Scalar(buf.at
         
          (i)), -1, 8 ); //第i个直方图对应的颜色Hue值 } }//该if语句只在第一帧选中目标后被执行一次,用于计算目标特征直方图hist和绘制histimg图像 //根据目标的特征直方图,计算反向映射的概率图 calcBackProject(&hue, 1, 0, hist, backproj, &phranges,1.0,true); backproj &= mask; //将不满足saturation和value值得取值范围约束的坐标点置0 //调用CamShift算法在反向映射得到的概率图上寻找目标 //返回的跟踪结果在trackBox中,该椭圆框代表了目标在当前帧上的位置和大小 RotatedRect trackBox = MyCamShift(backproj, trackWindow, TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 )); if( trackWindow.area() <= 1 )//如果跟踪窗口的面积小于1,说明搜索窗口的长和宽都 { //小于等于1个像素点了,强制调整窗口的大小,使其包含至少r个像素点 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); } if( backprojMode )//是否切换到概率图上显示结果 cvtColor( backproj, image, CV_GRAY2BGR );//要在概率图上显示,就要把它转换为RGB图像 //将当前帧上的跟踪结果绘制到当前帧图像上 ellipse( image, trackBox, Scalar(0,0,255), 3, CV_AA ); } } else if( trackObject < 0 ) paused = false; //下面这段if语句代码也只有在第一帧上选定目标时才会被执行 //在按下鼠标左键和抬起鼠标左键之间的这段时间,selectObject为真, //selection会随着鼠标的移动不断变化,直到抬起鼠标左键后, //selectObject为假,selection就是选中的目标矩形框 if( selectObject && selection.width > 0 && selection.height > 0 ) { Mat roi(image, selection); bitwise_not(roi, roi); } imshow( "CamShift Demo", image );//显示当前要处理的图像 imshow( "Histogram", histimg ); //显示目标的特征直方图 char c = (char)waitKey(10);//每处理完一帧图像,等待10毫秒 if( c == 27 ) //按ESC键退出程序 break; switch(c) { case 'b': //在反向投影图和原图之间切换 backprojMode = !backprojMode; break; case 'c': //停止跟踪 trackObject = 0; histimg = Scalar::all(0); break; case 'h': //是否显示目标的特征直方图 showHist = !showHist; if( !showHist ) destroyWindow( "Histogram" ); else namedWindow( "Histogram", 1 ); break; case 'p': //是否暂停 paused = !paused; break; default: ; } } return waitKey(); } //这是C++版的Camshift函数,在函数内部贮备好一些参数后,便调用了C语言版本的Camshift算法 //输入参数包括概率图像,初始矩形搜索窗口,以及算法的收敛终止条件 RotatedRect MyCamShift( InputArray _probImage, Rect& window,TermCriteria criteria ) { CvConnectedComp comp; //C语言版本中的连接组件的结构体 CvBox2D box; //C语言结构中描述旋转矩形框的的结构体 //comp参数和box参数初始化,这两个参数要被用于CvCamShift()函数 //box所代表的旋转矩形区域是目标在图像中所占的真实区域 box.center.x = box.center.y = 0; box.angle = 0; box.size.width = box.size.height = 0; //comp.rect所代表的up-right矩形区域是包围box的外接区域,是一个搜索框 comp.rect.x = comp.rect.y = comp.rect.width = comp.rect.height = 0; //调用getMat()函数,将输入参数类型为InputArray的 //_probImage参数的矩阵头赋给probImage,使其变为Mat类型的变量 Mat probImage = _probImage.getMat(); CvMat c_probImage = probImage; //将OpenCv2.x C++版本中的Mat类型变为C语言版本的CvMat类型 //调用C语言版本的CvCamShift算法跟踪目标,跟踪结果保存在box当中 CvCamShift(&c_probImage, window, (CvTermCriteria)criteria, &comp, &box); window = comp.rect; //返回跟踪结果,由于box是CvBox2D类型(C版本),所以将其转换为C++版本的旋转矩形框类 return RotatedRect(Point2f(box.center), Size2f(box.size), box.angle); } /*F/// // Name: CvCamShift // Purpose: CAMSHIFT 算法(C语言版本) // Context: // Parameters: // imgProb - 2D维目标概率分布 // windowIn - CvRect of CAMSHIFT 搜索窗口的初始尺寸 // criteria - 搜索迭代终止的条件 // windowOut - 收敛的CAMSHIFT窗口的 位置,高度,宽度 // orientation - If != NULL, return distribution orientation // len - If != NULL, return equivalent len // width - If != NULL, return equivalent width // area - 结果窗口中所有元素的和 // Returns: // CAMSHIFT 达到收敛条件所用的迭代次数 //F*/ //extern "C"指令仅指定编译和连接规约,但不影响语义 extern "C" int CvCamShift( const void* imgProb, CvRect windowIn,CvTermCriteria criteria ,CvConnectedComp* _comp,CvBox2D* box ) { const int TOLERANCE = 10; //经过尝试,发现这个设为0也没问题 CvMoments moments; //图像块的各阶矩的结构体 double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00; //定义空间矩和中心距变量 double a, b, c, xc, yc;//a,b,c用于估计旋转方向的变量,xc,yc是X和Y方向的重心或形心(centroid) double rotate_a, rotate_c; double theta = 0, square; double cs, sn; double length = 0, width = 0;//旋转椭圆的长轴和短轴 int itersUsed = 0; //迭代所用次数 CvConnectedComp comp; //主要用到了里面的Rect,该参数主要用来记忆每一帧的搜索框 //cur_win当前窗口下的图像,stub是一个临时参数,mat指向概率图像头的指针 CvMat cur_win, stub, *mat = (CvMat*)imgProb; comp.rect = windowIn; //把初始搜索窗口赋给comp mat = cvGetMat( mat, &stub );//获得矩阵头,以CvMat的方式引用图像imgProb //CamShift算法内部调用了MeanShift算法,通过均值漂移迭代找到当前概率图像上概率分布峰值, itersUsed = CvMeanShift( mat, windowIn, criteria, &comp ); //CvMeanShit算法是C语言版本的 windowIn = comp.rect; //调整由CvMeanShift返回的收敛窗口的大小 windowIn.x -= TOLERANCE; if( windowIn.x < 0 ) windowIn.x = 0; windowIn.y -= TOLERANCE; if( windowIn.y < 0 ) windowIn.y = 0; windowIn.width += 2 * TOLERANCE; if( windowIn.x + windowIn.width > mat->width ) windowIn.width = mat->width - windowIn.x; windowIn.height += 2 * TOLERANCE; if( windowIn.y + windowIn.height > mat->height ) windowIn.height = mat->height - windowIn.y; //获取已经移动到新位置的windowIn窗口中的概率图像快 cvGetSubRect( mat, &cur_win, windowIn ); /* 计算新位置上的图像块的矩 */ cvMoments( &cur_win, &moments );//该函数计算给定图像块的各阶矩 m00 = moments.m00; //零阶矩 m10 = moments.m10; //X方向一阶矩 m01 = moments.m01; //Y方向一阶矩 mu11 = moments.mu11;//X和Y的耦合二阶中心矩 mu20 = moments.mu20;//X方向二阶中心矩 mu02 = moments.mu02;//Y方向二阶中心矩 if( fabs(m00) < DBL_EPSILON ) return -1; inv_m00 = 1. / m00; xc = cvRound( m10 * inv_m00 + windowIn.x );//目标质心的新位置 x center yc = cvRound( m01 * inv_m00 + windowIn.y );//目标质心的新位置 y center a = mu20 * inv_m00; //未旋转之前的长轴 b = mu11 * inv_m00; c = mu02 * inv_m00; //未旋转之前的短轴 /* 计算宽度和高度 */ square = sqrt( 4 * b * b + (a - c) * (a - c) ); /* 计算旋转方向 */ theta = atan2( 2 * b, a - c + square ); /* 计算宽度和长度 of figure */ cs = cos( theta ); sn = sin( theta ); rotate_a = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02; rotate_c = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02; length = sqrt( rotate_a * inv_m00 ) * 4;//旋转后的椭圆长轴 width = sqrt( rotate_c * inv_m00 ) * 4; //旋转后的椭圆短轴 /* In case, when tetta is 0 or 1.57... 保证长轴比短轴长*/ if( length < width ) { double t; CV_SWAP( length, width, t ); CV_SWAP( cs, sn, t ); theta = CV_PI*0.5 - theta; } /* 保存结果 */ if( _comp || box ) { int t0, t1; int _xc = cvRound( xc );//椭圆区域的外接矩形的中心x坐标 int _yc = cvRound( yc );//椭圆区域的外接矩形的中心y坐标 t0 = cvRound( fabs( length * cs )); //长轴在X轴上的投影长度 t1 = cvRound( fabs( width * sn )); //短轴在X轴上的投影长度 //选择长短轴在x轴上的投影中最长的一个加上2作为旋转后的椭圆的外接矩形的宽度 t0 = MAX( t0, t1 ) + 2; //外接矩形的宽度 comp.rect.width = MIN( t0, (mat->width - _xc) * 2 );图像边界越界检查 t0 = cvRound( fabs( length * sn ));//长轴在Y轴上的投影长度 t1 = cvRound( fabs( width * cs ));//短轴在Y轴上的投影长度 //选择长短轴在Y轴上的投影中最长的一个加上2作为旋转后的椭圆的外接矩形的高度 t0 = MAX( t0, t1 ) + 2; comp.rect.height = MIN( t0, (mat->height - _yc) * 2 );//图像边界越界检查 //计算包围椭圆的矩形框的左上角顶点坐标,同时要保证外接矩形的左上角定点在图像内部 comp.rect.x = MAX( 0, _xc - comp.rect.width / 2 ); comp.rect.y = MAX( 0, _yc - comp.rect.height / 2 ); //保证外接矩形的右下角顶点在图像内部 comp.rect.width = MIN( mat->width - comp.rect.x, comp.rect.width ); comp.rect.height = MIN( mat->height - comp.rect.y, comp.rect.height ); comp.area = (float) m00;//椭圆区域的 mass } //在该算法中,矩形搜索框始终是与坐标轴垂直或平行的,是包围目标椭圆的up-right矩形框 if( _comp ) //返回包围目标的矩形框参数 *_comp = comp; //该矩形框将会作为下一帧的初始搜索框, if( box ) { //box是CvBox2D类型的变量,用于描述旋转矩形,该矩形是真正的目标区域矩形 //该矩形下的区域是在当前帧上找到的目标区域,该区域不一定和坐标轴垂直或平行 //而comp.rect中包含的区域是比box大的区域,它里面有非目标像素点 box->size.height = (float)length; box->size.width = (float)width; box->angle = (float)((CV_PI*0.5+theta)*180./CV_PI); while(box->angle < 0) box->angle += 360; while(box->angle >= 360) box->angle -= 360; if(box->angle >= 180) box->angle -= 180; box->center = cvPoint2D32f( comp.rect.x + comp.rect.width*0.5f, comp.rect.y + comp.rect.height*0.5f); } return itersUsed; } /*这是OpenCV2.4.5版本中的Meanshift算法的C++函数头,内部调用了C语言版本的MeanShift函数*/ int MyMeanShift( InputArray _probImage, Rect& window, TermCriteria criteria ) { CvConnectedComp comp; Mat probImage = _probImage.getMat(); CvMat c_probImage = probImage; int iters = CvMeanShift(&c_probImage, window, (CvTermCriteria)criteria, &comp ); window = comp.rect; return iters; } /*F/// // Name: CvMeanShift // Purpose: MeanShift 算法 // Context: // Parameters: // imgProb - 2D object probability distribution // windowIn - CvRect of CAMSHIFT Window intial size // numIters - If CAMSHIFT iterates this many times, stop // windowOut - Location, height and width of converged CAMSHIFT window // len - If != NULL, return equivalent len // width - If != NULL, return equivalent width // Returns: // Number of iterations CAMSHIFT took to converge // Notes: //F*/ extern "C" int CvMeanShift( const void* imgProb, CvRect windowIn, CvTermCriteria criteria, CvConnectedComp* comp ) { CvMoments moments;//保存图像各阶矩的结构体 int i = 0, eps; CvMat stub, *mat = (CvMat*)imgProb;//stub是个临时矩阵 CvMat cur_win; //当前窗口下的图像 CvRect cur_rect = windowIn; //当前(初始)搜索窗口区域 if( comp ) comp->rect = windowIn; //初始化comp moments.m00 = moments.m10 = moments.m01 = 0; //初始化各阶矩 mat = cvGetMat( mat, &stub );//从imgProb获得矩阵头,以CvMat的方式使用图像 if( CV_MAT_CN( mat->type ) > 1 )//目前只支持单通道图像 CV_Error( CV_BadNumChannels, cvUnsupportedFormat ); if( windowIn.height <= 0 || windowIn.width <= 0 ) CV_Error( CV_StsBadArg, "Input window has non-positive sizes" ); windowIn = cv::Rect(windowIn) & cv::Rect(0, 0, mat->cols, mat->rows); //边界检查 criteria = cvCheckTermCriteria( criteria, 1., 100 ); eps = cvRound( criteria.epsilon * criteria.epsilon ); //开始MeanShift迭代过程,实际迭代次数小于等于criteria.max_iter for( i = 0; i < criteria.max_iter; i++ ) { int dx, dy, nx, ny; double inv_m00; //搜索窗口在移动过程中,需要保证在图像内部 cur_rect = cv::Rect(cur_rect) & cv::Rect(0, 0, mat->cols, mat->rows); if( cv::Rect(cur_rect) == cv::Rect() ) {//如果当前窗口是空的,则把它默认放在图像中间 cur_rect.x = mat->cols/2; cur_rect.y = mat->rows/2; } //保证当前窗口的宽度和高度>0 cur_rect.width = MAX(cur_rect.width, 1); cur_rect.height = MAX(cur_rect.height, 1); //从当前帧概率图像中获取当前搜索窗口下的概率图像块 cvGetSubRect( mat, &cur_win, cur_rect ); cvMoments( &cur_win, &moments ); //计算当前窗口下的概率图像块的矩 /* 计算当前概率图像块的质心 center of mass */ if( fabs(moments.m00) < DBL_EPSILON ) break; inv_m00 = moments.inv_sqrt_m00*moments.inv_sqrt_m00; //就是 1/m00 //计算搜索窗口在x,y方向上应该移动的距离(漂移向量) dx = cvRound( moments.m10 * inv_m00 - windowIn.width*0.5 ); dy = cvRound( moments.m01 * inv_m00 - windowIn.height*0.5 ); //nx,ny是搜索窗口左上角的坐标, //他们加上dx,dy就相当于搜索窗口沿着(dx,dy)的方向漂移 nx = cur_rect.x + dx; ny = cur_rect.y + dy; //x方向的边界检查 if( nx < 0 ) nx = 0; else if( nx + cur_rect.width > mat->cols ) nx = mat->cols - cur_rect.width; //y方向的边界检查 if( ny < 0 ) ny = 0; else if( ny + cur_rect.height > mat->rows ) ny = mat->rows - cur_rect.height; //由于边界检查可能会改变nx,ny,所以要重新计算漂移向量(dx,dy) dx = nx - cur_rect.x; dy = ny - cur_rect.y; //将当前搜索窗口移动到新的位置,进入下一轮迭代 cur_rect.x = nx; cur_rect.y = ny; /* 检查窗口的移动距离是否小于eps,若小于,则算法收敛,退出循环 */ if( dx*dx + dy*dy < eps ) break; } if( comp ) {//返回迭代终止的时候的矩形窗口作为在当前帧上找到的概率分布峰值的位置 comp->rect = cur_rect; comp->area = (float)moments.m00;//零阶矩在后面被用来估计目标的size } return i;//返回实际使用的迭代次数 }

下面请看运行效果:

OpenCV自带的CamShift算法解读相关推荐

  1. 使用Python,OpenCV的Meanshift 和 Camshift 算法来查找和跟踪视频中的对象

    使用Python,OpenCV的Meanshift 和 Camshift 算法来查找和跟踪视频中的对象 1. 效果图 2. 源码 2.1 MeanShift 2.2 Camshift(Continuo ...

  2. python opencv入门 Meanshift 和 Camshift 算法(40)

    内容来自OpenCV-Python Tutorials 自己翻译整理 目标: 在本章,学习Meanshift算法和Camshift算法来寻找和追踪视频中的目标物体. Meanshift算法: mean ...

  3. 目标跟踪学习笔记_1(opencv中meanshift和camshift例子的应用)

    在这一节中,主要讲目标跟踪的一个重要的算法Camshift,因为它是连续自使用的meanShift,所以这2个函数opencv中都有,且都很重要.为了让大家先达到一个感性认识.这节主要是看懂和运行op ...

  4. opencv中meanshift和camshift函数的使用

    原文地址:http://www.360doc.com/content/13/1106/16/10724725_327176222.shtml 在这一节中,主要讲目标跟踪的一个重要的算法Camshift ...

  5. 前景检测算法_4(opencv自带GMM)

    参考:https://www.cnblogs.com/tornadomeet/archive/2012/06/02/2531705.html 前景检测算法_4(opencv自带GMM) 前面已经有3篇 ...

  6. OpenCV camshift算法的实例(附完整代码)

    OpenCV camshift算法的实例 OpenCV camshift算法的实例 OpenCV camshift算法的实例 #include <iostream> #include &l ...

  7. OpenCV均值移位(Meanshift)和Camshift算法

    OpenCV Meanshift和Camshift算法 Meanshift和Camshift算法 目标 均值漂移Meanshift OpenCV中的Meanshift Camshift OpenCV中 ...

  8. opencv python 多帧降噪算法_OpenCV-Python中用于视频跟踪的Meanshift和Camshift算法介绍...

    学习目标 在本章中, 我们将学习用于跟踪视频中对象的Meanshift和Camshift算法. Meanshift Meanshift背后的直觉很简单,假设你有点的集合.(它可以是像素分布,例如直方图 ...

  9. OpenCV总结:目标跟踪之CamShift算法

    CamShift算法全称是"Continuously Adaptive Mean-Shift"(连续的自适应MeanShift算法),是对MeanShift算法的改进算法,可以在跟 ...

  10. Camshift算法原理及其Opencv实现

    Camshift原理 camshift利用目标的颜色直方图模型将图像转换为颜色概率分布图,初始化一个搜索窗的大小和位置,并根据上一帧得到的结果自适应调整搜索窗口的位置和大小,从而定位出当前图像中目标的 ...

最新文章

  1. 【javascript】javascript函数整理之日期型(Date)篇
  2. a标签传值到另一个页面_前端开发入门——HTML基础标签
  3. c语言1e3和1e3,自考“高级语言程序设计”习题答案详解(33)
  4. json_decode用法
  5. 开发者70行代码破解苹果OSX远程锁定安全功能
  6. 手动指定 main 函数作为入口libvcruntimed.lib
  7. 用淘宝Fourinone实现Hadoop经典实例wordcount
  8. iOS通知的整理笔记
  9. mysql判断时间是否在某个区间_如何正确理解 RT 并监控 MySQL 的响应时间
  10. Caffe学习:Forward and Backward
  11. Hybird开发之webview
  12. 英语作文计算机80词九年级,英语作文80词左右初三带翻译
  13. VOC数据集简介与制作
  14. UE4 编辑器脚本-批量创建LOD
  15. c语言数组统计选票,C语言实现选票统计
  16. 「硬核JS」一次搞懂JS运行机制
  17. [需求管理-3]:什么是需求分析?常用的需求分析的流程与方法
  18. cypress 框架介绍及元素定位
  19. X509证书认证原理
  20. deepin更新失败_更新失败

热门文章

  1. Linux里sra文件是什么,生信软件 | Sratools (操作SRA文件)
  2. pip快速下载python包
  3. 如何从微博安装包里提取微博表情包?
  4. VS2015产品密钥
  5. HTML5+js+css3开心消消乐手机pc端通用源码|H5小游戏
  6. linux设计引物探针,如何设计荧光定量PCR的引物及TaqMan探针
  7. talib python文档_talib 中文文档(三):talib 方法大全
  8. 《javascript高级程序设计》核心知识总结
  9. 施乐s2110进入维修模式_施乐S2110维修手册(无密码)
  10. 经典算法——五大常用算法