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

来源:互联网 发布:算法概论注释版pdf 编辑:程序博客网 时间:2024/06/05 08:04

本文转自: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,在算法理论上并没有什么区别,甚至在编程的流程上也没什么区别,他们的区别体现在程序内部

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

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

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

[cpp] view plain copy
print?
  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. }  

将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

[cpp] view plain copy
print?
  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. }  
实验效果和之前的MeanShift差不多,图就懒得帖了。


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