Mean Shift算法(CamShift)/*****************************************************************************/ CamShift算法,即"Continuously Apative Mean-Shift"算法,是一种运动跟踪算法。它主要通过视频图像中运动物体的颜色信息来达到跟踪的目的。我把这个算法分解成三个部分,便于理解: 1) Back Projection计算 2) Mean Shift算法 3) CamShift算法 在这里主要讨论Back Projection,在随后的文章中继续讨论后面两个算法。 Back Projection 计算Back Projection的步骤是这样的: 1. 计算被跟踪目标的色彩直方图。在各种色彩空间中,只有HSI空间(或与HSI类似的色彩空间)中的H分量可以表示颜色信息。所以在具体的计算过程中,首先将其他的色彩空间的值转化到HSI空间,然后会其中的H分量做1D直方图计算。 2. 根据获得的色彩直方图将原始图像转化成色彩概率分布图像,这个过程就被称作"Back Projection"。 在OpenCV中的直方图函数中,包含Back Projection的函数,函数原型是: void cvCalcBackProject(IplImage** img, CvArr** backproject, const CvHistogram* hist); 传递给这个函数的参数有三个: 1. IplImage** img:存放原始图像,输入。 2. CvArr** backproject:存放Back Projection结果,输出。 3. CvHistogram* hist:存放直方图,输入 下面就给出计算Back Projection的OpenCV代码。 1.准备一张只包含被跟踪目标的图片,将色彩空间转化到HSI空间,获得其中的H分量: IplImage* target=cvLoadImage("target.bmp",-1); //装载图片 IplImage* target_hsv=cvCreateImage( cvGetSize(target), IPL_DEPTH_8U, 3 ); IplImage* target_hue=cvCreateImage( cvGetSize(target), IPL_DEPTH_8U, 3 ); cvCvtColor(target,target_hsv,CV_BGR2HSV); //转化到HSV空间 cvSplit( target_hsv, target_hue, NULL, NULL, NULL ); //获得H分量 2.计算H分量的直方图,即1D直方图: IplImage* h_plane=cvCreateImage( cvGetSize(target_hsv),IPL_DEPTH_8U,1 ); int hist_size[]={255}; //将H分量的值量化到[0,255] float* ranges[]={ {0,360} }; //H分量的取值范围是[0,360) CvHistogram* hist=cvCreateHist(1, hist_size, ranges, 1); cvCalcHist(&target_hue, hist, 0, NULL); 在这里需要考虑H分量的取值范围的问题,H分量的取值范围是[0,360),这个取值范围的值不能用一个byte来表示,为了能用一个byte表示,需要将H值做适当的量化处理,在这里我们将H分量的范围量化到[0,255]. 4.计算Back Projection: IplImage* rawImage; //---------------------------------------------- //get from video frame,unsigned byte,one channel //---------------------------------------------- IplImage* result=cvCreateImage(cvGetSize(rawImage),IPL_DEPTH_8U,1); cvCalcBackProject(&rawImage,result,hist); 5.结果:result即为我们需要的. 算法分析 用在cvCalcBackProject处理中的模板是目标图像色调(HUE)的直方图,而直方图可以看作是一种概率分布图。在处理前,目标图像中的每一个象素的值描述的在这一点的颜色信息,而处理后,图像中每一个象素的值就变成了这个颜色信息出现在此处的可能性的一种离散化的度量,出现的可能性大,象素的值就大,反之则小。这样就为后面的匹配和跟踪提供了线索。 这里来到了CamShift算法,OpenCV实现的第二部分,这一次重点讨论Mean Shift算法。 在讨论Mean Shift算法之前,首先讨论在2D概率分布图像中,如何计算某个区域的重心(Mass Center)的问题,重心可以通过以下公式来计算: 1.计算区域内0阶矩 for(int i=0;i<height;i++) for(int j=0;j<width;j++) M00+=I(i,j) 2.区域内1阶矩: for(int i=0;i<height;i++) for(int j=0;j<width;j++) { M10+=i*I(i,j); M01+=j*I(i,j); } 3.则Mass Center为: Xc=M10/M00; Yc=M01/M00 接下来,讨论Mean Shift算法的具体步骤,Mean Shift算法可以分为以下4步: 1.选择窗的大小和初始位置. 2.计算此时窗口内的Mass Center. 3.调整窗口的中心到Mass Center. 4.重复2和3,直到窗口中心"会聚",即每次窗口移动的距离小于一定的阈值。 在OpenCV中,提供Mean Shift算法的函数,函数的原型是: int cvMeanShift(IplImage* imgprob,CvRect windowIn, CvTermCriteria criteria,CvConnectedComp* out); 需要的参数为: 1.IplImage* imgprob:2D概率分布图像,传入; 2.CvRect windowIn:初始的窗口,传入; 3.CvTermCriteria criteria:停止迭代的标准,传入; 4.CvConnectedComp* out:查询结果,传出。 (注:构造CvTermCriteria变量需要三个参数,一个是类型,另一个是迭代的最大次数,最后一个表示特定的阈值。例如可以这样构造criteria:criteria=cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,10,0.1)。) 返回的参数: 1.int:迭代的次数。 实现代码:暂时缺
1.原理 2.实现 5. 采用 CAMSHIFT 算法快速跟踪和检测运动目标的 C/C++ 源代码,OPENCV BETA 4.0 版本在其 SAMPLE 中给出了这个例子。算法的简单描述如下(英文): This application demonstrates a fast, simple color tracking algorithm that can be used to track faces, hands . The CAMSHIFT algorithm is a modification of the Meanshift algorithm which is a robust statistical method of finding the mode (top) of a probability In this application, we use only the most simplistic approach: A 1-D Hue histogram is sampled from the object in an HSV color space version of the image. To produce the probability image to track, histogram "back projection" (we replace image pixels by their 算法的详细情况,请看论文: http://www./incoming/camshift.pdf 关于OPENCV B4.0 库的使用方法以及相关问题,请查阅下面的相关文章: http://forum./display_topic_threads.asp?ForumID=11&TopicID=3471 运行文件下载: http://www./product_tech/Demo_Download_files/camshiftdemo.exe 该运行文件在VC6.0环境下编译通过,是一个 stand-alone 运行程序,不需要OPENCV的DLL库支持。在运行之前,请先连接好USB接口的摄像头。然后可以用鼠标选定欲跟踪目标。 ===== IplImage *image = 0, *hsv = 0, *hue = 0, *mask = 0, *backproject = 0, *histimg = 0; int backproject_mode = 0; void on_mouse( int event, int x, int y, int flags ) if( image->origin ) if( select_object ) selection.x = MAX( selection.x, 0 ); } switch( event ) CvScalar hsv2rgb( float hue ) rgb[sector_data[sector][0]] = 255; #ifdef _DEBUG return cvScalar(rgb[2], rgb[1], rgb[0],0); int main( int argc, char** argv ) if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0]))) if( !capture ) printf( "Hot keys: /n" //cvNamedWindow( "Histogram", 1 ); for(;;) frame = cvQueryFrame( capture ); if( !image ) image = cvCreateImage( cvGetSize(frame), 8, 3 ); cvCopy( frame, image, 0 ); if( track_object ) cvInRangeS( hsv, cvScalar(0,smin,MIN(_vmin,_vmax),0), if( track_object < 0 ) cvZero( histimg ); // 画直方图 cvCalcBackProject( &hue, backproject, hist ); // 使用 back project 方法 // calling CAMSHIFT 算法模块 if( backproject_mode ) if( select_object && selection.width > 0 && selection.height > 0 ) cvShowImage( "CamShiftDemo", image ); c = cvWaitKey(10); cvReleaseCapture( &capture ); return 0; |
|