OpenCV DNN加载ONNX是最快上手路径,但需严格匹配模型输出结构、letterbox预处理及NMS后处理;追求INT8量化或多流并发时应选TensorRT。

直接用 OpenCV DNN 模块加载 ONNX 是最快上手的路径,但必须确认模型输出结构是否匹配;若追求极致性能或需 INT8 量化、多流并发,TensorRT 是绕不开的选择。
ONNX + OpenCV DNN 部署最简路径
OpenCV 4.5.4+ 的 dnn::readNetFromONNX 能直接加载标准 YOLO 输出结构的 ONNX 模型(如 YOLOv5/v8/v10/v12 的导出格式),无需额外推理库。关键不是“能不能跑”,而是“后处理对不对”:
- YOLOv5/v8 的 ONNX 默认输出是
[1, 25200, 85](含 conf + 4 bbox + 80 cls),而 YOLOv12 或某些自定义头可能输出多个 tensor(如boxes、scores、labels分离),此时net.forward()返回的vectorsize ≠ 1,必须按实际顺序取对应 blob - 预处理必须严格复现训练时的
letterbox:缩放+填充+归一化(通常除以 255.0,非 127.5),且 padding 值要填 114(灰值)而非 0,否则小目标检测率骤降 - NMS 必须用
dnn::NMSBoxes,但输入的scores必须是一维 vector,不能直接传入 shape=(N,1) 的 Mat;常见错误是把outs[0].col(4)当作 score 向量,实际应先outs[0].col(4).reshape(1, -1)再copyTo()到 vector - Windows 下若报
cv::dnn::dnn4_v20230705::Net::forward() exception: OpenCV(4.8.1) ... error: (-215:Assertion failed) ... in function 'forward',大概率是 ONNX opset 版本不兼容(建议固定用 opset=13)或输入 blob 的cv::Size与模型期望不符(如传入Size(640,640)但模型只接受Size(640,640,3)的 Mat)
TensorRT C++ 部署绕不开的硬门槛
TensorRT 不是“换一个 API 就行”,它要求你亲手处理从 ONNX 解析、builder 配置、engine 序列化到内存管理的全链路。尤其在 Windows 下,trtexec 生成的 .engine 文件无法跨 GPU 架构复用(如 A100 生成的 engine 在 RTX 4090 上会加载失败),必须本地构建:
- ONNX 导出时务必加
simplify=True(用onnxsim),否则 TensorRT builder 会在addQKV或MultiHeadAttention层卡死——YOLO26/YOLOv11 的 head 结构稍复杂就容易触发 - INT8 量化必须提供校准数据集(至少 500 张图),且
IInt8Calibrator实现中getBatch()返回的 input 数据必须与训练时归一化方式完全一致(例如 YOLOv10 默认用mean=[0,0,0], std=[1/255,1/255,1/255],校准时就不能错用 ImageNet 标准化) - Windows 下 VS2022 编译时,
TensorRT头文件路径必须用双反斜杠或正斜杠("C:/TensorRT/include"),混用单反斜杠("C:\TensorRT\include")会导致宏展开异常,编译报error C2065: 'nvinfer1' : undeclared identifier -
IExecutionContext::enqueueV3接口要求显式绑定cudaStream_t,若漏传或传入空 stream,推理会卡住无返回,且无明确错误提示
别踩 OpenVINO 的硬件绑定坑
OpenVINO 在 Windows 上对 Intel 独显(Arc 系列)支持尚不稳定,多数项目实测仍依赖 CPU 或 Iris Xe 核显。若你用的是 NVIDIA 显卡,强行走 OpenVINO IR 流程只会多一层转换开销:
- YOLOv11/v8-pose 的 OpenVINO 部署示例中,
model.xml和model.bin必须成对存在,且Core::read_model()加载时路径不能含中文或空格,否则抛InferenceEngine::details::InferenceEngineException - IR 模型的预处理由
ov::preprocess::PrePostProcessor定义,但 YOLO 类模型的 letterbox 逻辑无法用声明式 API 表达,必须在infer_request.set_input_tensor()前手动完成,否则输出坐标全部偏移 - OpenVINO 的
ov::CompiledModel在多线程调用时,每个线程必须持有独立的ov::InferRequest实例,共享同一CompiledModel可以,但复用InferRequest会导致set_tensor覆盖前序数据
真正难的从来不是“怎么调通第一个 demo”,而是当你要把 YOLO26 的 OBB 角度解码、YOLOv12 的 multi-scale head、或 YOLOv10 的 dynamic anchor 后处理逻辑,一行行写进 C++ 里并保证和 PyTorch 端数值完全对齐——这时候你会发现,连 atan2(sinθ, cosθ) 的象限判断都得对照原论文重验三遍。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











