Camshift算法(1)

      Camshift算法是Continuously Adaptive Mean Shift algorithm的简称。它是一个基于MeanSift的改进算法。它首次由Gary R.Bradski等人提出和应用在人脸的跟踪上,并取得了不错的效果。由于它是利用颜色的概率信息进行的跟踪,使得它的运行效率比较高。Camshift算法的过程由下面步骤组成:

(1)确定初始目标及其区域;

(2)计算出目标的色度(Hue)分量的直方图;

(3)利用直方图计算输入图像的反向投影图(后面做进一步的解释);

(4)利用MeanShift算法在反向投影图中迭代收索,直到其收敛或达到最大迭代次数。并保存零次矩;

(5)从第(4)步中获得收索窗口的中心位置和计算出新的窗口大小,以此为参数,进入到下一幀的目标跟踪。(即跳转到第(2)步);

几点说明:

1. 在输入图像进行反向投影图之前在HSV空间内做了一个阀值处理,用以滤掉一些噪声。

2. 反向投影图则是概率分布图,在反向投影图中某一像素点的值指的是这个点符合目标的概率分布的概率是多少,或者直接说其为目标图像像素点的像素点是多少。计算方法为:根据像素点的像素值查目标的直方图,其对应像素值的概率是多少就做为该点在反向投影图中的值。

3. Camshit算法到底是怎样自适应调整窗口的大小的。扩大:Canshift算法在计算窗口大小前,在MeanShift算出的窗口的四个方向上增大了TOLERANCE,即高和宽都增大了2TOLERANCE(此值自己调整设置),这才有可能使得窗口能够变大。缩小:在扩大的窗口内重新计算0阶矩,1阶矩和2阶矩,利用矩的值重新计算高和宽。因此Camshif算法相当于在MeanShift的结果上,再做了一个调整,从而使得跟踪的窗口大小能够随目标的大小变化。

优点:算法的效率比较高,如果能利用多少特征做出来的统计直方图,我估计实验效果会更好。

缺点:(1)只利用颜色统计做的跟踪,在背景有相似颜色时,会出现跟踪错误的情况。(2)不能做多目标跟踪。(3)由于它只在初始位置(而不是从每个像素点)开始迭代,所以有可能在初始位置错了后,收敛的位置还是原位置(即跟丢了后,可能会找不回来)。

问题:论文中有关于窗口大小调整,是根据直方图来迭代求解,不知是怎么回事?在代码中没看到实现。在此向大家请教!

下面是Camshift算法Demo的代码:

 

代码
  1  #ifdef _CH_
  2  #pragma  package <opencv>
  3  #endif
  4 
  5  #define  CV_NO_BACKWARD_COMPATIBILITY
  6 
  7  #ifndef _EiC
  8  #include  " cv.h "
  9  #include  " highgui.h "
 10  #include  < stdio.h >
 11  #include  < ctype.h >
 12  #endif
 13 
 14  IplImage  * image  =   0 * hsv  =   0 * hue  =   0 * mask  =   0 * backproject  =   0 * histimg  =   0 ;
 15  CvHistogram  * hist  =   0 ;
 16 
 17  int  backproject_mode  =   0 ;
 18  int  select_object  =   0 ;
 19  int  track_object  =   0 ;
 20  int  show_hist  =   1 ;
 21  CvPoint origin;
 22  CvRect selection;
 23  CvRect track_window;
 24  CvBox2D track_box;
 25  CvConnectedComp track_comp;
 26  int  hdims  =   16 ;
 27  float  hranges_arr[]  =  { 0 , 180 };
 28  float *  hranges  =  hranges_arr;
 29  int  vmin  =   10 , vmax  =   256 , smin  =   30 ;
 30 
 31  void  on_mouse(  int   event int  x,  int  y,  int  flags,  void *  param )
 32  {
 33       if ! image )
 34           return ;
 35 
 36       if ( image -> origin )
 37          y  =  image -> height  -  y;
 38 
 39       if ( select_object ) // 表明还正在框选目标
 40      {
 41          selection.x  =  MIN(x,origin.x);
 42          selection.y  =  MIN(y,origin.y);
 43          selection.width  =  selection.x  +  CV_IABS(x  -  origin.x);
 44          selection.height  =  selection.y  +  CV_IABS(y  -  origin.y);
 45 
 46           // 保证数据的有效性
 47          selection.x  =  MAX( selection.x,  0  );
 48          selection.y  =  MAX( selection.y,  0  );
 49          selection.width  =  MIN( selection.width, image -> width );
 50          selection.height  =  MIN( selection.height, image -> height );
 51          selection.width  -=  selection.x;
 52          selection.height  -=  selection.y;
 53      }
 54 
 55       switch event  )
 56      {
 57       case  CV_EVENT_LBUTTONDOWN: // 框选目标
 58          origin  =  cvPoint(x,y);
 59          selection  =  cvRect(x,y, 0 , 0 );
 60          select_object  =   1 ;
 61           break ;
 62       case  CV_EVENT_LBUTTONUP: // 框选结束
 63          select_object  =   0 ;
 64           if ( selection.width  >   0   &&  selection.height  >   0  )
 65              track_object  =   - 1 ;
 66           break ;
 67      }
 68  }
 69 
 70 
 71  CvScalar hsv2rgb(  float  hue )
 72  {
 73       int  rgb[ 3 ], p, sector;
 74       static   const   int  sector_data[][ 3 ] =
 75          {{ 0 , 2 , 1 }, { 1 , 2 , 0 }, { 1 , 0 , 2 }, { 2 , 0 , 1 }, { 2 , 1 , 0 }, { 0 , 1 , 2 }};
 76      hue  *=   0.033333333333333333333333333333333f ;
 77      sector  =  cvFloor(hue);
 78      p  =  cvRound( 255 * (hue  -  sector));
 79      p  ^=  sector  &   1   ?   255  :  0 ;
 80 
 81      rgb[sector_data[sector][ 0 ]]  =   255 ;
 82      rgb[sector_data[sector][ 1 ]]  =   0 ;
 83      rgb[sector_data[sector][ 2 ]]  =  p;
 84 
 85       return  cvScalar(rgb[ 2 ], rgb[ 1 ], rgb[ 0 ], 0 );
 86  }
 87 
 88  int  main(  int  argc,  char **  argv )
 89  {
 90      CvCapture *  capture  =   0 ;
 91 
 92       if ( argc  ==   1   ||  (argc  ==   2   &&  strlen(argv[ 1 ])  ==   1   &&  isdigit(argv[ 1 ][ 0 ])))
 93          capture  =  cvCaptureFromCAM( argc  ==   2   ?  argv[ 1 ][ 0 -   ' 0 '  :  0  );
 94       else   if ( argc  ==   2  )
 95          capture  =  cvCaptureFromAVI( argv[ 1 ] );
 96 
 97       if ! capture )
 98      {
 99          fprintf(stderr, " Could not initialize capturing...\n " );
100           return   - 1 ;
101      }
102 
103      printf(  " Hot keys: \n "
104           " \tESC - quit the program\n "
105           " \tc - stop the tracking\n "
106           " \tb - switch to/from backprojection view\n "
107           " \th - show/hide object histogram\n "
108           " To initialize tracking, select the object with mouse\n "  );
109 
110      cvNamedWindow(  " Histogram " 1  );
111      cvNamedWindow(  " CamShiftDemo " 1  );
112      cvSetMouseCallback(  " CamShiftDemo " , on_mouse,  0  );
113      cvCreateTrackbar(  " Vmin " " CamShiftDemo " & vmin,  256 0  );
114      cvCreateTrackbar(  " Vmax " " CamShiftDemo " & vmax,  256 0  );
115      cvCreateTrackbar(  " Smin " " CamShiftDemo " & smin,  256 0  );
116 
117       for (;;)
118      {
119          IplImage *  frame  =   0 ;
120           int  i, bin_w, c;
121 
122          frame  =  cvQueryFrame( capture );
123           if ! frame )
124               break ;
125 
126           if ! image )
127          {
128               /*  allocate all the buffers  */
129              image  =  cvCreateImage( cvGetSize(frame),  8 3  );
130              image -> origin  =  frame -> origin;
131              hsv  =  cvCreateImage( cvGetSize(frame),  8 3  );
132              hue  =  cvCreateImage( cvGetSize(frame),  8 1  );
133              mask  =  cvCreateImage( cvGetSize(frame),  8 1  );
134              backproject  =  cvCreateImage( cvGetSize(frame),  8 1  );
135              hist  =  cvCreateHist( 1 & hdims, CV_HIST_ARRAY,  & hranges,  1  );
136              histimg  =  cvCreateImage( cvSize( 320 , 200 ),  8 3  );
137              cvZero( histimg );
138          }
139 
140          cvCopy( frame, image,  0  );
141          cvCvtColor( image, hsv, CV_BGR2HSV );
142 
143           if ( track_object )
144          {
145               int  _vmin  =  vmin, _vmax  =  vmax;
146 
147              cvInRangeS( hsv, cvScalar( 0 ,smin,MIN(_vmin,_vmax), 0 ),
148                          cvScalar( 180 , 256 ,MAX(_vmin,_vmax), 0 ), mask );  // 去除噪声,在此数据内的值,确定mask为1
149              cvSplit( hsv, hue,  0 0 0  );  // 获得色调分量,并以此来做反向投影图
150 
151               if ( track_object  <   0  )
152              {
153                   float  max_val  =   0 .f;
154                  cvSetImageROI( hue, selection );
155                  cvSetImageROI( mask, selection );
156                  cvCalcHist(  & hue, hist,  0 , mask ); // 计算选中部分直方图
157                  cvGetMinMaxHistValue( hist,  0 & max_val,  0 0  );
158                  cvConvertScale( hist -> bins, hist -> bins, max_val  ?   255 /  max_val :  0 .,  0  );
159                  cvResetImageROI( hue );
160                  cvResetImageROI( mask );
161                  track_window  =  selection;
162                  track_object  =   1 ;
163 
164                  cvZero( histimg );
165                  bin_w  =  histimg -> width  /  hdims;
166                   for ( i  =   0 ; i  <  hdims; i ++  )
167                  {
168                       int  val  =  cvRound( cvGetReal1D(hist -> bins,i) * histimg -> height / 255  ); // 获取直方图的中每一项的高
169                      CvScalar color  =  hsv2rgb(i * 180 .f / hdims); // 直方图每一项的颜色是根据项数变化的
170                      cvRectangle( histimg, cvPoint(i * bin_w,histimg -> height),   // 画直方图
171                                   cvPoint((i + 1 ) * bin_w,histimg -> height  -  val),
172                                   color,  - 1 8 0  );
173                  }
174              }
175 
176              cvCalcBackProject(  & hue, backproject, hist );  // 计算反向投影图backproject
177              cvAnd( backproject, mask, backproject,  0  );         // 去除上下阀值外的点后的投影图
178              cvCamShift( backproject, track_window,         // 利用camshift搜索0-255的灰度图像
179                          cvTermCriteria( CV_TERMCRIT_EPS  |  CV_TERMCRIT_ITER,  10 1  ),
180                           & track_comp,  & track_box );
181              track_window  =  track_comp.rect;     // 获得新的跟踪窗口
182 
183               if ( backproject_mode )
184                  cvCvtColor( backproject, image, CV_GRAY2BGR );
185          
186               if ! image -> origin )  // 如果为假,需要改变椭圆的角度
187                  track_box.angle  =   - track_box.angle; 
188              cvEllipseBox( image, track_box, CV_RGB( 255 , 0 , 0 ),  3 , CV_AA,  0  ); // 画跟踪椭圆
189          }
190 
191           if ( select_object  &&  selection.width  >   0   &&  selection.height  >   0  ) // 在框住的时候反向显示
192          {
193              cvSetImageROI( image, selection );
194              cvXorS( image, cvScalarAll( 255 ), image,  0  );
195              cvResetImageROI( image );
196          }
197 
198          cvShowImage(  " CamShiftDemo " , image );
199          cvShowImage(  " Histogram " , histimg );
200 
201          c  =  cvWaitKey( 10 );
202           if ( ( char ) c  ==   27  )
203               break ;
204           switch ( ( char ) c )
205          {
206           case   ' b ' :
207              backproject_mode  ^=   1 ;
208               break ;
209           case   ' c ' :
210              track_object  =   0 ;
211              cvZero( histimg );
212               break ;
213           case   ' h ' :
214              show_hist  ^=   1 ;
215               if ! show_hist )
216                  cvDestroyWindow(  " Histogram "  );
217               else
218                  cvNamedWindow(  " Histogram " 1  );
219               break ;
220           default :
221              ;
222          }
223      }
224 
225      cvReleaseCapture(  & capture );
226      cvDestroyWindow( " CamShiftDemo " );
227 
228       return   0 ;
229  }
230 
231  #ifdef _EiC
232  main( 1 , " camshiftdemo.c " );
233  #endif
234 

 

 

你可能感兴趣的:(算法)