欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《视觉SLAM十四讲精品总结》5:VO——ORB特征提取和匹配

程序员文章站 2022-06-11 15:39:04
...

一、简介

视觉里程计法:提取、匹配图像特征点,然后估计两帧之间的相机运动,给后端提供较好的初始值。

角点的局限:例如:从远处上看上去是角点的地方,当相机走近后,可能就是角点了。或者,当旋转相机的时候,角点的外观会发生变换。 
进而,我们提出了SIFT,SURF,ORB特征。 

特征点由提取关键点和计算描述子两部分组成。

关键点:该特征点在图像的位置,具有朝向、大小的信息。

描述子:描述关键点周围像素信息的向量。

SIFT(尺度不变特征变换—Scale-Invariant Feature Transform):充分考虑图像变换过程中光照、尺度、旋转等变换,但计算量大。

FAST:只有关键点没有描述子,降低精度和健壮性,提升计算速度。

ORB:(Oriented FAST and Rotated BRIEF):改进了FAST 不具有方向性的问题,添加了尺度和旋转的特性。采用速度极快的二进制描述子BRIEF,

二、ORB特征(Oriented FAST and Rotated BRIEF)

两步骤:FAST角点提取+BRIEF描述子

1、FAST角点:

主要检测局部像素灰度变化明显的地方。思想:如果一个像素与邻域像素差别较大(过亮或者过暗),只需比较像素亮度大小。

1、在图像中选取像素p,假设他的亮度是IP 
2、设置一个阈值T,例如T=IP*20% 
3、以像素p为中心,选取半径为3的圆上的16个像素点, 这里的3应该是三个像素框 
4、如果在选取的圆上面,有连续N个点的亮度值大于IP+T(1.2*IP))或者小于IP-T(0.8*IP),那么像素p通常可以被认为是特征点(N通常取12,FAST-12,通常n取9和11,的时候,称为FAST-9和FAST-11) 

FAST-12算法:

添加预测试操作,于每个像素,直接检测在邻域圆上的第1,5,9,13个像素的亮度,只有当这四个像素当中有三个同时大于IP+T或者小于IP-T的时候,当前像素才有可能是是角点。

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

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

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

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

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

1、在一个小的图像块B中,定义图像块的矩为: 
《视觉SLAM十四讲精品总结》5:VO——ORB特征提取和匹配 
2、通过矩找到图像块的质心 
《视觉SLAM十四讲精品总结》5:VO——ORB特征提取和匹配 
3、连接图像块的几何中心o与质心C,得到一个oc的向量,把这个向量的方向定义特征点的方向 
《视觉SLAM十四讲精品总结》5:VO——ORB特征提取和匹配

2、BRIEF描述子

BRIEF是二进制描述子,它的描述向量是由许多个0和1组成,这里的0和1编码了关键点附近的两个像素p和q的大小关系,如果p>q,则取1 ;否则取0;p q 的挑选为随机选点。

 

三、特征匹配

通过对图像与地图之间的描述子进行准确匹配,为后续的姿态估计优化等操作减轻负担。

暴力匹配:对两帧图像中每一个特征点x(t)与所有的特征点x(t+1)测量描述子的距离,然后排序,取最近的一个作为匹配点。

描述子距离表明了两个特征之间的相似距离。

二进制描述子,使用汉明距离,指的是不同位数的个数。

快速近似最邻近算法(FLANN):适合匹配点数量多情况。

 

1、OpenCV中封装了常用的特征点算法(如SIFT,SURF,ORB等),提供了统一的接口,便于调用。

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	//0读取图像
	Mat img1 = imread("1.png");
	Mat img2 = imread("2.png");
	// 1 初始化特征点和描述子,ORB
	std::vector<KeyPoint> keypoints1, keypoints2;
	Mat descriptors1, descriptors2;
	Ptr<ORB> orb = ORB::create();
	// 2 提取 Oriented FAST 特征点
	orb->detect(img1, keypoints1);
	orb->detect(img1, keypoints1);
	// 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 绘制匹配结果
	Mat img_match;
	drawMatches(img1, keypoints1, img2, keypoints2, matches, img_match);
	imshow("所有匹配点对", img_match);
	waitKey(0);
	return 0;
}

得到了描述子后,可调用匹配算法进行特征点的匹配。上面代码中,使用了opencv中封装后的暴力匹配算法BFMatcher,该算法在向量空间中,将特征点的描述子一一比较,选择距离(上面代码中使用的是Hamming距离)较小的一对作为匹配点。

KeyPoint类型(比Point多一些数据)

class KeyPoint
{
Point2f  pt;  //特征点坐标
float  size; //特征点邻域直径
float  angle; //特征点的方向,值为0~360,负值表示不使用
float  response; //特征点的响应强度,代表了该点是特征点的程度,可以用于后续处理中特征点排序
int  octave; //特征点所在的图像金字塔的组
int  class_id; //用于聚类的id
}

DMatch类存放匹配结果

struct DMatch
{       
          int queryIdx;  //此匹配对应的查询图像的特征描述子索引
          int trainIdx;   //此匹配对应的训练(模板)图像的特征描述子索引
          int imgIdx;    //训练图像的索引(若有多个)
          float distance;  //两个特征向量之间的欧氏距离,越小表明匹配度越高。
          bool operator < (const DMatch &m) const;
};

匹配函数match (matches中保存着描述子之间的匹配关系)

	vector<DMatch> matches;
	BFMatcher bfmatcher(NORM_HAMMING);
	bfmatcher.match(descriptors1, descriptors2, matches);

画有特征点的图像drawKeypoints

drawKeypoints(img1,keypoints1,outimg1,Scalar::all(-1),DrawMatchesFlags::DEFAULT);

画匹配图像drawMatches

drawMatches(img1, keypoints1, img2, keypoints2, matches, img_match);

 img_match为目标图像。

《视觉SLAM十四讲精品总结》5:VO——ORB特征提取和匹配

2、筛选:汉明距离小于最小距离的两倍
选择已经匹配的点对的汉明距离小于最小距离的两倍作为判断依据,如果小于该值则认为是一个错误的匹配,过滤掉;大于该值则认为是一个正确的匹配。

    // 匹配对筛选
    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]);
    }

《视觉SLAM十四讲精品总结》5:VO——ORB特征提取和匹配