opencv 特征点匹配:ORB

特征点匹配的流程:

ORB:Oriented FAST and Rotated BRIE

ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化:

  1. 使用非最大值抑制,在一定区域内仅仅保留响应极大值的角点,避免FAST提取到的角点过于集中。FAST提取到的角点数量过多且不是很稳定,ORB中可以指定需要提取到的角点的数量N,然后对FAST提取到的角点分别计算Harris响应值,选择前N个具有最大响应值的角点作为最终提取到的特征点集合。
  2. FAST提取到的角点不具有尺度信息,在ORB中使用图像金字塔,并且在每一层金字塔上检测角点,以此来保持尺度的不变性。
  3. FAST提取到的角点不具有方向信息,在ORB中使用灰度质心法(Intensity Centroid)来保持特征的旋转不变性。

其中:

(1)FAST Features From Accelerated Segment Test

以速度快而著称,检测局部像素灰度变化明显的地方,

FAST算法提取角点的步骤:

在图像中选择像素p,假设其灰度值为:Ip
设置一个阈值T,例如:Ip的20%
选择p周围半径为3的圆上的16个像素,作为比较像素
假设选取的圆上有连续的N个像素大于Ip+T或者Ip−T,那么可以认为像素p就是一个特征点。(N通常取12,即为FAST-12;常用的还有FAST-9,FAST-11)。


不可避免的也有一些缺点:检测到的特征点过多并且会出现“扎堆”的现象,这可以在第一遍检测完成后,使用非最大值抑制(Non-maximal suppression),在一定区域内仅保留响应极大值的角点,避免角点集中的情况。FAST提取到的角点没有方向和尺度信息。


上面的介绍的SIFT和SURF算法都包含有各自的特征点描述子的计算方法,而FAST不包含特征点描述子的计算,仅仅只有特征点的提取方法,这就需要一个特征点描述方法来描述FAST提取到的特征点,以方便特征点的匹配。下面介绍一个专门的特征点描述子的计算算法。

(2)BRIEF描述子

BRIEF是一种二进制的描述子,其描述向量是0和1表示的二进制串。0和1表示特征点邻域内两个像素(p和q)灰度值的大小:如果p比q大则选择1,反正就取0。在特征点的周围选择128对这样的p和q的像素对,就得到了128维由0,1组成的向量。那么p和q的像素对是怎么选择的呢?通常都是按照某种概率来随机的挑选像素对的位置。
BRIEF使用随机选点的比较,速度很快,而且使用二进制串表示最终生成的描述子向量,在存储以及用于匹配的比较时都是非常方便的,其和FAST的搭配起来可以组成非常快速的特征点提取和描述算法。
(3)ORB说明

问题1:FAST特征点的数量很多,并且不是确定,而大多数情况下,我们希望能够固定特征点的数量。 

解决方法:在ORB当中,我们可以指定要提取的特征点数量。对原始的FAST角点分别计算Harris的响应值,然后选取前N个点具有最大相应值的角点,作为最终角点的集合。 

问题2:FAST角点不具有方向信息和尺度问题。

解决方法:尺度不变性构建的图像的金字塔,并且从每一层上面来检测角点。旋转性是由灰度质心法实现。 

灰度质心法:质心是指以图像块灰度值作为权重的中心。(目标是为找找到方向)

1、在一个小的图像块B中,定义图像块的矩为: 

è¿éåå¾çæè¿°
 2、通过矩找到图像块的质心 

è¿éåå¾çæè¿°
3、连接图像块的几何中心o与质心C,得到一个oc的向量,把这个向量的方向定义特征点的方向 

è¿éåå¾çæè¿°

代码示例:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
 
int main()
{
	//0读取图像
	Mat img1 = imread("..\\image\\left.jpg",0);
	Mat img2 = imread("..\\image\\right.jpg",0);
 
	// 1 初始化特征点和描述子,ORB
	std::vector<KeyPoint> keypoints1, keypoints2;
	Mat descriptors1, descriptors2;
	Ptr<ORB> orb = ORB::create();
 
	// 2 提取 Oriented FAST 特征点
	orb->detect(img1, keypoints1);
	orb->detect(img2, keypoints2);
 
	// 3 根据角点位置计算 BRIEF 描述子
	orb->compute(img1, keypoints1, descriptors1);
	orb->compute(img2, keypoints2, descriptors2);
 
	// 4 对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
	vector<DMatch> matches;
	BFMatcher bfmatcher(NORM_HAMMING);
	bfmatcher.match(descriptors1, descriptors2, matches);
 
	// 5 匹配对筛选
	double min_dist = 1000, max_dist = 0;
	// 找出所有匹配之间的最大值和最小值
	for (int i = 0; i < descriptors1.rows; i++)
	{
		double dist = matches[i].distance;//汉明距离在matches中
		if (dist < min_dist) min_dist = dist;
		if (dist > max_dist) max_dist = dist;
	}
	// 当描述子之间的匹配大于2倍的最小距离时,即认为该匹配是一个错误的匹配。
	// 但有时描述子之间的最小距离非常小,可以设置一个经验值作为下限
	vector<DMatch> good_matches;
	for (int i = 0; i < descriptors1.rows; i++)
	{
		if (matches[i].distance <= max(2 * min_dist, 30.0))
			good_matches.push_back(matches[i]);
	}
 
	// 6 绘制匹配结果
	Mat img_match;
	drawMatches(img1, keypoints1, img2, keypoints2, good_matches, img_match);
 
	return 0;
}
发布了210 篇原创文章 · 获赞 105 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_30263737/article/details/94631326