边缘与边界
前几章从底层的像素处理(滤波、频率分析、多尺度表示)逐步构建起了图像处理的工具箱。本章进入中层视觉(Mid-level Vision)——从像素出发,提取场景中有意义的结构。其中最基本的结构就是边缘。
直觉上,如果让你用一支笔勾勒出一张照片中的重要轮廓,你会画什么?物体的边界、表面朝向的突变、阴影的边缘——这些位置有一个共同特点:亮度在很短的距离内发生了剧烈变化。边缘检测的目标,就是让计算机自动完成这件事。
为什么检测边缘
边缘为什么值得单独花一章来讨论?因为它们是图像中信息密度最高的结构:
- 捕获形状信息:一幅图像可能有百万个像素,但其中真正描述物体形状的信息集中在少量边缘像素上。一张线条画只保留了原图不到 5% 的像素,人类却能轻松识别出场景中的物体——这说明边缘几乎包含了识别所需的全部信息
- 对光照不敏感:物体的边缘主要由几何结构(形状、深度)决定,而非光照条件。同一个杯子在日光和灯光下看起来颜色完全不同,但它的轮廓几乎不变
- 与人类视觉一致:人类视觉皮层 V1 区的简单细胞对特定方向的边缘有强烈响应——边缘检测是视觉处理的第一步,不论是生物的还是计算的
- 下游任务的基础:识别、追踪、三维重建等几乎所有视觉任务都可以从边缘信息中获益
但边缘检测比看起来困难得多。它没有精确的数学定义——什么算「显著的变化」?在什么尺度上?如何区分有意义的边缘和噪声产生的虚假变化?本章就是围绕这些问题展开的。
边缘 ≠ 边界
两个容易混淆的概念:边缘(Edge)是图像亮度函数发生剧烈变化的位置,是纯粹的信号处理概念,不涉及语义;边界(Boundary)是图像平面上像素归属从一个物体或表面变为另一个的轮廓,是语义概念。边缘检测器检测的是亮度变化,而我们真正想要的往往是物体边界——这两者之间的鸿沟是本章后半部分(Pb 检测器)试图弥合的核心问题。
从像素到语义:两条路径
从像素走向语义理解,有两条互补的路径:
基于区域的方法(Region-based)将图像分割为 个连通的区域,每个区域内的像素在颜色、纹理等方面具有一致性——这是一个聚类问题。
基于边缘的方法(Edge-based)则关注像素之间的不连续——在哪里,相邻像素发生了显著变化?输出是一张与原图同样大小的二值图,标记每个像素是否为边缘点。
这两种表示是否等价?从分割可以直接得到边界(区域的边界就是轮廓),但反过来不一定成立——要从边缘恢复出分割,轮廓必须是封闭的。如果边缘图中有断开的地方,就无法确定哪些像素属于同一个区域。
什么产生了边缘
图像中的边缘来自四种不同的物理原因:
| 原因 | 含义 | 示例 |
|---|---|---|
| 深度不连续 | 物体边界,前后景分离 | 物体轮廓 |
| 表面朝向变化 | 同一物体上曲面的转折 | 圆柱体的明暗分界线 |
| 反射率变化 | 材质或纹理的改变 | 斑马的黑白条纹 |
| 投射阴影 | 光被遮挡后的亮暗边界 | 树影投在地面上 |
一个关键的困难在于:边缘检测器无法区分这四种原因——它只能看到「亮度发生了变化」,却不知道这个变化是因为物体边界还是仅仅是阴影。这也是为什么边缘检测看似简单,实际上比想象中困难得多。
梯度与边缘
图像的导数
边缘是图像亮度函数快速变化的位置——用数学的语言说,就是导数大的地方。对离散图像,偏导数用有限差分(Finite Difference)近似:
这个操作可以实现为与滤波核 的卷积。类似地, 对应核 。
常用的差分滤波器
在实践中,简单的 滤波器对噪声非常敏感。更常用的差分滤波器在一个方向上做差分的同时,在另一个方向上做平滑,以增强鲁棒性:
| 滤波器 | (检测垂直边缘) | 特点 |
|---|---|---|
| Roberts | 2×2,定位好但噪声敏感 | |
| Prewitt | 3×3,均匀平滑 | |
| Sobel | 3×3,中心加权平滑 |
Sobel 算子是高斯导数的常用近似:它可以分解为沿一个方向的差分 和沿另一个方向的平滑 的外积。标准定义通常省略归一化系数 ——对于边缘检测只关心相对大小,但如果需要准确的梯度值,就需要补上这个系数。
滤波核的一般性质
平滑核和微分核有截然不同的数学性质,理解这一点有助于设计和选择滤波器:
| 性质 | 平滑核 | 微分核 |
|---|---|---|
| 系数符号 | 全部为正 | 正负交替 |
| 系数之和 | 等于 1(常数区域不变) | 等于 0(常数区域无响应) |
| 频率特性 | 低通滤波器 | 高通滤波器 |
| 核越大 | 平滑越强 | 检测越好但定位越差 |
微分核的系数之和为零这一点非常重要——它保证了在亮度均匀的区域中,微分核的响应恰好为零,只有在亮度发生变化的位置才会产生非零输出。
算子尺寸的权衡
不同大小的边缘算子之间存在一个根本性的权衡。以检测垂直边缘为例:
| 算子 | 大小 | 定位精度 | 噪声敏感度 | 检测能力 |
|---|---|---|---|---|
| 简单差分 | 最好 | 最敏感 | 最差 | |
| Roberts | 好 | 敏感 | 差 | |
| Sobel | 中 | 中 | 中 | |
| Sobel | 差 | 不敏感 | 好 |
核越大,在垂直于边缘的方向上平均了更多像素——这抑制了噪声但也模糊了边缘的精确位置。定位精度和噪声鲁棒性之间的矛盾贯穿整个边缘检测领域,Canny 检测器正是为了在这两者之间找到最优平衡而设计的。
图像梯度
将两个方向的偏导数组合在一起,得到图像梯度:
梯度指向亮度变化最快的方向,与边缘方向垂直。从梯度可以提取两个关键信息:
梯度方向(边缘法线方向):
梯度幅值(边缘强度):
噪声的影响与解决方案
直接对含噪图像求导,结果几乎无法使用——导数对高频信号最敏感,而噪声恰好是高频信号。在一维信号中,一个明显的阶跃边缘被淹没在噪声的导数响应中,完全无法辨认。
解决方案是先平滑再求导:用高斯滤波器平滑图像以抑制噪声,再对平滑后的图像求导。根据卷积的求导性质:
先平滑再求导等价于直接用高斯函数的导数与原图卷积——只需要做一次卷积而非两次。这就是高斯导数滤波器(Derivative of Gaussian Filter)的核心思想。
的选择决定了检测到的边缘尺度: 越大,平滑越强,检测到的是大尺度的粗糙边缘(如物体轮廓); 越小,保留的高频越多,检测到的是小尺度的精细特征(如纹理边缘)。
从梯度到边缘的基本流程
- 平滑:用高斯滤波器抑制噪声
- 增强:计算梯度幅值,突出高对比度区域
- 定位:确定哪些局部极大值是真正的边缘——需要阈值化和细化
单一阈值面临两难:阈值太低,大量噪声被当作边缘;阈值太高,弱边缘被遗漏。Canny 边缘检测器给出了一个经典的解决方案。
Canny 边缘检测器
设计准则
一个「好」的边缘检测器应当满足三个标准:
好的检测能力(Good Detection):尽可能找到所有真实边缘(低漏检),同时尽可能少误报噪声(低误检)。
好的定位能力(Good Localization):检测到的边缘位置应尽量接近真实边缘的精确位置。
单响应(Single Response):每条真实边缘只产生一个响应,而不是一团宽的「边缘区域」。
这三个标准之间存在内在的张力——更大的滤波核能减少噪声误检,但会牺牲定位精度。Canny 证明了高斯函数的一阶导数在信噪比和定位精度的乘积意义下是近似最优的。
算法流程
Canny 边缘检测器是计算机视觉中使用最广泛的边缘检测器,其流程分为四步:
flowchart LR
A["高斯导数滤波"] --> B["梯度幅值与方向"]
B --> C["非极大值抑制"]
C --> D["滞后阈值化"]
classDef step fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
class A,B,C,D step
第一步:高斯导数滤波。用高斯导数滤波器分别计算 和 。
第二步:计算梯度幅值与方向。从偏导数求出每个像素的梯度幅值 和方向 。
第三步:非极大值抑制(Non-Maximum Suppression, NMS)。梯度幅值图中的边缘通常是宽的「山脊」,需要细化为单像素宽度。方法是:对每个像素 ,沿其梯度方向找到前后两个采样点 和 (通常不落在整数格点上,需要插值估计值),如果 的梯度幅值大于 和 ,则保留;否则抑制(置零)。
由于 和 通常不在整数像素位置上,需要用双线性插值(Bilinear Interpolation)估计它们的梯度幅值。设 落在四个相邻像素 围成的单元格内,其相对坐标为 ,则插值公式为:
先沿一个方向做两次线性插值,再沿另一个方向做一次——总共只需要 3 次线性插值。
第四步:滞后阈值化(Hysteresis Thresholding)。NMS 之后的边缘图中可能有一些弱边缘段被遗漏——尤其是当一条完整的边缘在某些位置梯度稍弱时,单一阈值会把它截断。
滞后阈值化使用两个阈值 :
- 梯度幅值 的像素标记为「强边缘」
- 梯度幅值 但 的像素标记为「弱边缘」
- 从强边缘像素出发,沿边缘方向做连通分量搜索,将与强边缘相连的弱边缘也保留下来
直觉上,高阈值保证了「起点」的可靠性,低阈值保证了边缘的「连续性」——一条边缘只要在某些位置足够强,就可以沿着较弱的部分延伸下去,不会被截断。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import cv2 import numpy as np # 完整的 Canny 流程也可以手动实现: # 1. 高斯平滑 blurred = cv2.GaussianBlur(img, (5, 5), sigma) # 2. 计算梯度(Sobel) gx = cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize=3) gy = cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize=3) magnitude = np.sqrt(gx**2 + gy**2) direction = np.arctan2(gy, gx) # 3-4. NMS + 滞后阈值化(OpenCV 一步完成) edges = cv2.Canny(img, threshold1=50, threshold2=150) |
σ 的影响
Canny 检测器中高斯核的 同样决定了检测到的边缘尺度。大 检测粗边缘,小 检测细节——这与上一章讨论的多尺度分析思想一致。没有一个「正确」的 值,选择取决于具体任务。
边缘检测的另一种思路:LoG 与零交叉
除了找一阶导数的极值,还可以用二阶导数找零交叉。高斯的拉普拉斯算子(Laplacian of Gaussian, LoG)对图像做二阶微分:
LoG 响应的零交叉点(从正变负或从负变正的位置)对应边缘。LoG 可以用上一章讨论的 DoG 来近似。这种方法的好处是零交叉总是形成封闭曲线,但缺点是定位精度不如一阶导数方法。
从边缘到物体轮廓
到目前为止,我们建立了一套完整的低层边缘检测流水线:高斯平滑 → 梯度计算 → 非极大值抑制 → 滞后阈值化。Canny 检测器在信号处理的意义上几乎是最优的。但它的输出和人类感知的物体轮廓之间有多大差距?
低层边缘 ≠ 物体轮廓
Canny 检测器的输出和人类感知的物体轮廓之间有很大差距。Canny 对所有的亮度变化一视同仁——它既检测到了物体的真实边界,也检测到了纹理内部的边缘、阴影的边缘、以及噪声产生的伪边缘。而人类在标注边缘时,会自然地忽略纹理内部的变化,只标出物体之间和物体部件之间的语义边界。
边缘检测器回答的是「哪里的亮度变化大」,而人类关心的是「哪里是物体的边界」——这是两个本质不同的问题。
Pb 边缘检测器
Pb 边缘检测器试图弥合这道鸿沟。它的核心思想是:不再仅仅依赖亮度梯度,而是综合亮度、颜色和纹理三种线索,通过学习来判断每个位置是否为边界。
边界特征
Pb 在每个像素位置放一个圆形区域,沿某个方向将其分为两个半圆盘,然后比较两侧的特征分布差异:
亮度梯度(BG):比较两侧亮度()分布的 距离。
颜色梯度(CG):比较两侧颜色(、)分布的 距离。
纹理梯度(TG):先用滤波器组 + -means 得到 texton 标记图,再比较两侧 texton 直方图的 距离。
这里的 距离衡量两个直方图 、 的差异:
每种特征在不同的方向 和尺度 上计算,得到 、、。
线索融合
三种线索需要融合成一个边界概率。这里有一个重要的实验设计问题:什么类型的分类器最适合?Pb 的作者系统地测试了多种模型:
| 分类器 | 特点 |
|---|---|
| 分类树 | 自顶向下分裂最大化熵,有误差界 |
| 密度估计 | 用 -means 做自适应分箱 |
| 逻辑回归(3 个变体) | 线性项和二次项 |
| 层次化专家混合(HME) | 最多 8 个专家,EM 拟合 |
| 支持向量机(SVM) | 高斯核, 参数化 |
这些模型覆盖了从简单到复杂、从参数到非参数的广泛范围——目的是确保结论不依赖于特定分类器的选择。
评估方法
分类器在 Berkeley 分割数据集(BSDS)上训练——这个数据集包含 1020 张自然图像和 11595 份人工标注的分割结果(30 名标注者,耗时 8 个月,共 1458 人时)。
评估使用精确率-召回率曲线(Precision-Recall Curve):
- 召回率(Recall)= 找到的真实边界比例
- 精确率(Precision)= 检测结果中正确的比例
Pb 取得了 的成绩,显著优于 Canny 的 ,但离人类的 仍有不小差距。作为对比基线,课件还测试了二阶矩矩阵方法(2MM,即 Harris 角点检测器的理论基础,用特征谱做逻辑回归)——其 值介于 Canny 和 Pb 之间。
各线索单独使用时性能接近(BG 0.62、CG 0.60、TG 0.61),但组合后显著提升(BG+TG 0.65、BG+CG+TG 0.67),说明三种线索提供了互补信息。
关键发现
- 简单的线性模型就足以完成线索融合——所有线索在逻辑回归中的权重大致相等,更复杂的非线性分类器(如 SVM、HME)并没有带来显著提升。这是一个令人惊讶的结果:瓶颈在于特征而非分类器
- 纹理线索不可或缺:在复杂自然图像中,仅靠亮度和颜色无法区分纹理内部的边缘和真正的物体边界。纹理梯度的加入是性能提升的关键——单纯的「纹理抑制」(试图去掉纹理产生的边缘)是不够的,必须正面建模纹理
- 经验方法是关键:无论是单个线索的校准还是多线索的融合,数据驱动的方法都优于纯手工设计
- 后续的深度学习方法(如 HED、BDCN)在准确率上进一步接近了人类水平,但 Pb 确立的「多线索 + 学习融合」范式至今仍有影响
Global Pb
Pb 的改进版本 gPb(Global Pb)在局部边界检测的基础上加入了全局信息(通过谱聚类获取图像的全局结构),进一步提升了边界检测质量。gPb 与分水岭算法结合(gPb-owt-ucm)是传统边界检测方法中的最佳方案,将在下一章讨论。
直线检测
边缘检测输出的是零散的边缘点,但很多场景中我们更需要检测出直线段——比如建筑物的棱线、道路的车道线。从散乱的边缘像素中提取出结构化的几何元素,是从底层到中层的又一步跨越。
一种实用的方法是基于连通分量的直线段提取:
- 运行 Canny 边缘检测,得到边缘图和每个边缘像素的梯度方向
- 将 量化为 8 个方向(每 一个 bin)
- 对每个方向 ,找出方向在 范围内的边缘像素的连通分量,形成边缘子(Edgelet)
- 对每个边缘子,计算其点集的二阶矩矩阵 ,通过特征值分解判断直线度——特征值比 越大,越接近直线
- 对直线度设阈值,保留足够直的边缘子作为直线段
较大特征值对应的特征向量给出直线的方向。
其他直线检测方法
除了基于连通分量的方法,还有一种经典的直线检测方法——Hough 变换,它利用图像空间与参数空间的对偶关系,通过投票机制找到数据中的直线。Hough 变换将在第 10 章(对齐与图像变换)中详细讨论。
总结
flowchart TD
subgraph 底层边缘检测
G["图像梯度<br/>(有限差分 / Sobel)"] --> C["Canny 检测器<br/>平滑→增强→NMS→滞后阈值"]
end
subgraph 语义边界检测
PB["Pb 检测器<br/>亮度 + 颜色 + 纹理 → 学习融合"]
end
subgraph 结构提取
L["直线段检测<br/>Canny + 方向聚类 + 直线度检验"]
end
C -.->|"局限:无法区分<br/>纹理与物体边界"| PB
C -->|"输出边缘点"| L
classDef low fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
classDef mid fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
classDef struct fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
class G,C low
class PB mid
class L struct
边缘检测的核心思路可以概括为一句话:平滑 → 求导 → 细化 → 阈值 → 连接。Canny 检测器是这条流水线的经典实现,Pb 检测器则将其扩展到多种线索的学习融合,直线段检测进一步从散点中提取出几何结构。