nms及其各种变体

summary

  1. 普通nms, ICCV, 2017
  2. 加权平均nms, (Weighted NMS), ICME Workshop, 2017
  3. 定位优先nms, ECCV, 2018
  4. 自适应阈值,CVPR, 2019
  5. 中心点距离,DIoU, AAAI, 2020

一,普通nms

传统nms的逻辑是先对预测框分类别,针对具体某一个类别的框,先按照该框内是否有物体的置信度分排序,然后取得分最高的和他之后的框做iou,将iou大于阈值的框剔除。依次重复这个过程,直到所有框都被处理完毕,代码见nms.py,该方法的优点是简单容易理解且容易实现,缺点是效率低,刚性阈值,在稠密物体之间容易误杀真实的物体框。

二,加权平均nms

普通nms基于假设置信度最高的框它的框的定位也是最准确的,但是其实真实的结果并不是这样的,所以这里作者提出了加权平均nms,找出iou比较高的那一堆框,置信度取值最大的那个,定位取这一堆框的平均值。具体的过程是如同普通nms,先按照不同类别对框排序,然后取置信度最高的那个框,并计算剩下框与之iou大于阈值的那些框的集合,最后将这些框的集合位置取平均值,得到这个框的位置。

三,定位优先nms

作者在预测bounding box的时候,对网络多加了一个分支,用于表示该框定位的置信度,然后在做nms的时候,流程和普通的nms一样,但是选取的是位置的置信度,最后类别概率的置信度选取和最大位置置信度框有交叠的所有框中最大的那一个。

四, 自适应阈值

前面所有的nms都是设置固定的剔除阈值,但是在物体密度比较高的时候其实应该降低剔除的阈值,否则会误伤交叠物体,所以这篇文章作者提出了自适应阈值设置,即在不同物体密度的地方设置不同的阈值。具体的步骤是在网络中添加一个预测物体密度的分支,通过得到的密度值设置不同的iou剔除阈值

五, 中心点距离

前面所有的iou都是基于面积的交并比得出剔除阈值的,本问作者认为这并不是最好的办法,所以提出了新的衡量框是否是重叠的评估方法,通过两个框的中心点来衡量,设d为两个框的几何中心点的距离, c为两个框最远的角点点距离,然后用公式$$DIoU=IoU-\frac {d^2}{c^2} $$

简版nms实现

对所有框按分数从大到小排序,每一轮取分数最大的框,和剩下所有的框做iou,将大于阈值的去掉,去掉的办法是保留需要的框的下标,下一轮迭代从分数次高的开始,知道下标列表为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import numpy as np
import matplotlib.pyplot as plt

boxes = np.array([[100, 100, 210, 210, 0.72],
[250, 250, 420, 420, 0.8],
[220, 220, 320, 330, 0.92],
[100, 100, 210, 210, 0.72],
[230, 240, 325, 330, 0.81],
[220, 230, 315, 340, 0.9]])

def py_cpu_nms(dets, thresh):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
areas = (y2 - y1 + 1) * (x2 - x1 + 1)
scores = dets[:, 4]
keep = []
index = scores.argsort()[::-1]
while index.size > 0:
i = index[0]
keep.append(i)
# 计算交叠面积,取两个框左上角的最大值,两个框右下角的最小值,这两个点构成重叠矩形,矩形有可能不存在(两个框无重叠)
x11 = np.maximum(x1[i], x1[index[1:]]) # 当前框和剩下所有的框取最大
y11 = np.maximum(y1[i], y1[index[1:]])
x22 = np.minimum(x2[i], x2[index[1:]])
y22 = np.minimum(y2[i], y2[index[1:]])
w = np.maximum(0, x22 - x11 + 1) # the with of overlap
h = np.maximum(0, y22 - y11 + 1) # the height of overlap
overlaps = w * h
ious = overlaps / (areas[i] + areas[index[1:]] - overlaps)
idx = np.where(ious <= thresh)[0] # 根据iou > thr取出没被过滤的框
index = index[idx + 1] # 过滤掉所有重叠框,保留剩下框的下标
return keep

def plot_bbox(dets, c='k'):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]

plt.plot([x1, x2], [y1, y1], c)
plt.plot([x1, x1], [y1, y2], c)
plt.plot([x1, x2], [y2, y2], c)
plt.plot([x2, x2], [y1, y2], c)
plt.title(" nms")

plt.figure(1)
ax1 = plt.subplot(1, 2, 1)
ax2 = plt.subplot(1, 2, 2)
plt.sca(ax1)
plot_bbox(boxes, 'k') # before nms
keep = py_cpu_nms(boxes, thresh=0.7)
plt.sca(ax2)
plot_bbox(boxes[keep], 'r') # after nms
print("ttt")