GMS(Grid-based Motion Statistics)算法,是由 Jiawang Bian 等人于 2017 年提出的一种快速、鲁棒的特征匹配过滤算法,全名为:
GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence
该方法的目标是在传统特征匹配的结果上进行鲁棒过滤,显著提高内点比例(inlier ratio),避免使用 RANSAC 等代价高昂的方法。
一、论文背景与创新点
背景
- 在计算机视觉任务中(如 SfM、SLAM、图像拼接),特征匹配是关键步骤;
- 传统匹配器(如 ORB + BFMatcher)容易产生大量误匹配;
- 为了鲁棒性,通常使用 RANSAC 等几何模型估计器进行剔除,但计算代价较高。
创新点
编号 | 创新内容 |
---|---|
1 | 提出基于网格的一致性统计(GMS),利用特征分布的局部空间一致性代替代价昂贵的几何模型拟合 |
2 | 完全无需几何模型(如单应性、基础矩阵等),仅用点对分布统计即可剔除误匹配 |
3 | 支持尺度和旋转不变性扩展(Multi-scale + Multi-orientation) |
4 | 可实时处理(30+ fps),适合嵌入式系统和大规模特征任务 |
二、算法整体框架
整个 GMS 过程可视为两阶段:
第一阶段:快速构建初始匹配点对(如 ORB + BFMatcher)
第二阶段:基于局部运动一致性的网格过滤策略进行匹配剔除
三、算法原理与步骤详解
1. 特征提取与初始匹配
- 任意特征点提取器(如 ORB、AKAZE、SIFT)
- 使用暴力匹配(BFMatcher)或 KD-Tree 得到初始
N
个匹配点对 ( p i , q i ) (p_i, q_i) (pi,qi)
该阶段不涉及 GMS,仅作为输入。
2. 构建图像网格(Grid)
将图像划分为 G × G G \times G G×G 网格(例如 20×20)
对每个匹配点对,将其两个端点分别落入图像 1 和图像 2 的网格中:
- ( p i ∈ Grid k ) , ( q i ∈ Grid l ) (p_i \in \text{Grid}_{k}), (q_i \in \text{Grid}_{l}) (pi∈Gridk),(qi∈Gridl)
核心思想:如果匹配是正确的,那么在某一网格及其邻域中,源图像与目标图像网格间应该存在较多的一致映射。
3. 局部一致性统计(Motion Kernel)
对每个网格单元格 Grid k \text{Grid}_k Gridk,统计其到目标图像所有匹配方向的投票数:
- 以网格 Grid k \text{Grid}_k Gridk 的匹配点为中心;
- 将其对应目标图像网格的映射方向用作运动向量;
- 如果某方向得票最多且超过阈值(比如 >6),则认为该网格内的该方向为主运动。
GMS 就是利用这个局部“投票原则”来判断哪些匹配可信,哪些不是。
4. 多尺度与多旋转支持(增强 GMS)
由于真实场景中存在视角/尺度变化,GMS 原算法提供扩展:
多尺度 GMS(Scale-aware GMS)
- 使用金字塔图像分别构建匹配;
- 尝试多组网格尺寸(例如 G = 20, 40, 60…);
- 每层做一遍 GMS,合并最终内点。
多旋转 GMS(Rotation-aware GMS)
- 对目标图像旋转多个角度(通常是 8 个方向,如 0°, 45°, 90°…);
- 在每个旋转版本上执行一次 GMS;
- 得到最佳匹配组合。
这两项扩展显著增强了 GMS 的旋转 & 尺度不变性。
四、关键技术点总结
技术点 | 描述 |
---|---|
网格划分策略 | 可调网格数(G=20~40),用于投票统计 |
运动核检测 | 判断局部运动方向是否一致,以此投票过滤匹配 |
匹配投票阈值 | 通常设为 6,表示区域内要有足够匹配一致性 |
支持邻域 | 默认 3x3 邻域一致性统计(更鲁棒) |
Multi-scale + Multi-rotation | 增强匹配在实际变换场景下的稳定性 |
五、优点总结
优点 | 说明 |
---|---|
快速 | 无需几何模型拟合,纯粹使用空间投票 |
鲁棒 | 在重复纹理、仿射变换等下仍有效 |
无描述子依赖 | 可用于任意描述子的匹配结果过滤 |
易部署 | 可直接用于特征匹配前端、VIO、图像配准等系统 |
实时性 | 在 CPU 上可轻松 30~60 FPS 运行(上千点匹配) |
六、不足与局限性
不足 | 说明 |
---|---|
无全局几何检验 | 无法判断匹配是否符合某个几何模型(如单应、基础矩阵) |
对遮挡/大量错配无显式建模 | 多目标运动场景下存在误判风险 |
网格尺度敏感 | 网格大小选择影响性能(需调参) |
对特征分布密度敏感 | 稀疏特征场景下效果下降 |
七、论文与资源链接
- 论文地址:https://arxiv.org/abs/1701.08396
- GitHub 示例(C++):https://github.com/JiawangBian/GMS-Feature-Matcher
- OpenCV 实现:位于
opencv_contrib/modules/xfeatures2d/src/matching.cpp
八、可选改进方向建议
方向 | 描述 |
---|---|
融合 GMS 与 F matrix 单应性验证 | 提升鲁棒性 |
用 CNN 替代手工网格统计 | GMS with learned kernels |
利用 GMS 初步过滤,再进行局部几何估计(如 E matrix) | 应用于 VO/VIO |
九、 OpenCV中使用示例
在 OpenCV 中,GMS(Grid-based Motion Statistics)特征匹配功能位于 xfeatures2d
模块(contrib 扩展),提供了一个极简接口用于从粗匹配中快速剔除误匹配。
9.1、函数接口说明:cv::xfeatures2d::matchGMS
namespace cv {
namespace xfeatures2d {
/**
* @brief Performs GMS (Grid-based Motion Statistics) feature matching filtering
*
* @param size1 size of the first image
* @param size2 size of the second image
* @param keypoints1 keypoints from image1
* @param keypoints2 keypoints from image2
* @param matches1to2 initial matches (e.g., from BFMatcher)
* @param matchesGMS output: filtered matches after GMS
* @param withRotation enable rotation-invariant GMS (default: false)
* @param withScale enable scale-invariant GMS (default: false)
* @param thresholdFactor threshold for motion consistency (default: 6.0)
*/
void 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,
bool withRotation = false,
bool withScale = false,
double thresholdFactor = 6.0
);
}} // namespace cv::xfeatures2d
参数详解
参数名 | 类型 | 说明 |
---|---|---|
size1 / size2 |
cv::Size |
两张图像的尺寸 |
keypoints1 / keypoints2 |
std::vector<cv::KeyPoint> |
特征点列表 |
matches1to2 |
初始匹配点对 | 通常来自 ORB/SIFT + BFMatcher |
matchesGMS |
输出 | GMS 过滤后的匹配结果 |
withRotation |
bool |
是否启用旋转不变性 |
withScale |
bool |
是否启用尺度不变性 |
thresholdFactor |
double |
投票一致性阈值(建议为 6.0) |
9.2、使用代码示例
以下代码展示如何用 ORB 提取特征,然后用 GMS 过滤匹配:
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>
int main()
{
cv::Mat img1 = cv::imread("img1.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat img2 = cv::imread("img2.jpg", cv::IMREAD_GRAYSCALE);
if (img1.empty() || img2.empty()) {
std::cerr << "Image loading failed!" << std::endl;
return -1;
}
// 1. 特征提取
auto orb = cv::ORB::create(1000);
std::vector<cv::KeyPoint> kp1, kp2;
cv::Mat desc1, desc2;
orb->detectAndCompute(img1, cv::noArray(), kp1, desc1);
orb->detectAndCompute(img2, cv::noArray(), kp2, desc2);
// 2. 初始匹配(暴力匹配器)
std::vector<cv::DMatch> matches_all;
cv::BFMatcher matcher(cv::NORM_HAMMING);
matcher.match(desc1, desc2, matches_all);
// 3. GMS 过滤
std::vector<cv::DMatch> matches_gms;
bool withRotation = true;
bool withScale = true;
double threshold = 6.0;
cv::xfeatures2d::matchGMS(
img1.size(), img2.size(),
kp1, kp2,
matches_all,
matches_gms,
withRotation,
withScale,
threshold
);
std::cout << "Initial Matches: " << matches_all.size() << std::endl;
std::cout << "GMS Matches: " << matches_gms.size() << std::endl;
// 4. 可视化
cv::Mat img_matches;
cv::drawMatches(img1, kp1, img2, kp2, matches_gms, img_matches);
cv::imshow("GMS Filtered Matches", img_matches);
cv::waitKey(0);
return 0;
}
9.3、匹配结果示意图
匹配点通常可视化如下:
cv::drawMatches(img1, kp1, img2, kp2, matches_gms, img_matches);
cv::imshow("GMS Matches", img_matches);
可视化会看到原始匹配很多错误点,而 GMS 匹配线更加集中与正确区域。
9.4、实际应用建议
场景 | 是否推荐使用 GMS |
---|---|
图像拼接 | 强烈推荐 |
SLAM 前端 | 可大幅减少误匹配输入 |
目标跟踪 | 可提升匹配精度 |
小视差图像对齐 | 特别有效 |
多目标匹配 | 无法分辨多目标,建议配合几何验证(如单应矩阵) |