SURF算法中的ransac算法

1.RANSAC 原理

  就是首先随机抽取观测数据子集,我们假设视为这子集就是“内点”(局内点或者局内数据)。然后用这子集进行相关的拟合来计算模型参数(或者估计函数)。找到这模型(或者函数)以后,利用观测点(数据)进行是否正确,如果求出来的模型能够满足足够多的数据,我们视为很正确的数据。最后我们采纳。但是,如果不适合,也就是说求出来的模型(或者函数,也可以是模型参数)满足的数据点很少,我们就放弃,从新随机抽取观测数据子集,再进行上述的操作。这样的运算进行N次,然后进行比较,如果第M(M<N)次运算求出来的模型满足的观测数据足够多的话,我们视为最终正确的模型(或者称之为正确地拟合函数)。可见,所谓的随机抽样一致性算法很适合对包含很多局外点(噪声,干扰等)的观测数据的拟合以及模型参数估计。当然最小二乘法也是不错的算法,但是,最小二乘法虽然功能强大,不过,它所适合的范围没有RANSAC那么广。

2.SURF算法与ransac算法相结合

  (1)从SURF 算法预匹配数据集中随机取出一些匹配点对,计算出变换矩阵H,记为模型M。理论上只需要4对点。 
  (2)计算数据集中所有数据与模型M的投影误差,若误差小于阈值,加入内点集 I ; 
  (3)如果当前内点集 I 元素个数大于最优内点集 I_best , 则更新 I_best = I,同时更新迭代次数k ; 
  (4) 如果迭代次数大于k,则退出 ; 否则迭代次数加1,并重复上述步骤; 
  注:在OpenCV中迭代次数k在不大于最大迭代次数的情况下,是在不断更新而不是固定的; 
  这里写图片描述 
  其中,p为置信度,一般取0.995;w为”内点”的比例 ; m为计算模型所需要的最少样本数=4;

  在opencv里,findFundamentalMat函数来得到变换模型。 
  

3.RANSAC源码解析 

(1)cv::findFundamentalMat()返回一个基础矩阵。 
_points1:图像1的坐标点 
_points2:图像2的坐标点

cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2,
                               int method, double param1, double param2,
                               OutputArray _mask )
{
    Mat points1 = _points1.getMat(), points2 = _points2.getMat();
    int npoints = points1.checkVector(2);
    CV_Assert( npoints >= 0 && points2.checkVector(2) == npoints &&
              points1.type() == points2.type());

    Mat F(method == CV_FM_7POINT ? 9 : 3, 3, CV_64F);
    CvMat _pt1 = points1, _pt2 = points2;
    CvMat matF = F, c_mask, *p_mask = 0;
    if( _mask.needed() )
    {
        _mask.create(npoints, 1, CV_8U, -1, true);
        p_mask = &(c_mask = _mask.getMat());
    }
    int n = cvFindFundamentalMat( &_pt1, &_pt2, &matF, method, param1, param2, p_mask );//见(2)
    if( n <= 0 )
        F = Scalar(0);
    if( n == 1 )
        F = F.rowRange(0, 3);
    return F;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

(2)cvFindFundamentalMat()返回一个整数值

CV_IMPL int cvFindFundamentalMat( const CvMat* points1, const CvMat* points2,
                                  CvMat* fmatrix, int method,
                                  double param1, double param2, CvMat* mask )
{
    int result = 0;
    Ptr<CvMat> m1, m2, tempMask;

    double F[3*9];
    CvMat _F3x3 = cvMat( 3, 3, CV_64FC1, F ), _F9x3 = cvMat( 9, 3, CV_64FC1, F );
    int count;

    CV_Assert( CV_IS_MAT(points1) && CV_IS_MAT(points2) && CV_ARE_SIZES_EQ(points1, points2) );
    CV_Assert( CV_IS_MAT(fmatrix) && fmatrix->cols == 3 &&
        (fmatrix->rows == 3 || (fmatrix->rows == 9 && method == CV_FM_7POINT)) );

    count = MAX(points1->cols, points1->rows);
    if( count < 7 )
        return 0;

    m1 = cvCreateMat( 1, count, CV_64FC2 );
    cvConvertPointsHomogeneous( points1, m1 );

    m2 = cvCreateMat( 1, count, CV_64FC2 );
    cvConvertPointsHomogeneous( points2, m2 );

    if( mask )
    {
        CV_Assert( CV_IS_MASK_ARR(mask) && CV_IS_MAT_CONT(mask->type) &&
            (mask->rows == 1 || mask->cols == 1) &&
            mask->rows*mask->cols == count );
    }
    if( mask || count >= 8 )
        tempMask = cvCreateMat( 1, count, CV_8U );
    if( !tempMask.empty() )
        cvSet( tempMask, cvScalarAll(1.) );

    CvFMEstimator estimator(7);
    if( count == 7 )
        result = estimator.run7Point(m1, m2, &_F9x3);
    else if( method == CV_FM_8POINT )
        result = estimator.run8Point(m1, m2, &_F3x3);
    else
    {
        if( param1 <= 0 )
            param1 = 3;
        if( param2 < DBL_EPSILON || param2 > 1 - DBL_EPSILON )
            param2 = 0.99;

        if( (method & ~3) == CV_RANSAC && count >= 15 )
            result = estimator.runRANSAC(m1, m2, &_F3x3, tempMask, param1, param2 );//在数据点数大于15个时才调用ransac方法见(3else
            result = estimator.runLMeDS(m1, m2, &_F3x3, tempMask, param2 );
        if( result <= 0 )
            return 0;
        /*icvCompressPoints( (CvPoint2D64f*)m1->data.ptr, tempMask->data.ptr, 1, count );
        count = icvCompressPoints( (CvPoint2D64f*)m2->data.ptr, tempMask->data.ptr, 1, count );
        assert( count >= 8 );
        m1->cols = m2->cols = count;
        estimator.run8Point(m1, m2, &_F3x3);*/
    }

    if( result )
        cvConvert( fmatrix->rows == 3 ? &_F3x3 : &_F9x3, fmatrix );

    if( mask && tempMask )
    {
        if( CV_ARE_SIZES_EQ(mask, tempMask) )
            cvCopy( tempMask, mask );
        else
            cvTranspose( tempMask, mask );
    }

    return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

(3)

maxIters:最大迭代次数 
m1,m2 :数据样本 
model:变换矩阵 
reprojThreshold:投影误差阈值 3 
confidence:置信度 0.99

bool CvModelEstimator2::runRANSAC( const CvMat* m1, const CvMat* m2, CvMat* model,  
                                    CvMat* mask0, double reprojThreshold,  
                                    double confidence, int maxIters )  
{  
    bool result = false;  
    cv::Ptr<CvMat> mask = cvCloneMat(mask0);  
    cv::Ptr<CvMat> models, err, tmask;  
    cv::Ptr<CvMat> ms1, ms2;  

    int iter, niters = maxIters;  
    int count = m1->rows*m1->cols, maxGoodCount = 0;  
    CV_Assert( CV_ARE_SIZES_EQ(m1, m2) && CV_ARE_SIZES_EQ(m1, mask) );  

    if( count < modelPoints )  
        return false;  

    models = cvCreateMat( modelSize.height*maxBasicSolutions, modelSize.width, CV_64FC1 );  
    err = cvCreateMat( 1, count, CV_32FC1 );//保存每组点对应的投影误差  
    tmask = cvCreateMat( 1, count, CV_8UC1 );  

    if( count > modelPoints )  //多于4个点  
    {  
        ms1 = cvCreateMat( 1, modelPoints, m1->type );  
        ms2 = cvCreateMat( 1, modelPoints, m2->type );  
    }  
    else  //等于4个点  
    {  
        niters = 1; //迭代一次  
        ms1 = cvCloneMat(m1);  //保存每次随机找到的样本点  
        ms2 = cvCloneMat(m2);  
    }  

    for( iter = 0; iter < niters; iter++ )  //不断迭代  
    {  
        int i, goodCount, nmodels;  
        if( count > modelPoints )  
        {  
           //在(4)解析getSubset  
            bool found = getSubset( m1, m2, ms1, ms2, 300 ); //随机选择4组点,且三点之间不共线,见(4),最大迭代次数300
            if( !found )  
            {  
                if( iter == 0 )  
                    return false;  
                break;  
            }  
        }  

        nmodels = runKernel( ms1, ms2, models );  //估算近似变换矩阵,返回1 见(7) 
        if( nmodels <= 0 )  
            continue;  
        for( i = 0; i < nmodels; i++ )//执行一次   
        {  
            CvMat model_i;  
            cvGetRows( models, &model_i, i*modelSize.height, (i+1)*modelSize.height );//获取3×3矩阵元素  
            goodCount = findInliers( m1, m2, &model_i, err, tmask, reprojThreshold );  //找出内点,(5)解析  

            if( goodCount > MAX(maxGoodCount, modelPoints-1) )  //当前内点集元素个数大于最优内点集元素个数  
            {  
                std::swap(tmask, mask);  
                cvCopy( &model_i, model );  //更新最优模型  
                maxGoodCount = goodCount;  
                //confidence 为置信度默认0.995,modelPoints为最少所需样本数=4,niters迭代次数  
                niters = cvRANSACUpdateNumIters( confidence,  //更新迭代次数,(6)解析  
                    (double)(count - goodCount)/count, modelPoints, niters );  
            }  
        }  
    }  

    if( maxGoodCount > 0 )  
    {  
        if( mask != mask0 )  
            cvCopy( mask, mask0 );  
        result = true;  
    }  

    return result;  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

(4)getSubset()得到子集

bool CvModelEstimator2::getSubset( const CvMat* m1, const CvMat* m2,  
                                   CvMat* ms1, CvMat* ms2, int maxAttempts )  
{  
    cv::AutoBuffer<int> _idx(modelPoints); //modelPoints 所需要最少的样本点个数  
    int* idx = _idx;  
    int i = 0, j, k, idx_i, iters = 0;  
    int type = CV_MAT_TYPE(m1->type), elemSize = CV_ELEM_SIZE(type);  
    const int *m1ptr = m1->data.i, *m2ptr = m2->data.i;  
    int *ms1ptr = ms1->data.i, *ms2ptr = ms2->data.i;  
    int count = m1->cols*m1->rows;  

    assert( CV_IS_MAT_CONT(m1->type & m2->type) && (elemSize % sizeof(int) == 0) );  
    elemSize /= sizeof(int); //每个数据占用字节数  

    for(; iters < maxAttempts; iters++)  
    {  
        for( i = 0; i < modelPoints && iters < maxAttempts; )  
        {  
            idx[i] = idx_i = cvRandInt(&rng) % count;  // 随机选取1组点  
            for( j = 0; j < i; j++ )  // 检测是否重复选择  
                if( idx_i == idx[j] )  
                    break;  
            if( j < i )  
                continue;  //重新选择  
            for( k = 0; k < elemSize; k++ )  //拷贝点数据  
            {  
                ms1ptr[i*elemSize + k] = m1ptr[idx_i*elemSize + k];  
                ms2ptr[i*elemSize + k] = m2ptr[idx_i*elemSize + k];  
            }  
            if( checkPartialSubsets && (!checkSubset( ms1, i+1 ) || !checkSubset( ms2, i+1 )))//检测点之间是否共线  
            {  
                iters++;  //若共线,重新选择一组  
                continue;  
            }  
            i++;  
        }  
        if( !checkPartialSubsets && i == modelPoints &&  
            (!checkSubset( ms1, i ) || !checkSubset( ms2, i )))  
            continue;  
        break;  
    }  

    return i == modelPoints && iters < maxAttempts;  //返回找到的样本点个数  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

(5)findInliers()找内点

int CvModelEstimator2::findInliers( const CvMat* m1, const CvMat* m2,  
                                    const CvMat* model, CvMat* _err,  
                                    CvMat* _mask, double threshold )  
{  
    int i, count = _err->rows*_err->cols, goodCount = 0;  
    const float* err = _err->data.fl;  
    uchar* mask = _mask->data.ptr;  

    computeReprojError( m1, m2, model, _err );  //计算每组点的投影误差  
    threshold *= threshold;  
    for( i = 0; i < count; i++ )  
        goodCount += mask[i] = err[i] <= threshold;//误差在限定范围内,加入‘内点集’  
    return goodCount;  
}  
//--------------------------------------  
void CvHomographyEstimator::computeReprojError( const CvMat* m1, const CvMat* m2,const CvMat* model, CvMat* _err )  
{  
    int i, count = m1->rows*m1->cols;  
    const CvPoint2D64f* M = (const CvPoint2D64f*)m1->data.ptr;  
    const CvPoint2D64f* m = (const CvPoint2D64f*)m2->data.ptr;  
    const double* H = model->data.db;  
    float* err = _err->data.fl;  

    for( i = 0; i < count; i++ )        //保存每组点的投影误差,对应上述变换公式  
    {  
        double ww = 1./(H[6]*M[i].x + H[7]*M[i].y + 1.);      
        double dx = (H[0]*M[i].x + H[1]*M[i].y + H[2])*ww - m[i].x;  
        double dy = (H[3]*M[i].x + H[4]*M[i].y + H[5])*ww - m[i].y;  
        err[i] = (float)(dx*dx + dy*dy);  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

(6)cvRANSACUpdateNumIters 
对应上述k的计算公式 
p:置信度 
ep:外点比例

cvRANSACUpdateNumIters( double p, double ep,  
                        int model_points, int max_iters )  
{  
    if( model_points <= 0 )  
        CV_Error( CV_StsOutOfRange, "the number of model points should be positive" );  

    p = MAX(p, 0.);  
    p = MIN(p, 1.);  
    ep = MAX(ep, 0.);  
    ep = MIN(ep, 1.);  

    // avoid inf's & nan's  
    double num = MAX(1. - p, DBL_MIN);  //num=1-p,做分子  
    double denom = 1. - pow(1. - ep,model_points);//做分母  
    if( denom < DBL_MIN )  
        return 0;  

    num = log(num);  
    denom = log(denom);  

    return denom >= 0 || -num >= max_iters*(-denom) ?  
        max_iters : cvRound(num/denom);  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

(7)

int cv::Affine3DEstimator::runKernel( const CvMat* m1, const CvMat* m2, CvMat* model )
{
    const Point3d* from = reinterpret_cast<const Point3d*>(m1->data.ptr);
    const Point3d* to   = reinterpret_cast<const Point3d*>(m2->data.ptr);

    Mat A(12, 12, CV_64F);
    Mat B(12, 1, CV_64F);
    A = Scalar(0.0);

    for(int i = 0; i < modelPoints; ++i)
    {
        *B.ptr<Point3d>(3*i) = to[i];

        double *aptr = A.ptr<double>(3*i);
        for(int k = 0; k < 3; ++k)
        {
            aptr[3] = 1.0;
            *reinterpret_cast<Point3d*>(aptr) = from[i];
            aptr += 16;
        }
    }

    CvMat cvA = A;
    CvMat cvB = B;
    CvMat cvX;
    cvReshape(model, &cvX, 1, 12);
    cvSolve(&cvA, &cvB, &cvX, CV_SVD );//基于奇异值分解的最小二乘法

    return 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
版权声明:转载请注明出处。如有错误,恳请指出! //blog.csdn.net/lz20120808/article/details/49280443
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值