目标检测:IoU(交并比)和 NMS(非极大值抑制)、Soft_NMS方法Python实现

本文详细介绍了目标检测任务中的关键概念——交并比(IOU)和非极大值抑制(NMS)。通过Python代码展示了如何计算IOU、多维IOU以及GIoU,并解释了NMS的工作原理和实现步骤。此外,还提及了Soft_NMS作为NMS的一种优化策略。
部署运行你感兴趣的模型镜像

交并比(Intersection over Union)和非极大值抑制是(Non-Maximum Suppression)是目标检测任务中非常重要的两个概念。例如在用训练好的模型进行测试时,网络会预测出一系列的候选框。这时候我们会用NMS来移除一些多余的候选框。即移除一些IOU值大于某个阈值的框。然后在剩下的候选框中,分别计算与ground truth的IOU值,通常会规定当候选框和ground truth的IOU值大于0.5时,认为检测正确。下面我们分别用python实现IOU和NMS。

交并比IOU

如上图所示,IOU值定位为两个矩形框面积的交集和并集的比值。即:
IOU=A∩BA∪B I O U =\frac{A \cap B}{A \cup B} IOU=ABAB

python代码实现

import numpy as np
def compute_iou(box1, box2, wh=False):
	"""
	compute the iou of two boxes.
	Args:
		box1, box2: [xmin, ymin, xmax, ymax] (wh=False) or [xcenter, ycenter, w, h] (wh=True)
		wh: the format of coordinate.
	Return:
		iou: iou of box1 and box2.
	"""
	if wh == False:
		xmin1, ymin1, xmax1, ymax1 = box1
		xmin2, ymin2, xmax2, ymax2 = box2
	else:
		xmin1, ymin1 = int(box1[0]-box1[2]/2.0), int(box1[1]-box1[3]/2.0)
		xmax1, ymax1 = int(box1[0]+box1[2]/2.0), int(box1[1]+box1[3]/2.0)
		xmin2, ymin2 = int(box2[0]-box2[2]/2.0), int(box2[1]-box2[3]/2.0)
		xmax2, ymax2 = int(box2[0]+box2[2]/2.0), int(box2[1]+box2[3]/2.0)


	## 计算两个矩形框面积
	area1 = (xmax1-xmin1) * (ymax1-ymin1) 
	area2 = (xmax2-xmin2) * (ymax2-ymin2)
	
	## 获取矩形框交集对应的左上角和右下角的坐标(intersection)
	xx1 = np.max([xmin1, xmin2])
	yy1 = np.max([ymin1, ymin2])
	xx2 = np.min([xmax1, xmax2])
	yy2 = np.min([ymax1, ymax2])
	
	inter_area = (np.max([0, xx2-xx1+1])) * (np.max([0, yy2-yy1+1])) #计算交集面积
	iou = inter_area / (area1+area2-inter_area+1e-6) #计算交并比

	return iou

多维iou

def Ious(bbox, gt):
    assert bbox.shape[0] > 0, 'bbox len must>0'
    assert gt.shape[0] > 0, 'bbox len must>0'
    """
    :param bbox: (n, 4)
    :param gt: (m, 4)
    :return: (n, m)
    numpy 广播机制 从后向前对齐。 维度为1 的可以重复等价为任意维度
    eg: (4,3,2)   (3,2)  (3,2)会扩充为(4,3,2)
        (4,1,2)   (3,2) (4,1,2) 扩充为(4, 3, 2)  (3, 2)扩充为(4, 3,2) 扩充的方法为重复
    广播会在numpy的函数 如sum, maximun等函数中进行
    """
    lt = np.maximum(bbox[:, None, :2], gt[:, :2])  # left_top (x, y)
    rb = np.minimum(bbox[:, None, 2:], gt[:, 2:])  # right_bottom (x, y)
    wh = np.maximum(rb - lt, 0)  # inter_area (w, h)
    inter_areas = wh[:, :, 0] * wh[:, :, 1]  # shape: (n, m)
    box_areas = (bbox[:, 2] - bbox[:, 0]) * (bbox[:, 3] - bbox[:, 1])
    gt_areas = (gt[:, 2] - gt[:, 0]) * (gt[:, 3] - gt[:, 1])
    IoU = inter_areas / (box_areas[:, None] + gt_areas - inter_areas)
    return IoU

GIoU

def GIoU(box1, box2):
    xmin1, ymin1, xmax1, ymax1 = box1
    xmin2, ymin2, xmax2, ymax2 = box2

    area1 = (xmax1-xmin1+1) * (ymax1-ymin1+1)
    area2 = (xmax2-xmin2+1) * (ymax2-ymin2+1)

    xx1 = np.max([xmin1, xmin2])
    yy1 = np.max([ymin1, ymin2])
    xx2 = np.min([xmax1, xmax2])
    yy2 = np.min([ymax1, ymax2])

    A_B = np.max([0, xx2-xx1+1]) * np.max([0, yy2-yy1+1])
    AUB = area1 + area2 - A_B

    iou = A_B / AUB

    xxx1 = np.min([xmin1, xmin2])
    yyy1 = np.min([ymin1, ymin2])
    xxx2 = np.max([xmax1, xmax2])
    yyy2 = np.max([ymax1, ymax2])

    g_area = np.max([0, xxx2 - xxx1 + 1]) * np.max([0, yyy2 - yyy1 + 1])
    print(iou)
    g_iou_add = (g_area-(AUB)) / g_area
    return iou - g_iou_add

非极大值抑制(NMS)

NMS的算法步骤如下:

# INPUT:所有预测出的bounding box (bbx)信息(坐标和置信度confidence), IOU阈值(大于该阈值的bbx将被移除)
for object in all objects:
	(1) 获取当前目标类别下所有bbx的信息
	(2) 将bbx按照confidence从高到低排序,并记录当前confidence最大的bbx
	(3) 计算最大confidence对应的bbx与剩下所有的bbx的IOU,移除所有大于IOU阈值的bbx
	(4) 对剩下的bbx,循环执行(2)(3)直到所有的bbx均满足要求(即不能再移除bbx)

需要注意的是,NMS是对所有的类别分别执行的。举个例子,假设最后预测出的矩形框有2类(分别为cup, pen),在NMS之前,每个类别可能都会有不只一个bbx被预测出来,这个时候我们需要对这两个类别分别执行一次NMS过程。

def nms(boxes, thresh):# 计算 n 个候选框的面积大小
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    scores = boxes[:, 4]
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)# 对置信度进行排序, 获取排序后的下标序号, argsort 默认从小到大排序
    order = np.argsort(scores)
​
    keep = []  # 返回值
    while order.size > 0:
        # 将当前置信度最大的框加入返回值列表中
        index = order[-1]
        picked_boxes.append(bounding_boxes[index])# 获取当前置信度最大的候选框与其他任意候选框的相交面积
        x11 = np.maximum(x1[index], x1[order[:-1]])
        y11 = np.maximum(y1[index], y1[order[:-1]])
        x22 = np.minimum(x2[index], x2[order[:-1]])
        y22 = np.minimum(y2[index], y2[order[:-1]])
        w = np.maximum(0.0, x22 - x11 + 1)
        h = np.maximum(0.0, y22 - y11 + 1)
        intersection = w * h
​
        # 利用相交的面积和两个框自身的面积计算框的交并比, 将交并比大于阈值的框删除
        ious = intersection / (areas[index] + areas[order[:-1]] - intersection)
        left = np.where(ious < thresh)
        order = order[left]
    return keep

或者

def nms(dets, thresh=0.35):
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    scores = dets[:, 4]
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)

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

    keep = []

    while order.size:
        i = order[0]
        keep.append(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:]])

        inter = np.maximum(0.0, xx2 - xx1 + 1) * np.maximum(0.0, yy2 - yy1 + 1)
        ious = inter / (areas[i] + areas[order[1:]] - inter)

        index = np.where(ious <= thresh)[0]
        order = order[index+1]
    return keep

Soft_NMS

def soft_nms(dets, iou_thresh=0.3, sigma=0.5, thresh=0.5, method=2):
	# dets: [x1, y1, x2, y2, score]
    N = dets.shape[0]
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    areas = (x2-x1+1) * (y2-y1+1)

    for i in range(N):
        temp_box = dets[i, :4]
        temp_score = dets[i, 4]
        temp_area = areas[i]
        pos = i + 1

        if i != N-1:
            maxscore = np.max(dets[pos:, 4])
            maxpos = np.argmax(dets[pos:, 4])
        else:
            maxscore = dets[:, 4][-1]
            maxpos = 0

        if temp_score < maxscore:

            dets[i, :4] = dets[maxpos+pos, :4]
            dets[maxpos + pos, :4] = temp_box

            dets[i, 4] = dets[maxpos+pos, 4]
            dets[maxpos+pos, 4] = temp_score

            areas[i] = areas[maxpos+pos]
            areas[maxpos+pos] = temp_area

        xx1 = np.maximum(dets[i, 0], dets[pos:, 0])
        yy1 = np.maximum(dets[i, 1], dets[pos:, 1])
        xx2 = np.minimum(dets[i, 2], dets[pos:, 2])
        yy2 = np.minimum(dets[i, 3], dets[pos:, 3])

        w = np.maximum(0.0, xx2-xx1+1)
        h = np.maximum(0.0, yy2-yy1+1)

        inter = w*h
        ious = inter / (areas[i] + areas[pos:] - inter)

        if method == 1:
            weight = np.ones(ious.shape)
            weight[ious > iou_thresh] = weight[ious > iou_thresh] - ious[ious > iou_thresh]
        elif method == 2:
            weight = np.exp(-ious*ious/sigma)
        else:
            weight = np.ones(ious.shape)
            weight[ious > iou_thresh] = 0

        dets[pos:, 4] = dets[pos:, 4]*weight

    keep = np.where(dets[:, 4] > thresh)[0]
    return keep

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_1997

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值