本文共 4199 字,大约阅读时间需要 13 分钟。
在做目标跟踪的时候,mean sift算法是堪称经典,在opencv2.4.6中,mean sift的函数原型为如下:
/ /! updates the object tracking window using meanshift algorithm
CV_EXPORTS_W int meanShift( InputArray probImage, CV_OUT CV_IN_OUT Rect& window, TermCriteria criteria );
其中,probImage:在目标跟踪时,probImage表示我们待搜索目标图像的反向投影图(概率分布图),其实就是一幅图像
window:是初始矩形框大小,这里它既是输入,又是输出
criteria:迭代搜索的准则(如需要迭代的次数和需要控制最小值)
在opencv2.4.6中,我们可以看到meanShift的源代码如下:
int cv::meanShift( InputArray _probImage, Rect& window, TermCriteria criteria ){ CvConnectedComp comp; Mat probImage = _probImage.getMat(); //getMat()函数将传入的参数转换为Mat的结构,方便你函数内的操作 CvMat c_probImage = probImage; int iters = cvMeanShift(&c_probImage, window, (CvTermCriteria)criteria, &comp ); window = comp.rect; //更新窗口 return iters; //返回迭代次数}我们可以看到,实际上meanShift本身也调用了cvMeanShift函数(这个函数在opencv2.0之前使用),也就是说meanShift函数,只是在cvMeanShift的基础上进行了封装而已,并且我们也可以看到,meanShift更新window窗口时通过comp来实现的,为此,我们需要进一步分析cvMeanShift函数的源代码。
cvMeanShift函数的原型为:
int cvMeanShift( const void* imgProb, CvRect windowIn,CvTermCriteria criteria, CvConnectedComp* comp )
其中, 参数1:imgProb:输入的一张图像(跟踪的时候,这是一张反向投影图)
参数2:windowln:开始迭代的方框
参数3:criteria: 迭代的条件
参数4:comp: 作为输出,迭代完成的位置
cvMeanShift函数源代码如下:
cvMeanShift( const void* imgProb, CvRect windowIn, CvTermCriteria criteria, CvConnectedComp* comp ){ CvMoments moments; //计算多边形和光栅形状的最高达三阶的所有矩: int i = 0, eps; CvMat stub, *mat = (CvMat*)imgProb; //概率分布图 CvMat cur_win; CvRect cur_rect = windowIn; //初始的搜索窗口 if( comp ) comp->rect = windowIn; moments.m00 = moments.m10 = moments.m01 = 0; //0矩,1矩 //得到一个矩阵 mat = cvGetMat( mat, &stub ); if( CV_MAT_CN( mat->type ) > 1 ) CV_Error( CV_BadNumChannels, cvUnsupportedFormat ); if( windowIn.height <= 0 || windowIn.width <= 0 ) CV_Error( CV_StsBadArg, "Input window has non-positive sizes" ); //重新设置搜索框大小:让概率图的窗口和初始的矩形框做交集 //windowIn被更新了 windowIn = cv::Rect(windowIn) & cv::Rect(0, 0, mat->cols, mat->rows); //确定窗口搜索停止的准则(停止迭代的条件) criteria = cvCheckTermCriteria( criteria, 1., 100 ); //收敛值 eps = cvRound( criteria.epsilon * criteria.epsilon ); //开始迭代,默认的迭代为:100次 for( i = 0; i < criteria.max_iter; i++ ) { int dx, dy, nx, ny; double inv_m00; //0阶矩 //确定当前搜索窗口的大小(交集运算) cur_rect = cv::Rect(cur_rect) & cv::Rect(0, 0, mat->cols, mat->rows); //cur_rect为0 if( cv::Rect(cur_rect) == cv::Rect() ) { cur_rect.x = mat->cols/2; cur_rect.y = mat->rows/2; } //选择最大的边框 cur_rect.width = MAX(cur_rect.width, 1); cur_rect.height = MAX(cur_rect.height, 1); //从mat提取cur_rect大小的区域(cur_win) //cvGetSubRect作用是从一个图像中提取出来一部分 cvGetSubRect( mat, &cur_win, cur_rect ); //对提取的cur_win,计算它的矩(m00,m01,m10) //函数 cvMoments 计算最高达三阶的空间和中心矩,并且将结果存在结构 moments 中。 //矩用来计算形状的重心(亮度),面积,主轴和其它的形状特征,如 7 Hu 不变量等. cvMoments( &cur_win, &moments ); //计算多边形(图像)和光栅形状的最高达三阶的所有矩(中心距): /* Calculating center of mass */ if( fabs(moments.m00) < DBL_EPSILON ) break; inv_m00 = moments.inv_sqrt_m00*moments.inv_sqrt_m00; //m00的倒数:1/m00 dx = cvRound( moments.m10 * inv_m00 - windowIn.width*0.5 ); //moments.m10 * inv_m00:当前窗口图像cur_win的重心的x:x=m10/m00 dy = cvRound( moments.m01 * inv_m00 - windowIn.height*0.5 );//moments.m01 * inv_m00: 当前窗口图像cur_win的重心的y:y=m01/m00 //将当前矩阵进行平移dx,dy nx = cur_rect.x + dx; ny = cur_rect.y + dy; if( nx < 0 ) nx = 0; else if( nx + cur_rect.width > mat->cols ) nx = mat->cols - cur_rect.width; if( ny < 0 ) ny = 0; else if( ny + cur_rect.height > mat->rows ) ny = mat->rows - cur_rect.height; dx = nx - cur_rect.x; dy = ny - cur_rect.y; //更新当前矩阵的左上角坐标 cur_rect.x = nx; cur_rect.y = ny; /* Check for coverage centers mass & window */ if( dx*dx + dy*dy < eps ) break; } //迭代完成 if( comp ) //comp(位置)作为返回值 { comp->rect = cur_rect; //最终有用的返回值 comp->area = (float)moments.m00; } return i;}从上面的源代码,可以看出meansift算法在这里的作用就是在不断在寻找 矩形框框定的图像的重心,并不断迭代,即迭代的过程是向重心(x=m10/m00,y=m01/m00)转移.
在代码实现的过程中使用了关于的一些东西(空间距,中心距)可以参考[2],关于meansift的理论部分,可以参考[1]。
参考文献:
转载地址:http://jbpti.baihongyu.com/