【12】FAST角点检测:从算法原理到OpenCV实时实现详解
文章简介
本文深入解析FAST(加速分割测试特征)角点检测算法的设计逻辑——从“高速候选筛选”的核心规则,到“机器学习优化”的性能提升,再到“非极大值抑制”的去重策略,最后结合OpenCV-Python代码演示其实际应用,帮助读者掌握这一实时计算机视觉的核心技术,理解“高速”背后的工程智慧。
一、为什么需要FAST?——实时场景的痛点
在计算机视觉的实时应用中(比如SLAM即时定位与地图构建的移动机器人、实时视频跟踪),传统角点检测器(如Harris)虽能准确识别角点,但运算速度难以满足“资源受限”的硬件需求(比如机器人的嵌入式芯片)。
2006年,Edward Rosten和Tom Drummond提出了FAST算法(后续2010年修订版进一步优化),主打“高速检测”特性——其核心目标是:用最少的计算量,快速筛选出图像中的角点。
二、FAST的核心逻辑:16像素圆+连续12点规则
FAST的角点定义很直接:一个像素如果是角点,那么它周围一定存在“显著的亮度突变”。具体步骤如下:
- 候选像素与灰度阈值:对于图像中的每个候选像素(p),记录其灰度值(I_p),并设定一个灰度差阈值(t)(比如(t=20),表示亮度差异超过20才视为“显著”)。
- 16像素圆的覆盖:以(p)为中心,画一个半径3像素的圆——这个圆会均匀覆盖周围16个像素(想象钟表的16个刻度点,均匀分布在圆上)。

- 连续像素的判断:如果这16个像素中存在连续12个像素,要么全比(I_p + t)亮(亮于(p)+阈值),要么全比(I_p - t)暗(暗于(p)-阈值),那么(p)就是一个角点。
三、高速预检:用“四点筛选”减少计算量
直接检查16个像素会很慢——FAST的“高速预检”能过滤90%以上的非角点:
- 预检规则:只检查圆上4个关键位置的像素(比如钟表的12点、6点、3点、9点,对应圆上的位置1、9、5、13)。
- 筛选逻辑:如果(p)是角点,这4个点中至少有3个要满足“比(I_p + t)亮”或“比(I_p - t)暗”。如果不满足,直接排除(p),无需检查其他12个点。
四、早期FAST的缺点与优化方案
原始FAST虽快,但有4个明显缺点:
- 当连续像素数(n<12)时,预检过滤效果差;
- 像素检查顺序未优化,依赖角点的分布;
- 预检的中间结果未被利用;
- 容易检测到相邻重复的角点。
前3个问题通过机器学习解决,最后1个用非极大值抑制解决。
1. 机器学习优化:用决策树提升效率
为了让FAST更“聪明”地判断角点,研究者用ID3决策树训练了一个分类器:
- 步骤1:收集大量目标领域的训练图像(比如机器人场景的图像),用原始FAST找到所有角点。
- 步骤2:对每个角点,提取其周围16个像素的灰度状态(每个像素分为三类:(P_d)(比(p)暗)、(P_s)(和(p)灰度相近)、(P_b)(比(p)亮)),形成特征向量。
- 步骤3:用ID3算法训练决策树——以“是否为角点”为标签,优先选择信息增益最大的像素(即该像素的状态能最有效区分“是/否角点”)。
训练后的决策树会按信息增益顺序检查像素,不再盲目遍历16个点,同时利用预检结果,大幅提升检测效率。
2. 非极大值抑制:去除相邻重复角点
FAST容易在相邻位置检测到多个角点(比如同一个角点被重复识别),解决方法是非极大值抑制(NMS):
- 步骤1:为每个角点计算响应值(V)——(V)是(p)与周围16个像素的灰度绝对差之和((V = \sum_{i=1}^{16} |I_p - I_i|))。(V)越大,说明角点的“显著性”越强。
- 步骤2:对于相邻的角点(比如距离小于3像素),只保留(V)值最大的那个,其他的丢弃。
五、OpenCV-Python:实战FAST角点检测
OpenCV将FAST封装成了简单的API,我们可以直接调用。以下是完整的实战代码(变量名语义化修改,更易读):
代码实现
import numpy as np
import cv2 as cv# 1. 加载灰度图像(示例图像路径:<opencv_root>/samples/data/blox.jpg)
gray_img = cv.imread('blox.jpg', cv.IMREAD_GRAYSCALE)# 2. 初始化FAST检测器(默认开启非极大值抑制)
fast_detector = cv.FastFeatureDetector_create()# 3. 检测角点(第二个参数为掩码,这里用None表示无掩码)
keypoints = fast_detector.detect(gray_img, None)# 4. 绘制角点(用蓝色标记,BGR格式:(255,0,0)代表蓝色)
img_with_keypoints = cv.drawKeypoints(gray_img, keypoints, None, color=(255, 0, 0)
)# 5. 打印默认参数(帮助理解检测器的配置)
print(f"阈值(Threshold): {fast_detector.getThreshold()}")
print(f"是否开启非极大值抑制: {fast_detector.getNonmaxSuppression()}")
print(f"邻域类型(Neighborhood): {fast_detector.getType()}")
print(f"带非极大值抑制的角点数量: {len(keypoints)}")# 6. 保存带非极大值抑制的结果
cv.imwrite('fast_with_nms.png', img_with_keypoints)# 7. 关闭非极大值抑制,重新检测
fast_detector.setNonmaxSuppression(0)
keypoints_no_nms = fast_detector.detect(gray_img, None)
print(f"不带非极大值抑制的角点数量: {len(keypoints_no_nms)}")# 8. 绘制并保存不带非极大值抑制的结果
img_no_nms = cv.drawKeypoints(gray_img, keypoints_no_nms, None, color=(255, 0, 0)
)
cv.imwrite('fast_without_nms.png', img_no_nms)

代码说明
- 灰度图像:FAST角点检测用灰度图即可(减少计算量,颜色信息对ٳ点判断无帮助)。
- 默认参数:OpenCV的FAST默认阈值为10,默认开启非极大值抑制(
getNonmaxSuppression()返回True)。 - 邻域类型:
getType()返回的是邻域的大小(比如cv.FAST_FEATURE_DETECTOR_TYPE_9_16表示用9x16的邻域,是默认值)。
结果分析
运行代码后,你会得到两张结果图:
fast_with_nms.png:带非极大值抑制的结果——角点分布稀疏,相邻重复的角点被去除。fast_without_nms.png:不带非极大值抑制的结果——角点密集,能看到很多相邻的重复点。【在此处插入结果对比图】
六、FAST的优缺点总结
优点
- 速度极快:比传统Harris检测器快数倍,适合实时场景(如SLAM、视频跟踪)。
- 实现简单:核心逻辑容易理解,OpenCV封装后调用方便。
缺点
- 对噪声敏感:高噪声图像中,噪声会干扰灰度差判断,导致误检。
- 依赖阈值:阈值(t)的选择需要经验((t)太小会检测到过多噪声点,(t)太大则漏检角点)。
