2. ORB特征提取原理:FAST角点检测与BRIEF描述子的结合,ORB的旋转不变性与尺度不变性
说到ORB特征,我得先坦白一件事。刚入行那会儿,我总觉得SIFT和SURF才是王道,ORB就是个"低配版"。直到有一次在嵌入式设备上做实时SLAM,SIFT跑得跟幻灯片似的,我才老老实实回头研究ORB。结果发现——这玩意儿真香。
ORB的全称是Oriented FAST and Rotated BRIEF。说白了,就是把FAST角点检测和BRIEF描述子捏在一起,再打上旋转和尺度的补丁。咱们今天就把这层窗户纸捅破。
2.1 FAST角点检测:快就一个字
FAST算法的核心思想很简单:如果一个像素和它周围一圈的像素差别很大,那它很可能是个角点。具体怎么判断?
你想想看,一个真正的角点,周围像素的灰度值应该跟它明显不同。FAST的做法是:以候选点为中心,取半径为3的圆上的16个像素。如果其中有连续N个像素(通常N=12)的灰度值大于或小于中心点,那就判定为角点。
FAST检测步骤:
- 选取候选像素p,灰度值为Ip
- 设定阈值t
- 检查圆上16个像素,若有连续12个满足|Ix - Ip| > t,则p为角点
- 使用非极大值抑制,避免角点扎堆
这里有个加速技巧——我习惯先检查1、5、9、13这四个位置。如果其中至少三个满足条件,再检查剩下的。否则直接跳过。嗯,这个小优化能让检测速度提升好几倍。
我曾经踩过的坑:阈值t的选择很关键。设太大,角点太少,特征匹配容易失败;设太小,角点太多,计算量爆炸。我一般先设30,然后根据实际效果微调。没有万能参数,得看你的场景。
2.2 BRIEF描述子:二进制才是王道
检测到角点之后,怎么描述它?BRIEF的做法简单粗暴——在特征点周围随机选128对或256对像素,比较它们的灰度值。如果前一个比后一个大,记为1,否则记为0。这样每个特征点就得到一个二进制串。
为什么用二进制?因为计算汉明距离只需要异或操作,比欧氏距离快得多。在SLAM这种实时系统里,速度就是生命。
// BRIEF描述子生成伪代码
for i = 1 to 256:
(x1, y1) = random_pattern[i].first
(x2, y2) = random_pattern[i].second
if I(x1, y1) < I(x2, y2):
descriptor[i] = 1
else:
descriptor[i] = 0
但BRIEF有个致命问题——它不具备旋转不变性。你想想看,图像旋转后,同样的像素对位置变了,比较结果自然不同。这就是为什么ORB要对BRIEF进行改造。
2.3 旋转不变性:给特征点安个指南针
ORB的解决方案是:先给每个特征点算一个主方向,然后根据这个方向旋转像素对的位置。这样不管图像怎么转,描述子都能保持一致。
主方向怎么算?用灰度质心法。说白了,就是把特征点邻域内的像素灰度值当成质量,算出质心位置。特征点到质心的向量方向,就是主方向。
我的经验:计算主方向时,邻域半径我一般取15像素。太大容易受噪声影响,太小方向不稳定。这个值在大多数场景下表现都不错。
有了主方向后,BRIEF的像素对位置就跟着旋转。这样生成的描述子就叫steered BRIEF。嗯,名字挺形象——就像方向盘一样,带着描述子转。
2.4 尺度不变性:图像金字塔来帮忙
旋转问题解决了,尺度问题怎么办?ORB的做法是构建图像金字塔。简单说,就是把原图不断缩小,生成一系列不同分辨率的图像。然后在每一层上都检测FAST角点。
这样,不管目标在图像中是变大还是变小,总能在某一层金字塔上找到合适的尺度。匹配的时候,把不同层的特征点放在一起比较就行。
// 图像金字塔构建
for level = 0 to n_levels:
scale = 1.0 / (1.2 ^ level)
img_resized = resize(original_img, scale)
keypoints[level] = detect_FAST(img_resized)
金字塔层数怎么定?我一般用8层,缩放因子1.2。层数太少,尺度覆盖不全;层数太多,计算量上去了,收益却不大。8层是个不错的平衡点。
2.5 ORB的整体流程
把上面这些串起来,ORB的完整流程就是:
- 构建图像金字塔,获得多尺度图像
- 在每层金字塔上检测FAST角点
- 对每个角点,计算灰度质心,得到主方向
- 根据主方向,旋转BRIEF像素对位置
- 生成旋转后的BRIEF描述子
下面这张图能帮你更直观地理解整个流程:
2.6 性能对比:ORB vs SIFT vs SURF
说了这么多,ORB到底好在哪?我整理了一张对比表,你看一眼就明白了:
| 特性 | ORB | SIFT | SURF |
|---|---|---|---|
| 检测速度 | 极快(~15ms) | 慢(~300ms) | 中等(~100ms) |
| 旋转不变性 | 有(灰度质心法) | 有(梯度直方图) | 有(Haar小波) |
| 尺度不变性 | 有(图像金字塔) | 有(DoG尺度空间) | 有(尺度空间) |
| 描述子类型 | 二进制(256位) | 浮点(128维) | 浮点(64/128维) |
| 匹配速度 | 极快(汉明距离) | 慢(欧氏距离) | 中等(欧氏距离) |
| 专利限制 | 无 | 有 | 有 |
看到没?ORB在速度上有绝对优势。虽然纯精度上不如SIFT,但在SLAM这种实时系统里,速度往往比精度更重要。再说了,配合上RANSAC等优化手段,ORB的精度也完全够用。
总结一下:ORB的核心思想就是"用速度换精度,用工程技巧弥补理论不足"。FAST负责快,BRIEF负责轻量,灰度质心法解决旋转,金字塔解决尺度。四个组件各司其职,组合起来就是一个工程上非常好用的特征提取方案。
我个人觉得,ORB最厉害的地方不是某个单一技术,而是这种"组合拳"的思路。做工程嘛,有时候不需要最牛的算法,需要的是最合适的方案。ORB就是这种思路的典范。
避坑指南:我曾经在一个光照变化剧烈的场景里用ORB,结果特征点全跑偏了。后来发现是FAST阈值设得太死。解决办法是动态调整阈值——根据图像的平均灰度值自动计算阈值。这个小改动,让我的系统在白天黑夜都能稳定运行。
好了,ORB的原理就聊到这儿。下一节咱们会深入代码,看看怎么用OpenCV实现ORB特征提取,以及怎么调参才能让它在你的项目里发挥最大威力。
公众号:蓝海资料掘金营,微信deep3321