• Camshift算法(2)


         这里主要介绍下MeanShift算法的迭代过程,毕竟Camshift算法是以它为核心的。MeanShift算法是一种寻找局部极值的方法。做为一种直观上的理解是它一步一步爬向最高点即爬山算法.而怎么个爬法,用计算出的重心做为下一步窗口的中心,直到窗口的位置不再变化。在理解MeanShift算法的时候,可以先不加入核函数(是计算距离对统计分布的影响)和权重函数(如人为主观的影响)。  

    在Camshift算法中MeanShift是通过1阶矩除以0阶矩来计算重心的。其算法的代码如下:

    代码
    CV_IMPL int
    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;//当前矩形窗口初始化为输入窗口

        CV_FUNCNAME( 
    "cvMeanShift" );

        
    if( comp )
            comp
    ->rect = windowIn;//初始化联通区域

        moments.m00 
    = moments.m10 = moments.m01 = 0//初始化0、1阶矩

        __BEGIN__;

        CV_CALL( 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" );

        
    if( windowIn.x < 0 || windowIn.x + windowIn.width > mat->cols ||      //x,y是指角点坐标而不是中心坐标
            windowIn.y < 0 || windowIn.y + windowIn.height > mat->rows )
            CV_ERROR( CV_StsBadArg, 
    "Initial window is not inside the image ROI" );

        CV_CALL( criteria 
    = cvCheckTermCriteria( criteria, 1., 100 ));//迭代的结束条件,

        eps 
    = cvRound( criteria.epsilon * criteria.epsilon );

        
    for( i = 0; i < criteria.max_iter; i++ )
        {
            
    int dx, dy, nx, ny;
            
    double inv_m00;

            CV_CALL( cvGetSubRect( mat, 
    &cur_win, cur_rect )); //cur_win指向窗口内的数据
            CV_CALL( 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;
            dx 
    = cvRound( moments.m10 * inv_m00 - windowIn.width*0.5 );//中心点的坐标-宽的一半
            dy = cvRound( moments.m01 * inv_m00 - windowIn.height*0.5 );//中心点的坐标-高的一半

            nx 
    = cur_rect.x + dx;//新的x坐标
            ny = cur_rect.y + dy;//新的y坐标

            
    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;
        }

        __END__;

        
    if( comp )//返回矩形和0阶矩
        {
            comp
    ->rect = cur_rect;
            comp
    ->area = (float)moments.m00;
        }

        
    return i;  //返回迭代次数
    }

    Camshift算法代码:

    代码
    CV_IMPL int
    cvCamShift( 
    const void* imgProb, CvRect windowIn,
                CvTermCriteria criteria,
                CvConnectedComp
    * _comp,
                CvBox2D
    * box )
    {
        
    const int TOLERANCE = 10;
        CvMoments moments;
        
    double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00;
        
    double a, b, c, xc, yc;
        
    double rotate_a, rotate_c;
        
    double theta = 0, square;
        
    double cs, sn;
        
    double length = 0, width = 0;
        
    int itersUsed = 0;
        CvConnectedComp comp;
        CvMat  cur_win, stub, 
    *mat = (CvMat*)imgProb;

        CV_FUNCNAME( 
    "cvCamShift" );

        comp.rect 
    = windowIn;//初始化comp

        __BEGIN__;

        CV_CALL( mat 
    = cvGetMat( mat, &stub ));

        CV_CALL( itersUsed 
    = cvMeanShift( mat, windowIn, criteria, &comp ));//调用meanshift计算质心
        windowIn = comp.rect;//获得新的窗口的位置

        
    //为了容错,窗口的四边都增大了TOLERANCE
        windowIn.x -= TOLERANCE;
        
    if( windowIn.x < 0 )
            windowIn.x 
    = 0;

        windowIn.y 
    -= TOLERANCE;
        
    if( windowIn.y < 0 )
            windowIn.y 
    = 0;

        windowIn.width 
    += 2 * TOLERANCE;
        
    if( windowIn.x + windowIn.width > mat->width )
            windowIn.width 
    = mat->width - windowIn.x;

        windowIn.height 
    += 2 * TOLERANCE;
        
    if( windowIn.y + windowIn.height > mat->height )
            windowIn.height 
    = mat->height - windowIn.y;

        CV_CALL( cvGetSubRect( mat, 
    &cur_win, windowIn ));//获得指向子窗口的数据指针

        
    /* Calculating moments in new center mass */
        CV_CALL( cvMoments( 
    &cur_win, &moments ));//重新计算窗口内的各种矩

        m00 
    = moments.m00;
        m10 
    = moments.m10;
        m01 
    = moments.m01;
        mu11 
    = moments.mu11;
        mu20 
    = moments.mu20;
        mu02 
    = moments.mu02;

        
    if( fabs(m00) < DBL_EPSILON )
            EXIT;

        inv_m00 
    = 1/ m00;
        xc 
    = cvRound( m10 * inv_m00 + windowIn.x );//新的中心坐标
        yc = cvRound( m01 * inv_m00 + windowIn.y );
        a 
    = mu20 * inv_m00;
        b 
    = mu11 * inv_m00;
        c 
    = mu02 * inv_m00;

        
    /* Calculating width & height */
        square 
    = sqrt( 4 * b * b + (a - c) * (a - c) );

        
    /* Calculating orientation */
        theta 
    = atan2( 2 * b, a - c + square );

        
    /* Calculating width & length of figure */
        cs 
    = cos( theta );
        sn 
    = sin( theta );

        rotate_a 
    = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02;
        rotate_c 
    = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02;
        length 
    = sqrt( rotate_a * inv_m00 ) * 4;//长与宽的计算
        width = sqrt( rotate_c * inv_m00 ) * 4;

        
    /* In case, when tetta is 0 or 1.57... the Length & Width may be exchanged */
        
    if( length < width )
        {
            
    double t;
            
            CV_SWAP( length, width, t );
            CV_SWAP( cs, sn, t );
            theta 
    = CV_PI*0.5 - theta;
        }

        
    /* Saving results */
        
    //由于有宽和高的重新计算,使得能自动调整窗口大小
        if( _comp || box )
        {
            
    int t0, t1;
            
    int _xc = cvRound( xc );//取整
            int _yc = cvRound( yc );

            t0 
    = cvRound( fabs( length * cs ));
            t1 
    = cvRound( fabs( width * sn ));

            t0 
    = MAX( t0, t1 ) + 2;//宽的重新计算
            comp.rect.width = MIN( t0, (mat->width - _xc) * 2 );//保证宽不超出范围

            t0 
    = cvRound( fabs( length * sn ));
            t1 
    = cvRound( fabs( width * cs ));

            t0 
    = MAX( t0, t1 ) + 2;//高的重新计算
            comp.rect.height = MIN( t0, (mat->height - _yc) * 2 );//保证高不超出范围
            comp.rect.x = MAX( 0, _xc - comp.rect.width / 2 );
            comp.rect.y 
    = MAX( 0, _yc - comp.rect.height / 2 );

            comp.rect.width 
    = MIN( mat->width - comp.rect.x, comp.rect.width );
            comp.rect.height 
    = MIN( mat->height - comp.rect.y, comp.rect.height );
            comp.area 
    = (float) m00;
        }

        __END__;

        
    if( _comp )
            
    *_comp = comp;
        
        
    if( box )
        {
            box
    ->size.height = (float)length;
            box
    ->size.width = (float)width;
            box
    ->angle = (float)(theta*180./CV_PI);
            box
    ->center = cvPoint2D32f( comp.rect.x + comp.rect.width*0.5f,
                                        comp.rect.y 
    + comp.rect.height*0.5f);
        }

        
    return itersUsed;
    }

      

  • 相关阅读:
    SQL集合函数中case when then 使用技巧
    appium -- 页面出现弹窗,关闭后,无法识别页面元素
    SQLite3中dos命令下退出"...>"状态的方法
    android SharedPreferences 浅析
    BigDecimal简单说
    appium-手势密码实现-automationName 是Appium的情况
    Android color颜色-色号总结
    adb启动和关闭
    DesiredCapabilities的作用
    Android 使用intent传递返回值:startActivityForResult()与onActivityResult()与setResult()参数分析,activity带参数的返回
  • 原文地址:https://www.cnblogs.com/seacode/p/1827380.html
Copyright © 2020-2023  润新知