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检测步骤:

  1. 选取候选像素p,灰度值为Ip
  2. 设定阈值t
  3. 检查圆上16个像素,若有连续12个满足|Ix - Ip| > t,则p为角点
  4. 使用非极大值抑制,避免角点扎堆

这里有个加速技巧——我习惯先检查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的完整流程就是:

  1. 构建图像金字塔,获得多尺度图像
  2. 在每层金字塔上检测FAST角点
  3. 对每个角点,计算灰度质心,得到主方向
  4. 根据主方向,旋转BRIEF像素对位置
  5. 生成旋转后的BRIEF描述子

下面这张图能帮你更直观地理解整个流程:

ORB特征提取流程 输入图像 构建图像金字塔(8层,缩放因子1.2) 每层检测FAST角点 灰度质心法计算主方向 旋转BRIEF像素对,生成描述子 ORB特征点 + 描述子

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