边缘检测与特征提取:从像素到语义的桥梁

边缘检测和特征提取,说白了就是让机器学会「看」轮廓。人眼扫一眼就知道哪里是边界、哪里是角点,但摄像头看到的只是一堆灰度值。我做了这么多年嵌入式视觉,最深的体会就是:特征提取的质量,直接决定了后续识别、跟踪、拼接的上限。

这一章我们聚焦四个经典工具:Sobel、Canny、Hough 变换、Harris/Shi-Tomasi 角点检测。它们不是最前沿的,但是工业界最常用的。你想想看,从产线缺陷检测到自动驾驶车道线识别,底层跑的还是这些算法。

核心观点:边缘检测不是目的,而是手段。特征提取的质量,决定了整个视觉系统的天花板。
边缘检测与特征提取 · 知识体系 特征提取核心 Sobel 算子 Canny 边缘检测 Hough 变换 角点检测 一阶导数 · 梯度近似 3×3 卷积核 · 快速计算 高斯滤波 · 非极大值抑制 双阈值滞后 · 边缘连接 参数空间投票 · 直线/圆 累加器峰值检测 Harris · Shi-Tomasi 自相关矩阵 · 响应值 典型应用:缺陷检测 · 车道线识别 · 目标跟踪 · 图像拼接

3.1 Sobel 算子:最朴素的边缘响应

Sobel 算子做的是什么事?就是计算图像灰度在水平和垂直方向上的变化率。用两个 3×3 的卷积核分别滑过图像,得到 Gx 和 Gy,然后合成梯度幅值。

我个人习惯在嵌入式平台上优先用 Sobel,因为它计算量小、容易定点化。但要注意:Sobel 对噪声敏感。我在一个户外监控项目里吃过亏——图像 SNR 低的时候,Sobel 输出全是噪点,根本看不出边缘。

// Sobel 水平核 Gx
[-1, 0, 1]
[-2, 0, 2]
[-1, 0, 1]

// Sobel 垂直核 Gy
[-1, -2, -1]
[ 0,  0,  0]
[ 1,  2,  1]

// 梯度幅值:G = sqrt(Gx² + Gy²)
// 工程简化:G ≈ |Gx| + |Gy|
工程技巧:在 ARM Cortex-A 系列上,用 NEON 指令集并行计算 Gx 和 Gy,速度能提升 3-5 倍。如果精度要求不高,直接用值求和代替开平方,省掉浮点运算。

3.2 Canny 边缘检测:工业界的黄金标准

Canny 为什么是黄金标准?因为它把边缘检测拆成了四个清晰的步骤。我当年第一次在 FPGA 上实现 Canny,才真正理解什么叫「算法工程化」。

  1. 高斯滤波:先平滑,抑制噪声。核大小一般取 5×5,σ 取 1.0-1.5。
  2. 计算梯度:用 Sobel 算梯度幅值和方向。
  3. 非极大值抑制:只保留梯度方向上的局部最大值,把粗边变细。
  4. 双阈值滞后:高阈值确定强边缘,低阈值连接弱边缘。

这里有个坑——双阈值怎么设? 我曾经在一个项目中直接用固定阈值,结果换了个光照环境就全崩了。后来改用 Otsu 自适应阈值,才稳定下来。

避坑指南:低阈值建议设为高阈值的 1/2 到 1/3。如果边缘断裂严重,降低低阈值;如果伪边缘太多,提高高阈值。没有万能参数,必须根据实际图像调。

3.3 Hough 变换:从边缘到几何

边缘检测得到的是像素级的点,Hough 变换把它们聚合成直线或圆。核心思想是:在图像空间中共线的点,在参数空间中交于同一点。

直线检测用极坐标形式:ρ = x·cosθ + y·sinθ。每个边缘点投票给一组 (ρ, θ),峰值就是检测到的直线。

我记得在做一个 PCB 板缺陷检测项目时,Hough 变换帮了大忙。板子上的焊盘位置有偏差,用 Hough 圆检测直接定位每个焊盘中心,比模板匹配快了一个数量级。

参数 含义 典型值 调优方向
ρ 分辨率 距离步长 1 像素 越小越精确,但计算量增大
θ 分辨率 角度步长 1° 直线检测 1° 够用,圆检测可放宽
累加器阈值 最少投票数 图像短边的 10% 阈值越高,检测到的直线越少
最小线段长度 有效线段最短长度 20 像素 过滤短噪声线段
性能优化:Hough 变换的计算量跟参数空间分辨率成平方关系。在嵌入式平台上,先把 θ 步长设为 2°,检测到直线后再用局部搜索精化。这样能省掉 60% 以上的计算。

3.4 角点检测:Harris 与 Shi-Tomasi

角点就是图像中灰度变化剧烈的点——说白了就是「拐弯的地方」。Harris 角点检测用自相关矩阵 M 的特征值来判断:两个特征值都大,就是角点;一个大一个小,是边缘;两个都小,是平坦区域。

Shi-Tomasi 是 Harris 的改进版。它直接用 min(λ₁, λ₂) 作为响应值,比 Harris 的 R = det(M) - k·trace²(M) 更直观,也更稳定。

// Harris 响应值
R = det(M) - k * trace(M)²
// 其中 M = [Ix², IxIy; IxIy, Iy²]
// k 通常取 0.04 - 0.06

// Shi-Tomasi 响应值
R = min(λ₁, λ₂)
// 直接取两个特征值中的较小者

我在做视觉 SLAM 时,特征点提取用的就是 Shi-Tomasi。它比 Harris 更稳定,尤其是在纹理重复的场景中。但要注意:角点检测对图像金字塔层数敏感。层数太少,尺度变化大时匹配不上;层数太多,计算量翻倍。

实用建议:对于 640×480 的图像,金字塔层数取 3-4 层就够了。每层检测的角点数量建议控制在 200-500 个,太多反而降低匹配质量。

3.5 四种方法的对比与选型

实际项目中怎么选?我列个表,你一看就明白。

方法 输出 计算量 抗噪性 典型场景
Sobel 梯度幅值图 低 差 实时预处理、边缘强度估计
Canny 二值边缘图 中 好 缺陷检测、轮廓提取
Hough 直线/圆参数 高 较好 车道线、定位标记、焊盘检测
Harris/Shi-Tomasi 角点坐标 中 较好 特征匹配、SLAM、图像拼接

选型原则其实很简单:先明确你要什么。要边缘强度,用 Sobel;要干净的二值边缘,用 Canny;要几何参数,用 Hough;要稳定特征点,用 Shi-Tomasi。别想着一个算法包打天下。

重要提醒:在嵌入式平台上,Hough 变换的累加器数组可能很大。比如 640×480 图像,ρ 范围约 800,θ 步长 1° 有 180 个,累加器就是 800×180 = 144000 个单元。如果每个单元用 32 位整数,就是 576KB——对某些 MCU 来说已经不小了。建议用 16 位累加器,或者分块处理。

嗯,这一章的内容就到这里。四种方法各有千秋,但核心思路是一致的:从像素中提取出有语义的结构。你在实际项目中遇到问题,随时可以回来翻翻这张知识体系图,看看自己卡在哪一步。