跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++AI算法

YOLO11 模型 C++ 部署:ONNX 导出、NMS 判别与推理实战

YOLO11 模型 C++ 部署流程涵盖 ONNX 导出配置、NMS 节点判别及推理环境搭建。核心在于使用 nms=True 参数导出 end2end 模型,并通过输出张量形状验证是否包含 NonMaxSuppression 算子。针对无 NMS 的原始头模型,需在 C++ 端手动实现后处理逻辑,同时解决 CMake 配置及依赖库兼容性问题以确保稳定运行。

dehua dong发布于 2026/1/6更新于 2026/7/2135 浏览
YOLO11 模型 C++ 部署:ONNX 导出、NMS 判别与推理实战

YOLO11 模型 C++ 部署:ONNX 导出、NMS 判别与推理实战

1. 现象与本质

现象:C++ 推理出现'满屏框'或'0 框'。 根因:导出的 ONNX 多为不带 NMS 的原始头,输出形状常见 [1, 84, 8400](84=4+80),模型不会帮你做阈值与 NMS;C++ 若不做后处理,就会'满屏框'。

在这里插入图片描述

相对地,带 NMS 的 ONNX(end2end) 会直接输出最终框,形状常见 [N,6] 或 [1,N,6](x1 y1 x2 y2 score class),C++ 只需画框。


2. NMS 是什么?为什么一定要做?

YOLO 在多尺度(如 80×80、40×40、20×20)上为每个网格点预测很多候选框。同一目标附近会出现大量位置相近、得分也不低的候选。NMS(Non-Maximum Suppression,非极大值抑制)的任务就是:在重叠的候选中,只保留那个最靠谱的(分数最高),把与之高度重叠的重复框删掉。

2.1 NMS 是怎么做的(硬 NMS 伪代码)
  • IoU(交并比):IoU(A,B) = Area(A∩B) / Area(A∪B) ∈ [0, 1]
  • 硬 NMS 两步:
    1. 按置信度降序排序;
    2. 依次取最高分框,删除与它 IoU 大于阈值(如 0.45)的后续框。
# boxes: [N, 4] in x1y1x2y2
# scores: [N]
# thr_conf: 置信度阈值(如 0.25)
# thr_iou: NMS IoU 阈值(如 0.45)
keep = []
idxs = argsort(scores, descending=True)
while idxs not empty:
    i = idxs[0]
    keep.append(i)
    ious = IoU(boxes[i], boxes[idxs[1:]])
    idxs = idxs[1:][ious <= thr_iou]
return keep

常见做法是按类别分别做 NMS(class-wise NMS)。Ultralytics 默认如此。

2.2 一个极小数值例子
idxscorebox(x1,y1,x2,y2)
00.90(10,10,50,50)
10.85(12,12,49,49)
20.60(200,200,260,260)
30.40(205,205,258,258)
  • 0 与 1 重叠很高 → 留 0,抑制 1
  • 2 与 3 接近 → 留 2,抑制 3 最终仅保留 idx=0、idx=2 两个框

3. 最佳做法:导出'带 NMS'的 ONNX

必须从 .pt 权重导出;不能在现有 .onnx 上'打开 NMS'。

from ultralytics import YOLO
m = YOLO(r"path/to/best.pt")  # 训练得到的 .pt 或 yolov8n.pt
m.export(format="onnx", imgsz=640,  # 与训练一致 dynamic=False, simplify=True,  # 安装了 onnxsim 就 True,没有也行 nms=True,  # ★★★ 关键:将 NonMaxSuppression 写进图里 conf=0.25,  # 内置置信阈 iou=0.45,  # 内置 NMS IoU opset=12  # 12/13/16 均可,保持一致即可)

导出成功后,继续下一节检查是否真的带 NMS。


4. 如何判断是否'带 NMS'

4.1 Python(最快)
import onnxruntime as ort
sess = ort.InferenceSession("best.onnx", providers=["CPUExecutionProvider"])
print([(o.name, o.shape) for o in sess.get_outputs()])  # 看到 [..., 6] / [..., 7] => 带 NMS
# 看到 84/85 出现在任一维 => 原始头(不带 NMS)

在这里插入图片描述

4.2 C++(不依赖 Python)
#include <onnxruntime_cxx_api.h>
#include <iostream>
#include <vector>
#include <filesystem>

static std::string Shape2Str(const std::vector<int64_t>& v){
  std::string s = "[";
  for(size_t i = 0; i < v.size(); ++i){
    s += std::to_string(v[i]);
    if(i + 1 < v.size()) s += ',';
  }
  return s += ']';
}

static bool LikeNMS(const std::vector<int64_t>& shp){
  return shp.size() >= 2 && (shp.back() == 6 || shp.back() == 7);
}

static bool LikeRaw(const std::vector<int64_t>& shp){
  for(auto d : shp)
    if(d == 84 || d == 85) return true;
  return false;
}

static void InspectOutputs(const Ort::Session& sess){
  const size_t n = sess.GetOutputCount();
  bool bAnyN = false, bAnyR = false;
  for(size_t i = 0; i < n; ++i){
    Ort::AllocatorWithDefaultOptions alloc;
    auto name = sess.GetOutputNameAllocated(i, alloc);
    auto shp = sess.GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape();
    std::cout << "out[" << i << "] " << (name ? name.get() : "(null)") << " shape=" << Shape2Str(shp) << "\n";
    bAnyN |= LikeNMS(shp);
    bAnyR |= LikeRaw(shp);
  }
  if(bAnyN) std::cout << "[detect] ✅ 带 NMS 的 end2end 输出\n";
  else if(bAnyR) std::cout << "[detect] ⚠️ 原始头(不带 NMS)\n";
  else std::cout << "[detect] ℹ️ 非常见形状,建议用 Netron 检查是否含 NonMaxSuppression\n";
}

int main(int argc, char** argv){
  if(argc < 2){
    std::cout << "Usage:\n " << argv[0] << " <onnx_model>\n";
    return 0;
  }
  std::filesystem::path strOnnxPath = argv[1];
  if(!std::filesystem::exists(strOnnxPath)){
    std::cerr << "[error] model not found: " << strOnnxPath;
    return 1;
  }
  // ... truncated
}

5. 输出张量的物理含义

(此处省略后续内容,基于原文逻辑推断)

6. 最小 C++ 示例(带 NMS 的 ONNX)

(此处省略后续内容)

7. 如果手上只有'原始头'ONNX(84/85)怎么办?

(此处省略后续内容)

8. vcpkg 无 ORT Config.cmake:CMake 兜底

(此处省略后续内容)

9. 常见坑位与速查

(此处省略后续内容)

10. Checklist

(此处省略后续内容)

附录:NMS 变体与阈值选择

(此处省略后续内容)

目录

  1. YOLO11 模型 C++ 部署:ONNX 导出、NMS 判别与推理实战
  2. 1. 现象与本质
  3. 2. NMS 是什么?为什么一定要做?
  4. 2.1 NMS 是怎么做的(硬 NMS 伪代码)
  5. boxes: [N, 4] in x1y1x2y2
  6. scores: [N]
  7. thr_conf: 置信度阈值(如 0.25)
  8. thr_iou: NMS IoU 阈值(如 0.45)
  9. 2.2 一个极小数值例子
  10. 3. 最佳做法:导出“带 NMS”的 ONNX
  11. 4. 如何判断是否“带 NMS”
  12. 4.1 Python(最快)
  13. 看到 84/85 出现在任一维 => 原始头(不带 NMS)
  14. 4.2 C++(不依赖 Python)
  15. 5. 输出张量的物理含义
  16. 6. 最小 C++ 示例(带 NMS 的 ONNX)
  17. 7. 如果手上只有“原始头”ONNX(84/85)怎么办?
  18. 8. vcpkg 无 ORT Config.cmake:CMake 兜底
  19. 9. 常见坑位与速查
  20. 10. Checklist
  21. 附录:NMS 变体与阈值选择
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • CoPaw 与 OpenFang 开源 AI 工具本地部署及内存配置指南
  • Python 集合(Set)CURD 操作与集合运算详解
  • Llama 3:Meta 新一代开源大语言模型详解
  • 使用 Ollama、Open WebUI 和 Docker 本地部署可视化 AI 大语言模型
  • 神经网络算法全景解析
  • Nix 入门教程:5 步创建可复现开发环境
  • AutoGPT 结合 Python:构建自主 AI 智能体的实战指南
  • 计算机视觉基础与实战应用详解
  • Python warnings 库底层机制与企业级 API 演进实战
  • MBA 培训管理系统低代码开发实战指南
  • Dubbo 源码解析:与 Spring 如何实现无缝对接
  • 低代码结合大模型:中小企业半天构建专属 SaaS 应用路径
  • DeepSeek 结合通义万相制作 AI 视频实战详解
  • Vheer:免费免登录的一键 AI 绘画视频生成与智能编辑工具
  • 2024 年前端框架技术复盘与展望
  • 网络通讯核心协议:TCP、UDP 与 HTTP/HTTPS 详解
  • Python 构建 MCP 应用实战指南
  • 腾讯混元大模型 AIGC 系列产品深度体验
  • 大模型 LLM 在数据领域的应用探索
  • 基于 Kafka 的医嘱事件架构设计

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online