NMS的作用
NMS算法的主要作用是去除冗余的检测框,保留最有可能正确检测到目标的框。如下图所示,将同一个检测目标的重复检测框去除。
核心算法
主要通过计算检测框的IOU(交并比),先计算两个边界框的交集面积,即重叠部分的面积;再计算它们的并集面积,即两个边界框合并后所覆盖的总面积;最后将交集面积除以并集面积,得到的比值就是 IOU这个值的范围在 0 到 1 之间,0 表示两个边界框完全不重叠,1 表示两个边界框完全重合。
1、根据每个边界框的置信度进行降序排列。
2、从排序后的列表中选择置信度最高的边界框,标记为已选,并将其添加到最终的检测结果列表中。
3、计算IOU:对于剩余的每个边界框,计算它与已选边界框的交并比(IOU)。
4、如果某个边界框与已选框的IOU超过了预设的阈值,则剔除这个低置信度的边界框。
重复步骤2-4:继续选择剩余边界框中置信度最高的,重复计算IOU和剔除过程,直到所有边界框都被检查过。
代码如下(rknn_zoo代码)
def nms_boxes(boxes, scores):
"""Suppress non-maximal boxes.
# Arguments
boxes: ndarray, boxes of objects.
scores: ndarray, scores of objects.
# Returns
keep: ndarray, index of effective boxes.
"""
x = boxes[:, 0]
y = boxes[:, 1]
w = boxes[:, 2] - boxes[:, 0]
h = boxes[:, 3] - boxes[:, 1]
areas = w * h
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
xx1 = np.maximum(x[i], x[order[1:]])
yy1 = np.maximum(y[i], y[order[1:]])
xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])
w1 = np.maximum(0.0, xx2 - xx1 + 0.00001)
h1 = np.maximum(0.0, yy2 - yy1 + 0.00001)
inter = w1 * h1
ovr = inter / (areas[i] + areas[order[1:]] - inter)
inds = np.where(ovr <= NMS_THRESH)[0]
order = order[inds + 1]
keep = np.array(keep)
return keep
代码关注
box的格式[x1,y1,x2,y2],及左上坐标及右下坐标。
计算交集面积
xx1 = np.maximum(x[i], x[order[1:]]) #交集左上
yy1 = np.maximum(y[i], y[order[1:]]) #交集左上
xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]]) #交集右下
yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]]) #交集右下
w1 = np.maximum(0.0, xx2 - xx1 + 0.00001) #避免0
h1 = np.maximum(0.0, yy2 - yy1 + 0.00001)
inter = w1 * h1
更新boxs
ovr = inter / (areas[i] + areas[order[1:]] - inter)
inds = np.where(ovr <= NMS_THRESH)[0]
order = order[inds + 1]
ovr 是排序后的boxs(不包含当前box (order[1:] )),与当前box的iou。
使用np.where获取交并比小于阈值NMS_THRESH的索引,即inds,该索引加1即为原order索引。
通过上述计算交集面积及根据阈值更新box列表,循环执行直到所有box都被处理