非极大值抑制(NMS)算法入门指南 – wiki词典


非极大值抑制(NMS)算法入门指南

在计算机视觉领域,特别是目标检测(Object Detection)任务中,非极大值抑制(Non-Maximum Suppression, NMS)是一个至关重要且广泛应用的后处理算法。它旨在清理检测结果,消除冗余的边界框(Bounding Box),为每个检测到的目标只保留一个最精确的预测。本指南将详细介绍NMS的原理、工作流程,并提供一个简单的代码实现。

1. 为什么需要NMS?

现代目标检测算法(如YOLO, Faster R-CNN, SSD等)在预测时,通常会针对同一个物体生成大量的候选边界框。如下图所示,模型可能在检测一只猫时,输出了多个重叠的、不同置信度的边界框。

多个重叠的边界框

这种冗余的输出是不理想的,我们希望最终的结果是“一张图,每只猫一个框”。NMS算法就是解决这个问题的关键,它能够筛选出这些候选框中“最好”的一个,并抑制掉(删除)其他多余的框。

2. 核心概念

在深入NMS算法之前,我们需要理解几个核心概念:

  • 边界框 (Bounding Box): 一个矩形框,通常由左上角和右下角的坐标 (x1, y1, x2, y2) 或者中心点、宽高 (x, y, w, h) 来定义。它圈定了图像中检测到的物体。
  • 置信度 (Confidence Score): 每个边界框都伴随一个置信度得分,这个数值(通常在0到1之间)表示模型认为该边界框内存在一个物体的可能性有多大。得分越高,模型越“自信”。
  • 交并比 (Intersection over Union, IoU): 这是衡量两个边界框重叠程度的度量标准。它的计算方式是两个框交集(Intersection)的面积除以它们并集(Union)的面积。IoU的取值范围在0到1之间,0表示完全不重叠,1表示完全重合。

IoU 计算示意图

IoU是NMS算法决策过程中的核心依据。

3. NMS 算法的步骤

NMS是一个贪心算法,其工作流程直观且高效。

输入:
* B: 包含所有候选边界框的列表,每个框包含其坐标和置信度。
* S: 对应每个边界框的置信度得分列表。
* N_iou: IoU阈值,一个介于0和1之间的数字,用于判断重叠程度。

输出:
* D: 经过筛选后,最终保留的边界框列表。

算法流程如下:

  1. 按置信度排序:将所有候选边界框B按照它们的置信度得分S从高到低进行排序。
  2. 选择最高分框:选择置信度最高的边界框M,将它从B中移除,并添加到最终结果列表D中。
  3. 计算IoU并抑制:遍历B中剩余的所有边界框b_i,分别计算它们与M的IoU。
  4. 删除重叠框:如果某个边界框b_iMIoU大于预设的阈值N_iou,则认为它们检测的是同一个物体。将这个b_i从列表B中删除。
  5. 重复:回到第2步,从剩余的B中选择置信度最高的框,并重复此过程。
  6. 结束:直到列表B为空,算法结束。列表D中包含的就是最终的检测结果。

简而言之,NMS的思想就是:“保留最高分的,干掉它附近的”

4. Python 代码实现

下面是一个使用Python和NumPy实现的简单NMS算法。

“`python
import numpy as np

def non_max_suppression(boxes, scores, iou_threshold):
“””
非极大值抑制算法实现

参数:
- boxes: numpy数组,形状为(N, 4),每行代表一个边界框,格式为[x1, y1, x2, y2]
- scores: numpy数组,形状为(N,),代表每个边界框的置信度得分
- iou_threshold: float,IoU阈值

返回:
- keep_indices: list,保留下来的边界框的索引
"""
# 获取边界框的坐标
x1 = boxes[:, 0]
y1 = boxes[:, 1]
x2 = boxes[:, 2]
y2 = boxes[:, 3]

# 计算每个边界框的面积
areas = (x2 - x1 + 1) * (y2 - y1 + 1)

# 按照置信度得分从高到低排序,获取排序后的索引
order = scores.argsort()[::-1]

keep_indices = []
while order.size > 0:
    # 1. 选择置信度最高的边界框索引
    i = order[0]
    keep_indices.append(i)

    # 2. 获取该框与其他所有剩余框的交集区域坐标
    #    - np.maximum/minimum 用于逐元素比较
    xx1 = np.maximum(x1[i], x1[order[1:]])
    yy1 = np.maximum(y1[i], y1[order[1:]])
    xx2 = np.minimum(x2[i], x2[order[1:]])
    yy2 = np.minimum(y2[i], y2[order[1:]])

    # 3. 计算交集面积
    w = np.maximum(0.0, xx2 - xx1 + 1)
    h = np.maximum(0.0, yy2 - yy1 + 1)
    intersection = w * h

    # 4. 计算交并比 (IoU)
    #    iou = intersection / (area_i + area_j - intersection)
    iou = intersection / (areas[i] + areas[order[1:]] - intersection)

    # 5. 找到IoU小于等于阈值的边界框,这些是需要保留的
    #    inds_to_keep 的索引是相对于 order[1:] 的
    inds_to_keep = np.where(iou <= iou_threshold)[0]

    # 6. 更新order列表,保留未被抑制的框,并进入下一次循环
    #    +1 是因为 inds_to_keep 是从 order[1:] 开始的索引
    order = order[inds_to_keep + 1]

return keep_indices

— 示例 —

if name == ‘main‘:
# 假设有5个边界框
boxes = np.array([
[100, 100, 210, 210], # box 1
[120, 120, 220, 220], # box 2
[110, 110, 215, 215], # box 3
[300, 300, 380, 380], # box 4 (另一个物体)
[20, 20, 80, 80] # box 5 (远处的物体)
])

# 对应的置信度得分
scores = np.array([0.95, 0.85, 0.9, 0.92, 0.88])

# 设置IoU阈值
iou_threshold = 0.5

# 执行NMS
kept_indices = non_max_suppression(boxes, scores, iou_threshold)

print("原始边界框数量:", len(boxes))
print("NMS后保留的边界框索引:", kept_indices)
print("保留的边界框:", boxes[kept_indices])
print("保留的边界框得分:", scores[kept_indices])

# 预期输出:
# 算法会保留得分最高的 box 1 (索引0),然后抑制掉与之高度重叠的 box 2 和 box 3。
# 接着处理剩余的 box 4 和 box 5,因为它们与其他框不重叠,所以也会被保留。
# 最终保留的索引应该是 [0, 3, 4] (顺序可能因排序而异)。

“`

5. 参数与调优

NMS算法的行为主要由IoU阈值控制:

  • 低IoU阈值 (如 0.3):抑制条件更严格。稍微有一点重叠的框都可能被删除。这可能导致在物体密集的情况下,误删了属于不同但靠近的物体的框,导致漏检(False Negatives)。
  • 高IoU阈值 (如 0.7):抑制条件更宽松。只有当两个框高度重叠时,一个才会被删除。这可能导致对同一个物体的多个检测结果被保留下来,导致误检(False Positives)。

选择一个合适的IoU阈值非常重要,通常需要根据具体任务和数据集的特点进行调整。0.5是一个常见且通用的初始值。

6. NMS的局限性与变种

标准NMS的局限性:

当两个不同的物体在图像中靠得非常近时(例如,人群中的两个人),它们各自的真实边界框可能会有很高的IoU。在这种情况下,标准NMS的贪心策略可能会错误地将其中一个物体的框当作冗余框抑制掉,导致检测失败。

NMS的变种:

为了解决这个问题,研究者们提出了一些NMS的改进版本:

  • Soft-NMS: 与标准NMS粗暴地将重叠框得分置零(删除)不同,Soft-NMS会根据IoU值来降低重叠框的置信度。IoU越大,置信度被惩罚得越多。这样,靠近的物体框依然有机会被保留下来,只是得分变低了。
  • DIoU-NMS (Distance-IoU NMS): 在抑制标准中,除了IoU,还引入了边界框中心点之间的距离。即使IoU很高,如果两个框的中心点距离较远,也可能被认为是不同的物体,从而被保留。

7. 总结

非极大值抑制(NMS)是目标检测流程中不可或缺的一环。它通过一个简单而有效的贪心策略,清除了模型产生的冗余检测,使得最终输出干净、准确。理解NMS的工作原理、掌握其代码实现,并了解其参数和局限性,对于任何计算机视觉从业者来说都是一项基本技能。

滚动至顶部