opencv dnn 加载 onnx 模型必须用 cv::dnn::readnetfromonnx,且需 opencv 4.5+ 才支持动态轴和较新 opset;低于版本易报 resize 不支持等错误,且仅稳定支持 opset 12~13。

OpenCV DNN 加载 ONNX 模型必须用 cv::dnn::readNetFromONNX
OpenCV 4.5+ 才真正支持 ONNX(尤其带动态轴、自定义算子或较新 opset 的模型),低于这个版本会报 Unsupported op 'Resize' 或 Unknown layer type。不是所有 ONNX 都能跑,OpenCV DNN 只实现了一部分 ONNX opset 子集(目前稳定支持到 opset 12~13,opset 16+ 多数 Resize/Gather/ScatterND 会失败)。
加载时别用 cv::dnn::readNet —— 它会自动尝试推断格式,但对 ONNX 常误判为 TorchScript 或出错;必须显式调用 cv::dnn::readNetFromONNX:
cv::dnn::Net net = cv::dnn::readNetFromONNX("model.onnx");
如果返回空 net.empty() == true,大概率是 ONNX 版本不兼容,先用 onnx.checker.check_model 在 Python 里验证模型有效性,再降级导出(比如 PyTorch 导出时指定 opset_version=11)。
输入预处理要手动匹配 ONNX 模型的期望格式
ONNX 模型的输入 shape 和数据类型(float32 / uint8)、归一化方式(ImageNet mean/std?0–1?-1–1?)完全由训练框架决定,OpenCV DNN 不做任何自动适配。常见坑:
- 模型输入是
NCHW,但你给的是NHWC(OpenCV 默认是 HWC),必须用cv::dnn::blobFromImage并设swapRB=false+crop=false,再手动cv::transpose或用cv::dnn::blobFromImages的size参数强制 resize - 模型要求输入是
uint8 [0,255],但你用了blobFromImage(..., 1.0/255.0)→ 输出 float32 [0,1],直接喂进去结果全乱 - 模型输入 shape 是
1x3x640x640,你传了480x640图片,没 resize 到固定尺寸就调net.setInput→ 后续 infer 报Input blob size mismatch
查模型输入信息最靠谱的方式:用 Python + onnxruntime 打印:
import onnx
model = onnx.load("model.onnx")
print(model.graph.input[0].type.tensor_type.shape)
net.forward() 返回值结构取决于模型输出层设计
ONNX 模型可能有单输出(如分类 logits)、多输出(如 YOLO 的 boxes/scores/classes)、甚至带命名的输出节点。OpenCV DNN 不解析输出名,只按拓扑顺序返回 std::vector<:mat></:mat>:
- 若模型只有一个输出节点,
net.forward()返回长度为 1 的 vector,取[0]即可 - 若模型有多个输出(如 ONNX 导出时指定了多个
output_names),需用net.forward("output_name")显式指定,否则forward()默认只返回第一个 - YOLOv5/v8 导出的 ONNX 常把检测头合并成一个 tensor(如
1x25200x85),需要自己 reshape + 解码(非 OpenCV 内置逻辑)
别依赖 net.getUnconnectedOutLayersNames() —— 对 ONNX 模型它常返回空或错误名称,老实用模型文档或 onnxruntime 查输出名。
CPU 推理够快,但 GPU(CUDA)需额外编译且限制多
OpenCV 默认只启用 CPU backend(cv::dnn::DNN_BACKEND_OPENCV)。想用 CUDA,必须满足三个条件:
- OpenCV 编译时启用了
WITH_CUDA=ON且链接了 CUDA 11.2+ / cuDNN 8.2+ - 代码中显式设置:
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA); - 模型中不能含 CUDA 不支持的算子(如某些 ScatterND、Loop、DynamicQuantizeLinear)——这类模型 setPreferableBackend 后调
forward会静默 fallback 到 CPU,也不报错
验证是否真在 GPU 上跑:加一句 std::cout ,看耗时是否显著下降;或者用 <code>nvidia-smi 观察 GPU 利用率。很多用户以为开了 CUDA 就加速了,其实一直在 CPU 跑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











