• 非极大值抑制算法(NMS)的python实现


    前言

      本篇博客主要是介绍非极大值抑制NMS算法的python实现,并根据实例检测实现效果。

    在这里插入图片描述

    1. NMS概述

      非极大值抑制(Non-Maximum Supression, NMS),顾名思义,就是抑制非极大值,在目标检测领域中经常使用到,主要是用来对候选框进行去重处理。
      NMS算法的大致流程如下:
      (1) 根据概率分数score对候选框进行排序
      (2) 选择概率分数最大的bbox,记录这个bbox到输出列表中,并删除和这个选框IoU大于一定阈值的bbox
      (3) 继续选择概率分数最大的边框,并添加到输出列表中,重复上处过程直至没有候选框为止。

    2. 绘制候选框

      以上述迪迦奥特曼为例,我们首先提供三个候选框,并在候选框的基础之上生成其他的候选框,实现代码如下:

    import cv2
    import numpy as np
    import copy
    
    
    seed = 10001
    np.random.seed(seed)
    
    bounding_boxes = [
            [545, 125, 765, 440],
            [890, 100, 1115, 430],
            [1275, 170, 1490, 490]
        ]
    
    confidence_score = [0.95, 0.98, 0.96]
    
    num_anchor = 10
    anchors = copy.deepcopy(bounding_boxes)
    scores = copy.deepcopy(confidence_score)
    
    
    if __name__ == '__main__':
        img = cv2.imread('dijia.png')
    
        for i in range(num_anchor):
            index = np.random.randint(0, 3)
            offset = np.random.randint(-50, 50, size=4)
            score = np.random.uniform(0.5, 0.9)
            anchors.append(list(bounding_boxes[index] - offset))
            scores.append(round(score, 2))
    
        for i in range(len(scores)):
            cv2.rectangle(img, pt1=tuple(anchors[i][:2]), pt2=tuple(anchors[i][2:]), color=(0, 255, 0), thickness=2)
            cv2.putText(img, text=str(scores[i]), org=tuple(anchors[i][:2]), fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
                        fontScale=1, color=(255, 0, 255), thickness=2)
        cv2.imshow('dijia', img)
        cv2.waitKey()
        cv2.imwrite('dijia1.png', img)
    
    # cv2.rectangle: 	pt1: 左上角坐标
    # 					pt2: 右下角坐标
    # 					color: 边框颜色(B, G, R)
    # 					thickness: 边框粗细
    
    # cv2.putText: 	text: 文字信息
    # 				org: 起始点坐标(左下角)
    # 				fontFace: 字体类型
    # 				fontScale: 字体大小
    # 				color: 字体颜色(B, G, R)
    # 				thickness: 字体粗细
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

      代码运行结果如下:

    在这里插入图片描述

    3. NMS代码实现

      在实现NMS算法之前先看一下IoU是如何计算的:
      IoU就是我们常说的交并比(Intersection over Union, IoU),顾名思义,就是交集与并集的比值,反映的是两个物体间的重合程度。计算公式如下: I o U = A ∩ B A ∪ B IoU = \frac{A \cap B} {A \cup B} IoU=ABAB

      根据上图所示,IoU 就等于左边灰色面积与右边灰色面积的比值。
      下面来看一下NMS算法的具体实现:

    def nms(bboxes, scores, threshold=0.5):
        x1 = bboxes[:, 0]
        y1 = bboxes[:, 1]
        x2 = bboxes[:, 2]
        y2 = bboxes[:, 3]
    
        areas = (x2 - x1) * (y2 - y1)
    
        # 从大到小对应的的索引
        order = scores.argsort()[::-1]
    
        # 记录输出的bbox
        keep = []
        while order.size > 0:
            i = order[0]
            # 记录本轮最大的score对应的index
            keep.append(i)
    
            if order.size == 1:
                break
    
            # 计算当前bbox与剩余的bbox之间的IoU
            # 计算IoU需要两个bbox中最大左上角的坐标点和最小右下角的坐标点
            # 即重合区域的左上角坐标点和右下角坐标点
            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:]])
    
            # 如果两个bbox之间没有重合, 那么有可能出现负值
            w = np.maximum(0.0, (xx2 - xx1))
            h = np.maximum(0.0, (yy2 - yy1))
            inter = w * h
    
            iou = inter / (areas[i] + areas[order[1:]] - inter)
    
            # 删除IoU大于指定阈值的bbox(重合度高), 保留小于指定阈值的bbox
            ids = np.where(iou <= threshold)[0]
            # 因为ids表示剩余的bbox的索引长度
            # +1恢复到order的长度
            order = order[ids + 1]
    
        return keep
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    4. 完整代码

    import cv2
    import numpy as np
    import copy
    
    
    seed = 10001
    np.random.seed(seed)
    
    bounding_boxes = [
            [545, 125, 765, 440],
            [890, 100, 1115, 430],
            [1275, 170, 1490, 490]
        ]
    
    confidence_score = [0.95, 0.98, 0.96]
    
    num_anchor = 10
    anchors = copy.deepcopy(bounding_boxes)
    scores = copy.deepcopy(confidence_score)
    
    
    def nms(bboxes, scores, threshold=0.5):
        x1 = bboxes[:, 0]
        y1 = bboxes[:, 1]
        x2 = bboxes[:, 2]
        y2 = bboxes[:, 3]
    
        areas = (x2 - x1) * (y2 - y1)
    
        # 从大到小对应的的索引
        order = scores.argsort()[::-1]
    
        # 记录输出的bbox
        keep = []
        while order.size > 0:
            i = order[0]
            # 记录本轮最大的score对应的index
            keep.append(i)
    
            if order.size == 1:
                break
    
            # 计算当前bbox与剩余的bbox之间的IoU
            # 计算IoU需要两个bbox中最大左上角的坐标点和最小右下角的坐标点
            # 即重合区域的左上角坐标点和右下角坐标点
            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:]])
    
            # 如果两个bbox之间没有重合, 那么有可能出现负值
            w = np.maximum(0.0, (xx2 - xx1))
            h = np.maximum(0.0, (yy2 - yy1))
            inter = w * h
    
            iou = inter / (areas[i] + areas[order[1:]] - inter)
    
            # 删除IoU大于指定阈值的bbox(重合度高), 保留小于指定阈值的bbox
            ids = np.where(iou <= threshold)[0]
            # 因为ids表示剩余的bbox的索引长度
            # +1恢复到order的长度
            order = order[ids + 1]
    
        return keep
    
    
    if __name__ == '__main__':
        img = cv2.imread('dijia.png')
    
        for i in range(num_anchor):
            index = np.random.randint(0, 3)
            offset = np.random.randint(-50, 50, size=4)
            score = np.random.uniform(0.5, 0.9)
            anchors.append(list(bounding_boxes[index] - offset))
            scores.append(round(score, 2))
    
        anchors = np.asarray(anchors)
        scores = np.asarray(scores)
    
        keep = nms(anchors, scores, threshold=0.5)
        proposals = anchors[keep]
        proposals_score = scores[keep]
    
        for i in range(len(proposals)):
            cv2.rectangle(img, pt1=tuple(proposals[i][:2]), pt2=tuple(proposals[i][2:]), color=(0, 255, 0), thickness=2)
            cv2.putText(img, text=str(proposals_score[i]), org=tuple(proposals[i][:2]), fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
                        fontScale=1, color=(255, 0, 255), thickness=2)
        cv2.imshow('dijia', img)
        cv2.waitKey()
        cv2.imwrite('dijia2.png', img)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

      代码运行如下:

    在这里插入图片描述

    结束语

      从NMS的实现代码中可以看到主要是对数组的操作,而这部分可以通过GPU进行加速处理,比如PyTochPaddlePaddle等深度学习框架,其目标检测模块中的NMS算法已经内置实现,可以直接在GPU上使用。

  • 相关阅读:
    CF1535F String Distance
    数字孪生技术:新零售的未来之路
    最新AI写作创作系统源码ChatGPT源码,支持AI绘画/支持OpenAI-GPT全模型+国内AI全模型
    Go: 对数表示的幂(附完整源码)
    Apple Watch设计原则,让你开发app思路更清晰
    windows下redis 哨兵配置
    数据分散情况的统计图-盒须图
    Java零基础入门-数组
    Lombok包依赖的注入
    java-php-python-ssm校园绿化管理系统计算机毕业设计
  • 原文地址:https://blog.csdn.net/qq_42730750/article/details/126235745