【目标检测】非极大值抑制(NMS)的原理与实现

🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN人工智能领域的优质创作者,提供AI相关的技术咨询、项目开发和个性化解决方案等服务,如有需要请站内私信或者联系任意文章底部的的VX名片(ID:xf982831907
💬 博主粉丝群介绍:① 群内初中生、高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。③ 群内也有职场精英,大厂大佬,可交流技术、面试、找工作的经验。④ 进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬。⑤ 进群赠送CSDN评论防封脚本,送真活跃粉丝,助你提升文章热度。有兴趣的加文末联系方式,备注自己的CSDN昵称,拉你进群,互相学习共同进步。

@TOC

一、引言

在目标检测中,NMS是提升模型性能的关键后处理技术,但90%的初学者对其实现原理理解不足。本文将彻底解密NMS的工作原理,并提供可直接运行的Python代码,让你真正掌握这一核心技能!

二、为什么需要非极大值抑制(NMS)?

目标检测模型在处理图像时,往往会对同一个目标产生多个重叠的预测框。如下图所示:

图片来源于:https://blog.csdn.net/weixin_62264287/article/details/133936328

这些重叠框会导致:

  1. 重复检测:同一目标被多次识别
  2. 结果冗余:输出包含大量无效预测
  3. 性能下降:影响后续处理效率

非极大值抑制(Non-Maximum Suppression, NMS) 正是为了解决这一问题而诞生。它像一位"裁判",从众多候选框中选出最合适的一个,淘汰冗余预测。

三、NMS核心原理揭秘

3.1 NMS工作原理四步曲

  1. 排序候选框:将所有预测框按置信度(confidence score)从高到低排序
  2. 选择最高分:选取置信度最高的框作为保留框
  3. 计算IOU:计算该保留框与剩余所有框的IOU
  4. 剔除重叠框:删除IOU超过阈值的框(通常阈值设为0.5)

重复步骤2-4,直到所有框都被处理完毕。整个过程如下图所示:

四、Python实现NMS算法

下面我们实现一个完整的NMS算法,并可视化处理效果:

python 复制代码
import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib.patches as patches

def nms(boxes, scores, threshold=0.5):
    """
    非极大值抑制(NMS)实现
    
    参数:
        boxes: 边界框列表 [x1, y1, x2, y2]
        scores: 每个框的置信度
        threshold: IOU阈值
        
    返回:
        保留的框索引列表
    """
    # 如果没有框,直接返回空列表
    if len(boxes) == 0:
        return []
    
    # 将框坐标转换为float类型
    boxes = np.array(boxes, dtype=np.float32)
    
    # 获取框的坐标
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    
    # 计算每个框的面积
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    
    # 按置信度降序排序
    idxs = np.argsort(scores)
    
    # 初始化保留列表
    keep = []
    
    while len(idxs) > 0:
        # 取出当前置信度最高的框
        last = len(idxs) - 1
        i = idxs[last]
        keep.append(i)
        
        # 计算当前框与其他框的IOU
        xx1 = np.maximum(x1[i], x1[idxs[:last]])
        yy1 = np.maximum(y1[i], y1[idxs[:last]])
        xx2 = np.minimum(x2[i], x2[idxs[:last]])
        yy2 = np.minimum(y2[i], y2[idxs[:last]])
        
        # 计算交集区域的宽和高
        w = np.maximum(0, xx2 - xx1 + 1)
        h = np.maximum(0, yy2 - yy1 + 1)
        
        # 计算IOU
        intersection = w * h
        iou = intersection / (areas[i] + areas[idxs[:last]] - intersection)
        
        # 删除IOU大于阈值的框
        delete_idxs = np.where(iou > threshold)[0]
        idxs = np.delete(idxs, np.concatenate(([last], delete_idxs)))
    
    return keep

def visualize_boxes(image, boxes, scores, title, color='r'):
    """可视化边界框"""
    plt.figure(figsize=(10, 6))
    plt.imshow(image)
    ax = plt.gca()
    
    for i, box in enumerate(boxes):
        x1, y1, x2, y2 = box
        rect = patches.Rectangle(
            (x1, y1), x2-x1, y2-y1, 
            linewidth=2, edgecolor=color, facecolor='none'
        )
        ax.add_patch(rect)
        plt.text(
            x1, y1-10, f'{scores[i]:.2f}', 
            fontsize=10, color='white', 
            bbox=dict(facecolor=color, alpha=0.8)
        )
    
    plt.title(title)
    plt.axis('off')
    plt.show()

# 创建模拟图像
image = np.ones((400, 600, 3), dtype=np.uint8) * 255
cv2.putText(image, 'Target Detection', (150, 200), 
           cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 0), 3)

# 模拟检测结果 (格式: [x1, y1, x2, y2])
boxes = [
    [100, 100, 300, 300],   # 高置信度
    [120, 120, 320, 320],   # 与第一个高度重叠
    [150, 150, 350, 350],   # 与第一个高度重叠
    [400, 150, 550, 350],   # 不同位置
    [380, 130, 530, 330],   # 与第四个重叠
    [420, 170, 570, 370]    # 与第四个重叠
]

# 对应的置信度
scores = [0.95, 0.90, 0.85, 0.92, 0.88, 0.80]

# 可视化原始检测结果
visualize_boxes(image, boxes, scores, '原始检测结果 (NMS前)', color='r')

# 应用NMS
keep = nms(boxes, scores, threshold=0.5)

# 筛选保留的框
nms_boxes = [boxes[i] for i in keep]
nms_scores = [scores[i] for i in keep]

# 可视化NMS后的结果
visualize_boxes(image, nms_boxes, nms_scores, 'NMS处理后的结果', color='g')

五、代码解析与运行结果

5.1 核心函数解析

  1. nms函数

    • 输入:边界框列表、置信度列表、IOU阈值
    • 处理流程:
      • 按置信度排序
      • 循环处理每个框
      • 计算当前框与其他框的IOU
      • 删除IOU超过阈值的框
    • 输出:保留框的索引列表
  2. visualize_boxes函数

    • 功能:在图像上绘制边界框和置信度
    • 参数:图像、框列表、置信度列表、标题、颜色

5.2 运行效果展示

运行上述代码,你将看到两个对比图像:

NMS前

  • 红色框表示原始检测结果
  • 同一目标有多个重叠框

NMS后

  • 绿色框表示NMS处理后的结果

六、NMS的进阶应用与优化

6.1 软性NMS(Soft-NMS)

传统NMS直接删除重叠框,可能导致以下问题:

  • 密集目标漏检
  • 阈值设置敏感

软性NMS的改进方案:

python 复制代码
# 在NMS循环中替换删除操作
for j in delete_idxs:
    # 根据IOU降低置信度,而不是直接删除
    scores[j] = scores[j] * (1 - iou[j])

6.2 多类别NMS处理

当处理多类别检测时,需要:

python 复制代码
# 按类别分组处理
for class_id in unique_classes:
    class_indices = np.where(class_ids == class_id)[0]
    class_boxes = boxes[class_indices]
    class_scores = scores[class_indices]
    keep_indices = nms(class_boxes, class_scores)
    # 合并各类别结果

6.3 其他改进方法

方法 核心思想 适用场景
Weighted NMS 加权融合重叠框 需要更精确位置
Adaptive NMS 动态调整阈值 密集目标检测
DIoU NMS 考虑中心点距离 解决边界框错位

七、NMS在实际项目中的应用技巧

7.1 阈值选择策略

  • 通用目标:0.5-0.7
  • 密集小目标:0.3-0.5
  • 大目标/精确检测:0.7-0.9

7.2 性能优化技巧

python 复制代码
# 向量化加速
def vectorized_nms(boxes, scores, threshold):
    # 使用矩阵运算代替循环
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    order = scores.argsort()[::-1]
    
    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        
        # 向量化计算IOU
        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:]])
        
        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h
        
        iou = inter / (areas[i] + areas[order[1:]] - inter)
        
        # 向量化索引
        inds = np.where(iou <= threshold)[0]
        order = order[inds + 1]
    
    return keep

八、总结与思考

NMS作为目标检测中不可或缺的后处理步骤,其重要性不言而喻。通过本文的学习,你应该掌握:

  1. NMS的核心原理和工作流程
  2. NMS的Python实现方法
  3. NMS的优化技巧和变体
  4. 实际应用中的参数调整策略

关键思考:当两个目标非常接近时,传统NMS可能导致其中一个被错误抑制。如何解决这个问题?

答案:可以尝试以下方法:

  1. 使用Soft-NMS替代传统NMS
  2. 降低IOU阈值(如0.3)
  3. 增加位置权重(如DIoU-NMS)
  4. 调整模型锚框设计

九、附录:完整项目代码

获取NMS和可视化功能的完整代码:
https://gitee.com/zhang-xufang/object_detection_demo/blob/master/NMS.py


觉得本文有帮助?点击👍支持!如果有任何问题或建议,欢迎在评论区留言讨论~

相关推荐
liu5z6662 分钟前
HALCON相机标定
人工智能·计算机视觉
univerbright8 分钟前
百度飞桨(PaddlePaddle)案例分享:基于 PaddleOCR 的图像文字提取系统
人工智能·百度·paddlepaddle·paddleocr·图像文字提取
蓝纹绿茶13 分钟前
【本机已实现】使用Mac部署Triton服务,使用perf_analyzer、model_analyzer
人工智能·算法·macos·机器学习
移远通信15 分钟前
秒切双系统 赋能AI无界!移远通信发布QSM560DR全功能ARM主板
arm开发·人工智能
微信公众号:AI创造财富20 分钟前
Pyenv 跟 Conda 还有 Poetry 有什么区别?各有什么不同?
人工智能·python·conda·tensorflow
Allen_LVyingbo24 分钟前
从“数据困境”到“数据生态”:DaaS重塑三甲医院医疗数据治理
人工智能·搜索引擎·架构·健康医疗
ZONGYINLIU1 小时前
大白话说目标检测中的IOU(Intersection over Union)
人工智能·目标检测·计算机视觉
Python测试之道1 小时前
测试工程师如何利用通用千问大模型精准获取需求文档图片内容
人工智能·通义千问
盼小辉丶1 小时前
Transformer实战——Hugging Face环境配置与应用详解
人工智能·深度学习·transformer
光电大美美-见合八方中国芯1 小时前
【平面波导外腔激光器专题系列】用于干涉光纤传感的低噪声平面波导外腔激光器
网络·人工智能·科技·平面·性能优化·信息与通信