opencv2 camshift目标跟踪详解及测试代码

本文转自:http://blog.csdn.net/gdfsg/article/details/51029370

1. CamShift思想       

       Camshift全称是"Continuously Adaptive Mean-SHIFT",即连续自适应的MeanShift算法,是MeanShift算法的改进。CamShift的基本思想是视频图像的所有帧作MeanShift运算,并将上一帧的结果(即Search Window的中心和大小)作为下一帧MeanShift算法的Search Window的初始值,如此迭代下去。

       这个过程其实和用MeanShift做跟踪一样,可以参见我的另一篇博文“Meanshift之目标跟踪”,这里把我画的流程图搬过来。




2. cvCamShift( )详解

     CamShift号称连续自适应MeanShift,在算法理论上并没有什么区别,甚至在编程的流程上也没什么区别,他们的区别体现在程序内部

  1. int cvCamShift( const void* imgProb,        //概率图  
  2.             CvRect windowIn,                    //起始跟踪区域  
  3.             CvTermCriteria criteria,            //迭代终止条件  
  4.             CvConnectedComp* _comp,             //可选参数,表示连通域结构体    
  5.             CvBox2D* box )                      //可选参数,存储旋转矩形的坐标,包括中心,尺寸和旋转角  
int cvCamShift( const void* imgProb,        //概率图
            CvRect windowIn,                    //起始跟踪区域
            CvTermCriteria criteria,            //迭代终止条件
            CvConnectedComp* _comp,             //可选参数,表示连通域结构体  
            CvBox2D* box )                      //可选参数,存储旋转矩形的坐标,包括中心,尺寸和旋转角

和MeanShift一样,返回值是迭代次数。这里比MeanShift多了一个参数box。

函数原型见 ..\OpenCV249\sources\modules\video\src\camshift.cpp

  1. CV_IMPL int  
  2. cvCamShift( const void* imgProb, CvRect windowIn,  
  3.             CvTermCriteria criteria,  
  4.             CvConnectedComp* _comp,  
  5.             CvBox2D* box )  
  6. {  
  7.     const int TOLERANCE = 10;  //公差=10  
  8.     CvMoments moments;  
  9.     double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00;  
  10.     double a, b, c, xc, yc;  
  11.     double rotate_a, rotate_c;  
  12.     double theta = 0, square;  
  13.     double cs, sn;  
  14.     double length = 0, width = 0;  
  15.     int itersUsed = 0;  
  16.     CvConnectedComp comp;  
  17.     CvMat  cur_win, stub, *mat = (CvMat*)imgProb;  
  18.   
  19.     CV_FUNCNAME( "cvCamShift" );  
  20.   
  21.     comp.rect = windowIn;  
  22.   
  23.     __BEGIN__;  
  24.   
  25.     CV_CALL( mat = cvGetMat( mat, &stub ));  
  26.   
  27.     //调用cvMeanShift函数  
  28.     CV_CALL( itersUsed = cvMeanShift( mat, windowIn, criteria, &comp ));  
  29.     windowIn = comp.rect;  
  30.       
  31.     //-------------下面的程序是和MeanShift( )的区别所在------------  
  32.     //区别1:对边界情况进行处理,  
  33.     //CamShift()中将windowIn沿x和y方向拉大了2个TOLERANCE,并且确保windowIn不越界。Meanshift无此操作  
  34.     windowIn.x -= TOLERANCE;  
  35.     if( windowIn.x < 0 )  
  36.         windowIn.x = 0;  
  37.   
  38.     windowIn.y -= TOLERANCE;  
  39.     if( windowIn.y < 0 )  
  40.         windowIn.y = 0;  
  41.   
  42.     windowIn.width += 2 * TOLERANCE;  
  43.     if( windowIn.x + windowIn.width > mat->width )  
  44.         windowIn.width = mat->width - windowIn.x;  
  45.   
  46.     windowIn.height += 2 * TOLERANCE;  
  47.     if( windowIn.y + windowIn.height > mat->height )  
  48.         windowIn.height = mat->height - windowIn.y;  
  49.   
  50.     CV_CALL( cvGetSubRect( mat, &cur_win, windowIn ));//在mat中提取windowIn区域  
  51.   
  52.     /* Calculating moments in new center mass */  
  53.     //计算新中心处的颜色统计矩  
  54.     CV_CALL( cvMoments( &cur_win, &moments ));  
  55.   
  56.     //区别2:计算并保存了目标旋转的结果,meanshit()并未考虑旋转  
  57.     m00 = moments.m00;      //0阶空间矩  
  58.     m10 = moments.m10;      //水平1阶  
  59.     m01 = moments.m01;      //垂直1阶  
  60.     mu11 = moments.mu11;    //水平垂直2阶中心距  
  61.     mu20 = moments.mu20;    //水平2阶  
  62.     mu02 = moments.mu02;    //垂直2阶  
  63.   
  64.     //目标矩形的质量太小了就退出  
  65.     if( fabs(m00) < DBL_EPSILON )//系统预定于的值,DBL_EPSILON=2.2204460492503131e-016  
  66.         EXIT;  
  67.   
  68.     //质量的倒数,只是为了下面计算方便,可以把除法表示成乘法  
  69.     inv_m00 = 1. / m00;  
  70.     xc = cvRound( m10 * inv_m00 + windowIn.x );  
  71.     yc = cvRound( m01 * inv_m00 + windowIn.y );  //(xc,yc)是重心相对于图像的坐标想  
  72.     a = mu20 * inv_m00;  
  73.     b = mu11 * inv_m00;  
  74.     c = mu02 * inv_m00;  
  75.   
  76.     /* Calculating width & height */  
  77.     square = sqrt( 4 * b * b + (a - c) * (a - c) );  
  78.   
  79.     /* Calculating orientation */  
  80.     //计算目标主轴方向角度  
  81.     theta = atan2( 2 * b, a - c + square );   //theta是与x轴的夹角  
  82.   
  83.     /* Calculating width & length of figure */  
  84.     cs = cos( theta );  
  85.     sn = sin( theta );  
  86.   
  87.     rotate_a = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02;  
  88.     rotate_c = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02;  
  89.     //下次搜索窗口的长宽,注意不是width和height  
  90.     length = sqrt( rotate_a * inv_m00 ) * 4;  
  91.     width = sqrt( rotate_c * inv_m00 ) * 4;  
  92.   
  93.     /*根据length和width的大小对length、width、theta进行调整*/  
  94.     if( length < width )  
  95.     {  
  96.         double t;  
  97.           
  98.         CV_SWAP( length, width, t );  
  99.         CV_SWAP( cs, sn, t );  
  100.         theta = CV_PI*0.5 - theta;  
  101.     }  
  102.   
  103.     /* 结果保存在comp中 */  
  104.     if( _comp || box )  
  105.     {  
  106.         int t0, t1;  
  107.         int _xc = cvRound( xc );  
  108.         int _yc = cvRound( yc );  
  109.   
  110.         t0 = cvRound( fabs( length * cs ));  
  111.         t1 = cvRound( fabs( width * sn ));  
  112.   
  113.         t0 = MAX( t0, t1 ) + 2;  
  114.         comp.rect.width = MIN( t0, (mat->width - _xc) * 2 );  
  115.   
  116.         t0 = cvRound( fabs( length * sn ));  
  117.         t1 = cvRound( fabs( width * cs ));  
  118.   
  119.         t0 = MAX( t0, t1 ) + 2;  
  120.         comp.rect.height = MIN( t0, (mat->height - _yc) * 2 );  
  121.   
  122.         comp.rect.x = MAX( 0, _xc - comp.rect.width / 2 );  
  123.         comp.rect.y = MAX( 0, _yc - comp.rect.height / 2 );  
  124.   
  125.         comp.rect.width = MIN( mat->width - comp.rect.x, comp.rect.width );  
  126.         comp.rect.height = MIN( mat->height - comp.rect.y, comp.rect.height );  
  127.         comp.area = (float) m00;  
  128.     }  
  129.   
  130.     __END__;  
  131.   
  132.     if( _comp )  
  133.         *_comp = comp;  
  134.       
  135.     if( box )    //box里存的是目标的相关参数  
  136.     {  
  137.         box->size.height = (float)length;  
  138.         box->size.width = (float)width;  
  139.         box->angle = (float)(theta*180./CV_PI);  
  140.         box->center = cvPoint2D32f( comp.rect.x + comp.rect.width*0.5f,  
  141.                                     comp.rect.y + comp.rect.height*0.5f);  
  142.     }  
  143.   
  144.     return itersUsed;   //返回迭代次数  
  145. }  
CV_IMPL int
cvCamShift( const void* imgProb, CvRect windowIn,
            CvTermCriteria criteria,
            CvConnectedComp* _comp,
            CvBox2D* box )
{
    const int TOLERANCE = 10;  //公差=10
    CvMoments moments;
    double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00;
    double a, b, c, xc, yc;
    double rotate_a, rotate_c;
    double theta = 0, square;
    double cs, sn;
    double length = 0, width = 0;
    int itersUsed = 0;
    CvConnectedComp comp;
    CvMat  cur_win, stub, *mat = (CvMat*)imgProb;

    CV_FUNCNAME( "cvCamShift" );

    comp.rect = windowIn;

    __BEGIN__;

    CV_CALL( mat = cvGetMat( mat, &stub ));

    //调用cvMeanShift函数
    CV_CALL( itersUsed = cvMeanShift( mat, windowIn, criteria, &comp ));
    windowIn = comp.rect;
	
	//-------------下面的程序是和MeanShift( )的区别所在------------
	//区别1:对边界情况进行处理,
	//CamShift()中将windowIn沿x和y方向拉大了2个TOLERANCE,并且确保windowIn不越界。Meanshift无此操作
    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;

    CV_CALL( cvGetSubRect( mat, &cur_win, windowIn ));//在mat中提取windowIn区域

    /* Calculating moments in new center mass */
    //计算新中心处的颜色统计矩
    CV_CALL( cvMoments( &cur_win, &moments ));

	//区别2:计算并保存了目标旋转的结果,meanshit()并未考虑旋转
    m00 = moments.m00;		//0阶空间矩
    m10 = moments.m10;		//水平1阶
    m01 = moments.m01;		//垂直1阶
    mu11 = moments.mu11;	//水平垂直2阶中心距
    mu20 = moments.mu20;	//水平2阶
    mu02 = moments.mu02;	//垂直2阶

    //目标矩形的质量太小了就退出
    if( fabs(m00) < DBL_EPSILON )//系统预定于的值,DBL_EPSILON=2.2204460492503131e-016
        EXIT;

    //质量的倒数,只是为了下面计算方便,可以把除法表示成乘法
    inv_m00 = 1. / m00;
    xc = cvRound( m10 * inv_m00 + windowIn.x );
    yc = cvRound( m01 * inv_m00 + windowIn.y );  //(xc,yc)是重心相对于图像的坐标想
    a = mu20 * inv_m00;
    b = mu11 * inv_m00;
    c = mu02 * inv_m00;

    /* Calculating width & height */
    square = sqrt( 4 * b * b + (a - c) * (a - c) );

    /* Calculating orientation */
    //计算目标主轴方向角度
    theta = atan2( 2 * b, a - c + square );   //theta是与x轴的夹角

    /* Calculating width & length 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;
    //下次搜索窗口的长宽,注意不是width和height
    length = sqrt( rotate_a * inv_m00 ) * 4;
    width = sqrt( rotate_c * inv_m00 ) * 4;

    /*根据length和width的大小对length、width、theta进行调整*/
    if( length < width )
    {
        double t;
        
        CV_SWAP( length, width, t );
        CV_SWAP( cs, sn, t );
        theta = CV_PI*0.5 - theta;
    }

    /* 结果保存在comp中 */
    if( _comp || box )
    {
        int t0, t1;
        int _xc = cvRound( xc );
        int _yc = cvRound( yc );

        t0 = cvRound( fabs( length * cs ));
        t1 = cvRound( fabs( width * sn ));

        t0 = MAX( t0, t1 ) + 2;
        comp.rect.width = MIN( t0, (mat->width - _xc) * 2 );

        t0 = cvRound( fabs( length * sn ));
        t1 = cvRound( fabs( width * cs ));

        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;
    }

    __END__;

    if( _comp )
        *_comp = comp;
    
    if( box )    //box里存的是目标的相关参数
    {
        box->size.height = (float)length;
        box->size.width = (float)width;
        box->angle = (float)(theta*180./CV_PI);
        box->center = cvPoint2D32f( comp.rect.x + comp.rect.width*0.5f,
                                    comp.rect.y + comp.rect.height*0.5f);
    }

    return itersUsed;   //返回迭代次数
}

将CamShift( )和MeanShift( )对比,可以看到这些差别

1、CamShift( )中将cur_win沿x和y方向拉大了2个TOLERANCE,MeanShift( )无此操作

2、CamShift( )考虑了目标发生旋转的情况,并给出了旋转角,MeanShift( )无此操作


3.实验代码及结果

来看看OpenCV自带的demo,原程序见D:\Programs_L\OpenCV249\sources\samples\cpp\camshiftdemo.cpp

  1. #include "opencv2/video/tracking.hpp"  
  2. #include "opencv2/imgproc/imgproc.hpp"  
  3. #include "opencv2/highgui/highgui.hpp"  
  4.   
  5. #include <iostream>  
  6. #include <ctype.h>  
  7.   
  8. using namespace cv;  
  9. using namespace std;  
  10.   
  11. Mat image;  
  12.   
  13. bool backprojMode = false;  
  14. bool selectObject = false;  
  15. int trackObject = 0;  
  16. bool showHist = true;  
  17. Point origin;  
  18. Rect selection;  
  19. int vmin = 10, vmax = 256, smin = 30;  
  20.   
  21. static void onMouse( int event, int x, int y, intvoid* )  
  22. {  
  23.     if( selectObject )  
  24.     {  
  25.         selection.x = MIN(x, origin.x);  
  26.         selection.y = MIN(y, origin.y);  
  27.         selection.width = std::abs(x - origin.x);  
  28.         selection.height = std::abs(y - origin.y);  
  29.   
  30.         selection &= Rect(0, 0, image.cols, image.rows);  
  31.     }  
  32.   
  33.     switch( event )  
  34.     {  
  35.     case CV_EVENT_LBUTTONDOWN:  
  36.         origin = Point(x,y);  
  37.         selection = Rect(x,y,0,0);  
  38.         selectObject = true;  
  39.         break;  
  40.     case CV_EVENT_LBUTTONUP:  
  41.         selectObject = false;  
  42.         if( selection.width > 0 && selection.height > 0 )  
  43.             trackObject = -1;  
  44.         break;  
  45.     }  
  46. }  
  47.   
  48. static void help()  
  49. {  
  50.     cout << "\nThis is a demo that shows mean-shift based tracking\n"  
  51.             "You select a color objects such as your face and it tracks it.\n"  
  52.             "This reads from video camera (0 by default, or the camera number the user enters\n"  
  53.             "Usage: \n"  
  54.             "   ./camshiftdemo [camera number]\n";  
  55.   
  56.     cout << "\n\nHot keys: \n"  
  57.             "\tESC - quit the program\n"  
  58.             "\tc - stop the tracking\n"  
  59.             "\tb - switch to/from backprojection view\n"  
  60.             "\th - show/hide object histogram\n"  
  61.             "\tp - pause video\n"  
  62.             "To initialize tracking, select the object with mouse\n";  
  63. }  
  64.   
  65. const char* keys =  
  66. {  
  67.     "{1|  | 0 | camera number}"  
  68. };  
  69.   
  70. int main( int argc, const char** argv )  
  71. {  
  72.     help();  
  73.   
  74.     VideoCapture cap;  
  75.     Rect trackWindow;  
  76.     int hsize = 16;  
  77.     float hranges[] = {0,180};  
  78.     const float* phranges = hranges;  
  79.     CommandLineParser parser(argc, argv, keys);  
  80.     int camNum = parser.get<int>("1");  
  81.   
  82.     cap.open(camNum);  
  83.   
  84.     if( !cap.isOpened() )  
  85.     {  
  86.         help();  
  87.         cout << "***Could not initialize capturing...***\n";  
  88.         cout << "Current parameter's value: \n";  
  89.         parser.printParams();  
  90.         return -1;  
  91.     }  
  92.   
  93.     namedWindow( "Histogram", 0 );  
  94.     namedWindow( "CamShift Demo", 0 );  
  95.     setMouseCallback( "CamShift Demo", onMouse, 0 );  
  96.     createTrackbar( "Vmin""CamShift Demo", &vmin, 256, 0 );  
  97.     createTrackbar( "Vmax""CamShift Demo", &vmax, 256, 0 );  
  98.     createTrackbar( "Smin""CamShift Demo", &smin, 256, 0 );  
  99.   
  100.     Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;  
  101.     bool paused = false;  
  102.   
  103.     for(;;)  
  104.     {  
  105.         if( !paused )  
  106.         {  
  107.             cap >> frame;  
  108.             if( frame.empty() )  
  109.                 break;  
  110.         }  
  111.   
  112.         frame.copyTo(image);  
  113.   
  114.         if( !paused )  
  115.         {  
  116.             cvtColor(image, hsv, COLOR_BGR2HSV);  
  117.   
  118.             if( trackObject )  
  119.             {  
  120.                 int _vmin = vmin, _vmax = vmax;  
  121.   
  122.                 inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),  
  123.                         Scalar(180, 256, MAX(_vmin, _vmax)), mask);  //mask初始化  
  124.                 int ch[] = {0, 0};  
  125.                 hue.create(hsv.size(), hsv.depth());  
  126.                 mixChannels(&hsv, 1, &hue, 1, ch, 1);  //提取h通道  
  127.   
  128.                 if( trackObject < 0 )  
  129.                 {  
  130.                     Mat roi(hue, selection), maskroi(mask, selection);  
  131.                     calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);   //计算目标直方图  
  132.                     normalize(hist, hist, 0, 255, CV_MINMAX);  
  133.   
  134.                     trackWindow = selection;  
  135.                     trackObject = 1;  
  136.   
  137.                     histimg = Scalar::all(0);  
  138.                     int binW = histimg.cols / hsize;  
  139.                     Mat buf(1, hsize, CV_8UC3);  
  140.                     forint i = 0; i < hsize; i++ )  
  141.                         buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./hsize), 255, 255);  
  142.                     cvtColor(buf, buf, CV_HSV2BGR);  
  143.   
  144.                     forint i = 0; i < hsize; i++ )  
  145.                     {  
  146.                         int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);  
  147.                         rectangle( histimg, Point(i*binW,histimg.rows),  
  148.                                    Point((i+1)*binW,histimg.rows - val),  
  149.                                    Scalar(buf.at<Vec3b>(i)), -1, 8 );  
  150.                     }  
  151.                 }  
  152.   
  153.                 calcBackProject(&hue, 1, 0, hist, backproj, &phranges);  
  154.                 backproj &= mask;  
  155.                 RotatedRect trackBox = CamShift(backproj, trackWindow,  
  156.                                     TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));  
  157.                 if( trackWindow.area() <= 1 )  
  158.                 {  
  159.                     int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;  
  160.                     trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,  
  161.                                        trackWindow.x + r, trackWindow.y + r) &  
  162.                                   Rect(0, 0, cols, rows);  
  163.                 }  
  164.   
  165.                 if( backprojMode )  
  166.                     cvtColor( backproj, image, COLOR_GRAY2BGR );  
  167.                 ellipse( image, trackBox, Scalar(0,0,255), 3, CV_AA );  
  168.                 //rectangle( image, trackBox, Scalar(0,0,255), 3, CV_AA );  
  169.             }  
  170.         }  
  171.         else if( trackObject < 0 )  
  172.             paused = false;  
  173.   
  174.         if( selectObject && selection.width > 0 && selection.height > 0 )  
  175.         {  
  176.             Mat roi(image, selection);  
  177.             bitwise_not(roi, roi);  
  178.         }  
  179.   
  180.         imshow( "CamShift Demo", image );  
  181.         imshow( "Histogram", histimg );  
  182.   
  183.         char c = (char)waitKey(10);  
  184.         if( c == 27 )  
  185.             break;  
  186.         switch(c)  
  187.         {  
  188.         case 'b':  
  189.             backprojMode = !backprojMode;  
  190.             break;  
  191.         case 'c':  
  192.             trackObject = 0;  
  193.             histimg = Scalar::all(0);  
  194.             break;  
  195.         case 'h':  
  196.             showHist = !showHist;  
  197.             if( !showHist )  
  198.                 destroyWindow( "Histogram" );  
  199.             else  
  200.                 namedWindow( "Histogram", 1 );  
  201.             break;  
  202.         case 'p':  
  203.             paused = !paused;  
  204.             break;  
  205.         default:  
  206.             ;  
  207.         }  
  208.     }  
  209.   
  210.     return 0;  
  211. }  
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <iostream>
#include <ctype.h>

using namespace cv;
using namespace std;

Mat image;

bool backprojMode = false;
bool selectObject = false;
int 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;
        break;
    case CV_EVENT_LBUTTONUP:
        selectObject = false;
        if( selection.width > 0 && selection.height > 0 )
            trackObject = -1;
        break;
    }
}

static void help()
{
    cout << "\nThis is a demo that shows mean-shift based tracking\n"
            "You select a color objects such as your face and it tracks it.\n"
            "This reads from video camera (0 by default, or the camera number the user enters\n"
            "Usage: \n"
            "   ./camshiftdemo [camera number]\n";

    cout << "\n\nHot keys: \n"
            "\tESC - quit the program\n"
            "\tc - stop the tracking\n"
            "\tb - switch to/from backprojection view\n"
            "\th - show/hide object histogram\n"
            "\tp - pause video\n"
            "To initialize tracking, select the object with mouse\n";
}

const char* keys =
{
    "{1|  | 0 | camera number}"
};

int main( int argc, const char** argv )
{
    help();

    VideoCapture cap;
    Rect trackWindow;
    int hsize = 16;
    float hranges[] = {0,180};
    const float* phranges = hranges;
    CommandLineParser parser(argc, argv, keys);
    int camNum = parser.get<int>("1");

    cap.open(camNum);

    if( !cap.isOpened() )
    {
        help();
        cout << "***Could not initialize capturing...***\n";
        cout << "Current parameter's value: \n";
        parser.printParams();
        return -1;
    }

    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 );

    Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
    bool paused = false;

    for(;;)
    {
        if( !paused )
        {
            cap >> frame;
            if( frame.empty() )
                break;
        }

        frame.copyTo(image);

        if( !paused )
        {
            cvtColor(image, hsv, COLOR_BGR2HSV);

            if( trackObject )
            {
                int _vmin = vmin, _vmax = vmax;

                inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
                        Scalar(180, 256, MAX(_vmin, _vmax)), mask);  //mask初始化
                int ch[] = {0, 0};
                hue.create(hsv.size(), hsv.depth());
                mixChannels(&hsv, 1, &hue, 1, ch, 1);  //提取h通道

                if( trackObject < 0 )
                {
                    Mat roi(hue, selection), maskroi(mask, selection);
                    calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);   //计算目标直方图
                    normalize(hist, hist, 0, 255, CV_MINMAX);

                    trackWindow = selection;
                    trackObject = 1;

                    histimg = Scalar::all(0);
                    int binW = histimg.cols / hsize;
                    Mat buf(1, hsize, CV_8UC3);
                    for( int i = 0; i < hsize; i++ )
                        buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./hsize), 255, 255);
                    cvtColor(buf, buf, CV_HSV2BGR);

                    for( int i = 0; i < hsize; i++ )
                    {
                        int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);
                        rectangle( histimg, Point(i*binW,histimg.rows),
                                   Point((i+1)*binW,histimg.rows - val),
                                   Scalar(buf.at<Vec3b>(i)), -1, 8 );
                    }
                }

                calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
                backproj &= mask;
                RotatedRect trackBox = CamShift(backproj, trackWindow,
                                    TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));
                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);
                }

                if( backprojMode )
                    cvtColor( backproj, image, COLOR_GRAY2BGR );
                ellipse( image, trackBox, Scalar(0,0,255), 3, CV_AA );
				//rectangle( image, trackBox, Scalar(0,0,255), 3, CV_AA );
            }
        }
        else if( trackObject < 0 )
            paused = false;

        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);
        if( c == 27 )
            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 0;
}
实验效果和之前的MeanShift差不多,图就懒得帖了。


用的时候感觉有时候甚至不如MeanShift。比如用meanshift跟踪时,手消失在人脸的位置后,meanshift会跟踪人脸,当人手再次从人脸位置出现时,会再跟踪手;而camshift被人脸干扰后,不会再跟踪手。原因未明。。。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值