ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

边缘检测评估指标解析:ODS、OIS与AP的数学原理与工程实践

边缘检测评估指标解析:ODS、OIS与AP的数学原理与工程实践 做边缘检测评估这件事我前后折腾了不少时间。从最早用 Canny 调两张图的阈值到后来复现深度学习边缘检测模型最让人头疼的不是网络结构而是结果表里那三个缩写的含义——ODS、OIS、AP。很多人以为边缘检测评估就是算个像素准确率实际上这套指标体系从经典算法时代一路演化到深度学习时代背后的数学推导和设计逻辑非常值得掰开讲清楚。这篇文章我会从 Canny 说起梳理边缘检测指标的进化史并给出 ODS/OIS/AP 的推导过程和复现时的实操经验适合正在做边缘检测、轮廓提取或者复现相关论文的朋友参考。1. 边缘检测的发展脉络算法变了评价方式也得跟着变1.1 传统时代从 Sobel、Prewitt 到 Canny边缘检测这个任务的历史非常久。早期思路基本都围绕图像梯度展开Sobel、Prewitt 这些算子本质上是拿一个固定卷积核去逼近图像的 x 方向、y 方向导数和梯度幅值梯度大的像素就认为是边缘。Prewitt 算子的核结构是平均加差分对噪声有一定平滑作用但定位精度一般检测出来的边缘也偏粗。之后的 Canny 把边缘检测做成了一套完整流程高斯滤波去噪、计算梯度幅值和方向、非极大值抑制、双阈值滞后连接。这套方法在 1986 年提出到今天仍然是工业视觉里最常用的算法之一。Canny 能统治几十年核心在于它把“边缘检测”拆成了三个明确目标低错误率、好的定位、单像素响应。这三个目标也被后人称为 Canny 三准则虽然当时没有严格的数学统一最优解但工程的每一步都在朝这三个目标靠拢。Canny 的双阈值设计是个很巧妙的东西。高阈值确定强边缘低阈值保留弱边缘的连通性中间断裂的边缘能通过滞后连接补回来。实际用的时候你得调两组参数高斯核大小、高阈值、低阈值一旦图像光照或噪声变了参数经常要重新试。这也是我早期做视觉项目时最烦的事同一个 Canny 在这张图上效果不错换一张图就全是碎边缘。1.2 深度学习时代从手工特征到端到端概率图深度学习进入边缘检测领域后玩法完全不同了。以 HEDHolistically-Nested Edge Detection为代表的方法把 VGG 等骨干网络的多层特征融合起来输出一张和原图同尺寸的边缘概率图再配合深度监督做训练。到了 RCF进一步把各层的丰富特征都利用起来边缘定位和细节保留能力又上了一个台阶。这类模型本质上解决的是“边缘是语义的还是低层的”这个老问题。传统梯度算子只能看到局部灰度变化很容易被纹理、阴影干扰。深度网络通过大感受野学到上下文能输出更接近人眼感知的边缘。比如一张有斑马纹理的图传统算子可能把纹理内部也当成边缘深度模型则能区分纹理边缘和物体轮廓。但深度学习模型的输出和 Canny 有个本质差异Canny 输出的是二值图深度学习输出的是概率图。你要得到最终边缘必须再设一个阈值把概率图变成二值图。这个阈值选多少、怎么选直接影响最终效果。于是问题来了不同方法在不同阈值下表现不一样有些方法阈值低时召回高但精度低阈值高时反过来这就需要一个更系统的评估协议不能再用“我调了个好阈值给你看”这种方式比较算法了。1.3 评估需求的转变从人工挑图到统一度量传统时代大家评估边缘检测很多就是给一两张图看看视觉效果或者在同一组人工设定的阈值下算像素准确率。这种做法的最大问题是不可复现换个阈值结果就变而且算准确率本身在边缘检测里就是有陷阱的。到了深度学习时代几十种方法要在同一条赛道公平比较就必须把“阈值怎么选”这个问题从算法作者手里收回来交给评估协议统一处理。这直接催生了后来大家熟悉的固定阈值最优ODS、图像自适应阈值最优OIS和平均精度AP这套评估框架。理解了这条演进线你就能明白 ODS/OIS/AP 不是三个孤立的指标而是一套针对“概率图输出”设计的标准化考核方案。2. 评估指标演进逻辑从单图准确率到数据集级指标2.1 为什么不能只看准确率先看一个很直觉的问题为什么边缘检测不直接算像素准确率你随手拿一张自然图像边缘像素占整张图的比例通常只有百分之几。如果模型输出一张全黑图也就是说把所有像素都预测为非边缘那准确率可能高达 95% 以上。这个结果显然没有意义因为边缘一个都没找到。所以边缘检测必须同时看两个方向一个是预测出来的边缘里有多少是对的也就是精确率Precision另一个是真实边缘里有多少被找到了也就是召回率Recall。这两个指标一个管“不能乱报”一个管“不能漏报”放在一起才能比较客观地反映模型能力。2.2 Precision、Recall、F-measure评估骨架用数学一点的说法给定二值预测图和二值真实标签逐像素对比后能得到四个数真正例 TP预测为边缘且真是边缘、假正例 FP预测为边缘但实际不是、假负例 FN实际是边缘但没预测出来。精确率和召回率定义如下$$Precision \frac{TP}{TP FP}$$$$Recall \frac{TP}{TP FN}$$单独看任何一个都不够。一个把整张图都预测成边缘的模型召回率可能是 100%但精确率极低一个只输出最确信那几条边缘的模型精确率可以很高但召回率很低。实际使用中边缘检测更关注两者的平衡因此最常用的是 F-measure也就是精确率和召回率的调和平均标准形式是 F1$$F_1 \frac{2 \cdot Precision \cdot Recall}{Precision Recall}$$调和平均的特点是只有当精确率和召回率都高时F1 才会高。如果其中一个掉到 0.1哪怕另一个是 1.0F1 也不会超过 0.2 左右。这正好惩罚了那种“顾头不顾尾”的模型。后来很多论文里也出现过 Fβ 这类变体可以调整精确率和召回率的权重但边缘检测主流还是用 F1。2.3 单图阈值扫描还是全局固定阈值有了 F1下一步就是处理“阈值”。深度学习模型输出的是概率图你设阈值 0.3 和设阈值 0.7得到的 P/R 完全不同。于是评估协议自然要回答这个阈值应该谁来定最理想的情况肯定是在每张图上都选一个让该图 F1 最高的阈值然后对所有图的 F1 取平均。但这么做相当于给每个测试样本都配了一个“作弊器”因为它假设你在测试时能知道这张图的真实标签从而选出最优阈值。现实中部署模型时你不可能每来一张图还拿真值去调阈值。所以更公平的做法是在整个数据集上统一用一个固定阈值跑出每张图的 P/R再把所有结果汇总得到一个总体 F1扫描所有可能的固定阈值取 F1 最大的那个作为最终分数。这个分数就是 ODSOptimal Dataset Scale代表“全局最优固定阈值下的性能”。而每张图各自最优再平均的分数就是 OISOptimal Image Scale代表“理想上限”。两种做法都有意义一个偏现实一个偏理想所以论文一般两个都报。2.4 AP 的引入对阈值不敏感的鲁棒性度量ODS 和 OIS 本质上都是“单点最优”不管扫描多少阈值最后汇报的是那个最好的 F1有点“只展示高光时刻”的意思。可实际使用中你要部署一个模型不可能精确知道该用哪个阈值。这时候需要一种能在所有阈值下综合评价模型表现的指标这就是 APAverage Precision。AP 的思路很简单把阈值从 0 到 1 扫一遍每扫一个阈值得到一组 Precision 和 Recall把所有 (Recall, Precision) 点连成一条 PR 曲线曲线下的面积就是 AP。面积越大说明模型在阈值变化时仍然能保持较高的精度和召回率也就是对阈值不敏感、整体更稳。边缘检测论文里把 ODS、OIS、AP 并排放在结果表里前两个看“最优状态”AP 看“全程稳定度”三者互补。3. ODS/OIS 的数学定义与推导3.1 符号约定从概率图到二值图为了把 ODS/OIS 讲清楚先约定符号。设测试数据集共有 N 张图像第 i 张图像为 (I_i)对应的真实边缘图为 (G_i)是二值图1 表示边缘像素。模型对 (I_i) 的输出记作概率图 (S_i)值在 0 到 1 之间。给定阈值 (\tau)对概率图做二值化$$B_i(\tau) \mathbb{1}[S_i \ge \tau]$$也就是说概率大于等于阈值的像素预测为边缘否则为非边缘。接下来把 (B_i(\tau)) 和 (G_i) 逐像素比较统计出这一张图的 (TP_i(\tau))、(FP_i(\tau))、(FN_i(\tau))。这里要注意实际评估协议往往不是简单逐像素相等而是会把预测边缘和真实边缘做一定宽度的邻域匹配这一点在第 5 部分会细说这里先用逐像素模型做推导原理是一样的。3.2 全局累计的 Precision、Recall 和 FODS 最关键的一点是它不是在每张图上算完 F1 再平均。而是在每个阈值下先把所有图像的 TP、FP、FN 分别累加起来得到一个“数据集级”的混淆矩阵$$TP_{\text{dataset}}(\tau) \sum_{i1}^{N} TP_i(\tau)$$$$FP_{\text{dataset}}(\tau) \sum_{i1}^{N} FP_i(\tau)$$$$FN_{\text{dataset}}(\tau) \sum_{i1}^{N} FN_i(\tau)$$然后用这些累计值计算全局精确率和全局召回率$$P_{\text{dataset}}(\tau) \frac{TP_{\text{dataset}}(\tau)}{TP_{\text{dataset}}(\tau) FP_{\text{dataset}}(\tau)}$$$$R_{\text{dataset}}(\tau) \frac{TP_{\text{dataset}}(\tau)}{TP_{\text{dataset}}(\tau) FN_{\text{dataset}}(\tau)}$$对应的全局 F1 为$$F_{\text{dataset}}(\tau) \frac{2 \cdot P_{\text{dataset}}(\tau) \cdot R_{\text{dataset}}(\tau)}{P_{\text{dataset}}(\tau) R_{\text{dataset}}(\tau)}$$直觉上这个做法的意思是把所有测试图像拼成一张“大图”在这张大图上算一次 P/R/F。这样做能避免一些边缘很少的图像对整体分数造成过大的波动让分数更能反映模型在大规模数据上的平均水准。3.3 ODS 的数学推导与扫描实现ODS 的定义就是在所有可能的阈值中选一个让全局 F1 最大的值$$ODS \max_{\tau} ; F_{\text{dataset}}(\tau)$$这个表达式看起来像一个优化问题但实际没有闭式解因为你没法对 (F_{\text{dataset}}(\tau)) 求导并解方程它是关于阈值 (\tau) 的阶梯函数因为预测图的二值化结果随 (\tau) 变化会发生像素级的跳变。所以标准做法是网格扫描在 0 到 1 之间按一定步长枚举阈值对每个阈值计算全局 F1最后取最大值。扫描步长通常取 0.01或者按照评估脚本里的预设阈值序列来。如果步长太粗比如只扫 0.1、0.5、0.9可能漏掉最优点如果太细计算量会成倍增加尤其当数据集图片很多、分辨率又高时。我实际跑的时候习惯先用 0.05 粗扫一轮找到峰值大概在哪个区间再在峰值附近用 0.001 精扫能省不少时间。3.4 OIS 的数学定义与差异OIS 的计算方式和 ODS 有本质不同。OIS 允许每张图像使用自己独立的最优阈值先在第 i 张图上扫描阈值得到该图的最大 F1$$F_i^{\max} \max_{\tau_i} ; F_i(\tau_i)$$其中 (F_i(\tau_i)) 是第 i 张图的局部 F1由第 i 张图自己的 TP、FP、FN 算出。然后对所有图像的最大 F1 取平均$$OIS \frac{1}{N} \sum_{i1}^{N} F_i^{\max}$$从定义就能看出来OIS 一般会高于或等于 ODS因为它的约束更宽松每个图都可以为它选一个最优阈值。你可以把 ODS 理解成“一个阈值打天下”OIS 理解成“每个图都能私调阈值”。两个指标之间的距离也有信息量如果某模型的 OIS 比 ODS 高出一大截说明模型对阈值的敏感度很高不同图像的输出概率分布差异很大部署时你需要谨慎选择固定阈值如果两者很接近说明模型输出质量很稳随便设一个合理阈值效果都不会差太多。3.5 亲手算一遍一个只有两张图的例子为了把 ODS 和 OIS 的差别讲透我做一个非常小的模拟计算。假设只有两张测试图扫描阈值 0.2、0.4、0.6 三档。先在数据集层面对所有像素累计得到全局 P/R/F1阈值全局Precision全局Recall全局F10.20.550.700.6160.40.620.610.6150.60.700.520.597全局 F1 最大值出现在阈值 0.2所以 ODS 0.616。再看每张图各自的最优 F1。假设第一张图在三个阈值下的 F1 分别为 0.58、0.64、0.60最优是 0.64第二张图在三个阈值下的 F1 分别为 0.72、0.63、0.55最优是 0.72。那么 OIS (0.64 0.72) / 2 0.68。这个例子里的数值纯粹是为了演示计算流程不要求与上一张表的全局数值严格保持一致实际计算时全局累计和单图累计是两条路径。但你已经能看出来了OIS 0.68 明显高于 ODS 0.616说明两张图各自有自己偏好的阈值。真实模型里这种差距如果过大往往意味着模型的概率输出还没校准好。4. AP 的数学推导与边界情况4.1 PR 曲线与 AP 的几何意义AP 全称 Average Precision在边缘检测里的定义和目标检测里一脉相承都是对 PR 曲线求积分。做法是让阈值 (\tau) 从 1 往 0 移动一开始阈值很高只有极少数高置信度像素被预测为边缘这时 Precision 通常很高、Recall 很低随着阈值降低越来越多的像素被判定为边缘Recall 升高但误检也变多Precision 开始下降。把所有 (Recall, Precision) 点连起来就得到一条 PR 曲线。AP 就是这条曲线下的面积。数学上可以写成对 Recall 的定积分$$AP \int_{0}^{1} P(R) , dR$$但实际计算时PR 曲线不是光滑函数只是有限个离散点所以需要用求和来近似。4.2 VOC2010 前与后两种 AP 定义目标检测社区里 AP 有两种主流历史定义边缘检测评估脚本大多沿用这两套思路。在 PASCAL VOC 2010 之前AP 用的是“11 点插值法”把 Recall 从 0 到 1 均匀分成 11 个点对每个 Recall 点取该点右侧所有 Precision 的最大值再对 11 个值求平均$$AP \frac{1}{11} \sum_{r \in {0, 0.1, \dots, 1.0}} P_{\text{interp}}(r)$$其中 (P_{\text{interp}}(r) \max_{\tilde{r} \ge r} P(\tilde{r}))。VOC2010 之后官方换成了更精确的面积法对 PR 曲线上的每个点根据 Recall 的增量乘上该位置的插值 Precision然后求和$$AP \sum_{n} (R_{n1} - R_n) \cdot P_{\text{interp}}(R_{n1})$$这里 (R_n) 是排序后第 n 个样本的 Recall 值(P_{\text{interp}}(R_{n1})) 是 PR 曲线上Recall 不小于 (R_{n1}) 时能达到的最大 Precision。和 11 点法相比这种定义几乎保留了曲线的全部形状信息不会被稀疏的 11 个点抹平差异。现在的主流评估都推荐这种。两种定义在曲线平滑时结果接近但曲线波动大时差异很明显。我见过不少方法11 点插值 AP 看起来不错换成面积法 AP 就掉了一截因为后者对曲线中段的下降更敏感。所以复现论文时一定要先确认对方用的是哪种 AP直接拿不同版本的 AP 对比不公平。4.3 边缘检测 AP 与目标检测 AP 的区别很多人一看到 AP 就往目标检测上联想以为边缘检测 AP 也是按检测框 IoU 来算其实不是。目标检测 AP 的匹配单位是一个个候选框两个框的 IoU 超过阈值就算匹配成功边缘检测 AP 的匹配单位是像素或者边缘片段预测边缘像素落在真实边缘的某个邻域内就算 TP落在远离真实边缘的区域就是 FP。本质上都是“预测和真值之间的距离度量”但具体实现差得很远。边缘检测里有个特殊问题叫边缘厚度。深度网络输出的概率图如果你不经过非极大值抑制直接阈值化得到的边缘往往有好几个像素宽。宽度越宽TP 容易增加但同时 FP 也会增加尤其是阈值较低时边缘附近“擦边”的像素会被大面积预测为边缘导致 Precision 快速下降AP 被拖低。因此在主流评估协议里计算 AP 前通常会对概率图做 NMS 瘦身否则 AP 反映的就不完全是定位能力还混杂了厚度惩罚。4.4 计算 AP 的代码骨架我贴一段简化版的 Python 思路帮你理解 AP 的累计过程。实际工程代码远比这个复杂要处理匹配宽度、NMS、多标注者合并等问题但核心骨架是这些import numpy as np def compute_ap_over_thresholds(prob_map, gt_map, thresholds): precisions [] recalls [] for tau in thresholds: pred (prob_map tau).astype(np.uint8) # 实际评估中这里要做膨胀匹配不能直接逐像素相等 tp np.sum((pred 1) (gt_map 1)) fp np.sum((pred 1) (gt_map 0)) fn np.sum((pred 0) (gt_map 1)) precisions.append(tp / max(tp fp, 1e-8)) recalls.append(tp / max(tp fn, 1e-8)) # 按 recall 从低到高排序计算插值后的 AP order np.argsort(recalls) recalls np.array(recalls)[order] precisions np.array(precisions)[order] # 对每个 recall 点取右侧最大 precision 作为插值 interp_precisions np.array([ np.max(precisions[i:]) for i in range(len(precisions)) ]) ap 0.0 for i in range(1, len(recalls)): ap (recalls[i] - recalls[i - 1]) * interp_precisions[i] return ap这段代码最后求和的部分其实就是在近似 PR 曲线下的面积。注意 precision 和 recall 里都加了 1e-8 防止除零这是实践里必须处理的边界情况。5. 实战中的坑与经验指标是工具不是信仰5.1 匹配宽度与边缘膨胀一个小数位就能左右排名ODS/OIS/AP 这些指标看着定义很清楚但落到评估脚本里最影响结果的是“匹配规则”。BSDS500 这类数据集评估边缘不会要求预测边缘和真实边缘严格逐像素相等因为人工标注本身就有主观偏移。通常做法是先把真实边缘图膨胀几个像素比如膨胀半径为 2然后预测边缘只要落在膨胀后的区域内就算 TP。这个“半径”一改所有数字都会变。同一个模型膨胀半径从 1 加到 3ODS 可能从 0.71 跳到 0.78这并不代表模型变强了只是评估宽松了。我踩过这个坑一开始拿自己实现的评估脚本和论文对数字怎么都对不上后来发现是默认的匹配宽度不同。所以复现一项工作第一件事是确认评估代码版本尽量不要混着用不同仓库的指标实现。5.2 NMS 与多响应同一根边缘不能反复计分深度网络输出的边缘概率图天然有厚度如果不做 NMS一根真实边缘可能会对应预测结果里好几条平行的亮带这会虚增 FP。传统的 Canny 流程里 NMS 是内置的一步到了深度学习时代很多评估脚本也默认先对概率图做标准的非极大值抑制再计算指标。实验对比时一定要在论文里写明是否做了 NMS。有些方法专门把厚边缘当成卖点声称“保留更多结构信息”但如果评估协议强制做 NMS这种优势可能体现不出来。反过来如果你复现时忘了对概率图做 NMS你的数字大概率会比论文更难看因为额外的 FP 会拖低 Precision进而拉低 ODS 和 AP。5.3 多标注者与 BSDS500 的标注处理BSDS500 每张训练图有多个人工标注结果不同标注者画出来的边缘在细节上差异不小。评估时怎么合并这些标注始终是个问题。常见做法有三类一是取并集把所有标注者的边缘都当成真值只要预测到任意一个人的标注边缘就算对二是逐个标注者计算指标再平均三是通过多数投票产生一张统一的真值图。不同的选择会造成指标数值的明显差异。早期很多论文默认用“取并集”方式因为这样对预测方法更宽容指标普遍高一些后来有些工作强调逐标注者评估更能反映真实主观性但那样算出的分数往往偏低。你复现时如果发现自己的结果和论文对不上除了看匹配宽度也要看看是不是标注合并方式不同。5.4 阈值扫描与插值的细节步长、顺序和对称性ODS 和 AP 都涉及阈值扫描这中间还有些容易忽略的小毛病。阈值序列不一定是从 0 到 1 均匀采样有的评估代码会用 0.01 步长有的用 0.05有的甚至用图像对应概率图最大值的百分比来生成导致同一个模型得到的 ODS 有细微差别。AP 计算时还会涉及 PR 曲线的排序方向你是从高阈值往低阈值走还是从低阈值往高阈值走插值结果会有一点差异尤其在曲线不光滑时。我的习惯是扫描阈值时尽量覆盖概率图的实际动态范围。有的网络输出概率集中在 0.1 到 0.9你从 0 到 1 均匀扫 100 个点其实够了有的网络输出偏向两端很多阈值下预测图完全不变导致 PR 曲线出现长平台这时候需要保留所有重复点否则插值的面积会算错。别小看这部分处理不好 AP 会有几个点的误差。5.5 训练时能否直接优化 ODS/OIS直接回答不能。ODS/OIS 这类指标对阈值做的是 max 操作整个计算过程不可导没法直接接在 loss 后面反向传播。所以你去看现在的主流边缘检测网络训练时用的都是像素级 loss比如加权交叉熵、类别平衡交叉熵或者 HED 里的多尺度深度监督 loss。这些年也有人尝试用可微的代理函数去近似 ODS比如用 Soft-F1 这类光滑版本作为训练目标但在边缘检测领域效果并没有形成绝对优势。我个人的看法是评估指标主要用来衡量模型最终性能训练时不必强求指标本身可微核心还是把概率输出校准好让模型对阈值不那么敏感。如果指标和 loss 对不上优先怀疑 loss 是否过度偏向某类像素。实际跑实验时还有个小技巧你可以把验证集上不同阈值的 Precision、Recall 曲线打出来观察模型概率分布的校准情况。如果曲线在某一段特别陡说明大多数像素的概率值挤在狭窄区间模型输出不够自信这时可以考虑调整训练 loss 的类别权重。这套诊断方法比单纯看 ODS 数字好用得多。我自己做边缘检测项目最深的感受是ODS/OIS/AP 这套指标是给研究工作准备的“标尺”但它不能替你做工程判断。Canny 时代我们调的是高斯核和双阈值深度学习时代我们调的是网络结构和损失函数但最终落地时你还是要在真实数据上看效果、调阈值、做后处理。指标帮你筛选出靠谱的模型却替代不了你对业务场景的理解。每次看到一张漂亮的边缘图我都会下意识想想它在不同阈值下是不是还稳这也算是个职业病了吧。
返回列表