PVN3D ORT C++ Custom Op 编译与联调记录

张开发
2026/6/9 8:23:25 15 分钟阅读
PVN3D ORT C++ Custom Op 编译与联调记录
1. 目标本次目标不是继续走onnxruntime-extensionsPython custom op 路线而是单独落一条使用 ONNX Runtime 官方 C API 实现 PointNet2 custom op编译生成可被SessionOptions.register_custom_ops_library(...)加载的.so用新的 Python 脚本加载该.so运行完整pvn3d_full.onnx和 PyTorch 原生权重做输出精度对比这里的“完整 ONNX”指的是单个 pvn3d_full.onnx图中包含ai.onnx.contrib域下的 PointNet2 custom nodes2. 本次最终产物2.1 C custom op 工程CMakeLists.txtpvn3d_pointnet2_ops.cc2.2 Python 联调脚本run_full_onnx_ort_cpp.py2.3 构建产物libpvn3d_ort_custom_ops.so2.4 运行结果linemod_ape_full_onnx_ort_cpp.json3. 环境基线本次记录基于pvn3d-dev容器内事实Ubuntu 18.04.5g 7.5.0onnxruntime1.16.3onnx1.14.1torch1.10.0cu113ORT 官方开发包已下载并解压到/workspace/tmp/onnxruntime-linux-x64-1.16.3本次实际使用的 ORT 官方开发包内容包括/workspace/tmp/onnxruntime-linux-x64-1.16.3/include/onnxruntime_c_api.h/workspace/tmp/onnxruntime-linux-x64-1.16.3/include/onnxruntime_cxx_api.h/workspace/tmp/onnxruntime-linux-x64-1.16.3/lib/libonnxruntime.so4. 实现范围为了让完整pvn3d_full.onnx能被 ORT 执行仅实现ThreeNN / BallQuery / FurthestPointSample不够。本次 C.so一次性承接了 6 个 PointNet2 custom opPVN3D_FurthestPointSamplePVN3D_GatherPointsPVN3D_BallQueryPVN3D_GroupPointsPVN3D_ThreeNNPVN3D_ThreeInterpolate全部注册在custom domain:ai.onnx.contrib这是为了和当前 full ONNX 导出侧保持一致。5. C 实现要点5.1 注册方式本次使用的是 ONNX Runtime 官方 C custom op 路线#define ORT_API_MANUAL_INIT#include onnxruntime_cxx_api.h导出extern C OrtStatus* ORT_API_CALL RegisterCustomOps(...)RegisterCustomOps中做的事情是Ort::InitApi(api_base-GetApi(ORT_API_VERSION))创建Ort::CustomOpDomain(ai.onnx.contrib)把 6 个Ort::CustomOpBase...实例加进 domainsession_options.Add(domain)库内已确认导出了RegisterCustomOps符号。容器内实际检查命令dockerexecpvn3d-devbash-lc cd /workspace/workflow/self/PVN3D nm -D deploy/ort_custom_ops/build/libpvn3d_ort_custom_ops.so | rg RegisterCustomOps -n -S 实际结果0000000000002610 T RegisterCustomOps5.2 为什么本次没有把.so显式链接到libonnxruntime.so本次工程里使用了ORT_API_MANUAL_INIT。因此编译时只需要 ONNX Runtime 的头文件运行时由 Python 侧已经加载的onnxruntime提供 API 入口容器内ldd结果也说明这份.so没有直接依赖libonnxruntime.sodockerexecpvn3d-devbash-lc cd /workspace/workflow/self/PVN3D ldd deploy/ort_custom_ops/build/libpvn3d_ort_custom_ops.so 可见依赖主要是libstdc.so.6libgcc_s.so.1libc.so.6libm.so.6这对当前 Python 侧register_custom_ops_library(...)的加载方式更稳。5.3 6 个算子的语义对齐本次 C 实现不是重新定义语义而是对齐现有 PVN3D / PointNet2 CUDA 扩展行为。重点包括FurthestPointSample首个采样点固定从0开始模长过小的点会被跳过BallQuery邻域不足时用第一个命中的索引重复填充若完全没有命中则保持0ThreeNNcustom op 输出的是dist2而不是sqrt(dist2)与 CUDA kernel 一致ThreeInterpolate直接按(B, C, M) (B, N, 3) (B, N, 3)做三点加权插值GatherPoints输入(B, C, N)和(B, S)输出(B, C, S)GroupPoints输入(B, C, N)和(B, S, K)输出(B, C, S, K)6. Python 侧加载脚本新脚本run_full_onnx_ort_cpp.py它和之前 Python custom op 版的区别是不导入onnxruntime-extensions不注册 Python custom op只通过sess_options.register_custom_ops_library(str(custom_ops_lib))加载原生.so核心运行命令python deploy/scripts/run_full_onnx_ort_cpp.py\--checkpointweights/ape_pvn3d_best.pth.tar\--onnxdeploy/models/onnx_ape/pvn3d_full.onnx\--custom-ops-lib deploy/ort_custom_ops/build/libpvn3d_ort_custom_ops.so\--clsape\--sample-index0\--num-points4096\--height480\--width624\--crop-left8\--outputdeploy/benchmarks/linemod_ape_full_onnx_ort_cpp.json7. 容器内完整操作记录7.1 检查容器状态dockerps--format{{.Names}}\t{{.Status}}确认pvn3d-dev在运行。7.2 检查编译器和 ORT 版本dockerexecpvn3d-devbash-lc g --version | head -n 1 source /opt/conda/etc/profile.d/conda.sh conda activate pvn3d python - PY import onnxruntime as ort print(ort.__version__) PY 实际结果g 7.5.0onnxruntime 1.16.37.3 安装cmake容器内最开始没有cmake。实际安装命令dockerexecpvn3d-devbash-lc apt-get update DEBIAN_FRONTENDnoninteractive apt-get install -y cmake 安装结果cmake 3.10.27.4 配置并编译.so由于容器里是cmake 3.10.2最终使用的是旧语法dockerexecpvn3d-devbash-lc cd /workspace/workflow/self/PVN3D/deploy/ort_custom_ops rm -rf build mkdir build cd build cmake -DONNXRUNTIME_ROOT/workspace/tmp/onnxruntime-linux-x64-1.16.3 .. cmake --build . -- -j2 编译成功后生成libpvn3d_ort_custom_ops.so7.5 先做 smoke test先跳过 PyTorch 对比和 pose 评估只验证.so能否加载ORT 能否执行完整图命令dockerexecpvn3d-devbash-lc cd /workspace/workflow/self/PVN3D source /opt/conda/etc/profile.d/conda.sh conda activate pvn3d python deploy/scripts/run_full_onnx_ort_cpp.py \ --checkpoint weights/ape_pvn3d_best.pth.tar \ --onnx deploy/models/onnx_ape/pvn3d_full.onnx \ --custom-ops-lib deploy/ort_custom_ops/build/libpvn3d_ort_custom_ops.so \ --cls ape \ --sample-index 0 \ --num-points 4096 \ --height 480 \ --width 624 \ --crop-left 8 \ --skip-torch-compare \ --skip-pose-eval 这一步成功说明register_custom_ops_library(...)生效6 个 custom op 都被 ORT 正常承接整张pvn3d_full.onnx已可执行7.6 再做完整精度闭环命令dockerexecpvn3d-devbash-lc cd /workspace/workflow/self/PVN3D source /opt/conda/etc/profile.d/conda.sh conda activate pvn3d python deploy/scripts/run_full_onnx_ort_cpp.py \ --checkpoint weights/ape_pvn3d_best.pth.tar \ --onnx deploy/models/onnx_ape/pvn3d_full.onnx \ --custom-ops-lib deploy/ort_custom_ops/build/libpvn3d_ort_custom_ops.so \ --cls ape \ --sample-index 0 \ --num-points 4096 \ --height 480 \ --width 624 \ --crop-left 8 \ --output deploy/benchmarks/linemod_ape_full_onnx_ort_cpp.json 8. 实际结果运行结果保存在linemod_ape_full_onnx_ort_cpp.json关键指标如下pred_kp_ofmax_abs 0.005813956260681152mean_abs 6.647158897976624e-06pred_rgbd_segmax_abs 0.0374298095703125mean_abs 0.00042097506229765713pred_ctr_ofmax_abs 0.0012696310877799988mean_abs 4.5104511627869215e-06ADD 0.004492259584367275ADD-S 0.0028761550784111023这说明当前原生 C ORT custom op 路线已经具备完整图执行能力和 PyTorch 原生权重的稳定数值对齐pose 级别的闭环验证能力9. 过程中遇到的问题与解决方案9.1 问题一容器里没有cmake现象which cmake无输出处理直接在容器内apt-get install -y cmake结论不需要单独下载源码编译cmakeUbuntu 18.04 官方仓库版本足够完成当前工程9.2 问题二容器内cmake只有 3.10.2现象工程最初写成cmake_minimum_required(VERSION 3.16)使用了cmake -S . -B build使用了cmake --build build -j2这些都和容器内cmake 3.10.2不兼容。处理把工程最低版本改成3.10改用旧语法mkdir build cd build cmake ..并把并行构建写成cmake --build . -- -j2结论当前容器内不建议默认使用新版本 CMake 命令行语法9.3 问题三最初误以为 ORT 头文件在嵌套目录下现象最初按常见源码树路径假设头文件在include/onnxruntime/core/session/...但你当前下载的官方 release 包实际是include/onnxruntime_c_api.hinclude/onnxruntime_cxx_api.h处理CMake 直接把 include 指向/workspace/tmp/onnxruntime-linux-x64-1.16.3/include结论release 包和源码树的头文件布局不同不能混用路径假设9.4 问题四ORT 1.16.3 的KernelContext返回的是ConstValue / UnownedValue现象初版代码把 helper 写成只接收Ort::Value但KernelContext::GetInput()返回ConstValueKernelContext::GetOutput()返回UnownedValue这会导致编译期类型不匹配。处理把 shape helper 改成模板形式对输出 tensor 改用Ort::GetApi().GetTensorMutableData(...)辅助函数取指针结论在 ORT 1.16.3 上custom op 代码最好显式按ConstValue / UnownedValue处理9.5 问题五新脚本最初错误复用了 Python custom op 版脚本现象初版run_full_onnx_ort_cpp.py复用了 run_full_onnx_ort.py 里的 helper但后者会导入onnxruntime-extensions和 Python custom op 模块这会让“C.so路线”仍然暗中依赖 Python custom op。处理把采样、路径解析、数组对比等 helper 直接内联到新脚本确保新脚本只依赖onnxruntime torch onnx 自定义 .so结论现在 run_full_onnx_ort_cpp.py 是一条干净的原生.so联调入口

更多文章