RANSAC在图像匹配上应用

前言:

1、如何对匹配好的点做进一步的处理,更好保证匹配效果

答:1、汉明距离应小于最小距离两倍(二进制的描述子,不同位数的个数为汉明距离)  2、RANSAC消除误匹配

2、RANSAC与最小二乘区别:

最小二乘法尽量去适应包括局外点在内的所有点。相反,RANSAC能得出一个仅仅用局内点计算出模型,并且概率还足够高。但是,RANSAC并不能保证结果一定正确,为了保证算法有足够高的合理概率,必须小心的选择算法的参数(参数配置)。经实验验证,对于包含80%误差的数据集,RANSAC的效果远优于直接的最小二乘法。

3、介绍RANSAC算法

基本假设是样本中包含正确数据(inliers),也包含异常数据(outliers)。根据一组正确的数据,可以计算出符合这些数据的模型参数的方法。

优点:

能鲁棒的估计模型参数。

缺点:1、计算参数的迭代次数没有上限,如果设置迭代次数的上限,得到的结果可能不是最优。

           2、 RANSAC只有一定概率得到可信的模型,概率与迭代次数成正比。


简介

RANSAC是“RANdom SAmple Consensus(随机抽样一致)”的缩写。它可以从一组包含“局外点”的观测数据集中,通过迭代方式估计数学模型的参数。它是一种不确定的算法——它有一定的概率得出一个合理的结果;为了提高概率必须提高迭代次数

RANSAC的基本假设是: 
(1)数据由“局内点”组成,例如:数据的分布可以用一些模型参数来解释; 
(2)“局外点”是不能适应该模型的数据; 
(3)除此之外的数据属于噪声。 
局外点产生的原因有:噪声的极值;错误的测量方法;对数据的错误假设。 


RANSAC也做了以下假设:给定一组(通常很小的)局内点,存在一个可以估计模型参数的过程;而该模型能够解释或者适用于局内点。

示例 


一个简单的例子是从一组观测数据中找出合适的2维直线。假设观测数据中包含局内点和局外点,其中局内点近似的被直线所通过,而局外点远离于直线。简单的最小二乘法不能找到适应于局内点的直线,原因是最小二乘法尽量去适应包括局外点在内的所有点。相反,RANSAC能得出一个仅仅用局内点计算出模型,并且概率还足够高。但是,RANSAC并不能保证结果一定正确,为了保证算法有足够高的合理概率,我们必须小心的选择算法的参数。 

è¿éåå¾çæè¿°

1.RANSAC原理  

  OpenCV中滤除误匹配对采用RANSAC算法寻找一个最佳单应性矩阵H,矩阵大小为3×3。RANSAC目的是找到最优的参数矩阵使得满足该矩阵的数据点个数最多,通常令h3=1来归一化矩阵。由于单应性矩阵有8个未知参数,至少需要8个线性方程求解,对应到点位置信息上,一组点对可以列出两个方程,则至少包含4组匹配点对。

                                                                      其中(x,y)表示目标图像角点位置,(x',y')为场景图像角点位置,s为尺度参数。

  RANSAC算法从匹配数据集中随机抽出4个样本并保证这4个样本之间不共线,计算出单应性矩阵,然后利用这个模型测试所有数据,并计算满足这个模型数据点的个数与投影误差(即代价函数),若此模型为最优模型,则对应的代价函数最小

2、RANSAC算法步骤: 

          1. 随机从数据集中随机抽出4个样本数据 (此4个样本之间不能共线),计算出单应矩阵H,记为模型M;

          2. 计算数据集中所有数据与模型M的投影误差,若误差小于阈值,加入内点集 I ;

          3. 如果当前内点集 I 元素个数大于最优内点集 I_best , 则更新 I_best = I,同时更新迭代次数k ;

          4. 如果迭代次数大于k,则退出 ; 否则迭代次数加1,并重复上述步骤;

  注:迭代次数k在不大于最大迭代次数的情况下,是在不断更新而不是固定的;

                                       其中,p为置信度,一般取0.995;w为"内点"的比例 ; m为计算模型所需要的最少样本数=4;

3、程序注释

OpenCV中此功能通过调用findHomography函数调用,下面是个例程:

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
	Mat obj = imread("1.jpg");   //载入目标图像
	Mat scene = imread("2.jpg"); //载入场景图像
	if (obj.empty() || scene.empty())
	{
		cout << "Can't open the picture!\n";
		return 0;
	}
	//滤除误匹配前
	vector<KeyPoint> obj_keypoints, scene_keypoints;
	Mat obj_descriptors, scene_descriptors;
	Ptr<ORB> detector = ORB::create();//采用ORB算法提取特征点
	detector->detect(obj, obj_keypoints);
	detector->detect(scene, scene_keypoints);
	detector->compute(obj, obj_keypoints, obj_descriptors);
	detector->compute(scene, scene_keypoints, scene_descriptors);
	BFMatcher matcher(NORM_HAMMING, true); //汉明距离做为相似度度量
	vector<DMatch> matches;
	matcher.match(obj_descriptors, scene_descriptors, matches);
	Mat match_img;
	drawMatches(obj, obj_keypoints, scene, scene_keypoints, matches, match_img);
	imshow("滤除误匹配前", match_img);

	//匹配点转为Point2f像素坐标形式保存到points中
	vector<int> queryIdxs(matches.size()), trainIdxs(matches.size());
	for (size_t i = 0; i < matches.size(); i++)
	{
		queryIdxs[i] = matches[i].queryIdx;
		trainIdxs[i] = matches[i].trainIdx;
	}
	vector<Point2f> points1; KeyPoint::convert(obj_keypoints, points1, queryIdxs);
	vector<Point2f> points2; KeyPoint::convert(scene_keypoints, points2, trainIdxs);
	int ransacReprojThreshold = 5;  //拒绝阈值

	Mat H12;   //单应性矩阵H
	H12 = findHomography(Mat(points1), Mat(points2), CV_RANSAC, ransacReprojThreshold);
	vector<char> matchesMask(matches.size(), 0);
	Mat points1t;
	perspectiveTransform(Mat(points1), points1t, H12);//透视变换,points1t为变换后的点
	for (size_t i1 = 0; i1 < points1.size(); i1++)  //保存‘内点’
	{
		if (norm(points2[i1] - points1t.at<Point2f>((int)i1, 0)) <= ransacReprojThreshold) //给内点做标记
		{
			matchesMask[i1] = 1;
		}
	}
	Mat match_img2;   //滤除‘外点’后
	drawMatches(obj, obj_keypoints, scene, scene_keypoints, matches, match_img2, Scalar(0, 0, 255), Scalar::all(-1), matchesMask);

	//画出目标位置
	std::vector<Point2f> obj_corners(4);//初始化为整个目标图像的四个角
	obj_corners[0] = cvPoint(0, 0); obj_corners[1] = cvPoint(obj.cols, 0);
	obj_corners[2] = cvPoint(obj.cols, obj.rows); obj_corners[3] = cvPoint(0, obj.rows);
	std::vector<Point2f> scene_corners(4);
	perspectiveTransform(obj_corners, scene_corners, H12);//透视变换
	line(match_img2, scene_corners[0] + Point2f(static_cast<float>(obj.cols), 0),
		scene_corners[1] + Point2f(static_cast<float>(obj.cols), 0), Scalar(0, 0, 255), 2);
	line(match_img2, scene_corners[1] + Point2f(static_cast<float>(obj.cols), 0),
		scene_corners[2] + Point2f(static_cast<float>(obj.cols), 0), Scalar(0, 0, 255), 2);
	line(match_img2, scene_corners[2] + Point2f(static_cast<float>(obj.cols), 0),
		scene_corners[3] + Point2f(static_cast<float>(obj.cols), 0), Scalar(0, 0, 255), 2);
	line(match_img2, scene_corners[3] + Point2f(static_cast<float>(obj.cols), 0),
		scene_corners[0] + Point2f(static_cast<float>(obj.cols), 0), Scalar(0, 0, 255), 2);

	imshow("滤除误匹配后", match_img2);
	waitKey(0);

	return 0;
}

主要函数为:

//计算单应性矩阵H
FindHomography(src_points, dst_points,  homography, int method=0, double ransacReprojThreshold=3, CvMat* mask=0 )
//透视变换
perspectiveTransform(InputArray src,OutputArray dst,InputArray 	m )		

findHomography内部调用cvFindHomography函数

4、应用

RANSAC可以用于哪些场景呢?最著名的莫过于图片的拼接技术。优于镜头的限制,往往需要多张照片才能拍下那种巨幅的风景。在多幅图像合成时,事先会在待合成的图片中提取一些关键的特征点。计算机视觉的研究表明,不同视角下物体往往可以通过一个透视矩(3X3或2X2)阵的变换而得到。RANSAC被用于拟合这个模型的参数(矩阵各行列的值),由此便可识别出不同照片中的同一物体。可参考下图: 

RANSAC算法详解

RANSACç®æ³è¯¦è§£

优点与缺点:


RANSAC的优点是它能鲁棒的估计模型参数。例如,它能从包含大量局外点的数据集中估计出高精度的参数。RANSAC的缺点是它计算参数的迭代次数没有上限;如果设置迭代次数的上限,得到的结果可能不是最优的结果,甚至可能得到错误的结果。RANSAC只有一定的概率得到可信的模型,概率与迭代次数成正比。RANSAC的另一个缺点是它要求设置跟问题相关的阀值
RANSAC只能从特定的数据集中估计出一个模型,如果存在两个(或多个)模型,RANSAC不能找到别的模型。


重点参考:

1、RANSAC算法理解

2、【特征匹配】RANSAC算法原理与源码解析

3、https://www.cnblogs.com/dverdon/p/4579544.html

4、https://blog.csdn.net/u010128736/article/details/53422070

猜你喜欢

转载自blog.csdn.net/try_again_later/article/details/88666915