欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

在OpenCV中使用RANSAC

程序员文章站 2022-07-13 22:07:54
...

OpenCV中的[solvePnPRansac](https://github.com/opencv/opencv/blob/master/modules/calib3d/src/solvepnp.cpp)函数和[findHomography](https://github.com/opencv/opencv/blob/master/modules/calib3d/src/fundam.cpp)函数都具有RANSAC特性,该特性使算法对少量的错误数据鲁棒。这两个函数利用RANSACPointSetRegistrator类实现RANSAC算法,但这个类并没有对外开放,因此只能通过阅读OpenCV源代码学习RANSAC算法的实现和使用。
类的实现在[ptsetreg.cpp](https://github.com/opencv/opencv/blob/master/modules/calib3d/src/ptsetreg.cpp)中,可通过调用[precomp.hpp](https://github.com/opencv/opencv/blob/master/modules/flann/src/precomp.hpp)文件中的
createRANSACPointSetRegistrator函数使用。此外,该文件还提供了createLMeDSPointSetRegistrator“函数调用最小中值算法。
关于RANSAC的介绍详见这篇博客


createRANSACPointSetRegistrator函数的原型为

Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& cb,
        int modelPoints, double threshold,
        double confidence = 0.99, int maxIters = 1000);

参数cb类型为PointSetRegistrator::Callback,是要传入的被包装的估计算法对象,被RANSAC类调用以进行试探的估计过程;参数modelPoints 设定计算模型需要的最少数据点;参数threshold设定区分内点和外点的阈值;参数confidence设定返回正确值的概率(决定抽样次数);参数maxIters设定最大走样次数。后三个参数与solvePnPRansac函数中的意义相同。

其中PointSetRegistrator::Callback类的声明为

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

使用时,需要继承该类,并至少实现其中的前两个函数。
runKernel函数需要根据输入的数据集m1 m2,计算并输出模型model,函数返回输出模型的个数,一般都返回1。左右数据均按行排列。为什么这里有两个输入参数呢,因为OpenCV中的RANSAC算法最初设计是用来求解2D-3D变换的,所以这两个参数分别对应源数据点集和目标数据点集。即便某算法只需要一个参数m1,也必须传入同样大小的m2以避免运行错误。
computerError函数需要根据输入的数据集m1 m2,模型model,计算并输出各数据点的残差err。输出残差为列向量,每行对应一个输入数据点, 由于RANSACPointSetRegistratorerr内部的实现,err的类型必须为float否则出现指针错误
checkSubset(非必须)函数需要根据输入的数据,初步验证其是否为可行的子集。若不可行,返回false,这样可以提前剔除一些错误的子集。

由于OpenCV中并没有开放RANSACPointSetRegistrator的头文件,故需要自己编写以下头文件包含在工程中,方可使用createRANSACPointSetRegistrator函数

//cvRANSAC.h
#include <opencv2/core.hpp>
#include <opencv2/calib3d.hpp>

namespace cv
{
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;
    };

    CV_EXPORTS Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& cb,
        int modelPoints, double threshold,
        double confidence = 0.99, int maxIters = 1000);

    CV_EXPORTS Ptr<PointSetRegistrator> createLMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& cb,
        int modelPoints, double confidence = 0.99, int maxIters = 1000);
}

使用时,调用bool result = createRANSACPointSetRegistrator(cb, modelPoints, threshold, confidence, maxIters)->run(m1, m2, model, mask); 即可运行RANSAC算法,返回是否成功。


RANSACPointSetRegistrator类的实现

RANSACPointSetRegistrator类实现了RANSAC算法,主要由以下成员函数组成

//在给定数据集``m1 m2``中,利用随机数发生器``rng``,生成样本集``ms1 ms2``
bool getSubset(const Mat& m1, const Mat& m2, Mat& ms1, Mat& ms2, RNG& rng, int maxAttempts = 1000) const

//根据已拟合出的模型model,给定的thresh阈值,调用computeError函数计算残差err,并返回内外点掩码mask向量,返回内点的个数
int findInliers(const Mat& m1, const Mat& m2, const Mat& model, Mat& err, Mat& mask, double thresh) const

//根据期望正确概率和内点比例,确定迭代次数
int RANSACUpdateNumIters(double p, double ep, int modelPoints, int maxIters);

//运行RANSAC估计 先调用getSubset采样样本,然后调用runKernel执行拟合,调用findInliers判断内点数量,取最大,并调用RANSACUpdateNumIters更新采样次数。到达采样次数后,返回内点数最多的
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const
相关标签: 算法 OpenCV