国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

OpenCV中關(guān)于二維仿射變換函數(shù)estimateAffinePartial2D的源碼分析

這篇具有很好參考價(jià)值的文章主要介紹了OpenCV中關(guān)于二維仿射變換函數(shù)estimateAffinePartial2D的源碼分析。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

二維仿射變換及其接口

關(guān)于二維仿射變化的介紹:https://www.cnblogs.com/yinheyi/p/6148886.html

OpenCV3.4.1中提供的接口為:estimateAffinePartial2D(),用于計(jì)算兩個(gè)2D點(diǎn)集之間具有4個(gè)自由度的最優(yōu)有限仿射變換。

其函數(shù)具體實(shí)現(xiàn)位于:./opencv/sources/modules/calib3d/src/ptsetreg.cpp

函數(shù)原型:

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 
)	

函數(shù)具體實(shí)現(xiàn):

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

代碼分析

數(shù)據(jù)準(zhǔn)備工作

  1. 先看第一段程序,將函數(shù)接收的參數(shù)_from_to變?yōu)?code>Mat數(shù)據(jù)類型
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():返回符合要求的矩陣中的元素個(gè)數(shù)

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

參數(shù)

  • elemChannels:矩陣應(yīng)具有的通道數(shù)或列數(shù)。對(duì)于二維矩陣,當(dāng)矩陣只有1列時(shí),它應(yīng)該有elemChannels通道;當(dāng)矩陣只有1個(gè)通道時(shí),它應(yīng)該有elemChannels列。對(duì)于三維矩陣,它應(yīng)該只有一個(gè)通道。此外,如果平面的數(shù)目不是一,則每個(gè)平面內(nèi)的行數(shù)必須是1;如果每個(gè)平面內(nèi)的行數(shù)不是1,則平面數(shù)必須是1。
  • depth:矩陣應(yīng)有的深度

返回值:如果不滿足要求,則為-1。否則,它將返回矩陣中元素的數(shù)量。注意,一個(gè)元素可以有多個(gè)通道。

  1. 第二段程序則是負(fù)責(zé)將fromto的矩陣類型變?yōu)?code>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. 接下來(lái)這一段有注釋:轉(zhuǎn)成 N × 1 N\times1 N×1的向量
// convert to N x 1 vectors
from = from.reshape(2, count);
to = to.reshape(2, count);

reshape()函數(shù)原型

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

具體實(shí)現(xiàn)為:

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. 以下代碼自然是創(chuàng)建內(nèi)點(diǎn)矩陣
Mat inliers;
if(_inliers.needed())
{
    _inliers.create(count, 1, CV_8U, -1, true);
    inliers = _inliers.getMat();
}

運(yùn)行RANSAC算法

  1. 然后注意到下面這一行代碼,創(chuàng)建了一個(gè)智能指針cb。
Ptr<PointSetRegistrator::Callback> cb = makePtr<AffinePartial2DEstimatorCallback>();

這一行有點(diǎn)麻煩,接下來(lái)追根溯源一下,這一行代碼中涉及兩個(gè)類型:PointSetRegistrator::CallbackAffinePartial2DEstimatorCallback。其中,PointSetRegistrator是一個(gè)繼承自Algorithm的虛基類,定義在/opencv/sources/modules/calib3d/src/precomp.hpp中。

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,具體實(shí)現(xiàn)如下

/*
 * 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,具體實(shí)現(xiàn)如下。

/*
 * 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);
    }
};

因此,原來(lái)AffinePartial2DEstimatorCallback也是自PointSetRegistrator::Callback的子類派生而來(lái)的。

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會(huì)作為參數(shù)傳入函數(shù)createRANSACPointSetRegistrator中,即若是以RANSAC算法解算變換矩陣,則進(jìn)入代碼。這一行會(huì)將所有的數(shù)據(jù)全部代入,包括:cb、ransacReprojThreshold、confidence、maxIters、from、to、H、inliers
result = createRANSACPointSetRegistrator(cb, 2, ransacReprojThreshold, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);

首先通過(guò)createRANSACPointSetRegistrator創(chuàng)建一個(gè)RANSAC點(diǎn)集的注冊(cè)器,其本質(zhì)就是創(chuàng)建一個(gè)指向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));
}

終于到了重要的一個(gè)類實(shí)現(xiàn):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;
};

因此,最終是通過(guò)RANSACPointSetRegistrator類指針調(diào)用run方法,從而篩選出數(shù)據(jù)的內(nèi)點(diǎn)。而我們需要學(xué)習(xí)的正是這個(gè)run方法,揭開它神秘的面紗。

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

首先分析一下參數(shù),_m1from_m2to,也就是求解fromto的變換矩陣,_model為變換矩陣H,_mask為內(nèi)點(diǎn)inliers

(1)數(shù)據(jù)準(zhǔn)備

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,直接通過(guò)cb調(diào)用runKernel。

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

(3)迭代計(jì)算文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-480846.html

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

根據(jù)內(nèi)點(diǎn)計(jì)算變換矩陣

  1. 再回到最初的estimateAffinePartial2D函數(shù)中,會(huì)發(fā)現(xiàn)后續(xù)正是利用內(nèi)點(diǎn)來(lái)計(jì)算二維仿射變換矩陣。即如下代碼
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];
    }
}

到了這里,關(guān)于OpenCV中關(guān)于二維仿射變換函數(shù)estimateAffinePartial2D的源碼分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【OpenCV ? c++】圖像幾何變換 | 圖像仿射變換

    【OpenCV ? c++】圖像幾何變換 | 圖像仿射變換

    ?? 個(gè)人簡(jiǎn)介:CSDN「 博客新星 」TOP 10 , C/C++ 領(lǐng)域新星創(chuàng)作者 ?? 作 ?? 者: 錫蘭_CC ?? ?? 專 ?? 欄: 【OpenCV ? c++】計(jì)算機(jī)視覺(jué) ?? 若有幫助,還請(qǐng) 關(guān)注?點(diǎn)贊?收藏 ,不行的話我再努努力??????

    2024年02月16日
    瀏覽(92)
  • 【OpenCV】-仿射變換

    【OpenCV】-仿射變換

    1、認(rèn)識(shí)仿射變換 仿射變換(Affine Map)又稱仿射映射,是指在幾何中,一個(gè)向量空間進(jìn)行一次線性變換并接上一個(gè)平移,變換為另一個(gè)向量空間的過(guò)程。保持二維圖形之間的相對(duì)位置保持不變,平行線依然是平行線,且直線上的點(diǎn)的位置順序不變。 一個(gè)任意的仿射變換都可以

    2024年02月08日
    瀏覽(21)
  • OpenCV(十一):圖像仿射變換

    OpenCV(十一):圖像仿射變換

    目錄 1.圖像仿射變換介紹 ?仿射變換: 仿射變換矩陣: 仿射變換公式: 2.仿射變換函數(shù) 仿射變換函數(shù):warpAffine() 圖像旋轉(zhuǎn):getRotationMatrix2D() 計(jì)算仿射變換矩陣:getAffineTransform()? 3.demo 1.圖像仿射變換介紹 ?仿射變換: ? ? ? ?仿射變換是由平移、縮放、旋轉(zhuǎn)、翻轉(zhuǎn)和錯(cuò)切組

    2024年02月10日
    瀏覽(90)
  • 仿射變換代碼opencv

    仿射變換代碼opencv

    順時(shí)針為正,左上角為默認(rèn)坐標(biāo)原點(diǎn) CV_INSTRUMENT_REGION() 是一個(gè)OpenCV宏,用于在代碼中進(jìn)行性能分析和測(cè)量。它可以用于標(biāo)記代碼的某個(gè)區(qū)域,以便在運(yùn)行時(shí)獲取該區(qū)域的性能指標(biāo)。(不太明白,先放一下) ?

    2024年01月17日
    瀏覽(31)
  • opencv仿射變換

    #include opencv2/opencv.hpp /* 功能:對(duì)一系列坐標(biāo)點(diǎn)進(jìn)行平移仿射變換 參數(shù): srcPoints:輸入點(diǎn)坐標(biāo) dstPoints:變換后的點(diǎn)坐標(biāo) x:x方向平移的距離 y:y方向平移的距離 */ void tranlatePoints(std::vectorcv::Point2f srcPoints, std::vectorcv::Point2f dstPoints,double x,double y) { ?? ?cv::Mat affineMatrix = (cv::Mat_double

    2024年01月16日
    瀏覽(22)
  • OpenCV庫(kù)進(jìn)行圖像旋轉(zhuǎn)、仿射變換和透視變換
  • 【C++ OpenCV】圖像變換:連接、尺寸、翻轉(zhuǎn)、旋轉(zhuǎn)、仿射變換

    【C++ OpenCV】圖像變換:連接、尺寸、翻轉(zhuǎn)、旋轉(zhuǎn)、仿射變換

    目錄 圖像縮放變換 圖像翻轉(zhuǎn) 圖像拼接 縱向拼接 橫向拼接 圖像插值原理 作用 單線性插值 雙線性插值的公式 雙線性插值的例子 雙線性插值的直觀展示 意義 仿射變換 圖像旋轉(zhuǎn) 實(shí)操 一、實(shí)現(xiàn)圖像旋轉(zhuǎn) 二、根據(jù)定義的三個(gè)點(diǎn)實(shí)現(xiàn)仿射變換,并且求取仿射變換矩陣 源碼 src -

    2024年01月18日
    瀏覽(1257)
  • 【OpenCV】圖像變換(縮放、平移、旋轉(zhuǎn)、仿射)

    圖像變換是指通過(guò)對(duì)圖像進(jìn)行縮放、平移、旋轉(zhuǎn)、仿射、透視等變換來(lái)改變圖像的形狀和大小。在本篇博客中,我們將詳細(xì)介紹OpenCV中的圖像變換函數(shù),并提供示例代碼以幫助讀者更好地理解這些函數(shù)的使用方法。 縮放變換是指通過(guò)改變圖像的大小來(lái)改變圖像的形狀。在Op

    2024年02月07日
    瀏覽(94)
  • OPENCV C++(六)canny邊緣檢測(cè)+仿射變換+透射變換

    圖像的縮放 ?輸入圖像 輸出圖像 大小變換 canny邊緣算子的使用 ?必須先轉(zhuǎn)化為灰度圖,作為輸入 超過(guò)100是真的邊緣 低于40是確定不是邊緣 在中間若連接邊緣 則為邊緣? 普通旋轉(zhuǎn)縮放變換(仿射變換) 獲取仿射變換的矩陣 中心點(diǎn) 旋轉(zhuǎn)角度 大小是否變換 -10是順時(shí)針轉(zhuǎn) 輸入

    2024年02月14日
    瀏覽(20)
  • OpenCV圖像的仿射變換、旋轉(zhuǎn)和縮放

    OpenCV圖像的仿射變換、旋轉(zhuǎn)和縮放

    以下是對(duì)代碼的逐行解釋:

    2024年02月13日
    瀏覽(86)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包