GMS一种基于运动统计的快速鲁棒特征匹配过滤算法,能明显地改善匹配结果,目前已经集成进入OpenCV之中
1. 文章及代码地址
项目地址:GMS: Fast and Robust Feature Matcher (CVPR 17 & IJCV 20) – Jia-Wang Bian
论文** GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence**
代码地址 GitHub - JiawangBian/GMS-Feature-Matcher: GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence (CVPR 17 & IJCV 20)
2. 创新点
- 将运动平滑约束转换为剔除错误匹配的统计量,实验证明该算法能够应对较为棘手的场景;
- 提出了一种高效的基于网格的得分估计器,使得该算法能够用于实时特征匹配;
- 能够取得比Lowe Ratio更好的特征匹配筛选效果,该结论已经在传统特征如SIFT,SURF以及CNN特征如LIFT上得到验证;
3. 主要框架以及关键技术点
首先给出第一个假设:
运动平滑性:真实匹配的小邻域内匹配通常是在三维空间中的同一块区域。同样地,一个错误匹配的邻域内的匹配通常是几何上不同的三维位置。
这个假设告诉我们:正确匹配的邻域内有多个支持它的匹配,而错误匹配的邻域内支持它的匹配是很少的。这里其实隐含着一个逻辑:作者通过观察发现,正确匹配的邻域内匹配的数量多的概率就会很大,反之则少;那么根据贝叶斯法则,那些匹配对邻域内有很多匹配的匹配关系有很大概率是正确匹配。
一句话:正确匹配周围会有较多的匹配去支持它,而错误的匹配周围支持它的匹配很少。
4. 运动统计
上图展示了作者在Oxford Affine Dataset上验证模型合理性的示意图。利用SIFT特征进行匹配,根据真值标记出正确以及错误匹配。统计每个匹配所在小邻域内的匹配数量。可以发现,正确匹配的支持域得分明显高于错误匹配,即使在非常困难的匹配序列上,该现象仍然存在。
5. 基于网格的框架
6. 运动核
如果网格很小,则很少邻域信息将被考虑, 这会降低算法性能。 但是,如果网格很大,则将包括更多不正确的对应关系。为解决这个矛盾,我们将网格大小设置为较小以提高准确性,并提出运动内核以考虑更多邻域信息。
7. 多尺度+多旋转
为了应对匹配过程中的尺度与旋转问题,本文提出了多尺度以及多旋转策略。
7.1. 多尺度
7.2. 多旋转
利用旋转运动核模拟不同方向的旋转,如下图所示,固定,对按照顺时针旋转,这样可以得到8个运动核。然后利用GMS算法在所有的运动核上,然后选择最好的结果(选择匹配数量最多那个)。
8. 伪代码
9. 局限性
- 首先,算法假设图像运动是分段平滑的时,在违反假设的区域,例如图像边界,性能可能退化;
- 其次,在视觉上相似但空间位置不同的图像区域,算法性能受到限制。此问题通常发生在具有大量重复纹理的场景中;
- 最后,由于算法使用了网格化对图像进行处理,算法判定正确的匹配网格中仍然存在不准确匹配。
10. 实验
10.1. 旋转以及尺度变化
10.2. 高精确率与召回率
10.3. 算法快速
GMS能够在PC端速度2ms,multi-scale(GMS-S)以及multi-rotation(GMS-R)会增加一定的耗时。
10.4. 高效解算位姿
求解位姿速度快,且位姿精确。
10.5. 有利于SLAM初始化
集成到SLAM也可获得较好的结果(该实验不够充分,仅测试了将GMS集成到SLAM初始化的阶段,为了展示GMS能够更快/更好的进行初始化,同时能够产生较多的地图点)。
11. 借鉴意义
本文提供了一种高效/快速的外点滤除算法,能够在PC端实现实现实时滤除外点;
本算法已经被集成到OpenCV中,接口名为
matchGMS()
,可直接调用;
本算法可用于SLAM/SFM等领域,可提高位姿解算的精度以及速度;
(局限)本算法需要提取较多的特征点以提高正确匹配与错误匹配的可区分度,若特征匹配较少;该算法性能会有一定下降;
(局限)由于仅统计特征匹配数量,在重复纹理条件下该算法的性能也会下降;
12. 使用实践
12.1. 函数介绍
#include <opencv2/xfeatures2d.hpp>
void cv::xfeatures2d::matchGMS(const Size& size1,
const Size& size2,
const std::vector< KeyPoint >& keypoints1,
const std::vector< KeyPoint >& keypoints2,
const std::vector< DMatch >& matches1to2,
std::vector< DMatch >& matchesGMS,
const bool withRotation = false,
const bool withScale = false,
const double thresholdFactor = 6.0)
Parameters
size1Input size of image1.size2Input size of image2.keypoints1Input keypoints of image1.keypoints2Input keypoints of image2.matches1to2Input 1-nearest neighbor matches.matchesGMSMatches returned by the GMS matching strategy.withRotationTake rotation transformation into account.withScaleTake scale transformation into account.thresholdFactorThe higher, the less matches.
12.2. 代码示例
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/flann.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;
int main()
{
Mat img_1 = imread("01.jpg");
Mat img_2 = imread("02.jpg");
//Ptr<Feature2D> sift = xfeatures2d::SIFT::create();
//Ptr<Feature2D> surf = xfeatures2d::SURF::create();
Ptr<ORB> orb = ORB::create(5000);
orb->setFastThreshold(0);
vector<KeyPoint> keypoints_1, keypoints_2;
Mat descriptors_1,descriptors_2;
//sift->detect(img_1, keypoints_1);
//sift->compute(img_1, keypoints_1, descriptors);
orb->detectAndCompute(img_1, Mat(),keypoints_1, descriptors_1);
orb->detectAndCompute(img_2, Mat(), keypoints_2, descriptors_2);
//orb ->detect(img_1, keypoints_1);
//orb ->compute(img_1, keypoints_1, descriptors_1);
//orb->detect(img_2, keypoints_2);
//orb->compute(img_2, keypoints_2, descriptors_2);
Mat ShowKeypoints1, ShowKeypoints2;
drawKeypoints(img_1, keypoints_1, ShowKeypoints1);
drawKeypoints(img_2, keypoints_2, ShowKeypoints2);
imshow("Result_1", ShowKeypoints1);
imshow("Result_2", ShowKeypoints2);
vector<DMatch> matchesAll, matchesGMS;
BFMatcher matcher(NORM_HAMMING);
matcher.match(descriptors_1, descriptors_2, matchesAll);
cout << "matchesAll: " << matchesAll.size() << endl;
matchGMS(img_1.size(), img_2.size(), keypoints_1, keypoints_2, matchesAll, matchesGMS);
std::cout << "matchesGMS: " << matchesGMS.size() << std::endl;
Mat finalMatches;
drawMatches(img_1, keypoints_1, img_2, keypoints_2, matchesGMS, finalMatches, Scalar::all(-1), Scalar::all(-1),
std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
imshow("Matches GMS", finalMatches);
imwrite("MatchesGMS.jpg", finalMatches);
waitKey(0);
return 0;
}
12.3. 匹配结果
参考文献
📝笔记:GMS一种基于运动统计的快速鲁棒特征匹配过滤算法 | RealCat
opencv3.4.5实现GMS+ORB特征匹配_ChenCong7375的博客-CSDN博客
OpenCV: Experimental 2D Features Matching Algorithm
版权归原作者 瞻邈 所有, 如有侵权,请联系我们删除。