在OpenCV中使用RANSAC

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


createRANSACPointSetRegistrator函数的原型为

Ptr createRANSACPointSetRegistrator(const Ptr& 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 
#include 

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& cb) = 0;
		virtual bool run(InputArray m1, InputArray m2, OutputArray model, OutputArray mask) const = 0;
	};

	CV_EXPORTS Ptr createRANSACPointSetRegistrator(const Ptr& cb,
		int modelPoints, double threshold,
		double confidence = 0.99, int maxIters = 1000);

	CV_EXPORTS Ptr createLMeDSPointSetRegistrator(const Ptr& 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

你可能感兴趣的:(图像处理与识别,数据结构与算法)