OpenCV中关于二维仿射变换函数estimateAffinePartial2D的源码分析

二维仿射变换及其接口

关于二维仿射变化的介绍:https://www.cnblogs.com/yinheyi/p/6148886.html

OpenCV3.4.1中提供的接口为:estimateAffinePartial2D(),用于计算两个2D点集之间具有4个自由度的最优有限仿射变换。

其函数具体实现位于:./opencv/sources/modules/calib3d/src/ptsetreg.cpp

函数原型:

cv::Mat cv::estimateAffinePartial2D	(
	InputArray 	from,
	InputArray  to,
	OutputArray  inliers = noArray(),
	int  method = RANSAC,
	double  ransacReprojThreshold = 3,
	size_t 	maxIters = 2000,
	double 	confidence = 0.99,
	size_t 	refineIters = 10 
)	

函数具体实现:

Mat estimateAffinePartial2D(InputArray _from, InputArray _to, OutputArray _inliers,
                            const int method, const double ransacReprojThreshold,
                            const size_t maxIters, const double confidence,
                            const size_t refineIters)
{
    
    
    Mat from = _from.getMat(), to = _to.getMat();
    const int count = from.checkVector(2);
    bool result = false;
    Mat H;

    CV_Assert( count >= 0 && to.checkVector(2) == count );

    if (from.type() != CV_32FC2 || to.type() != CV_32FC2)
    {
    
    
        Mat tmp1, tmp2;
        from.convertTo(tmp1, CV_32FC2);
        from = tmp1;
        to.convertTo(tmp2, CV_32FC2);
        to = tmp2;
    }
    else
    {
    
    
        // avoid changing of inputs in compressElems() call
        from = from.clone();
        to = to.clone();
    }

    // convert to N x 1 vectors
    from = from.reshape(2, count);
    to = to.reshape(2, count);

    Mat inliers;
    if(_inliers.needed())
    {
    
    
        _inliers.create(count, 1, CV_8U, -1, true);
        inliers = _inliers.getMat();
    }

    // run robust estimation
    Ptr<PointSetRegistrator::Callback> cb = makePtr<AffinePartial2DEstimatorCallback>();
    if( method == RANSAC )
        result = createRANSACPointSetRegistrator(cb, 2, ransacReprojThreshold, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
    else if( method == LMEDS )
        result = createLMeDSPointSetRegistrator(cb, 2, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
    else
        CV_Error(Error::StsBadArg, "Unknown or unsupported robust estimation method");

    if(result && count > 2 && refineIters)
    {
    
    
        // reorder to start with inliers
        compressElems(from.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
        int inliers_count = compressElems(to.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
        if(inliers_count > 0)
        {
    
    
            Mat src = from.rowRange(0, inliers_count);
            Mat dst = to.rowRange(0, inliers_count);
            // H is
            //     a -b tx
            //     b  a ty
            // Hvec model for LevMarq is
            //     (a, b, tx, ty)
            double *Hptr = H.ptr<double>();
            double Hvec_buf[4] = {
    
    Hptr[0], Hptr[3], Hptr[2], Hptr[5]};
            Mat Hvec (4, 1, CV_64F, Hvec_buf);
            createLMSolver(makePtr<AffinePartial2DRefineCallback>(src, dst), static_cast<int>(refineIters))->run(Hvec);
            // update H with refined parameters
            Hptr[0] = Hptr[4] = Hvec_buf[0];
            Hptr[1] = -Hvec_buf[1];
            Hptr[2] = Hvec_buf[2];
            Hptr[3] = Hvec_buf[1];
            Hptr[5] = Hvec_buf[3];
        }
    }

    if (!result)
    {
    
    
        H.release();
        if(_inliers.needed())
        {
    
    
            inliers = Mat::zeros(count, 1, CV_8U);
            inliers.copyTo(_inliers);
        }
    }

    return H;
}

代码分析

数据准备工作

  1. 先看第一段程序,将函数接收的参数_from_to变为Mat数据类型
Mat from = _from.getMat(), to = _to.getMat();
const int count = from.checkVector(2);
bool result = false;
Mat H;

CV_Assert( count >= 0 && to.checkVector(2) == count );

checkVector():返回符合要求的矩阵中的元素个数

int cv::Mat::checkVector(	
	int 	elemChannels,
	int 	depth = -1,
	bool 	requireContinuous = true 
)const

参数

  • elemChannels:矩阵应具有的通道数或列数。对于二维矩阵,当矩阵只有1列时,它应该有elemChannels通道;当矩阵只有1个通道时,它应该有elemChannels列。对于三维矩阵,它应该只有一个通道。此外,如果平面的数目不是一,则每个平面内的行数必须是1;如果每个平面内的行数不是1,则平面数必须是1。
  • depth:矩阵应有的深度

返回值:如果不满足要求,则为-1。否则,它将返回矩阵中元素的数量。注意,一个元素可以有多个通道。

  1. 第二段程序则是负责将fromto的矩阵类型变为CV_32FC2
if (from.type() != CV_32FC2 || to.type() != CV_32FC2)
{
    
    
    Mat tmp1, tmp2;
    from.convertTo(tmp1, CV_32FC2);
    from = tmp1;
    to.convertTo(tmp2, CV_32FC2);
    to = tmp2;
}
else
{
    
    
    // avoid changing of inputs in compressElems() call
    from = from.clone();
    to = to.clone();
}
  1. 接下来这一段有注释:转成 N × 1 N\times1 N×1的向量
// convert to N x 1 vectors
from = from.reshape(2, count);
to = to.reshape(2, count);

reshape()函数原型

Mat cv::Mat::reshape(	
	int 	cn,
	int 	rows = 0 
)const

具体实现为:

Mat Mat::reshape(int new_cn, int new_rows) const
{
    
    
    int cn = channels();
    Mat hdr = *this;

    if( dims > 2 )
    {
    
    
        if( new_rows == 0 && new_cn != 0 && size[dims-1]*cn % new_cn == 0 )
        {
    
    
            hdr.flags = (hdr.flags & ~CV_MAT_CN_MASK) | ((new_cn-1) << CV_CN_SHIFT);
            hdr.step[dims-1] = CV_ELEM_SIZE(hdr.flags);
            hdr.size[dims-1] = hdr.size[dims-1]*cn / new_cn;
            return hdr;
        }
        if( new_rows > 0 )
        {
    
    
            int sz[] = {
    
     new_rows, (int)(total()/new_rows) };
            return reshape(new_cn, 2, sz);
        }
    }

    CV_Assert( dims <= 2 );

    if( new_cn == 0 )
        new_cn = cn;

    int total_width = cols * cn;

    if( (new_cn > total_width || total_width % new_cn != 0) && new_rows == 0 )
        new_rows = rows * total_width / new_cn;

    if( new_rows != 0 && new_rows != rows )
    {
    
    
        int total_size = total_width * rows;
        if( !isContinuous() )
            CV_Error( CV_BadStep,
            "The matrix is not continuous, thus its number of rows can not be changed" );

        if( (unsigned)new_rows > (unsigned)total_size )
            CV_Error( CV_StsOutOfRange, "Bad new number of rows" );

        total_width = total_size / new_rows;

        if( total_width * new_rows != total_size )
            CV_Error( CV_StsBadArg, "The total number of matrix elements "
                                    "is not divisible by the new number of rows" );

        hdr.rows = new_rows;
        hdr.step[0] = total_width * elemSize1();
    }

    int new_width = total_width / new_cn;

    if( new_width * new_cn != total_width )
        CV_Error( CV_BadNumChannels,
        "The total width is not divisible by the new number of channels" );

    hdr.cols = new_width;
    hdr.flags = (hdr.flags & ~CV_MAT_CN_MASK) | ((new_cn-1) << CV_CN_SHIFT);
    hdr.step[1] = CV_ELEM_SIZE(hdr.flags);
    return hdr;
}
  1. 以下代码自然是创建内点矩阵
Mat inliers;
if(_inliers.needed())
{
    
    
    _inliers.create(count, 1, CV_8U, -1, true);
    inliers = _inliers.getMat();
}

运行RANSAC算法

  1. 然后注意到下面这一行代码,创建了一个智能指针cb
Ptr<PointSetRegistrator::Callback> cb = makePtr<AffinePartial2DEstimatorCallback>();

这一行有点麻烦,接下来追根溯源一下,这一行代码中涉及两个类型:PointSetRegistrator::CallbackAffinePartial2DEstimatorCallback。其中,PointSetRegistrator是一个继承自Algorithm的虚基类,定义在/opencv/sources/modules/calib3d/src/precomp.hpp中。

扫描二维码关注公众号,回复: 14976108 查看本文章
class CV_EXPORTS PointSetRegistrator : public Algorithm
{
    
    
public:
    class CV_EXPORTS Callback
    {
    
    
    public:
        virtual ~Callback() {
    
    }
        virtual int runKernel(InputArray m1, InputArray m2, OutputArray model) const = 0;
        virtual void computeError(InputArray m1, InputArray m2, InputArray model, OutputArray err) const = 0;
        virtual bool checkSubset(InputArray, InputArray, int) const {
    
     return true; }
    };

    virtual void setCallback(const Ptr<PointSetRegistrator::Callback>& cb) = 0;
    virtual bool run(InputArray m1, InputArray m2, OutputArray model, OutputArray mask) const = 0;
};

AffinePartial2DEstimatorCallback类继承自Affine2DEstimatorCallback,具体实现如下

/*
 * Compute
 *  x    c -s    X    t1
 *    =       *     +
 *  y    s  c    Y    t2
 *
 *  - every element in _m1 contains (X,Y), which are called source points
 *  - every element in _m2 contains (x,y), which are called destination points
 *  - _model is of size 2x3, which contains
 *    c  -s  t1
 *    s   c  t2
 */
class AffinePartial2DEstimatorCallback : public Affine2DEstimatorCallback
{
    
    
public:
    int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
    {
    
    
        Mat m1 = _m1.getMat(), m2 = _m2.getMat();
        const Point2f* from = m1.ptr<Point2f>();
        const Point2f* to   = m2.ptr<Point2f>();
        _model.create(2, 3, CV_64F);
        Mat M_mat = _model.getMat();
        double *M = M_mat.ptr<double>();

        // we need only 2 points to estimate transform
        double x1 = from[0].x;
        double y1 = from[0].y;
        double x2 = from[1].x;
        double y2 = from[1].y;

        double X1 = to[0].x;
        double Y1 = to[0].y;
        double X2 = to[1].x;
        double Y2 = to[1].y;

        /*
        we are solving AS = B
            | x1 -y1 1 0 |
            | y1  x1 0 1 |
        A = | x2 -y2 1 0 |
            | y2  x2 0 1 |
        B = (X1, Y1, X2, Y2).t()
        we solve that analytically
        */
        double d = 1./((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));

        // solution vector
        double S0 = d * ( (X1-X2)*(x1-x2) + (Y1-Y2)*(y1-y2) );
        double S1 = d * ( (Y1-Y2)*(x1-x2) - (X1-X2)*(y1-y2) );
        double S2 = d * ( (Y1-Y2)*(x1*y2 - x2*y1) - (X1*y2 - X2*y1)*(y1-y2) - (X1*x2 - X2*x1)*(x1-x2) );
        double S3 = d * (-(X1-X2)*(x1*y2 - x2*y1) - (Y1*x2 - Y2*x1)*(x1-x2) - (Y1*y2 - Y2*y1)*(y1-y2) );

        // set model, rotation part is antisymmetric
        M[0] = M[4] = S0;
        M[1] = -S1;
        M[2] = S2;
        M[3] = S1;
        M[5] = S3;
        return 1;
    }
};

AffinePartial2DEstimatorCallback的父类Affine2DEstimatorCallback继承自PointSetRegistrator::Callback,具体实现如下。

/*
 * Compute
 *  x     a  b   X    c
 *     =       *    +
 *  y     d  e   Y    f
 *
 *  - every element in _m1 contains (X,Y), which are called source points
 *  - every element in _m2 contains (x,y), which are called destination points
 *  - _model is of size 2x3, which contains
 *    a b c
 *    d e f
 */
class Affine2DEstimatorCallback : public PointSetRegistrator::Callback
{
    
    
public:
    int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
    {
    
    
        Mat m1 = _m1.getMat(), m2 = _m2.getMat();
        const Point2f* from = m1.ptr<Point2f>();
        const Point2f* to   = m2.ptr<Point2f>();
        _model.create(2, 3, CV_64F);
        Mat M_mat = _model.getMat();
        double *M = M_mat.ptr<double>();

        // we need 3 points to estimate affine transform
        double x1 = from[0].x;
        double y1 = from[0].y;
        double x2 = from[1].x;
        double y2 = from[1].y;
        double x3 = from[2].x;
        double y3 = from[2].y;

        double X1 = to[0].x;
        double Y1 = to[0].y;
        double X2 = to[1].x;
        double Y2 = to[1].y;
        double X3 = to[2].x;
        double Y3 = to[2].y;

        /*
        We want to solve AX = B

            | x1 y1  1  0  0  0 |
            |  0  0  0 x1 y1  1 |
            | x2 y2  1  0  0  0 |
        A = |  0  0  0 x2 y2  1 |
            | x3 y3  1  0  0  0 |
            |  0  0  0 x3 y3  1 |
        B = (X1, Y1, X2, Y2, X3, Y3).t()
        X = (a, b, c, d, e, f).t()

        As the estimate of (a, b, c) only depends on the Xi, and (d, e, f) only
        depends on the Yi, we do the *trick* to solve each one analytically.

        | X1 |   | x1 y1 1 |   | a |
        | X2 | = | x2 y2 1 | * | b |
        | X3 |   | x3 y3 1 |   | c |

        | Y1 |   | x1 y1 1 |   | d |
        | Y2 | = | x2 y2 1 | * | e |
        | Y3 |   | x3 y3 1 |   | f |
        */

        double d = 1. / ( x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2) );

        M[0] = d * ( X1*(y2-y3) + X2*(y3-y1) + X3*(y1-y2) );
        M[1] = d * ( X1*(x3-x2) + X2*(x1-x3) + X3*(x2-x1) );
        M[2] = d * ( X1*(x2*y3 - x3*y2) + X2*(x3*y1 - x1*y3) + X3*(x1*y2 - x2*y1) );

        M[3] = d * ( Y1*(y2-y3) + Y2*(y3-y1) + Y3*(y1-y2) );
        M[4] = d * ( Y1*(x3-x2) + Y2*(x1-x3) + Y3*(x2-x1) );
        M[5] = d * ( Y1*(x2*y3 - x3*y2) + Y2*(x3*y1 - x1*y3) + Y3*(x1*y2 - x2*y1) );
        return 1;
    }

    void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
    {
    
    
        Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
        const Point2f* from = m1.ptr<Point2f>();
        const Point2f* to   = m2.ptr<Point2f>();
        const double* F = model.ptr<double>();

        int count = m1.checkVector(2);
        CV_Assert( count > 0 );

        _err.create(count, 1, CV_32F);
        Mat err = _err.getMat();
        float* errptr = err.ptr<float>();
        // transform matrix to floats
        float F0 = (float)F[0], F1 = (float)F[1], F2 = (float)F[2];
        float F3 = (float)F[3], F4 = (float)F[4], F5 = (float)F[5];

        for(int i = 0; i < count; i++ )
        {
    
    
            const Point2f& f = from[i];
            const Point2f& t = to[i];

            float a = F0*f.x + F1*f.y + F2 - t.x;
            float b = F3*f.x + F4*f.y + F5 - t.y;

            errptr[i] = a*a + b*b;
        }
    }

    bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const CV_OVERRIDE
    {
    
    
        Mat ms1 = _ms1.getMat();
        Mat ms2 = _ms2.getMat();
        // check collinearity and also check that points are too close
        return !haveCollinearPoints(ms1, count) && !haveCollinearPoints(ms2, count);
    }
};

因此,原来AffinePartial2DEstimatorCallback也是自PointSetRegistrator::Callback的子类派生而来的。

makePtr()

template<typename T>
Ptr<T> makePtr()
{
    
    
    return Ptr<T>(new T());
}

Ptr:智能指针类型

template<typename _Tp >
using cv::Ptr = typedef std::shared_ptr<_Tp>
  1. 智能指针cb会作为参数传入函数createRANSACPointSetRegistrator中,即若是以RANSAC算法解算变换矩阵,则进入代码。这一行会将所有的数据全部代入,包括:cbransacReprojThresholdconfidencemaxItersfromtoHinliers
result = createRANSACPointSetRegistrator(cb, 2, ransacReprojThreshold, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);

首先通过createRANSACPointSetRegistrator创建一个RANSAC点集的注册器,其本质就是创建一个指向RANSACPointSetRegistrator的匿名指针。

Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb,
                                                         int _modelPoints, double _threshold,
                                                         double _confidence, int _maxIters)
{
    
    
    return Ptr<PointSetRegistrator>(
        new RANSACPointSetRegistrator(_cb, _modelPoints, _threshold, _confidence, _maxIters));
}

终于到了重要的一个类实现:RANSACPointSetRegistrator,而RANSACPointSetRegistrator正是继承自PointSetRegistrator

class RANSACPointSetRegistrator : public PointSetRegistrator
{
    
    
public:
    RANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb=Ptr<PointSetRegistrator::Callback>(),
                              int _modelPoints=0, double _threshold=0, double _confidence=0.99, int _maxIters=1000)
      : cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters) {
    
    }

    int findInliers( const Mat& m1, const Mat& m2, const Mat& model, Mat& err, Mat& mask, double thresh ) const
    {
    
    
        cb->computeError( m1, m2, model, err );
        mask.create(err.size(), CV_8U);

        CV_Assert( err.isContinuous() && err.type() == CV_32F && mask.isContinuous() && mask.type() == CV_8U);
        const float* errptr = err.ptr<float>();
        uchar* maskptr = mask.ptr<uchar>();
        float t = (float)(thresh*thresh);
        int i, n = (int)err.total(), nz = 0;
        for( i = 0; i < n; i++ )
        {
    
    
            int f = errptr[i] <= t;
            maskptr[i] = (uchar)f;
            nz += f;
        }
        return nz;
    }

    bool getSubset( const Mat& m1, const Mat& m2, Mat& ms1, Mat& ms2, RNG& rng, int maxAttempts=1000 ) const
    {
    
    
        cv::AutoBuffer<int> _idx(modelPoints);
        int* idx = _idx.data();

        const int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
        const int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;

        int esz1 = (int)m1.elemSize1() * d1;
        int esz2 = (int)m2.elemSize1() * d2;
        CV_Assert((esz1 % sizeof(int)) == 0 && (esz2 % sizeof(int)) == 0);
        esz1 /= sizeof(int);
        esz2 /= sizeof(int);

        const int count = m1.checkVector(d1);
        const int count2 = m2.checkVector(d2);
        CV_Assert(count >= modelPoints && count == count2);

        const int *m1ptr = m1.ptr<int>();
        const int *m2ptr = m2.ptr<int>();

        ms1.create(modelPoints, 1, CV_MAKETYPE(m1.depth(), d1));
        ms2.create(modelPoints, 1, CV_MAKETYPE(m2.depth(), d2));

        int *ms1ptr = ms1.ptr<int>();
        int *ms2ptr = ms2.ptr<int>();

        for( int iters = 0; iters < maxAttempts; ++iters )
        {
    
    
            int i;

            for( i = 0; i < modelPoints; ++i )
            {
    
    
                int idx_i;

                for ( idx_i = rng.uniform(0, count);
                    std::find(idx, idx + i, idx_i) != idx + i;
                    idx_i = rng.uniform(0, count) )
                {
    
    }

                idx[i] = idx_i;

                for( int k = 0; k < esz1; ++k )
                    ms1ptr[i*esz1 + k] = m1ptr[idx_i*esz1 + k];

                for( int k = 0; k < esz2; ++k )
                    ms2ptr[i*esz2 + k] = m2ptr[idx_i*esz2 + k];
            }

            if( cb->checkSubset(ms1, ms2, i) )
                return true;
        }

        return false;
    }

    bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const CV_OVERRIDE
    {
    
    
        bool result = false;
        Mat m1 = _m1.getMat(), m2 = _m2.getMat();
        Mat err, mask, model, bestModel, ms1, ms2;

        int iter, niters = MAX(maxIters, 1);
        int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
        int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
        int count = m1.checkVector(d1), count2 = m2.checkVector(d2), maxGoodCount = 0;

        RNG rng((uint64)-1);

        CV_Assert( cb );
        CV_Assert( confidence > 0 && confidence < 1 );

        CV_Assert( count >= 0 && count2 == count );
        if( count < modelPoints )
            return false;

        Mat bestMask0, bestMask;

        if( _mask.needed() )
        {
    
    
            _mask.create(count, 1, CV_8U, -1, true);
            bestMask0 = bestMask = _mask.getMat();
            CV_Assert( (bestMask.cols == 1 || bestMask.rows == 1) && (int)bestMask.total() == count );
        }
        else
        {
    
    
            bestMask.create(count, 1, CV_8U);
            bestMask0 = bestMask;
        }

        if( count == modelPoints )
        {
    
    
            if( cb->runKernel(m1, m2, bestModel) <= 0 )
                return false;
            bestModel.copyTo(_model);
            bestMask.setTo(Scalar::all(1));
            return true;
        }

        for( iter = 0; iter < niters; iter++ )
        {
    
    
            int i, nmodels;
            if( count > modelPoints )
            {
    
    
                bool found = getSubset( m1, m2, ms1, ms2, rng, 10000 );
                if( !found )
                {
    
    
                    if( iter == 0 )
                        return false;
                    break;
                }
            }

            nmodels = cb->runKernel( ms1, ms2, model );
            if( nmodels <= 0 )
                continue;
            CV_Assert( model.rows % nmodels == 0 );
            Size modelSize(model.cols, model.rows/nmodels);

            for( i = 0; i < nmodels; i++ )
            {
    
    
                Mat model_i = model.rowRange( i*modelSize.height, (i+1)*modelSize.height );
                int goodCount = findInliers( m1, m2, model_i, err, mask, threshold );

                if( goodCount > MAX(maxGoodCount, modelPoints-1) )
                {
    
    
                    std::swap(mask, bestMask);
                    model_i.copyTo(bestModel);
                    maxGoodCount = goodCount;
                    niters = RANSACUpdateNumIters( confidence, (double)(count - goodCount)/count, modelPoints, niters );
                }
            }
        }

        if( maxGoodCount > 0 )
        {
    
    
            if( bestMask.data != bestMask0.data )
            {
    
    
                if( bestMask.size() == bestMask0.size() )
                    bestMask.copyTo(bestMask0);
                else
                    transpose(bestMask, bestMask0);
            }
            bestModel.copyTo(_model);
            result = true;
        }
        else
            _model.release();

        return result;
    }

    void setCallback(const Ptr<PointSetRegistrator::Callback>& _cb) CV_OVERRIDE {
    
     cb = _cb; }

    Ptr<PointSetRegistrator::Callback> cb;
    int modelPoints;
    double threshold;
    double confidence;
    int maxIters;
};

因此,最终是通过RANSACPointSetRegistrator类指针调用run方法,从而筛选出数据的内点。而我们需要学习的正是这个run方法,揭开它神秘的面纱。

bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const CV_OVERRIDE

首先分析一下参数,_m1from_m2to,也就是求解fromto的变换矩阵,_model为变换矩阵H_mask为内点inliers

(1)数据准备

bool result = false;
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
Mat err, mask, model, bestModel, ms1, ms2;

int iter, niters = MAX(maxIters, 1);
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
int count = m1.checkVector(d1), count2 = m2.checkVector(d2), maxGoodCount = 0;

RNG rng((uint64)-1);

CV_Assert( cb );
CV_Assert( confidence > 0 && confidence < 1 );

CV_Assert( count >= 0 && count2 == count );
if( count < modelPoints )
    return false;

Mat bestMask0, bestMask;

if( _mask.needed() )
{
    
    
    _mask.create(count, 1, CV_8U, -1, true);
    bestMask0 = bestMask = _mask.getMat();
    CV_Assert( (bestMask.cols == 1 || bestMask.rows == 1) && (int)bestMask.total() == count );
}
else
{
    
    
    bestMask.create(count, 1, CV_8U);
    bestMask0 = bestMask;
}

(2)count等于modelPoints,直接通过cb调用runKernel

if( count == modelPoints )
{
    
    
    if( cb->runKernel(m1, m2, bestModel) <= 0 )
        return false;
    bestModel.copyTo(_model);
    bestMask.setTo(Scalar::all(1));
    return true;
}

(3)迭代计算

for( iter = 0; iter < niters; iter++ )
{
    
    
    int i, nmodels;
    if( count > modelPoints )
    {
    
    
        bool found = getSubset( m1, m2, ms1, ms2, rng, 10000 );
        if( !found )
        {
    
    
            if( iter == 0 )
                return false;
            break;
        }
    }

    nmodels = cb->runKernel( ms1, ms2, model );
    if( nmodels <= 0 )
        continue;
    CV_Assert( model.rows % nmodels == 0 );
    Size modelSize(model.cols, model.rows/nmodels);

    for( i = 0; i < nmodels; i++ )
    {
    
    
        Mat model_i = model.rowRange( i*modelSize.height, (i+1)*modelSize.height );
        int goodCount = findInliers( m1, m2, model_i, err, mask, threshold );

        if( goodCount > MAX(maxGoodCount, modelPoints-1) )
        {
    
    
            std::swap(mask, bestMask);
            model_i.copyTo(bestModel);
            maxGoodCount = goodCount;
            niters = RANSACUpdateNumIters( confidence, (double)(count - goodCount)/count, modelPoints, niters );
        }
    }
}

根据内点计算变换矩阵

  1. 再回到最初的estimateAffinePartial2D函数中,会发现后续正是利用内点来计算二维仿射变换矩阵。即如下代码
if(result && count > 2 && refineIters)
{
    
    
    // reorder to start with inliers
    compressElems(from.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
    int inliers_count = compressElems(to.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
    if(inliers_count > 0)
    {
    
    
        Mat src = from.rowRange(0, inliers_count);
        Mat dst = to.rowRange(0, inliers_count);
        // H is
        //     a -b tx
        //     b  a ty
        // Hvec model for LevMarq is
        //     (a, b, tx, ty)
        double *Hptr = H.ptr<double>();
        double Hvec_buf[4] = {
    
    Hptr[0], Hptr[3], Hptr[2], Hptr[5]};
        Mat Hvec (4, 1, CV_64F, Hvec_buf);
        createLMSolver(makePtr<AffinePartial2DRefineCallback>(src, dst), static_cast<int>(refineIters))->run(Hvec);
        // update H with refined parameters
        Hptr[0] = Hptr[4] = Hvec_buf[0];
        Hptr[1] = -Hvec_buf[1];
        Hptr[2] = Hvec_buf[2];
        Hptr[3] = Hvec_buf[1];
        Hptr[5] = Hvec_buf[3];
    }
}

猜你喜欢

转载自blog.csdn.net/Star_ID/article/details/128684947