一、 概述视觉里程计
视觉里程计work流程:
视觉里程计步骤:
- 找到图像关联点
- 估计相机位姿
- 如需要增加地图
二、 特征点法
(一)特征点
特征点组成:
图像特征:
人工设计特征点拥有性质:
- 可重复性(Repeatability):相同的“区域”可以在不同的图像中被找到。
- 可区别性(Distinctiveness):不同的“区域”有不同的表达。
- 高效率(Efficiency):同一图像中,特征点的数量应远小于像素的数量。
- 本地性(Locality):特征仅与一小片图像区域相关。
(二)ORB特征
简介:
ORB 特征亦由关键点和描述子两部分组成。它的关键点称为“Oriented FAST”,是
一种改进的 FAST(Features from Accelerated Segments Test) 角点。它的描述子称为 BRIEF(Binary Robust Independent Elementary Features)。ORB特征提取分为两个步骤:
- FAST角点提取:找出图像中的” 角点”。相较于原版的 FAST, ORB 中计算了特征点的主方向,为后续的 BRIEF 描述子增加了旋转不变特性。
- BRIEF描述子:对前一步提取出特征点的周围图像区域进行描述。
FAST算法
算法步骤:
- 在图像中选取像素 p,假设它的亮度为 Ip。
- 设置一个阈值 T(比如 Ip 的 20%)。
- 以像素 p 为中心, 选取半径为 3 的圆上的 16 个像素点。
- 假如选取的圆上,有连续的 N 个点的亮度大于 Ip + T 或小于 Ip -T,那么像素 p 可以被认为是特征点 (N 通常取 12,即为 FAST-12。其它常用的 N 取值为 9 和 11,他们分别被称为 FAST-9,FAST-11)。
- 循环以上四步,对每一个像素执行相同的操作。
FAST-12 算法,仅将 p 与圆圈中的 4 个等距像素相比。这种方法已经证明和比较 16 个周围像素的效果相同。如果至少有一对连续像素的亮度高于或低于 p,则将 p 选作关键点。这种优化使得在整个图像中搜索关键点的时间缩短了四倍。
可以看出关键点位于亮度有变化的区域,此类区域通常确定了某种边缘,例如猫的爪子。边缘定义了猫的界限,以及脸部区域的界限,因此这些关键点使我们能够识别这只猫,而不是图像中的任何其他对象或背景。
原始的FAST角点经常出现“扎堆”现象,需要采取非极大值抑制(Non-maximal suppresion),在一定区域内保留了响应的极大值的角点,避免角点集中问题。
FAST 角点不具有方向信息。而且,由于它固定取半径为 3 的圆,存在尺度问题:远处看着像是角点的地方,接近后看可能就不是角点了。针对 FAST 角点不具有方向性和尺度的弱点,ORB 添加了尺度和旋转的描述。尺度不变性由构建图像金字塔,并在金字塔的每一层上检测角点来实现。而特征的旋转是由灰度质心法(Intensity Centroid)实现的,所谓之心是指以图像块灰度值作为权重的中心。
图像金字塔是单个图像的多尺度表示法,由一系列原始图像的不同分辨率版本组成。金字塔的每个级别都由上个级别的图像下采样版本组成。下采样是指图像分辨率被降低,比如图像按照 1/2 比例下采样。因此一开始的 4x4 正方形区域现在变成 2x2 正方形。图像的下采样包含更少的像素,并且以 1/2 的比例降低大小。
ORB 创建好图像金字塔后,它会使用 FAST 算法从每个级别不同大小的图像中快速找到关键点。因为金字塔的每个级别由原始图像的更小版本组成,因此原始图像中的任何对象在金字塔的每个级别也会降低大小。
通过确定每个级别的关键点 ORB 能够有效发现不同尺寸的对象的关键点,这样的话 ORB 实现了部分缩放不变性。这一点很重要,因为对象不太可能在每个图像中的大小都完全一样,尤其是像猫这样的对象某个时刻可能靠近相机,在另一个时刻离相机很远。
灰度质心法假设角点的灰度与质心之间存在一个偏移,这个向量可以用于表示一个方向。
BRIEF描述子
BRIEF 是 Binary Robust Independent Elementary Features 的简称,它的作用是根据一组关键点创建二元特征向量。二元特征向量又称为二元描述符,是仅包含 1 和 0 的特征向量。在 BRIEF 中每个关键点由一个二元特征向量描述,该向量一般为 128-512 位的字符串,其中仅包含 1 和 0。
BRIEF算法的核心思想是在关键点P的周围以一定模式选取N个点对,把这N个点对的比较结果组合起来作为描述子。
(三) 特征匹配
提取特征,进行匹配,为下一步估计相机运动做准备。
实践代码:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main ( int argc, char** argv )
{
if ( argc != 3 )
{
cout<<"usage: feature_extraction img1 img2"<<endl;
return 1;
}
//-- 读取图像
Mat img_1 = imread ( argv[1], CV_LOAD_IMAGE_COLOR );
Mat img_2 = imread ( argv[2], CV_LOAD_IMAGE_COLOR );
//-- 初始化
std::vector<KeyPoint> keypoints_1, keypoints_2;
Mat descriptors_1, descriptors_2;
Ptr<FeatureDetector> detector = ORB::create();
Ptr<DescriptorExtractor> descriptor = ORB::create();
// Ptr<FeatureDetector> detector = FeatureDetector::create(detector_name);
// Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create(descriptor_name);
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create ( "BruteForce-Hamming" );
//-- 第一步:检测 Oriented FAST 角点位置
detector->detect ( img_1,keypoints_1 );
detector->detect ( img_2,keypoints_2 );
//-- 第二步:根据角点位置计算 BRIEF 描述子
descriptor->compute ( img_1, keypoints_1, descriptors_1 );
descriptor->compute ( img_2, keypoints_2, descriptors_2 );
Mat outimg1;
drawKeypoints( img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT );
imshow("ORB特征点",outimg1);
//-- 第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
vector<DMatch> matches;
//BFMatcher matcher ( NORM_HAMMING );
matcher->match ( descriptors_1, descriptors_2, matches );
//-- 第四步:匹配点对筛选
double min_dist=10000, max_dist=0;
//找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离
for ( int i = 0; i < descriptors_1.rows; i++ )
{
double dist = matches[i].distance;
if ( dist < min_dist ) min_dist = dist;
if ( dist > max_dist ) max_dist = dist;
}
// 仅供娱乐的写法
min_dist = min_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;
max_dist = max_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;
printf ( "-- Max dist : %f \n", max_dist );
printf ( "-- Min dist : %f \n", min_dist );
//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
std::vector< DMatch > good_matches;
for ( int i = 0; i < descriptors_1.rows; i++ )
{
if ( matches[i].distance <= max ( 2*min_dist, 30.0 ) )
{
good_matches.push_back ( matches[i] );
}
}
//-- 第五步:绘制匹配结果
Mat img_match;
Mat img_goodmatch;
drawMatches ( img_1, keypoints_1, img_2, keypoints_2, matches, img_match );
drawMatches ( img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch );
imshow ( "所有匹配点对", img_match );
imshow ( "优化后匹配点对", img_goodmatch );
waitKey(0);
return 0;
}
结果展示:
参考:
高翔-视觉SLAM14讲
https://www.cnblogs.com/zjiaxing/p/5616653.html
https://www.cnblogs.com/alexme/p/11345701.html