非极大抑制(NMS)算法原理与Python实现

在目标检测任务中,一个检测目标通常会产生多个检测框,为了合理缩减输出检测框数量,往往需要在目标检测模型输出前添加非极大抑制Non-Maximum Suppression, NMS。非极大抑制算法能够删除冗余的检测框,并优先保留可靠性最高的结果。

一、算法的主要思路

  假设有以下输入det

[[x_center, y_center, width, height, prob, labels...], 
 [x_center, y_center, width, height, prob, labels...], 
  ...
 [x_center, y_center, width, height, prob, labels...]]

和一个阙值theta。其中,x_center,y_center为检测框中心点坐标,width,height为检测框宽度和高度,prob表示检测框正确检测的概率,labels则为一个独热向量,用于表示检测框类别。
  非极大抑制算法基于上述输入的主要思想是:首先选择一个prob最高的检测框A,然后计算A与剩余所有检测框的交并比Intersection over Union, IoU以衡量两个检测框之间的相似度,交并比高于阈值theta的检测框可以被认为是冗余的劣质检测框,直接删除。接下来再从除了A以外的保留检测框中选择一个prob最高的检测框,重复上述过程,直到所有检测框都被遍历一遍即可。

二、伪代码

  算法伪代码如下:

function nms(det, theta):
	index = 所有检测框的索引列表
	keep = 存储保留的检测框索引,初始化为空列表
	while index is not empty:
		i = prob值最高的检测框的索引
		index.remove(i)
		keep.add(i)
		for each j in index:
			if IoU(det[i], det[j]) > theta:
				index.remove(j)
	return det[keep]
三、Python实现
3.1 交并比

  所谓交并比,就是矩阵A与矩阵B交集的面积除以AB并集的面积。图示如下:
IoU
  两矩阵并集的面积可以通过公式size_A + size_B - inter_size得到,因此IoU计算的重点就是计算两矩阵交集的面积。假设计算可得矩阵A,B左上角和右下角的坐标分别为:

A: (x_A_min, y_A_min, x_A_max, y_A_max), B: (x_B_min, y_B_min, x_B_max, y_B_min)

如果A, B相交则可以得到矩阵A, B交集左上角和右下角的坐标分别如下:

x_inter_min = max(x_A_min, x_B_min)
y_inter_min = max(y_A_min, y_B_min)
x_inter_max = min(x_A_max, x_B_max)
y_inter_max = min(y_A_max, y_B_max)

在这里插入图片描述
那么,矩阵A, B交集的宽度和高度计算公式则如下:

width_inter = x_inter_max - x_inter_min
height_inter = y_inter_max - y_inter_min

  需要注意的是,当矩阵A, B不相交时,则可能出现x_inter_max小于x_inter_min或者y_inter_max小于y_inter_min的情况。图示如下:
在这里插入图片描述

为了避免计算出现负数,则可以使用max函数将不相交情况下的宽度和高度变为0,即:

width_inter = max(0, x_inter_max - x_inter_min)
height_inter = max(0, y_inter_max - y_inter_min)

最终,可以实现交并比的计算函数如下:

def IoU(det, i, j):
    # 提取 bounding box 信息
    x = det[[i, j], 0]
    y = det[[i, j], 1]
    w = det[[i, j], 2]
    h = det[[i, j], 3]
    x_min = x - w / 2
    x_max = x + w / 2
    y_min = y - h / 2
    y_max = y + h / 2
    size = w * h
    inter_x_min = max(x_min)
    inter_x_max = min(x_max)
    inter_y_min = max(y_min)
    inter_y_max = min(y_max)
    inter_size = max(0, inter_x_max - inter_x_min) * max(0, inter_y_max - inter_y_min)
    return inter_size / (sum(size) - inter_size)
3.2 非极大抑制

  根据提供的伪代码,可以实现非极大抑制算法如下:

def nms(det, theta):
    prob = det[:, 4]
    index = prob.argsort().tolist() # 剩余索引, 按可能性从小到大排序
    keep = [] # 需要保留的索引
    while index:
        # 将可能性最高的留下
        i = index[-1]
        index.remove(i)
        keep.append(i)
        delete = []
        for j in index:
            if IoU(det, i, j) >= theta:
                delete.append(j)
        for j in delete:
            index.remove(j)
    return det[keep]
3.3 完整代码
def IoU(det, i, j):
    # 提取 bounding box 信息
    x = det[[i, j], 0]
    y = det[[i, j], 1]
    w = det[[i, j], 2]
    h = det[[i, j], 3]
    x_min = x - w / 2
    x_max = x + w / 2
    y_min = y - h / 2
    y_max = y + h / 2
    size = w * h
    inter_x_min = max(x_min)
    inter_x_max = min(x_max)
    inter_y_min = max(y_min)
    inter_y_max = min(y_max)
    inter_size = max(0, inter_x_max - inter_x_min) * max(0, inter_y_max - inter_y_min)
    return inter_size / (sum(size) - inter_size)

# det: [[x_center, y_center, width, height, classes ... ], ...]
# theta: IoU 阙值
def nms(det, theta):
    prob = det[:, 4]
    index = prob.argsort().tolist() # 剩余索引, 按可能性从小到大排序
    keep = [] # 需要保留的索引
    while index:
        # 将可能性最高的留下
        i = index[-1]
        index.remove(i)
        keep.append(i)
        delete = []
        for j in index:
            if IoU(det, i, j) >= theta:
                delete.append(j)
        for j in delete:
            index.remove(j)
    return det[keep]

if __name__ == "__main__":
    import cv2
    import torch
    img = cv2.imread("1.png")
    det = torch.tensor([[80, 280, 30, 40, 0.9],
                        [82, 278, 32, 45, 0.8],
                        [77, 281, 30, 38, 0.6],
                        [260, 270, 30, 60, 0.7],
                        [254, 273, 34, 62, 0.8]])
    for d in det:
        img = cv2.rectangle(img, (d[0] - d[2] / 2, d[1] - d[3] / 2), (d[0] + d[2] / 2, d[1] + d[3] / 2), (255, 255, 0), 1)
    cv2.imwrite("1_.png", img)
    img = cv2.imread("1.png")
    for d in nms(det, 0.5):
        img = cv2.rectangle(img, (d[0] - d[2] / 2, d[1] - d[3] / 2), (d[0] + d[2] / 2, d[1] + d[3] / 2), (255, 255, 0), 1)
    cv2.imwrite("1_nms.png", img)
四、测试结果

  非极大抑制前:
在这里插入图片描述
  非极大抑制后:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
极大抑制(Non-Maximum Suppression,NMS算法是一种用于目标检测中的方法,它的主要原理抑制不是极大值的元素,从而选择具有最高置信度的边界框。NMS算法的目的是消除多余的窗口,找到最佳物体检测位置。 NMS算法的基本思想是,在目标检测过程中,检测器会生成多个候选边界框,每个边界框都有一个对应的置信度分数。为了选择最佳的边界框,NMS算法会先按照置信度对边界框进行排序,然后从置信度最高的边界框开始,逐个比较其它边界框。 具体步骤如下: 1. 对所有候选边界框按照置信度进行降序排序。 2. 选择置信度最高的边界框作为初始边界框,并将其添加到最终的结果列表中。 3. 逐个比较当前边界框与其它边界框的重叠程度。如果重叠程度高于预设的阈值(例如IoU阈值),则将该边界框剔除,否则将其添加到最终的结果列表中。 4. 重复步骤3,直到所有的边界框都被比较完毕。 5. 最终,最终的结果列表中只保留了置信度最高且没有被剔除的边界框,即为NMS算法的输出结果。 通过这种方式,NMS算法能够有效地消除重复的边界框,只保留具有最高置信度的边界框,从而提取出目标检测中分数最高的窗口。<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_61961691/article/details/129415238)[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 ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值