opencv学习笔记-入门(6)-camshift

CamShift算法
简介
CamShift算法,即"ContinuouslyApative Mean-Shift"算法,是一种运动跟踪算法。它主要通过视频图像中运动物体的颜色信息来达到跟踪的目的。我把这个算法分解成三个部分,便于理解:

Back Projection计算。

Mean Shift算法

CamShift算法

1 )Back Projection计算(反向投影)

计算Back Projection的步骤是这样的:

1. 计算被跟踪目标的色彩直方图。在各种色彩空间中,只有HSI空间(或与HSI类似的色彩空间)中的H分量可以表示颜色信息。所以在具体的计算过程中,首先将其他的色彩空间的值转化到HSI空间,然后会其中的H分量做1D直方图计算。

2. 根据获得的色彩直方图将原始图像转化成色彩概率分布图像,这个过程就被称作"BackProjection"。

即将图像的cvCalcHist(&h_plane, hist); 之后,得到所计算的直方图

并且对生成的新的图像的H分量进行提取后,用cvCalcBackProject(&h_plane_dst, dst, hist );

得到概率图像的分布,即根据区间和bins的个数,得到每一个bins的范围,即区间/bins,然后得到每一个数据所在的直方图区间的位置,直方图表示的是在区间中的数据的个数

比如

[1 2 3

4 5 6

7 8 9

4 5 8]

Bins = 3, 我们定的范围时0-10,

则每个小区间 10/3

所以bins[0] = 3, bins[1] = 5, bins[2] = 4;

这样我们对新的图像进行反向投影所得到的结果就是,如果新的数据点位于bins[2]则此处的值就为3,这样我们就得到了,opencv中的最后那句话,用肤色直方图来推导肤色图像的概率。(补充,如果直方图是归一化的,则此反向投影值与一个条件概率相关)。

2)mean-shift算法

我的理解过程,通过在通过在hue维度上,找到我们所要的颜色的范围,之后进行的是跟踪即可,跟踪过程:

opencv学习笔记-入门(6)-camshift_第1张图片

逐级寻找窗口的重心,复杂的公式用到了核函数的知识,如果大家有兴趣,可以看下,挺好的!

这里只介绍其简化形式:

opencv学习笔记-入门(6)-camshift_第2张图片

分析一幅图像的密度分布!!!!!!!!!!!!!物理含义

3) CamShift算法
1.原理

在了解了MeanShift算法以后,我们将MeanShift算法扩展到连续图像序列(一般都是指视频图像序列),这样就形成了CamShift算法。CamShift算法的全称是"Continuously Apaptive Mean-SHIFT",它的基本思想是视频图像的所有帧作MeanShift运算,并将上一帧的结果(即Search Window的中心和大小)作为下一帧MeanShift算法的Search Window的初始值,如此迭代下去,就可以实现对目标的跟踪。整个算法的具体步骤分5步:

Step 1:将整个图像设为搜寻区域。

Step 2:初始话Search Window的大小和位置。

Step 3:计算Search Window内的彩色概率分布,此区域的大小比Search Window要稍微大一点。

Step 4:运行MeanShift。获得Search Window新的位置和大小。

Step 5:在下一帧视频图像中,用Step 3获得的值初始化Search Window的位置和大小。跳转到Step 3继续运行。

2.实现

在OpenCV中,有实现CamShift算法的函数,此函数的原型是:

  cvCamShift(IplImage* imgprob, CvRect windowIn,

                CvTermCriteria criteria,

                CvConnectedComp* out, CvBox2D* box=0);

其中:

   imgprob:色彩概率分布图像。

   windowIn:Search Window的初始值。

   Criteria:用来判断搜寻是否停止的一个标准。

   out:保存运算结果,包括新的Search Window的位置和面积。

   box:包含被跟踪物体的最小矩形。


实现代码:

 IplImage* frame = 0;
        int i, bin_w, c;

        frame = cvQueryFrame( capture );
        if( !frame )
            break;

        if( !image )   //如果此时的image 还是新的图时
        {
            image = cvCreateImage( cvGetSize(frame), 8, 3 );
            image->origin = frame->origin;
            hsv = cvCreateImage( cvGetSize(frame), 8, 3 );
            hue = cvCreateImage( cvGetSize(frame), 8, 1 );
            mask = cvCreateImage( cvGetSize(frame), 8, 1 );
            backproject = cvCreateImage( cvGetSize(frame), 8, 1 );
            hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );   //float hranges_arr[] = {0,180};  float* hranges = hranges_arr;  //int hdims = 16;
			//构建直方图
            histimg = cvCreateImage( cvSize(320,200), 8, 3 );   //????why the porpuse
            cvZero( histimg );
        }

        cvCopy( frame, image, 0 );    
        cvCvtColor( image, hsv, CV_BGR2HSV );  //convert to hsv

        if( track_object )   //init the track_object is 0
        {
            int _vmin = vmin, _vmax = vmax;   // vmin = 10, vmax = 256

            cvInRangeS( hsv, cvScalar(0,smin,MIN(_vmin,_vmax),0),    //the function so cool, 在hsv颜色空间中,很酷  //更直观cvInRangeS(imgHSV, cvScalar(15, 234, 120), cvScalar(21, 234, 120), imgThreshed);
                        cvScalar(180,256,MAX(_vmin,_vmax),0), mask );   //mask one channel
			//在hsv空间中,所要找的颜色位于特定的范围内,因为它是由皮肤颜色来确定的
			//cvShowImage( "eg1", mask);
			//while(1);
            cvSplit( hsv, hue, 0, 0, 0 );
			//将hsv中的一个通道h通道的图像提出出来 // h为色相,如红色,黄色
			//cvShowImage( "eg", hue);

            if( track_object < 0 )   
            {
                float max_val = 0.f;
                cvSetImageROI( hue, selection );   //在hsv单通道的图像中找到自己所感兴趣的区域
                cvSetImageROI( mask, selection );   //在经过cvInRangeS二值显示的那个图像
                cvCalcHist( &hue, hist, 0, mask );  //mask是掩膜,是要计算直方图的区域, mask为0是表示整幅图像
                cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 );
                cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 );   //归一化的转化
                cvResetImageROI( hue ); 
                cvResetImageROI( mask );  //解除感兴趣的区域
                track_window = selection;   //CvRect selection;
                track_object = 1;

                cvZero( histimg );
                bin_w = histimg->width / hdims;  //int hdims = 16;
                for( i = 0; i < hdims; i++ )  //共有hdims个直方图的图驻
                {
					//cout <<  cvGetReal1D(hist->bins,i) << endl;
				
                    int val = cvRound( cvGetReal1D(hist->bins,i)*histimg->height/255 );
                    CvScalar color = hsv2rgb(i*180.f/hdims);   //convertto RGB to display  the hist
                    cvRectangle( histimg, cvPoint(i*bin_w,histimg->height),
                                 cvPoint((i+1)*bin_w,histimg->height - val),
                                 color, -1, 8, 0 );  //plot a rectangle(矩形)   //将统计的直方图柱画为矩形小块
                }
				//cvShowImage( "eg", histimg);
            }

            cvCalcBackProject( &hue, backproject, hist );    //方向投影,将图像投影到16个柱的统计数目的图
			//cvShowImage( "eg", backproject );
            cvAnd( backproject, mask, backproject, 0 );  //进行位与运算
			//cvShowImage( "eg", backproject ); 
            cvCamShift( backproject, track_window,    //backproject 代表可能位置的密度   //在框中寻找中心
                        cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ), // use mean-shift to track the project
                        &track_comp, &track_box );   //track_box由上一图的box包含的二阶矩计算出来的尺寸  ??为什么现在只是检测出了头
            track_window = track_comp.rect;    //现在找到的感兴趣的区域作为下一幅图的窗口

            if( backproject_mode )   
                cvCvtColor( backproject, image, CV_GRAY2BGR );  //将方向投影图转化回RGB
            if( !image->origin )    //
                track_box.angle = -track_box.angle;  //跟踪盒子和那个视频的矩的一个反向偏差
            cvEllipseBox( image, track_box, CV_RGB(255,0,0), 3, CV_AA, 0 ); //draw 椭圆 // cvCamShift and cvFitEllipse do
			lose++;
			if(lose>=50)
				track_object=0;   //50个一个循环

			x_str = track_window.x + track_window.width/2;
			y_str = track_window.y + track_window.height/2;   
        }




你可能感兴趣的:(算法,image,object,search,float,DST)