资讯动态

YOLO 训练好的模型怎么在 QT 下使用?从 ONNX 到 DirectML GPU 加速的完整落地

发布时间:2026/10/24 2:19:00 来源:尧图企业网站定制
YOLO 训练好的模型怎么在 QT 下使用从 ONNX 到 DirectML GPU 加速的完整落地YOLO 训练这步现在很省心。Ultralytics 一条命令best.pt转best.onnx几分钟的事。麻烦的是下一步。模型交到桌面端怎么用我们的场景很典型产线检测工具要发给产线工程师对方机器上不能要求装 Python更不能要求每台机器都有 N 卡。最后用 VS2019 Qt 5.14.2 ONNX RuntimeDirectML 版做了个纯 C 的检测工具YoloDetQt导入模型、批量检测、统计导出一条链路走完全程零 Python 依赖。先看跑起来的效果。PCB 缺陷检测六类缺陷GPU 加速下单张 24ms模型导入之后类别名、输入尺寸、执行设备全是自动解析的。换别的模型一行代码不用动下面把整个过程摊开讲工程怎么配推理代码长什么样CPU 和 GPU 的配置差在哪以及当时为什么没走 CUDA 而是选了 DirectML。整体路线pt 是进不了 Qt 的先转 onnx一件事先说死Qt/C 端加载不了.pt。别在这上面浪费时间标准做法就两步。第一步在训练环境里导出fromultralyticsimportYOLO modelYOLO(best.pt)model.export(formatonnx,imgsz640)# 得到 best.onnx第二步是这篇文章的正题。推理后端我们踩过一次坑。一开始试的 OpenCV DNN想着能少带一个依赖库结果 YOLO26 导出的计算图OpenCV 4.14 的 legacy 导入器和 5.0 的新导入器都啃不动。换 ONNX Runtime一次通过。而且后来发现它还有个更大的好处——DirectML 版本自带 GPU 加速不需要 CUDA。这个后面单独说。工程配置整个工程就两样依赖Qt5 管界面onnxruntime 管推理。图像读写和画框全用 Qt 自带的 QImage 和 QPainter连 OpenCV 都没带。onnxruntime 的运行库直接随工程自带目录长这样YoloDetQt/ ├── YoloDetQt.pro # qmake 工程入口 ├── CMakeLists.txt # CMake 入口跟 .pro 二选一 ├── build_msvc.bat # 一键构建脚本 ├── deps/ │ └── onnxruntime/ # DirectML 版 ONNX Runtime │ ├── include/ # onnxruntime_cxx_api.h 等头文件 │ ├── lib/ # onnxruntime.lib │ └── bin/ # onnxruntime.dll DirectML.dll ★ └── src/ ├── YoloDetector.* # 推理封装本文核心 ├── DetectWorker.* # 批量推理后台线程 ├── MainWindow.* # 主界面 └── ...bin 里那个 DirectML.dll是 GPU 加速的关键必须和 exe 待在同一目录。记住这句话后面有个坑就是它挖的。qmake 配置没什么花头头文件路径、链接库、构建完拷 DLL三件事# ---- onnxruntime随工程自带---- ORT_ROOT $$PWD/deps/onnxruntime INCLUDEPATH $$ORT_ROOT/include LIBS -L$$ORT_ROOT/lib -lonnxruntime win32 { # 源码是 UTF-8不加这个 MSVC 会按本地代码页解析中文字符串乱码 QMAKE_CXXFLAGS /utf-8 # 构建完成后把三个 DLL 拷到 exe 目录 QMAKE_POST_LINK $$QMAKE_COPY \$$ORT_ROOT/bin/onnxruntime.dll\ \$$OUT_PWD\ QMAKE_POST_LINK $$QMAKE_COPY \$$ORT_ROOT/bin/onnxruntime_providers_shared.dll\ \$$OUT_PWD\ QMAKE_POST_LINK $$QMAKE_COPY \$$ORT_ROOT/bin/DirectML.dll\ \$$OUT_PWD\ }CMake 那边同理链接onnxruntime.libPOST_BUILD 里拷同样三个 DLL就不重复贴了。顺带一句/utf-8这个选项建议所有 Qt MSVC 工程都无脑加上不然界面中文乱码这个坑不值得踩第二遍。发布的时候exe 旁边要带齐这几个文件文件作用缺了会怎样onnxruntime.dll推理引擎本体程序起不来onnxruntime_providers_shared.dllEP 共享库部分提供者加载失败DirectML.dllDirectML 后端静默回退 CPU不报任何错Qt5*.dll / platforms/ 等Qt 运行时界面起不来表格里第三行要展开讲讲。DirectML.dll 缺失的时候程序不会崩、不会报错只是悄悄用 CPU 跑一切功能照常。我们就被这个静默回退坑过一次——工具发出去用了两天同事说他那边怎么这么慢一看执行设备CPU。查下来是构建时 DLL 没拷过去。从那以后我们在模型信息栏加了一行执行设备的显示。GPU 配置到底生没生效界面上得能直接看出来不能靠猜核心代码推理封装成一个YoloDetector类对上层就两个接口load()和detect()。核心代码不到一百行。加载模型GPU 和 CPU 的分岔口就在这DirectML 的 EP 入口函数DML 版的 onnxruntime.dll 里是导出了的但公共头文件没声明得自己补一行 extern “C”。然后加载时先试着走 GPU不行就落回 CPUexternCOrtStatus*ORT_API_CALLOrtSessionOptionsAppendExecutionProvider_DML(OrtSessionOptions*options,intdevice_id);boolYoloDetector::load(constQStringonnxPath,QString*error){m_envstd::make_uniqueOrt::Env(ORT_LOGGING_LEVEL_WARNING,YoloDetQt);automakeOptions[nThreads](){Ort::SessionOptions opts;opts.SetIntraOpNumThreads(nThreads);// CPU 线程数默认 min(4, 核数)opts.SetGraphOptimizationLevel(ORT_ENABLE_ALL);returnopts;};booldmlOkfalse;if(m_useGpu){// 先试 GPU (DirectML)try{Ort::SessionOptions optsmakeOptions();OrtStatus*stOrtSessionOptionsAppendExecutionProvider_DML(opts,0);if(!st){m_sessionstd::make_uniqueOrt::Session(*m_env,onnxPath.toStdWString().c_str(),opts);dmlOktrue;}}catch(conststd::exception){m_session.reset();// DML 不可用落回 CPU}}if(!dmlOk){// CPU 路径Ort::SessionOptions optsmakeOptions();m_sessionstd::make_uniqueOrt::Session(*m_env,onnxPath.toStdWString().c_str(),opts);}m_info.providerdmlOk?GPU (DirectML):CPU;// ... 解析输入输出形状、类别名}这就是 CPU 和 GPU 的全部配置差异了。同一个 DLL同一份代码跑在哪个设备只取决于中间那一行AppendExecutionProvider_DML。CPU 路径什么都不用加默认就是 CPU EP。这个写法还有个白送的好处DML 注册失败或者显卡不支持会自动落到 CPU 上接着跑。工具发到任何一台 Windows 机器上都能用区别只是快慢。预处理跟训练侧对不齐全白搭推理结果对不对预处理说了算。这里必须和 Ultralytics 训练、验证时的处理严格一致等比缩放、短边补 114 的灰边、RGB、除 255、NCHW。少对齐一项精度就对不上。// letterbox等比缩放短边补 114 灰边constfloatrqMin(float(H)/ih,float(W)/iw);constintnhqRound(ih*r),nwqRound(iw*r);constinttop(H-nh)/2,left(W-nw)/2;QImage resizedimage.convertToFormat(QImage::Format_RGB888).scaled(nw,nh,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);// NCHW 浮点输入padding 区保持 114 灰std::vectorfloatinput(size_t(W)*H*3,114.f/255.f);for(inty0;ynh;y){constuchar*lineresized.constScanLine(y);float*rRowinput.data()size_t(topy)*Wleft;for(intx0;xnw;x){rRow[x]line[x*30]/255.f;// R 平面rRow[xplane]line[x*31]/255.f;// G 平面rRow[x2*plane]line[x*32]/255.f;// B 平面}}前向Ort::MemoryInfo memOrt::MemoryInfo::CreateCpu(OrtArenaAllocator,OrtMemTypeDefault);constint64_tshape[4]{1,3,H,W};Ort::Value inputTensorOrt::Value::CreateTensorfloat(mem,input.data(),input.size(),shape,4);constchar*inNameimages;// 从 session 读出的实际输入输出名constchar*outNameoutput0;autot0std::chrono::steady_clock::now();std::vectorOrt::Valueoutputsm_session-Run(Ort::RunOptions{nullptr},inName,inputTensor,1,outName,1);autot1std::chrono::steady_clock::now();// t1 - t0 就是单张推理耗时写法跟 Python 端的session.run()一一对应用过 Python 推理的人看一眼就懂。解码和 NMSYOLO26 的输出布局是[1, 4nc, A]A 是锚点数640 输入对应 8400。前 4 行是cx,cy,w,h后面每行一个类别的分数没有 objectness。解码流程文字描述就够遍历锚点取各类别最大分数过 conf 阈值按类别做贪心 NMS最后把坐标经 letterbox 逆变换还原回原图也就是(x - pad) / scale这么一下。有两个细节容易翻车。一是布局要自适应。YOLOv5 导出的图带 objectness5ncYOLOv8/26 不带4nc我们按rows nc 5自动区分两类模型通吃。二是类别名不用硬编码。Ultralytics 导出时把names写进了 ONNX 的metadata_props手工解析 protobuf 把它取出来就行多少个类别都能正确显示。检测出来的效果框按类别配色置信度标在框上CPU 和 GPU 差多少配置上的差异前面已经说了就一行代码加一个 DLL。那性能呢我们在 RTX 3050 上跑了 100 张 640×640 的 PCB 图。CPU 四线程单张 286.6ms折合 3.5 FPS换 DirectML 走 GPU单张 24.1ms41.4 FPS。十一倍多的加速。比数字更重要的是另一件事两边逐图比对过检测出的目标数、类别分布完全一致。换后端没有引入任何数值偏差这点验完我们才敢把 GPU 开关默认打开。界面上的切换就是个勾选框切完重载模型生效检测结果会缓存在内存里改置信度阈值只重新解码、不重新推理一百张图刷一遍不到一百毫秒。调阈值的时候这个体验差距非常大为什么不是 CUDA这是选型时被问得最多的问题。我们的结论分发给别人的 Windows 桌面工具DirectML 就是最优解。CUDA 适合自家机房、硬件全可控的场景。理由展开说。最直接的一条CUDA 只认 N 卡。DirectML 建在 DirectX 12 上NVIDIA、AMD、Intel 全覆盖办公本的核显也算数。我们发出去的工具产线上一半机器是没有独显的办公机——走 CUDA 的话这些机器直接不可用除非再维护一条 CPU 路径等于两套逻辑两套测试。第二条是部署。CUDA 方案的依赖清单显卡驱动版本要匹配CUDA Runtime 要装或者要带cuDNN 要装或者要带onnxruntime-gpu 的版本还得跟 CUDA、cuDNN 三者对齐。任何一台机器上组合不对就是一串CUDA_ERROR_XXX远程排查能把人折腾死。DirectML 呢一个 DirectML.dll18MB放 exe 旁边完事。驱动要求宽松到支持 DX12 就行近十年的卡全满足。第三条性能。说实话这是我最开始担心的实测之后不担心了。24ms 一张、41 FPS 的吞吐产线单张检测场景绰绰有余。DirectML 作为通用后端极限性能确实比不过同卡深度调优的 CUDA TensorRT但那是服务端批处理该操心的事一个桌面工具犯不着。第四条算是保险ONNX Runtime 的 DirectML EP 是微软官方维护的执行提供者不是社区魔改失败回退 CPU 的行为也是框架原生的。接入成本一行代码鲁棒性白送。一句话收尾CUDA 快但只快在 N 卡上DirectML 稍慢一点可它让工具在任何一台 Windows 机器上都跑得起来。桌面工具要的是发出去就能用不是单机极限性能。最后的建议整个过程压缩一下就五步导出 onnx工程里带上 directml 版的 onnxruntime写推理封装加载、letterbox、Run、解码跟 Python 端逐图比对验证部署时 DLL 拷齐。但真正想留给大家的是前面那个教训**一定要在 UI 上把执行设备显示出来。**DirectML 缺 DLL 的时候是静默回退的没有这个显示你以为在用 GPU其实一直在用 CPU还纳闷为什么加速没效果。工程环境VS2019 Qt 5.14.2msvc2017_64 ONNX Runtime 1.24.4 DirectML 版测试模型 Ultralytics YOLO26mPCB 缺陷 6 类测试硬件 RTX 3050。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑