三维重构学习笔记(4):坚实的后盾OpenCV(ORB)

ORB特征提取、匹配

ORB(Oriented FAST and Rotated BRIEF)是一种局部不变特征,从名字可以看出ORB具有FAST和旋转不变的特性。相比于经典的SIFT、SURF,ORB具备了实时使用能力。我的三维重构任务对于实时性具有较高的要求,且三维重构的最为重要的任务就是如何找到对应的特征点,所以ORB如何应用值得好好研究一下。
ORB的介绍看这里,对于ORB的介绍我不再进行过多的说明,本博客主要看看OpenCV中与ORB相关的库函数是如何应用的。

ORB特征的提取

已知对于ORB特征的描述主要包括两个部分:特征点(keypoint),描述(descriptor)。一般定义为:

vector<KeyPoint> key_points;
Mat descriptor;

而目前我见到的创建ORB的特征对象的方式有三种,如:

Ptr<ORB> orb = ORB::create();

或者:

Ptr<FeatureDetector> orb1 = ORB::create();
Ptr<DescriptorExtractor> orb2 = ORB::create();

上述三种声明的意义是一样的,无论是FeatureDetector还是DescriptorExtractor都是类Feature2D别名。而类ORB是类Feature2D的公有继承。具体OpenCV中的定义如下:

typedef Feature2D FeatureDetector;
typedef Feature2D DescriptorExtractor;
class CV_EXPORTS_W ORB : public Feature2D{}

所以无论哪种声明本质上都是一样的,可能ORB类中存在别的函数实现。类的别名主要是为了开发方便。

声明了Feature2D的对象之后,就可以直接调用detectcompute或者detectAndCompute进行计算“重要的” keypoint descriptor了。
计算方式有两种:

//分开计算
orb->detect(img, key_points);
orb->compute(img, key_points, descriptor);
//一起计算
orb->detectAndCompute(img, noArray(),key_points, descriptor);

至此ORB特征提取完毕。

ORB特征匹配

匹配器matcher声明与ORB特征对象声明一样也存在多种声明方式如:

Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");

或者:

BFMatcher matcher(NORM_L2);

在OpenCV中我们发现BFMatcherDescriptorMatcher的公有继承:

class CV_EXPORTS_W BFMatcher : public DescriptorMatcher{}

这样上述不同的matcher声明就容易理解了。

匹配器matcher是用于计算特征点的匹配情况的,而计算的结果通常保存在vector<DMatch> matches中。
匹配函数的调用如下:

matcher->match(descriptor1, descriptor2, matches);

其中descriptor1, descriptor2分别代表两张不同的图片的特征点(descriptor用于描述特征点),matches中保存匹配信息。
此时matches中保存的匹配点中存在匹配不准确的情况,为了提高匹配的精度,通常需要对这些匹配点进行筛选。筛选的方式多种多样,但基本思想都是保留distance满足一定条件的点对,例如
找出匹配点之间的最大距离和最小距离,也就是匹配最相似和最不相似的点对。选择一个点对的距离阈值对点对进行筛选。

double min_dist = 10000, max_dist = 0;
//查找距离最小和距离最大的点
for (int i = 0; i < query.rows; i++)
{
	double dist = matches[i].distance;
	if (dist < min_dist) min_dist = dist;
	if (dist > max_dist) max_dist = dist;
}

std::vector< DMatch > good_matches;
//对匹配的点对进行筛选
for (int i = 0; i < query.rows; i++)
{
	if (matches[i].distance <= max(2 * min_dist, 30.0))
	{
		good_matches.push_back(matches[i]);
	}
}
good_matches.swap(matches);

或者:
对在knnMatch中得到的knn_matches的筛选时,除了distance不能过大,还需要考虑Ratio test(KNN为特征点保留了两个待选匹配点,第一匹配点与第二匹配点的比,越接近1,匹配点越模糊,则被排除)

	vector<vector<DMatch>> knn_matches;
	BFMatcher matcher(NORM_L2);
	matcher.knnMatch(query, train, knn_matches, 2);

	//获取满足Ratio Test的最小匹配的距离
	float min_dist = FLT_MAX;
	for (int r = 0; r < knn_matches.size(); ++r)
	{
		//Ratio Test
		if (knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance)
			continue;

		float dist = knn_matches[r][0].distance;
		if (dist < min_dist) min_dist = dist;
	}

	matches.clear();
	for (size_t r = 0; r < knn_matches.size(); ++r)
	{
		//排除不满足Ratio Test的点和匹配距离过大的点
		if (
			knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance ||
			knn_matches[r][0].distance > 5 * max(min_dist, 10.0f)
			)
			continue;

		//保存匹配点
		matches.push_back(knn_matches[r][0]);
	}

对匹配点进行筛选之后,就解决了三维重构中最为棘手的问题。

tips:
在匹配ORB特征时,我发现网上一般使用的筛选方式都是基于距离的。从我的仿真实验中发现,由于ORB检测到的匹配点较少,使用距离筛选ORB匹配点较为合理,因为使用KNN的方式排除的特征点对较多,导致我的匹配结果无法正常使用SFM三维重构。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值