自定义训练YOLOv7模型C++部署遇ONNX解析错误求解决方案
问题背景
使用自定义数据集训练YOLOv7单类别(坑洞)检测模型,尝试部署到C++项目时,无论是.pt还是重参数化后转换的.onnx权重均报错。使用OpenCV 4.1.1加载ONNX模型时,出现以下错误:
OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(4.1.1) Error: Unsupported format or combination of formats (Failed to parse onnx model) in cv::dnn::dnn4_v20190621::ONNXImporter::ONNXImporter, file C:\opencv-4.1.1\modules\dnn\src\onnx\onnx_importer.cpp, line 57
核心原因
OpenCV 4.1.1的DNN模块对YOLOv7使用的新型算子(如SiLU、Focus等)支持不足,无法正确解析对应的ONNX模型结构,必须升级OpenCV版本。
可行解决方案
1. 升级OpenCV版本
- 最低要求升级到OpenCV 4.5.5,推荐使用4.6.0及以上稳定版本,这些版本完善了对YOLOv7相关ONNX算子的支持。
- 可直接下载官方预编译包(需包含DNN模块),或从源码编译自定义配置。
2. 重新转换ONNX模型
使用YOLOv7官方提供的export.py脚本转换重参数化后的.pt模型,确保适配OpenCV的DNN模块:
python export.py --weights yolov7-reparam.pt --grid --end2end --simplify --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640 --classes 1
--classes 1:指定单类别模型,避免输出维度不匹配--simplify:简化ONNX模型结构,提升OpenCV兼容性
3. 调整C++代码适配
(1)替换类别文件
将原代码中的coco.names替换为自定义类别文件(如pothole.names),文件内仅需一行内容:pothole
(2)修复输入尺寸解析逻辑
原代码从模型文件名提取尺寸的逻辑存在局限性,改为从ONNX模型中直接读取输入尺寸:
YOLOV7::YOLOV7(Net_config config) { this->confThreshold = config.confThreshold; this->nmsThreshold = config.nmsThreshold; this->net = readNetFromONNX(config.modelpath); ifstream ifs("pothole.names"); // 替换为自定义类别文件 string line; while (getline(ifs, line)) this->class_names.push_back(line); this->num_class = class_names.size(); // 替换原尺寸解析代码,直接从模型获取输入尺寸 auto input_layer = net.getLayer(0); inpWidth = input_layer->inputInfo[0].dims[3]; inpHeight = input_layer->inputInfo[0].dims[2]; }
(3)验证输出处理逻辑
单类别模型的输出维度为[num_proposals, 6](5个框参数+1个类别得分),原代码的colRange(5, nout)可正常工作,但需确保minMaxLoc的处理逻辑正确,无需额外修改。
4. 验证ONNX模型有效性
使用Netron工具打开ONNX模型,检查:
- 输入输出维度是否符合预期(输入通常为
[1,3,640,640],输出为[1,num_proposals,6]) - 模型中是否包含OpenCV不支持的算子(若有,需重新转换时添加
--simplify或调整转换参数)
内容的提问来源于stack exchange,提问作者Mohamed Hedeya

