目标检测入门之非最大值抑制(NMS)算法

1 引言

非最大值抑制是一种主要用于目标检测的技术,旨在从一组重叠框中选择最佳边界框。在下图中,非最大值抑制的目的是删除黄色和蓝色框,这样我们只剩下绿色框作为最终的预测结果。

闲话少说,我们直接进入NMS算法的介绍中.

2. 计算NMS的步骤

为了了解什么是boundingbox,以及IOU的含义,我在[前篇]发表了关于IOU的文章。前篇文章中描述的术语将在本文中继续介绍。我将首先描述NMS在这个特定示例中的工作过程,然后解释一个更通用的算法,将其扩展到不同的场景下。

术语定义:

  • 我们将使用的每个边界框的格式如下: [公式]
  • 让我们假设,对于上述这个特定的图像我们有3个边界框,即:

[公式]

  • 对于每个框,相关定义如下:

[公式]

2.1 根据置信度过滤候选框

  • 作为NMS中的第一步,我们按照置信度降序对框进行排序。排序后我们得到结果为:

[公式]

  • 然后我们定义一个置信度阈值。任何置信度低于此阈值的框都将被删除。对于本例,假设置信度阈值为0.8。使用该阈值,我们将删除黄色框,因为其置信度<0.8。这就给我们留下了:

[公式]

  • 这一步操作后的结果图如下所示:

2.2 根据IOU过滤

  • 由于框的置信度按降序排列,我们知道列表中的第一个框的置信度最高。我们从列表中删除第一个框,并将其添加到新列表中。在我们的例子中,我们将删除绿色框,并将其放入一个新列表中,比如bbox_list_new。
  • 在这个阶段,我们为IOU定义了一个额外的阈值。此阈值用于删除具有高重叠度的框。其原因如下:如果两个框有大量重叠,并且它们也属于同一类,则很可能两个框都覆盖了同一对象(我们可以从上图验证这一点)。由于真实情况是每个对象只有一个框,因此我们尝试删除置信度较低的框。
  • 在上述示例中,假设我们的IOU阈值为0.5
  • 我们现在开始计算绿框的IOU,其中bbox_list中剩余的每个框也具有相同的类。在我们的例子中,我们将只使用蓝色框来计算绿色框的IOU。
  • 如果绿框和蓝框的IOU大于我们定义的阈值0.5,我们将删除蓝框,因为它的置信度较低,并且有明显的重叠。
  • 对图像中的每个框重复此过程,上述示例中最终只生成具有高置信度的唯一框。如下所示:

3. NMS算法

总结上述过程,我们可以得到NMS的计算过程如下:

  • 定义置信度阈值和IOU阈值取值。
  • 按置信度降序排列边界框bounding_box
  • 从bbox_list中删除置信度小于阈值的预测框
  • 循环遍历剩余框,首先挑选置信度最高的框作为候选框.
  • 接着计算其他和候选框属于同一类的所有预测框和当前候选框的IOU。
  • 如果上述任两个框的IOU的值大于IOU阈值,那么从box_list中移除置信度较低的预测框
  • 重复此操作,直到遍历完列表中的所有预测框。

代码实现如下:

相关解释如下:

def nms(boxes, conf_threshold=0.7, iou_threshold=0.4):

此函数将图像候选框列表、置信阈值和iou阈值作为输入。(这里将相应的默认值分别设置为0.7和0.4)

bbox_list_thresholded = []
bbox_list_new = []

接着我们创建了两个名为bbox_list_threshold和bbox_list_new的列表。

  • bbox_list_threshold:包含筛选低置信度框后的新框列表
  • bbox_list_new:包含执行NMS后的最终框列表
boxes_sorted = sorted(boxes, reverse=True, key = lambda x : x[5])

在上述2.1节中,按照置信度的降序对框列表进行排序,并将新列表存储在变量boxes_sorted中.

这里我们采用python的内置sorted函数对其进行排序,该函数根据key字段指定排序规则.

在我们的例子中,我们指定一个关键字reverse=True来按降序对列表进行排序,同时指定第二个关键字用于排序的约束。这里我们使用的lambda函数提供了一个映射,返回每个边界框的第5个元素(置信度)。

经过上述两个参数的设定,在遍历每个框时,排序函数将按照置信度对候选框按照降序排序。

for box in boxes_sorted:
     if box[5] > conf_threshold:
         bbox_list_thresholded.append(box)
     else:
         pass

我们遍历所有已排序的框,并移除置信度低于我们设置的阈值(conf_threshold=0.7)的框

while len(bbox_list_thresholded) > 0:
    current_box = bbox_list_thresholded.pop(0)
    bbox_list_new.append(current_box)

在上述2.2节,我们逐个循环遍历阈值框列表(bbox_list_threshold)中的所有框,直到列表清空。

我们首先从这个列表中删除(弹出)第一个框(当前框),因为它具有最高的可信度,然后将它附加到我们的最终列表中(bbox_list_new)。

 for box in bbox_list_thresholded:
     if current_box[4] == box[4]:
         iou = IOU(current_box[:4], box[:4])
         if iou > iou_threshold:
             bbox_list_thresholded.remove(box)

然后,我们迭代列表bbox_list_threshold中所有剩余的框,并检查它们是否与当前框类别相同。(box[4]对应于类别).

如果两个框属于同一类,我们计算这些框之间的IOU, 如果IOU>IOU_threshold,我们将从列表bbox_list_thresholded中移除置信度较低的框。

return bbox_list_new

在非最大移植之后,我们返回更新后的框的列表bbox_list_new。

4. 总结

上述非最大抑制过程可根据具体应用进行相应的修改。本文从非最大抑制算法的原理开始讲其,并将其详细步骤用代码逐一实现,并针对相应的代码做了详细的解释.

您学废了吗?