NMS(非极大值抑制)代码一步步详解

NMS(非极大值抑制)一步步详解

要达到的效果

首先,我们要知道NMS要实现的功能是什么,知道它要做什么,再去了解它是怎么做到的。
我们现在这里有一张图片,模型算法识别到有五个候选框,NMS要做的,就是将每一个目标中,只留下一个候选框。
请添加图片描述
这种我们用肉眼看,一眼就能看出,哪个anchor需要留下,哪个anchor需要剔除,但是代码中需要用算法来计算,也就是NMS(非极大值抑制)。

好,首先我们根据图片中的5个anchor,建立一组数据,用来做NMS的计算。

a = np.array([[191, 89, 413, 420, 0.80],      # 0
              [281, 152, 573, 510, 0.99],     # 1
              [446, 294, 614, 471, 0.65],     # 2
              [50, 453, 183, 621, 0.98],      # 3
              [109, 474, 209, 635, 0.78]])    # 4

这一组数据中的几个元素解释为:[左上角x, 左上角y, 右下角x, 右下角y, score]。

源码

再看一下NMS算法的源码,我们再根据源码一步步地来做解释:

def nms(boxes, thresh):
    """Pure Python NMS baseline."""
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    scores = boxes[:, 4]

    # 计算每一个anchor的面积
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    # 按照从小到大排序后返回下标,然后顺序取反,即从大到小对应的下标
    order = scores.argsort()[::-1]

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        # 置信度高的预测框即当前框与其他框的交集
        # 选择的区域就是取最大的x1, y1和最小的 x2, y2
        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:]])
        # 计算交叉区域的面积,就是用当前的anchor与其它的anchor计算,是否有相交的面积,如果有,那相交的面积是多少
        w = np.maximum(0.0, xx2 - xx1 + 1)  # 计算w
        h = np.maximum(0.0, yy2 - yy1 + 1)  # 计算h
        inter = w * h  # 交叉面积
        # 计算IOU,  相交区域 / (当前区域 + 某区域面积 - 相交区域面积)
        ovr = inter / (areas[i] + areas[order[1:]] - inter)
        # 保留IOU小于阈值的框
        inds = np.where(ovr <= thresh)[0]
        # 因为ovr数组的长度比order数组少一个,所以这里要将所有下标后移一位
        order = order[inds + 1]
    return boxes[keep]


if __name__ == "__main__":
    a = np.array([[191, 89, 413, 420, 0.80],      # 0
                  [281, 152, 573, 510, 0.99],     # 1
                  [446, 294, 614, 471, 0.65],     # 2
                  [50, 453, 183, 621, 0.98],      # 3
                  [109, 474, 209, 635, 0.78]])    # 4
    nms_result = nms(a, 0.2)

源码一步步详解

1)首先看代码开头

    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    scores = boxes[:, 4]

我们刚才讲过,数据中的几个元素解释为:[左上角x, 左上角y, 右下角x, 右下角y, score]。
那么这里的代码,其实就是将x值、y值、score都分别取出来而已。
可以得到结果:

x1= [191. 281. 446.  50. 109.]
y1= [ 89. 152. 294. 453. 474.]
x2= [413. 573. 614. 183. 209.]
y2= [420. 510. 471. 621. 635.]
scores= [0.8  0.99 0.65 0.98 0.78]

2)然后再计算一下,每一个anchor的面积

areas = (x2 - x1 + 1) * (y2 - y1 + 1)

因为x1,y1为左上角的坐标点,x2,y2为右下角的坐标点,所以 x2-x1相当于求宽,y2-y1相当于求高,宽乘以高就能求出anchor的面积。
结果:

areas= [ 74036. 105187.  30082.  22646.  16362.]

3)根据score从大到小排序

order = scores.argsort()[::-1]

argsort() 这个函数其实是求的从小到大排序,且返回的是下标。例如在我的数据中,从小到大排序应该是[0.65, 0.78, 0.80, 0.98, 0.99],但是其返回的是下标,所以score.argsort()=[2, 4, 0, 3, 1]。
然后再用[::-1]反序排列,就可以得到从大到小的下标顺序,所以最后得到的结果应该为:

order= [1 3 0 4 2]

4)遍历scores

keep = []
while order.size > 0:
    i = order[0]
    keep.append(i)

这里遍历的是order,也就是已经被从大到小排序过的scores下标。
因为已经做好了排序,所以第一个必定是评分最高的anchor,默认它为正确的anchor,将其下标保存至keep中。

5)求当前框与其他框的交集

        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:]])

当前框即为x1[i], y1[i], x2[i], y2[i]。因为i为order[0],所以order从1开始往后所有的值,都为其他框。
而又因为order是经过从大到小排序的,所以order[0]应该就是评分为0.99的anchor数据。
这里要计算的是,将当前框与其他所有框一个个做对比,求左上角坐标更大的值,右下角坐标更小的值。
在这里插入图片描述
从图中可以看到,经过计算之后,xx1,yy1,xx2,yy2就可以求出两个anchor相交部分的顶点坐标,然后用当前框去一一计算,算出它与其它每一个框的相交部分。

xx1= [281. 281. 281. 446.]
yy1= [453. 152. 474. 294.]
xx2= [183. 413. 209. 573.]
yy2= [510. 420. 510. 471.]

6)计算每一个anchor相交部分的面积

w = np.maximum(0.0, xx2 - xx1 + 1)  # 计算w
h = np.maximum(0.0, yy2 - yy1 + 1)  # 计算h
inter = w * h  # 交叉面积

从第5)步可以得知,
xx1,yy1,xx2,yy2为两个anchor的相交部分的顶点坐标,也就是左上和右下的顶点坐标。
跟第2)步相同,求出相交部分的宽高,即可求出相交面积。但因为可能某些anchor并不相交,所以其宽高可能为负数,所以用np.maximum来保证w,h最小也为0。

inter= [0.  35777.  0.  22784.]

这里可以看到,一共是求出了4个交叉面积,其中两个均为0,说明没有相交的面积。
需要注意的是,因为我们这里用的是评分为0.99的当前框与其他框来做的计算,所以inter的值中只有两个相交面积,这是正确的。

7)计算IOU

ovr = inter / (areas[i] + areas[order[1:]] - inter)

这里涉及到一个算法,IOU的计算。IOU即交并比,是目标检测中衡量目标检测算法准确度的一个重要指标,顾名思义,即交集与并集的比值。
请添加图片描述
代码中的inter就是第6)步求出的交集,并集则是将score为0.99的当前框,加上score为0.80的其它框面积想加,再减去两个框中间的交集。
用当前框0.99与其它每一个其他框都一一计算,得到ovr的结果为:

ovr= [0.  0.24941093  0.  0.20255145]

8)计算阈值

inds = np.where(ovr <= thresh)[0]
order = order[inds + 1]

因为我们需要剔除不符合要求的anchor,所以设定一个阈值,当IOU大于这个阈值时,就认为它们与当前框是属于同一个目标的anchor,而因为当前框的score最大,所以其他框都会被剔除。
这里我们将阈值thresh设为0.2,那么ovr经过计算剔除后,得到结果:

inds= [0 2]

注意,这里返回的是下标。因为可能一张图片中有多个目标多个anchor,所以再移动下标后,进行下一次while循环,继续计算。
根据第3)步order的计算结果,移动下标后:

order= [3 4]

9)返回结果

return boxes[keep]

计算完所有的while之后,keep中已经保存下所有目标的anchor下标了,再把这个下标用于anchor的数据boxes中,就可以得到最终结果:

nms_result= [[281.   152.   573.   510.     0.99]
 [ 50.   453.   183.   621.     0.98]]

OVER!

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
nms非极大值抑制有以下几种方法和改进: 1. 基本的NMS方法是利用得分高的边框来抑制得分低且重叠程度高的边框。这种方法简单有效,但在更高的目标检测需求下存在一些缺点。 2. Soft NMS是一种改进的方法,它通过对重叠框的得分进行一定的衰减,而不是直接抑制掉得分低的边框。这样可以保留一些得分低但可能是真正目标的边框。 3. Softer NMS是Soft NMS的进一步改进,它在计算重叠框的得分衰减时引入了一个可学习的参数。这个参数可以根据具体的数据进行优化,从而更加灵活地调整得分衰减的方式。 4. Adaptive NMS是根据物体密集程度自适应调整NMS阈值的方法。它通过使用卷积神经网络(CNN)来判断人群的密集程度,并根据密集程度决定NMS阈值的大小。 5. IoUNet是一种基于IoU(Intersection over Union)的方法,它通过训练一个神经网络来预测边框之间的IoU值。然后,根据IoU值来判断是否进行非极大值抑制。 综上所述,nms非极大值抑制有基本的NMS方法、Soft NMS、Softer NMS、Adaptive NMS和IoUNet等不同的方法和改进。每种方法都有其特点和适用场景,可以根据具体需求选择合适的方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【目标检测系列】非极大值抑制(NMS)的各类变体汇总](https://blog.csdn.net/weixin_47196664/article/details/106754955)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值