4、特征点法前端:ORB特征、SIFT特征、特征匹配与误匹配剔除
聊到SLAM前端,说白了就是解决一个核心问题:怎么让相机认出自己看过的地方。
我刚开始做SLAM的时候,觉得这事儿挺玄乎的。相机就是个镜头加传感器,它又没长脑子,怎么知道「哦,这个墙角我三秒前见过」?
嗯,答案就是特征点。
特征点就像图像的「指纹」。我们提取出这些指纹,然后让算法去比对。今天这一讲,我们就来聊聊两种最经典的特征——ORB和SIFT,以及怎么把匹配做准。
4.1 特征点:图像里的「地标」
先问个问题:什么样的点适合做特征?
我个人习惯用三个标准来衡量:
- 可重复性:换个角度、换个光照,还能找到它
- 独特性:这个点跟周围不一样,一眼就能认出来
- 高效性:计算要快,毕竟SLAM是实时系统
你想想看,如果一张图里全是白墙,那特征点就提取不出来。所以特征点往往出现在角点、边缘、纹理丰富的区域。
核心概念:特征点 = 关键点(位置) + 描述子(身份信息)
关键点告诉你「在哪」,描述子告诉你「它是谁」。
4.2 SIFT特征:老牌劲旅,稳但慢
SIFT(Scale-Invariant Feature Transform)是2004年Lowe提出的。说实话,这算法在学术界地位很高,但在工程界——嗯,我很少在实时SLAM里用它。
为什么?因为太慢了。
SIFT的核心思想是:在尺度空间中检测极值点。它用高斯金字塔构建多尺度图像,然后找那些在不同尺度下都能稳定出现的点。
SIFT的主要步骤
- 尺度空间极值检测:用DoG(高斯差分)近似LoG,找候选点
- 关键点定位:去掉低对比度的点和边缘响应点
- 方向分配:统计梯度方向直方图,给每个点分配主方向
- 生成描述子:在关键点周围取16x16区域,分成4x4子块,每个子块统计8方向梯度直方图
最终得到一个128维的描述子向量。
我的经验:SIFT的128维描述子虽然鲁棒性极强,但在嵌入式设备上跑不动。我曾经在树莓派上试过,一帧640x480的图像要处理将近1秒——这还做什么SLAM?直接放弃。
SIFT的优点很明显:旋转不变性、尺度不变性、光照鲁棒性都很好。但缺点就是计算量太大,不适合实时系统。
4.3 ORB特征:工程界的「万金油」
ORB(Oriented FAST and Rotated BRIEF)是2011年提出的。说白了,它就是FAST角点 + BRIEF描述子的改良版。
我个人非常喜欢ORB,因为它做到了速度与精度的平衡。在ORB-SLAM系列中,它就是绝对的主角。
ORB的两大组件
1. FAST角点检测
FAST的思路很简单:如果一个像素点跟它周围一圈的像素差别很大,那它就是角点。
- 取一个像素p,亮度为Ip
- 以p为圆心,半径为3的圆上取16个像素
- 如果连续N个点的亮度 > Ip + t 或 < Ip - t,则p是角点
- 通常N取12(FAST-12)
注意:FAST本身不具备尺度不变性。ORB的解决办法是——构建图像金字塔,在每层金字塔上分别提取FAST角点。这样就能应对尺度变化了。
2. BRIEF描述子
BRIEF是一种二进制描述子。它不计算梯度,而是比较像素对的亮度大小。
- 在关键点周围选256对像素点
- 每对比较:如果第一个点比第二个点亮,记为1,否则记为0
- 最终得到一个256位的二进制串
ORB给BRIEF加上了方向信息(steered BRIEF),让它具备旋转不变性。具体做法是:根据关键点的灰度质心方向,旋转像素对的位置。
ORB vs SIFT 对比
| 特性 | SIFT | ORB |
|---|---|---|
| 描述子维度 | 128维(浮点) | 256位(二进制) |
| 匹配速度 | 慢(欧氏距离) | 快(汉明距离) |
| 尺度不变性 | 优秀(尺度空间) | 一般(金字塔) |
| 旋转不变性 | 优秀 | 良好(灰度质心法) |
| 计算速度 | 慢 | 快(约SIFT的30倍) |
| 适用场景 | 离线、高精度 | 实时SLAM、嵌入式 |
4.4 特征匹配:让两张图「认亲」
提取完特征点,下一步就是匹配。说白了,就是找两张图里「长得像」的特征点对。
暴力匹配(Brute-Force Matcher)
最简单粗暴的方法:对于左图的每个特征点,计算它与右图所有特征点的距离,取最近的那个。
- ORB用汉明距离:两个二进制串异或后统计1的个数
- SIFT用欧氏距离:向量差的L2范数
避坑指南:我曾经在项目中直接用暴力匹配,结果一帧图像匹配耗时200ms。后来改用FLANN(快速最近邻搜索库),速度提升了5倍。对于ORB这种二进制描述子,用FLANN的LSH索引效果很好。
快速最近邻搜索(FLANN)
FLANN不是一种匹配算法,而是一种索引结构。它构建KD-Tree或LSH哈希表,加速最近邻搜索。
对于高维浮点描述子(如SIFT),用KD-Tree;对于二进制描述子(如ORB),用LSH。
4.5 误匹配剔除:别让「假亲戚」混进来
匹配完你会发现,总有一些错误的匹配对。嗯,这是不可避免的。我刚开始做的时候,看到匹配结果里那些交叉的线,心里就发毛。
怎么办?用下面这些方法。
方法一:距离阈值法
最简单的过滤:如果最近距离 > 某个阈值,就认为这个匹配不可靠。
// 伪代码示例
if (best_distance > threshold) {
reject_match();
}
但阈值不好设。设大了误匹配多,设小了正确匹配也被删了。
方法二:最近邻与次近邻比值法(Lowe's Ratio Test)
这是SIFT论文里提出的方法,非常经典。
- 对于左图的一个特征点,找到右图中距离最近的两个点
- 计算最近距离d1和次近距离d2
- 如果 d1 / d2 < 0.7(经验值),则接受这个匹配
为什么有效?因为正确的匹配,最近距离会远小于次近距离;而错误的匹配,两个距离往往差不多。
我的习惯:在ORB-SLAM中,我一般把比值设为0.75~0.8。太严格会丢失匹配,太宽松会引入误匹配。具体调参要看你的场景——纹理丰富的场景可以严格点,纹理少的场景要放宽。
方法三:RANSAC(随机采样一致性)
RANSAC是SLAM里的大杀器。它不直接过滤匹配,而是估计一个几何模型(比如单应矩阵或基础矩阵),然后剔除不符合模型的匹配。
步骤很简单:
- 随机选4对匹配点,计算单应矩阵H
- 用H去投影所有点,计算投影误差
- 误差小于阈值的点算「内点」,否则是「外点」
- 重复N次,取内点最多的那次结果
// OpenCV中的RANSAC用法
cv::Mat mask;
cv::findHomography(srcPoints, dstPoints, cv::RANSAC, 3.0, mask);
// mask中为1的点是内点,为0的是外点
注意:RANSAC不是万能的。如果误匹配比例超过50%,RANSAC可能失效。我曾经在一个低纹理走廊里遇到过这种情况——匹配全乱了,RANSAC怎么迭代都找不到正确的模型。后来我加了IMU辅助,才把问题解决。
4.6 本章知识体系
下面这张图总结了特征点法前端的核心流程:
4.7 小结
这一讲我们聊了:
- SIFT:精度高、鲁棒性强,但计算量太大,不适合实时SLAM
- ORB:速度快、性能均衡,是工程界的首选
- 特征匹配:暴力匹配简单但慢,FLANN是更好的选择
- 误匹配剔除:Lowe比值法 + RANSAC 是黄金组合
说实话,特征点法前端看起来简单,但真正调起来坑不少。我记得有一次在室外场景,光照变化剧烈,ORB的匹配率掉到了30%以下。后来我调整了FAST的阈值,并且用了自适应阈值,才把匹配率提上来。
嗯,这些经验都是一次次踩坑换来的。希望你能少走些弯路。