大家好,我是灿视。
目前有好几位粉丝,跟我反馈说考到了NMS。今天开始,我们好好把NMS这个点给lu过去。今天说的是针对传统NMS存在的问题而提出的优化。后面还会分享针对不同任务,推出的NMS,欢迎各位持续关注!
技术汇总
点击文末阅读原文,跳转到Github汇总地址!
代码与实现
-
-
(非极大值抑制): 当两个
空间位置非常接近,就以
更高的那个作为基准,看
即重合度如何,如果与其重合度超过阈值,就抑制
更小的
,只保留
大的就
,其它的
就都应该过滤掉。对于
而言,适合于水平框,针对各种不同形状的框,会有不同的
来进行处理。
具体的步骤如下:

个带置信率的
,我们先预设一个
的阈值如
。
个框排序,举例为
。
的
为一个物体框;
个
中进行循环遍历,去掉与
物体框
大于
的。
~
的步骤,直到没有
为止。
就是我们筛选出来的目标。
参考代码如下:
import numpy as np
def NMS(dets, thresh):
"""Pure Python NMS baseline."""
# tl_x,tl_y,br_x,br_y及score
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
#计算每个检测框的面积,并对目标检测得分进行降序排序
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
keep = [] #保留框的结果集合
while order.size > 0:
i = order[0]
keep.append(i) #保留该类剩余box中得分最高的一个
# 计算最高得分矩形框与剩余矩形框的相交区域
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:]])
#计算相交的面积,不重叠时面积为0
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
#计算IoU:重叠面积 /(面积1+面积2-重叠面积)
ovr = inter / (areas[i] + areas[order[1:]] - inter)
#保留IoU小于阈值的box
inds = np.where(ovr <= thresh)[0]
order = order[inds + 1] #注意这里索引加了1,因为ovr数组的长度比order数组的长度少一个
return keep
运行后,则删除了多余的框,结果如图所示:

的代码与实现
说到
,首先需要了解传统
有哪些缺点。其主要缺点包括如下:
的话就会把其他置信度稍低,但是表示另一个物体的预测框删掉(由于和最高置信度的框
过大)

都预测不准:不是所有的框都那么精准,有时甚至会出现某个物体周围的所有框都标出来了,但是都不准的情况,如下图所示。

方法是基于分类分数的,只有最高分数的预测框能留下来,但是大多数情况下
和分类分数不是强相关,很多分类标签置信度高的框都位置都不是很准。

主要是针对
过度删除框的问题。
吸取了
的教训,在算法执行过程中不是简单的对
大于阈值的检测框删除,而是降低得分。算法流程同
相同,但是对原置信度得分使用函数运算,目标是降低置信度得分。其算法步骤如下:

红色的部分表示原始
算法,绿色部分表示
-
算法,区别在于,绿色的框只是把
降低了,而不是把
直接去掉,极端情况下,如果
只返回
,那么等同于普通的
。
为待处理
框,
为待处理
框集合,
是
框更新得分,
是
的阈值,
集合用来放最终的
,
是置信度得分的重置函数。
和
的
越大,
的得分
就下降的越厉害。
函数是为了降低目标框的置信度,满足条件,如果
和
的
越大,
就应该越小,
-
提出了两种
函数:
经典的
算法将
大于阈值的窗口的得分全部置为
,可表述如下:

论文置信度重置函数有两种形式改进,一种是线性加权的:

一种是高斯加权形式:
算法的优点如下:
是
-
特殊形式,当得分重置函数采用二值化函数时,
-
和
是相同的。
-
算法是一种更加通用的非最大抑制算法。
而,在一些场景的实验中,可以看到
的效果也是优于
的。

img
这里提供一个
中的
代码展示:
def cpu_soft_nms(np.ndarray[float, ndim=2] boxes, float sigma=0.5, float Nt=0.3, float threshold=0.001, unsigned int method=0):
cdef unsigned int N = boxes.shape[0]
cdef float iw, ih, box_area
cdef float ua
cdef int pos = 0
cdef float maxscore = 0
cdef int maxpos = 0
cdef float x1,x2,y1,y2,tx1,tx2,ty1,ty2,ts,area,weight,ov
for i in range(N):
# 在i之后找到confidence最高的框,标记为max_pos
maxscore = boxes[i, 4]
maxpos = i
tx1 = boxes[i,0]
ty1 = boxes[i,1]
tx2 = boxes[i,2]
ty2 = boxes[i,3]
ts = boxes[i,4]
pos = i + 1
# 找到max的框
while pos < N:
if maxscore < boxes[pos, 4]:
maxscore = boxes[pos, 4]
maxpos = pos
pos = pos + 1
# 交换max_pos位置和i位置的数据
# add max box as a detection
boxes[i,0] = boxes[maxpos,0]
boxes[i,1] = boxes[maxpos,1]
boxes[i,2] = boxes[maxpos,2]
boxes[i,3] = boxes[maxpos,3]
boxes[i,4] = boxes[maxpos,4]
# swap ith box with position of max box
boxes[maxpos,0] = tx1
boxes[maxpos,1] = ty1
boxes[maxpos,2] = tx2
boxes[maxpos,3] = ty2
boxes[maxpos,4] = ts
tx1 = boxes[i,0]
ty1 = boxes[i,1]
tx2 = boxes[i,2]
ty2 = boxes[i,3]
ts = boxes[i,4]
# 交换完毕
# 开始循环
pos = i + 1
while pos < N:
# 先记录内层循环的数据bi
x1 = boxes[pos, 0]
y1 = boxes[pos, 1]
x2 = boxes[pos, 2]
y2 = boxes[pos, 3]
s = boxes[pos, 4]
# 计算iou
area = (x2 - x1 + 1) * (y2 - y1 + 1)
iw = (min(tx2, x2) - max(tx1, x1) + 1) # 计算两个框交叉矩形的宽度,如果宽度小于等于0,即没有相交,因此不需要判断
if iw > 0:
ih = (min(ty2, y2) - max(ty1, y1) + 1) # 同理
if ih > 0:
ua = float((tx2 - tx1 + 1) * (ty2 - ty1 + 1) + area - iw * ih) #计算union面积
ov = iw * ih / ua #iou between max box and detection box
if method == 1: # linear
if ov > Nt:
weight = 1 - ov
else:
weight = 1
elif method == 2: # gaussian
weight = np.exp(-(ov * ov)/sigma)
else: # original NMS
if ov > Nt:
weight = 0
else:
weight = 1
boxes[pos, 4] = weight*boxes[pos, 4]
# if box score falls below threshold, discard the box by swapping with last box
# update N
if boxes[pos, 4] < threshold:
boxes[pos,0] = boxes[N-1, 0]
boxes[pos,1] = boxes[N-1, 1]
boxes[pos,2] = boxes[N-1, 2]
boxes[pos,3] = boxes[N-1, 3]
boxes[pos,4] = boxes[N-1, 4]
N = N - 1
pos = pos - 1
pos = pos + 1
keep = [i for i in range(N)]
return keep
的代码与实现
针对剩余的两个问题,
做出了自己的努力。
不是强相关的问题,构建一种
的置信度,来建模有多大把握认为当前框和
是重合的。
置信度加权合并多个框优化最终生成框。
-
文章对预测框建模,以下公式中
表示偏移前的预测框,
表示偏移后的预测框,输出的
表示
框,使用高斯函数对预测框建模:
对于
框建模:使用
分布(即标准方差为
的高斯分布极限)。
对于
分布,当
越小,其函数图像就会越瘦高,同时,当
越小,表示网络越确定,可以使用
就可以作为网络的置信度。
同时,论文使用
散度来最小化
。既
的高斯分布和
的狄拉克
分布的
散度。直观上解释,
使得
预测呈高斯分布,且与
相近。而将包围框预测的标准差看作置信度。
如
中添加了
之后的示意图如图所示:

多加了一个
预测,也就是
,而
的预测其实就是上面公式中的
。
因此,整个计算过程如下:
与
的2范数距离和
计算出
.
与
的2范数距离算出
.
与
计算
散度作为
,最小化
。
关于坐标回归的损失函数:
而后面两项是与
无关,可以去掉~
因此,计算过程如下图所示:

网络预测出来的结果是
。前面四个为坐标,而后面四个是坐标的
。
上表中的蓝色的是
-
,只是降低了
的权值。重点看绿色的,绿字第一行表示拿出所有与
的
大于
的框(用
表示),然后将所有这些框做一个加权,
其实是
,后者是置信度
,并使用
做了归一化。需要注意的是,
-
算法中,
是不变的,
-
只调整每个框的位置,而不筛选框。
贴一张效果图:
