在OpenCV中使用RANSAC
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算法的实现和使用。
createRANSACPointSetRegistrator
类的实现在[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)文件中的函数使用。此外,该文件还提供了
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
上一篇: 在ROS中使用OpenCV
下一篇: 在ROS中使用OpenCV
推荐阅读
-
关于在vscode使用webpack指令显示"因为在此系统中禁止运行脚本"问题(完美解决)
-
在vue中,v-for的索引index在html中的使用方法
-
Word2000和Word2002中的宏在Office Word 2003中的使用
-
在Word2010中使用“撤销键入”或“恢复键入”功能
-
在MySQL中自定义参数的使用详解
-
clipboard在vue中的使用的方法示例
-
Winform中在使用Dock属性设计页面布局控件的顺序导致页面效果不同的问题
-
Linux中在不破坏磁盘的情况下使用dd命令
-
使用Python在Excel中嵌入附件(txt文件,zip压缩包)对象
-
OpenCV中的新函数connectedComponentsWithStats使用(python和c++实例)