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