YOLOX 基于 ncnn 的端侧目标检测部署全流程:ONNX 转换、param 手工修复与 C++ 推理实战
人工智能计算机视觉深度学习【免费下载链接】YOLOXYOLOX is a high-performance anchor-free YOLO, exceeding yolov3~v5 with MegEngine, ONNX, TensorRT, ncnn, and OpenVINO supported. Documentation: https://yolox.readthedocs.io/项目地址https://gitcode.com/gh_mirrors/yo/YOLOX点击查看免费下载YOLOX 仓库在demo/ncnn目录下提供了基于 ncnn 及配套教程 docs/demo/ncnn_cpp_readme.md、docs/demo/ncnn_android_readme.md为主线完整走通导出 ONNX → onnx2ncnn 转模型 → 手工修复 Focus 层 → ncnnoptimize 优化 → 编写并编译 C 推理程序 → 运行检测的端到端流程并给出官方推荐的 PNNXTorchScript替代路线。读完本文你将能独立把 YOLOX 权重转换为 ncnn 可加载的 param/bin 模型并在自己的设备上运行实时目标检测。一、部署方案概览为什么选择 ncnnncnn 是面向移动端与嵌入式场景的高性能神经网络前向计算框架支持 Vulkan GPU 加速非常适合端侧目标检测部署。YOLOX 仓库在 demo/ncnn 目录下提供了C 桌面端示例demo/ncnn/cpp/yolox.cpp一个基于 ncnn API 的完整推理程序输入图片路径输出标注了检测框和类别的窗口Android 示例工程demo/ncnn/android完整的 Gradle 工程可直接用 Android Studio 构建转换与推理工具链tools/export_onnx.py、tools/export_torchscript.py。需要特别说明的是YOLOX 已被 ncnn 官方收录demo/ncnn/README.md明确提示 YOLOX is included in ncnn now你可以直接从 ncnn 源码构建 YOLOX 示例但若希望使用本仓库的权重与导出脚本、掌握从 PyTorch 权重到 ncnn 模型的完整转换细节仍可按照下文流程操作。整个部署链路如下PyTorch 权重*.pth │ tools/export_onnx.py ▼ yolox.onnx │ onnx2ncnn ▼ model.param / model.bin │ 手工修复 Focus 层 ncnnoptimize ▼ yolox.param / yolox.bin │ yolox.cpp注册自定义 YoloV5Focus 层 ▼ ./yolox demo.jpg二、前置准备克隆并编译 ncnnStep 1先克隆 ncnn 源码并按照 ncnn 官方构建教程在你的设备上完成编译。构建产物中需要包含onnx2ncnn与ncnnoptimize两个工具位于构建目录的build/tools/ncnn与build/tools下。编译 ncnn 时的建议桌面端默认构建即可若目标设备支持 Vulkan可开启 Vulkan 相关选项yolox.cpp 中默认开启use_vulkan_compute true若交叉编译到 ARM 平台请参考 ncnn 官方对交叉编译工具链的说明。三、导出 ONNX 模型Step 2使用仓库提供的 tools/export_onnx.py 把 PyTorch 权重导出为 ONNX 文件。以 YOLOX-S 为例cd path of yolox python3 tools/export_onnx.py -n yolox-s执行后在当前目录生成yolox.onnx。该脚本常用参数如下摘自 tools/export_onnx.py 的参数定义参数默认值说明--output-nameyolox.onnx输出 ONNX 文件名--inputimagesONNX 输入节点名与 yolox.cpp 中ex.input(images, ...)对应--outputoutputONNX 输出节点名-o, --opset11ONNX opset 版本--batch-size1推理 batch 大小--dynamic关是否导出动态 batch 维--no-onnxsim关默认使用 onnxsim是否跳过 onnx-simplifier 简化-f, --exp_fileNone自定义实验配置文件路径-n, --nameNone模型名称如yolox-s、yolox-m与exps/default下的配置对应-c, --ckptNone权重文件路径不传时默认读取output_dir/experiment_name/best_ckpt.pth--decode_in_inference关是否在模型中内置解码逻辑影响导出后输出的形式见下文opts—通过命令行覆盖实验配置项导出脚本内部做了两件关键事情见 tools/export_onnx.py算子归一化调用replace_module(model, nn.SiLU, SiLU)将 PyTorch 的nn.SiLU替换为仓库自定义的 SiLU 实现保证 ONNX 导出时算子一致可选简化默认调用 onnx-simplifier 精简模型图减小体积、提升后续转换稳定性如需保留原始图可用--no-onnxsim。关于--decode_in_inferenceYOLOX 的解码头默认在推理时执行解码把网格偏移和 stride 还原为真实坐标对应 yolox/models/yolo_head.py 的decode_in_inference分支。导出 ONNX 时默认关闭解码model.head.decode_in_inference args.decode_in_inference默认False让 ONNX 直接输出三尺度原始特征图解码逻辑放在 C 端完成这也是 yolox.cpp 采用的方案。四、onnx2ncnn 转换遇到 Focus 层怎么办Step 3使用 ncnn 自带的转换工具把 ONNX 转为 ncnn 的 param/bin 格式cd path of ncnn cd build/tools/ncnn ./onnx2ncnn yolox.onnx model.param model.bin由于 ncnn 原生不支持 Focus 模块转换时会出现类似下面的警告Unsupported slice step!不用担心仓库已经在 yolox.cpp 中用 C 实现了 YoloV5Focus 层后续通过手工修改 param 运行时注册自定义层两步解决。为什么 YOLOX 需要 Focus从源码看yolox/models/network_blocks.py 中Focus的作用是把宽高信息折叠进通道维度将输入(b, c, w, h)按 2×2 邻域切分为四块后拼接成(b, 4c, w/2, h/2)再经过一次卷积。onnx2ncnn会把这种隔行切片再拼接的图转换成一系列Split/Crop/Concat组合对应不支持的slice step警告但直接生成的图既冗长又可能出错所以需要手工替换为单个YoloV5Focus层。五、手工修复 model.param关键步骤Step 4打开上一步生成的model.param文件删除由 Focus 展开产生的一堆Split/Crop/Concat层换成单条YoloV5Focus记录。param/bin 文件结构说明见 ncnn 官方 wikiparam-and-model-file-structure。以某个导出结果为例转换后 param 开头大致如下295 328 Input images 0 1 images Split splitncnn_input0 1 4 images images_splitncnn_0 images_splitncnn_1 images_splitncnn_2 images_splitncnn_3 Crop Slice_4 1 1 images_splitncnn_3 647 -233091,0 -233101,2147483647 -233111,1 Crop Slice_9 1 1 647 652 -233091,0 -233101,2147483647 -233111,2 Crop Slice_14 1 1 images_splitncnn_2 657 -233091,0 -233101,2147483647 -233111,1 Crop Slice_19 1 1 657 662 -233091,1 -233101,2147483647 -233111,2 Crop Slice_24 1 1 images_splitncnn_1 667 -233091,1 -233101,2147483647 -233111,1 Crop Slice_29 1 1 667 672 -233091,0 -233101,2147483647 -233111,2 Crop Slice_34 1 1 images_splitncnn_0 677 -233091,1 -233101,2147483647 -233111,1 Crop Slice_39 1 1 677 682 -233091,1 -233101,2147483647 -233111,2 Concat Concat_40 4 1 652 672 662 682 683 00 ...修改分三步修正层数第一行的295是层数。由于要删除 10 行从Split到Concat共 10 层并新增 1 行YoloV5Focus净减少 9 层因此把295改为286即295 - 9 286记住数据 blob 编号被删除的Concat行中倒数第二个数字683是 Concat 输出的 blob 编号它会被后面的层继续引用必须记下来插入 Focus 层在Input行之后插入一行把输出 blob 编号指向683YoloV5Focus focus 1 1 images 683修改完成后的 param 开头286 328 Input images 0 1 images YoloV5Focus focus 1 1 images 683 ...注意上面是示例数据实际转换时 blob 编号、层数会随权重与导出方式不同而变化请以你自己转换出的 param 为准理解删层换层、保持输出 blob 编号连续的原则即可。六、用 ncnnoptimize 优化模型Step 5使用 ncnn 自带的优化器生成最终模型同时完成 fp32→fp16 等精度存储转换# suppose you are still under ncnn/build/tools/ncnn dir. ../ncnnoptimize model.param model.bin yolox.param yolox.bin 65536其中65536是存储数据对齐尺寸storage size属于 ncnn 常规用法按默认值填写即可。优化后得到可直接加载的yolox.param与yolox.bin。七、编写并编译 C 推理程序Step 6把仓库的 demo/ncnn/cpp/yolox.cpp 复制到 ncnn 的examples目录修改CMakeLists.txt加入该源文件后重新编译。这里重点解读 yolox.cpp 的实现理解它才能正确修改 param7.1 自定义 Focus 层YoloV5Focus 继承ncnn::Layer实现forward输入宽高减半、通道数 ×4通过ptr 2的取点方式等价于 PyTorch 源码x[..., ::2, ::2]等四个切片的拼接并用 OpenMP 并行加速。加载模型前必须注册该层yolox.register_custom_layer(YoloV5Focus, YoloV5Focus_layer_creator);这也是 param 中那一行YoloV5Focus能正常工作的前提见 yolox.cpp。7.2 预处理与后处理等比例缩放 右下 padding与 YOLOv5 不同YOLOX 只在右侧和底部补边copy_make_border的0, hpad, 0, wpad填充值114.f这样解码坐标时不需要额外的 padding 补偿信息见 yolox.cpp 的注释说明解码逻辑generate_yolox_proposalsyolox.cpp逐锚点恢复真实坐标与 yolox/models/yolo_head.py 的解码公式一一对应// outputs[..., :2] (outputs[..., :2] grids) * strides // outputs[..., 2:4] torch.exp(outputs[..., 2:4]) * strides float x_center (feat_ptr[0] grid0) * stride; float y_center (feat_ptr[1] grid1) * stride; float w exp(feat_ptr[2]) * stride; float h exp(feat_ptr[3]) * stride;多尺度融合输出特征来自 stride 为 8/16/32 的三个尺度yolox.cppgenerate_grids_and_stride为每个尺度生成网格NMS先按得分降序排序qsort_descent_inplace再做基于 IoU 的 NMSnms_sorted_bboxes见 yolox.cpp坐标还原除以缩放系数scale并裁剪回原图范围最后draw_objects用 OpenCV 绘制检测框与类别标签COCO 80 类名称内置于源码中。7.3 阈值与输入尺寸调节yolox.cpp 顶部三个宏控制检测行为可按需调整#define YOLOX_NMS_THRESH 0.45 // nms threshold #define YOLOX_CONF_THRESH 0.25 // threshold of bounding box prob #define YOLOX_TARGET_SIZE 640 // target image size after resize, might use 416 for small modelYOLOX_CONF_THRESH置信度阈值objectness × class score调低可召回更多目标、也会带来更多误检YOLOX_NMS_THRESHNMS 的 IoU 阈值重叠目标多时可适当调高YOLOX_TARGET_SIZE推理输入边长。小模型如 nano/tiny可改为 416 提速但注意导出 ONNX 时 tools/export_onnx.py 的 dummy input 尺寸取自exp.test_size两者需保持一致。八、编译运行跑通第一次检测Step 7编译得到可执行文件后传入一张图片即可运行./yolox demo.jpg程序会弹出 OpenCV 窗口显示检测结果并在终端打印每个目标的类别、置信度与坐标格式见 yolox.cpp0 0.92345 at 120.30 45.12 76.80 x 182.40九、BonusPNNX TorchScript 路线推荐除了 onnx2ncnnncnn 还提供了更现代的模型转换工具PNNX可以直接把 PyTorch TorchScript 一步转为 ncnn 模型省去 ONNX 中转。Step 1先用仓库的 tools/export_torchscript.py 导出 TorchScript 模型。以 YOLOX-S 为例python3 tools/export_torchscript.py -n yolox-s -c /path/to/your_checkpoint_files生成yolox.torchscript.pt。该脚本与 tools/export_onnx.py 参数风格一致-f/-n/-c/--decode_in_inference/opts均支持内部通过torch.jit.trace(model, dummy_input)完成跟踪导出见 tools/export_torchscript.py。Step 2把.pt文件放到 PNNX 构建目录PNNX 也提供预编译包执行转换# suppose you put the yolox.torchscript.pt in a seperate folder ./pnnx yolox/yolox.torchscript.pt inputshape[1,3,640,640] # for zsh users, please use inputshape[1,3,640,640]Step 3转换完成后你的 yolox 目录下会生成多个 PNNX 相关文件使用yolox.torchscript.ncnn.param和yolox.torchscript.ncnn.bin作为转换后的模型。同样地由于 ncnn 不支持slice步进操作即前文 Step 3 提到的Unsupported slice step!警告来源转换过程中仍会出现相关警告——无需理会后续回到上文的Step 4手工修改 param、插入YoloV5Focus层继续处理即可最终推理仍复用 yolox.cpp 中注册的自定义层。十、Android 端部署补充除桌面端外仓库还提供完整的 Android 示例工程 demo/ncnn/android其部署步骤记录在 docs/demo/ncnn_android_readme.md从 ncnn 官方 releases 下载ncnn-android-vulkan.zip仓库构建时使用 20210525 版本解压后将目录放到app/src/main/jni下或修改app/src/main/jni/CMakeLists.txt中的ncnn_DIR指向解压目录下载示例yolox.param/yolox.bin或按前文流程自己转换解压到app/src/main/assets工程内已带有一份示例 paramdemo/ncnn/android/app/src/main/assets/yolox.param用 Android Studio 打开工程直接构建运行。Android 端推理核心逻辑位于 demo/ncnn/android/app/src/main/jni/yoloXncnn_jni.cppJava 层通过 JNI 调用界面布局见 demo/ncnn/android/app/src/main/res/layout/main.xml实现思路与桌面端 yolox.cpp 一致注册YoloV5Focus、等比例缩放 右下 padding、三尺度解码 NMS。十一、常见问题与排查建议转换时出现Unsupported slice step!正常现象Focus 层将由 C 自定义层补齐按 Step 4 手工修改 param 即可运行时报找不到YoloV5Focus层说明 yolox.cpp 的register_custom_layer未执行或 param 中层名与注册名不一致检测框整体偏移检查YOLOX_TARGET_SIZE是否与导出 ONNX 时的输入尺寸一致以及 param 中 blob 编号是否正确衔接小模型检测慢可把YOLOX_TARGET_SIZE调低如 416并确认 Vulkan 加速开关use_vulkan_compute已生效或开启use_bf16_storageyolox.cpp 中该行默认注释可按硬件支持放开。参考与致谢ncnn 高性能神经网络推理框架Tencent/ncnnYOLOX 的 ncnn 部署文件均基于其 API 实现本仓库 ncnn 部署相关文档demo/ncnn/README.md、docs/demo/ncnn_cpp_readme.md、docs/demo/ncnn_android_readme.md模型转换与导出脚本tools/export_onnx.py、tools/export_torchscript.py核心推理实现demo/ncnn/cpp/yolox.cpp、demo/ncnn/android/app/src/main/jni/yoloXncnn_jni.cpp。赞分享人工智能计算机视觉深度学习【免费下载链接】YOLOXYOLOX is a high-performance anchor-free YOLO, exceeding yolov3~v5 with MegEngine, ONNX, TensorRT, ncnn, and OpenVINO supported. Documentation: https://yolox.readthedocs.io/项目地址https://gitcode.com/gh_mirrors/yo/YOLOX点击查看免费下载相关推荐YOLOX 基于 OpenVINO 的部署实战模型转换、Python 与 C 推理全流程指南YOLOX 基于 OpenVINO 的部署实战模型转换、Python 与 C 推理全流程指南 YOLOX 是一款高性能的 anchor free 检测框架人工智能计算机视觉深度学习YOLOX ncnn部署实战C推理与模型转换全链路解析YOLOX ncnn部署实战C推理与模型转换全链路解析 YOLOX 是旷视开源的高性能无锚点anchor free目标检测模型性能超过 YOLOv3人工智能计算机视觉深度学习YOLOX Android ncnn 部署实战基于 ncnn 的安卓目标检测 App 构建指南YOLOX Android ncnn 部署实战基于 ncnn 的安卓目标检测 App 构建指南 YOLOX 仓库在 demo/ncnn/android htt人工智能计算机视觉深度学习上一篇NetWinformControl 项目推荐下一篇tapiriik完全指南如何让你的健身数据无缝同步至所有平台创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考