一种非极大值抑制(non_max_suppression, nms)的代码实现方式

目录

  • 1. 简介
  • 2. 代码
    *
  • 2.1 坐标形式转换
  • 2.2 iou计算
  • 2.3 nms

  • 简介

  • 非极大值抑制,non_max_suppression,简称nms,常用于目标检测的后处理,去除多余的检测框。

  • 流程大致是:根据某个类别,按照检测框的置信度从大到小排序,选择置信度最高的检测框记为A,计算A与剩余的检测框(B1、B2、… BN)的iou值,若iou值大于设置的阈值,则将B1、B2、… BN中对应的检测框去掉,如此重复操作。

  • 代码

  • 结合yolov5模型输出,实现nms的代码。

  • 在yolov5中,模型的输出记为pred,(这里pred是模型的直接输出,还没有经过后处理),pred的shape为:(batch_size, num_bbox, 4 + 1 + num_classes),其中batch_size表示输入的图片数量,num_bbox表示检测到的矩形框数量,4表示4个坐标,1表示检测框的置信度,num_classes表示每个类别的分数。
  • pred的检测框坐标格式为:xywh,即检测框的中心坐标以及它的宽高

2.1 坐标形式转换


import numpy as np

def xywh2xyxy(x):
    y = np.copy(x)
    y[..., 0] = x[..., 0] - x[..., 2] / 2
    y[..., 1] = x[..., 1] - x[..., 3] / 2
    y[..., 2] = x[..., 0] + x[..., 2] / 2
    y[..., 3] = x[..., 1] + x[..., 3] / 2
    return y

2.2 iou计算

def cal_iou(det1, det2):
    det1_x1, det1_y1 = det1[..., 0], det1[..., 1]
    det1_x2, det1_y2 = det1[..., 2], det1[..., 3]

    det2_x1, det2_y1 = det2[..., 0], det2[..., 1]
    det2_x2, det2_y2 = det2[..., 2], det2[..., 3]

    x1 = np.maximum(det1_x1, det2_x1)
    y1 = np.maximum(det1_y1, det2_y1)
    x2 = np.minimum(det1_x2, det2_x2)
    y2 = np.minimum(det1_y2, det2_y2)

    area_det1 = (det1_y2 - det1_y1 + 1) * (det1_x2 - det1_x1 + 1)
    area_det2 = (det2_y2 - det2_y1 + 1) * (det2_x2 - det2_x1 + 1)
    inter = np.maximum(0, (y2 - y1 + 1)) * np.maximum(0, (x2 - x1 + 1))

    ious = inter / (area_det1 + area_det2 - inter)
    return ious

2.3 nms

def nms(detections, conf_thres=0.4, nms_thres=0.5):
    outputs = []
    detections = xywh2xyxy(detections)
    detections[..., 5:] *= detections[..., 4:5]
    num_classes = detections.shape[2] - 5
    candidates = detections[..., 4] > conf_thres
    for img_idx, dets in enumerate(detections):
        output = []
        dets = dets[candidates[img_idx]]
        indexes = dets[..., 4].argsort()[::-1]
        dets = dets[indexes]
        classes_dets = dets[..., 5:].argmax(axis=1)
        for cls in range(num_classes):
            dets_cls = dets[classes_dets == cls]
            while len(dets_cls):
                det_select = dets_cls[0]

                det_select = np.concatenate((det_select[..., :4], [det_select[..., 5:].max(), cls]))
                output.append(det_select)
                dets_cls = dets_cls[1:]
                if len(dets_cls):
                    ious = cal_iou(det_select, dets_cls)
                    indexes = np.where(ious  nms_thres)[0]
                    dets_cls = dets_cls[indexes]
        outputs.append(output)
    return np.array(outputs)

这里得到的outputs还需要映射回原图像,以下图为例:

一种非极大值抑制(non_max_suppression, nms)的代码实现方式
  • 如图所示,原始图像经过resize之后得到红色框,红色框经过padding得到黑色框,绿色框表示检测框在黑色框中的位置(即以黑色框左上角为原点的位置)。
  • 将检测结果映射回原始图像时,要先去除padding的影响,再去除resize的影响,这样就得到检测框在原图中的位置。具体步骤如下:

  • 检测框先向上移动t个像素,再向左移动p个像素,其中t、p表示在上方向、左方向padding的像素个数,具体操作将x1 = x1 – p, x2 = x2 – p, y1 = y1 – t, y2 = y2 – t; 这样就得到检测框在没有padding操作下的位置(即以红色框左上角为原点的位置);

  • 去除resize的影响。只要将坐标除以图像resize后与原图像的的比例gain即可,假设原始图像高、宽为ori_h, ori_w,resize之后的宽高为new_h, new_w,则ratio = min(new_h/ori_h, new_w/ori_w), 然后让x1 = x1/ratio, y1 = y1/ratio, x2 = x2/ratio, y2 = y2/ratio,即得到原始图像的位置。
  • 代码如下:
def scale_coords(output, ori_shape, ratio=1.0, top_pad=0, left_pad=0):
    ori_h, ori_w = ori_shape
    output[:, [0, 2]] -= left_pad
    output[:, [1, 3]] -= top_pad
    output[:, :4] /= ratio
    output[:, [0, 2]] = output[:, [0, 2]].clip(0, ori_w)
    output[:, [1, 3]] = output[:, [1, 3]].clip(0, ori_h)

完成。

Original: https://blog.csdn.net/weixin_43508499/article/details/125884082
Author: lokvke
Title: 一种非极大值抑制(non_max_suppression, nms)的代码实现方式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/762934/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球