NMS
解决问题:多个建议框指向了同一个物体,我们只需要该物体IoU最大的建议框
基本思路:多个类别分开处理;每个列别中 iou 较大的删除,iou 较小的 保留
具体操作
1. 输入为2000x20矩阵,2000代表2000个建议框,20代表20个类别
2. 对每个类别进行排序,从大到小
3. 首先取得分最高的建议框,设为物体1,然后遍历后面所有的建议框,如果建议框和物体1的建议框IoU大于阈值,则认为是一个物体,把这些建议框删除,
如果小于阈值,则认为是另一个物体,暂时保留,保留下来的建议框仍然是有序的
4. 去掉该列被认定的建议框,如物体1,将剩下的建议框进行步骤3操作,直到认定完所有物体
5. 对每列进行上述操作
如上图F与BD重合度较大,可以去除BD。AE重合度较大,我们删除A,保留scores较大的E。C和其他重叠都小保留C。最终留下了C、E、F三个
存在问题
1.如果 IOU 大于阈值,直接删除,忽略了 该 bbox 的 score,试想 如果 score 很高,很可能存在一个物体的,但被删除了,这对于密集物体十分不友好;
2.阈值 不太好确定
NMS 下 绿框 会被删除
demo
针对 单类别
# coding:utf-8 import numpy as np def py_cpu_nms(dets, thresh): """Pure Python NMS baseline.""" # 所有图片的坐标信息,字典形式储存?? 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] # i 是还未处理的图片中的最大评分 keep.append(i) # 保留改图片的值 # 矩阵操作,下面计算的是图片i分别与其余图片相交的矩形的坐标 tmp=x1[order[1:]] xxxx = x1[i] 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:]]) # 计算出各个相交矩形的面积 w = np.maximum(0.0, xx2 - xx1 + 1) h = np.maximum(0.0, yy2 - yy1 + 1) inter = w * h # 计算重叠比例 ovr = inter / (areas[i] + areas[order[1:]] - inter) # 只保留比例小于阙值的图片,然后继续处理 inds = np.where(ovr <= thresh)[0] indsd= inds+1 order = order[inds + 1] return keep boxes = np.array([[100, 100, 150, 168, 0.63],[166, 70, 312, 190, 0.55],[221, 250, 389, 500, 0.79],[12, 190, 300, 399, 0.9],[28, 130, 134, 302, 0.3]]) thresh = 0.1 keep = py_cpu_nms(boxes, thresh) print(keep)
soft-NMS
soft-nms 优化点在于:并非暴力删除 iou 较大的 框,而是 降低 这个框的 score
存在问题
阈值仍需手工设定
优势
1、Soft-NMS可以很方便地引入到object detection算法中,不需要重新训练原有的模型、代码容易实现,不增加计算量(计算量相比整个object detection算法可忽略)。并且很容易集成到目前所有使用NMS的目标检测算法。
2、soft-NMS在训练中采用传统的NMS方法,仅在推断代码中实现soft-NMS。
3、NMS是Soft-NMS特殊形式,当得分重置函数采用二值化函数时,Soft-NMS和NMS是相同的。soft-NMS算法是一种更加通用的非最大抑制算法。
demo
def my_soft_nms(bboxes, scores, iou_thresh=0.5, sigma=0.5, score_threshold=0.25): bboxes = bboxes.contiguous() x1 = bboxes[:, 0] y1 = bboxes[:, 1] x2 = bboxes[:, 2] y2 = bboxes[:, 3] areas = (x2 - x1 + 1) * (y2 - y1 + 1) order = scores.sort(0, descending=True) keep = [] while order.numel() > 0: if order.numel() == 1: i = order.item() break else: i = order[0].item() keep.append(i) xx1 = x1[order[1:]].clamp(min=x1[i]) yy1 = y1[order[1:]].clamp(min=y1[i]) xx2 = x2[order[1:]].clamp(max=x2[i]) yy2 = y2[order[1:]].clamp(max=y2[i]) inter = (xx2 - xx1).clamp(min=0) * (yy2 - yy1).clamp(min=0) idx = (iou > iou_thresh).nonzero().squeeze() if idx.numel() > 0: iou = iou[idx] newScores = torch.exp(-torch.pow(iou, 2) / sigma) scores[order[idx + 1]] *= newScores newOrder = (scores[order[1:]] > score_threshold).nonzero().squeeze() if newOrder.numel() == 0: break else: maxScoreIndex = torch.argmax(newScores) if maxScoreIndex != 0: newOrder[[0, maxScoreIndex],] = newOrder[[maxScoreIndex, 0],] order = order[newOrder + 1] return torch.LongTensor(keep)
有人在多个数据集上做了大量实验,针对不同的数据集效果不同,有轻微的提升作用。
softer-NMS
未完待续...
参考资料:
https://mp.weixin.qq.com/s?__biz=MzUyODY5ODg5NQ==&mid=2247483782&idx=1&sn=bae1d8b8c56c2e5fe82886ca2f418f9e&chksm=fa6d1ae6cd1a93f0adaa3cae6458e238d32e6d5436b0e3b70e35cd0881b56403f99be48d0443&scene=132#wechat_redirect YOLOv5改进之八:非极大值抑制NMS算法改进Soft-nms
https://zhuanlan.zhihu.com/p/42018282 NMS与soft NMS
https://zhuanlan.zhihu.com/p/89426063 NMS、 soft-nms、softer-nms