一、简介
视觉里程计法:提取、匹配图像特征点,然后估计两帧之间的相机运动,给后端提供较好的初始值。
角点的局限:例如:从远处上看上去是角点的地方,当相机走近后,可能就是角点了。或者,当旋转相机的时候,角点的外观会发生变换。
进而,我们提出了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中,定义图像块的矩为:
2、通过矩找到图像块的质心
3、连接图像块的几何中心o与质心C,得到一个oc的向量,把这个向量的方向定义特征点的方向
2、BRIEF描述子
BRIEF是二进制描述子,它的描述向量是由许多个0和1组成,这里的0和1编码了关键点附近的两个像素p和q的大小关系,如果p>q,则取1 ;否则取0;p q 的挑选为随机选点。
三、特征匹配
通过对图像与地图之间的描述子进行准确匹配,为后续的姿态估计优化等操作减轻负担。
暴力匹配:对两帧图像中每一个特征点x(t)与所有的特征点x(t+1)测量描述子的距离,然后排序,取最近的一个作为匹配点。
描述子距离表明了两个特征之间的相似距离。
二进制描述子,使用汉明距离,指的是不同位数的个数。
快速近似最邻近算法(FLANN):适合匹配点数量多情况。
1、计算特征点和描述子
if (argc != 3)
{
cout << "usage: feature_extraction img1 img2" << endl;
return 1;
}
//1. 读取图像
Mat img_1 = imread(argv[1], CV_LOAD_IMAGE_COLOR);
Mat img_2 = imread(argv[2], CV_LOAD_IMAGE_COLOR);
//1.1 初始化特征点和描述子和汉明距离
std::vector<KeyPoint> keypoints_1, keypoints_2;//???特征点向量KeyPoint
Mat descriptors_1, descriptors_2;//描述子用Mat存储
Ptr<FeatureDetector> detector = ORB::create();//???Ptr模板类的FeatureDector类型
Ptr<DescriptorExtractor> descriptor = ORB::create();//???ORB::create()
// Ptr<FeatureDetector> detector = FeatureDetector::create(detector_name);
// Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create(descriptor_name);
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
//1.2. 检测 Oriented FAST 特征点位置
detector->detect(img_1, keypoints_1);//???detect函数
detector->detect(img_2, keypoints_2);
//1.3. 根据角点位置计算 BRIEF 描述子
descriptor->compute(img_1, keypoints_1, descriptors_1);// ???computer函数
descriptor->compute(img_2, keypoints_2, descriptors_2);
//1.4. huigui模块,画出特征点????
Mat outimg1;
drawKeypoints(img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
imshow("ORB特征点", outimg1);
2、对BRIEF描述子进行匹配
//2. 对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
vector<DMatch> matches;//???Dmatch类型
//BFMatcher matcher ( NORM_HAMMING );
matcher->match(descriptors_1, descriptors_2, matches);//???match函数
//2.1 匹配点对筛选,最相似和最不相似的两组点之间的距离
double min_dist = 10000, max_dist = 0;
//2.2 迭代,找出所有匹配之间的最小距离和最大距离
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;
}
//2.3 输出最大距离和最小距离
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);
3、匹配结果
//3 匹配结果
//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.
//但有时候最小距离会非常小,设置一个经验值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]);
}
}
//3.1 绘制匹配结果
Mat img_match;
Mat img_goodmatch;
//???drawMatches函数
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);