文章目录
在目标检测算法中,模型输出的原始预测框往往数量庞大且高度重叠。为了得到清晰、准确的最终检测结果,NMS非极大值抑制是必不可少的关键步骤。
摘要:NMS(非极大值抑制)是目标检测后处理中的关键步骤,用于从大量高度重叠的候选框中筛选出清晰、准确的最终检测结果。本文先介绍 NMS 的核心思想,再通过一个包含 4 个候选框的完整数值示例,逐步演示"按置信度排序 → 迭代选择 → 计算 IoU → 抑制冗余框"的全过程,并解释多类别目标为何要按类别分组执行 NMS,最后给出完整的执行流程与 Python 伪代码参考。
核心概念
NMS的核心思想非常直观:面对大量可能指向同一目标的候选框,我们只需要保留最佳的那一个,并抑制掉其他冗余的框。
但需要纠正一个常见的误解:NMS的最终结果并不一定是只保留一个候选框。 它保留的数量取决于图像中实际存在的独立目标数量。如果图中存在多个互不重叠的目标,算法会为每个目标各保留一个最佳候选框。
数值计算过程示例
假设在图像中的同一个目标上,算法生成了4个候选框,同时我们对另一个远处目标也生成了一个框:
- 预设IoU阈值:0.5(判定框与框之间是否"高度重叠"的标准)
- 输入检测框:
| 框 | 置信度 | 坐标格式 x1, y1, x2, y2 |
|---|---|---|
| A | 0.95 | 0, 0, 100, 100 |
| B | 0.90 | 10, 10, 90, 90 |
| C | 0.85 | 20, 20, 80, 80 |
| D | 0.80 | 200, 200, 300, 300 |
步骤1:按置信度排序
将所有候选框按照置信度从高到低排序:
A(0.95) > B(0.90) > C(0.85) > D(0.80)
步骤2:迭代选择(第1轮)
取出当前置信度最高的框 A,并直接将其加入到最终结果集中。
- 最终结果集:A
- 剩余待处理框:B, C, D
步骤3:计算IoU重叠度
IoU即"交并比",计算公式为:IoU = 交集面积 / 并集面积。
框 A 与其余各框的IoU计算:
-
A与B的IoU:
- 框B完全被框A包含,交集面积就是B的面积:80 × 80 = 6400
- 并集面积 = 框A面积 + 框B面积 - 交集面积 = 10000 + 6400 - 6400 = 10000
- IoU = 6400 / 10000 = 0.64
-
A与C的IoU:
- 过计算可得交集面积为3600,并集面积为14400
- IoU = 3600 / 14400 = 0.36
-
A与D的IoU:
- 两个框距离较远,没有交集
- IoU = 0
步骤4:抑制冗余候选框
将计算得到的IoU值与预设阈值0.5进行比较:
| 对比项 | IoU值 | 结论 |
|---|---|---|
| A与B | 0.64 | > 0.5,判定为重复框,将B移除(抑制) |
| A与C | 0.36 | < 0.5,重叠度不高,保留C |
| A与D | 0.00 | < 0.5,完全不同的目标,保留D |
经过本轮处理,剩余待处理框为:C, D
步骤5:重复迭代(第2轮)
- 从剩余框中取出置信度最高的 C ,加入最终结果集。
- 最终结果集:A, C
- 剩余待处理框:D
- 计算C与D的IoU:由于两个框相距很远,IoU = 0
- 0 < 0.5,因此保留D。
- 剩余待处理框:D
步骤6:重复迭代(第3轮)
取出剩余框中最后的 D,加入最终结果集。
- 最终结果集:A, C, D
- 剩余待处理框:无
最终结论: 本次NMS操作保留了3个候选框(A、C、D)。框B由于与最高置信度的框A高度重叠而被成功抑制;而框D因为代表不同的目标,被正确保留了下来。
多类别目标如何处理?
在实际的目标检测应用中,图像中往往包含多个类别的目标。此时的处理方式是在每个类别内部单独循环执行NMS。
具体流程为:
- 按类别分组:将所有候选框根据其预测的类别标签进行分组,例如所有"猫"的框在一组,所有"狗"的框在另一组。
- 组内执行NMS:依次对每个类别组独立执行上述的NMS数值计算过程。
- 合并结果:将每个类别组最终保留的框汇总起来,作为整体的检测输出。
这样做的原因是为了避免跨类别的误抑制。例如,一只猫和一只狗紧紧挨着,两个框高度重叠。如果不区分类别地全局执行NMS,算法会误认为这是同一个目标的重复框,从而强行移除其中一个,导致漏检。按类别分开处理,就可确保高置信度的"猫"框永远不会去抑制属于"狗"类的候选框。
完整的执行流程
整个目标检测后处理NMS过程的完整流程如下:
1. 按类别对所有候选框进行分组
2. 对每个类别依次执行:
a. 按置信度排序
b. 选择分数最高的框,加入结果集
c. 计算该框与其他所有框的IoU
d. 移除所有IoU大于预设阈值(如0.5)的框
e. 重复步骤b-d,直至候选框为空
3. 合并所有类别结果,输出最终检测结果
代码参考
NMS的Python伪代码实现如下:
python
def nms(bboxes, scores, iou_threshold=0.5):
# bboxes: 候选框坐标列表,形式为[[x1,y1,x2,y2],...]
# scores: 对应的置信度列表,形式为[score1, score2,...]
# 1. 按置信度从大到小排序
order = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)
keep = []
while order:
# 2. 取出当前最高分的索引
i = order[0]
keep.append(i)
# 3. 计算当前框与其余所有框的IoU
ious = [compute_iou(bboxes[i], bboxes[j]) for j in order[1:]]
# 4. 保留IoU < 阈值的框,移除IoU > 阈值的框
order = [order[j+1] for j, iou in enumerate(ious) if iou < iou_threshold]
return keep
实践中的补充说明
- IoU阈值:通常设为0.4~0.6之间,值过高会导致重叠框残留较多,值过低则容易误删邻近的不同目标。
- 置信度阈值:通常先过滤掉打分过低的候选框(如小于0.5),减少NMS的计算量。
- 跨类别NMS:特定场景下,若不同类别在语义上互斥,可全局应用NMS以获得更好的性能。