欢迎光临
我们一直在努力

为什么 YOLO 导出 ONNX 后检测框全是负数?一文讲透 NMS 导出差异与后处理适配

前言:换了一个模型,为什么代码就崩了?

在 AI 视觉落地开发中,很多开发者都经历过这样一个“灵异事件”:原本跑得好好的 YOLO ONNX 推理代码,仅仅因为重新训练或更换了一个新模型,检测结果就突然满屏乱飞,甚至出现大量坐标为负数的异常框。

其实,这并非你的 Python 代码写错了,而是ONNX 模型的底层输出格式发生了变化。YOLO 在导出 ONNX 时,关于是否内置 NMS(非极大值抑制)后处理,存在两种截然不同的模式。本文将带你彻底理清这两种模式的本质区别,并给出对应的标准代码解决方案。


一、 核心本质:ONNX 导出的两种流派

在使用 Ultralytics (YOLOv5/v8/v11等) 导出模型时,nms 参数的设置决定了计算图的最终形态。这直接导致了推理端后处理逻辑的分野:

1. 不带 NMS 的原始输出(Raw Logits / One-to-Many Head)

  • 导出方式:默认导出,或显式指定 nms=False。
  • 输出格式:张量形状通常为 [1, 84, 8400](以 YOLOv8 为例,84 = 4个边界框坐标 + 80个类别分数)。
  • 物理含义:此时输出的前4个值通常是归一化的中心点坐标和宽高 (cx, cy, w, h),数值范围在 0~1 之间。它没有经过任何解码,也没有做 NMS 过滤。
  • 常见坑点:如果你用处理绝对坐标的代码去解析这种归一化小数(例如把 cx=0.5 减去 padding=50),就会得到 -49.5 的负数坐标!这就是负数框产生的根本原因。

2. 带 NMS 的端到端输出(End-to-End / Built-in NMS)

  • 导出方式:导出时指定 nms=True。
  • 输出格式:张量形状通常为 [1, max_boxes, 6](6代表 x1, y1, x2, y2, score, class_id)。
  • 物理含义:模型内部已经完成了 xywh -> xyxy 的转换、置信度阈值过滤以及 NMS 去重。直接输出基于输入尺寸(如 640×640)的绝对像素坐标。
  • 优势:部署极其简单,C++ 或 Python 端只需将坐标缩放回原图即可画框。

二、 如何判断我的 ONNX 是哪种模式?

不要靠猜,最科学的方法是用 Netron 打开 .onnx 文件查看最后一个节点,或者在 Python 中打印输出:

outputs = session.run(None, {

\”images\”: blob})[0]
print(\”模型输出形状:\”, outputs.shape)
print(\”前3个预测框的前4个值:\”, outputs[0][:3, :4])

  • 情况 A:如果打印出来是类似 [0.45, 0.67, 0.12, 0.23] 的小数 无 NMS 原始输出。
  • 情况 B:如果打印出来是类似 [120.5, 340.2, 180.1, 400.5] 的大数 带 NMS 端到端输出。

三、 对症下药:两种情况的标准处理代码

场景 A:针对【不带 NMS】的原始输出

你需要手动完成:转置维度 -> 提取最大类别分数 -> 坐标解码(xywh转xyxy) -> Letterbox反算 -> 手动NMS。

import numpy as np
import cv2

def postprocess_raw(outputs, scale, top, left, orig_h,<

赞(0)
未经允许不得转载:171主机测评 » 为什么 YOLO 导出 ONNX 后检测框全是负数?一文讲透 NMS 导出差异与后处理适配
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址