多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

YOLOv8 人体姿态估计(关键点检测) python推理 ONNX RUNTIME C++部署:TaoToken 统一 Key 打通双语言链路

YOLOv8 人体姿态估计(关键点检测) python推理  ONNX RUNTIME C++部署:TaoToken 统一 Key 打通双语言链路 1. 从 Python 验证到 C 生产YOLOv8 姿态估计双语言链路到底难在哪YOLOv8 人体姿态估计关键点检测在 Python 里跑通只要一行命令但真正要落到本地服务里很多团队会卡在“Python 验证没问题C 生产推理对不上”这一步。我自己在做跌倒检测和健身动作计数时就遇到过 Python 输出的 17 个关键点和 C 端差了几个像素、置信度阈值不一致、letterbox 参数没对齐导致框偏移的问题。核心检索词先摆出来YOLOv8 人体姿态估计是从单张图或视频里检测出人体框并输出 17 个 COCO 关键点的 x、y、可见性 vONNX Runtime C 部署则是把 PyTorch 权重导出成 ONNX 后用 C 会话加载在本地服务里做低延迟推理。适合谁适合需要在同一套业务里同时维护 Python 快速验证脚本和 C 生产推理模块的开发者尤其是做边缘盒子、本地视频分析、工业质检里人体动作判断的人。这条链路的难点不在模型本身而在“两端一致性”。Python 端 Ultralytics 封装了预处理、后处理、NMS你几乎不用管C 端这些全要自己写letterbox 的缩放比、padding 偏移、输出张量 [1,56,8400] 的转置、关键点从 640 输入映射回原图坐标任何一步差一点关键点就会飘。再加上模型调用凭证管理如果 Python 和 C 各维护一套 Key轮换和审计都很麻烦。所以这篇按“Python 推理 → ONNX 导出 → C 会话配置 → 关键点后处理 → 两端对比验证”的顺序写中间用 TaoToken 统一 Key 通道管理模型调用凭证避免两套语言各写一套鉴权逻辑。先明确输出结构。YOLOv8n-pose 导出后只有一个输出output0: float32[1,56,8400]。8400 是检测框数量56 4 个边界框坐标 1 个人类别分数 17×3 个关键点信息。每个关键点由 x、y、v 组成v 小于 0.5 时表示该点可能在图外可以考虑去掉。COCO 的 17 个关键点依次是 nose、left_eye、right_eye、left_ear、right_ear、left_shoulder、right_shoulder、left_elbow、right_elbow、left_wrist、right_wrist、left_hip、right_hip、left_knee、right_knee、left_ankle、right_ankle。记住这个顺序后面 C 画骨架和 Python 对比都靠它。我试过在同一个项目里 Python 用 Ultralytics 直接推理C 用 ONNX Runtime 加载同一份 onnx结果第一版 C 的关键点整体往左上偏了十几个像素排查半天发现是 letterbox 里dw / 2.0f; dh / 2.0f;之后取整方式跟 Python 不一致。这类坑后面第 5 节会集中列。先把环境说清楚Python 侧需要 ultralytics、onnx、onnxruntimeC 侧需要 OpenCV 4、ONNX Runtime 1.14、CMake 3.5。模型权重用yolov8n-pose.pt导出 onnx 后大约 12.9 MB。2. TaoToken 统一 Key 打通 Python 与 C 的模型调用凭证这一节解决“凭证管理”问题。Python 验证脚本和 C 生产服务如果各自硬编码 API Key轮换时要改两处审计也分散。TaoToken 提供统一 Key/API 通道Python 和 C 都通过同一个 Base URL 和 Key 访问模型调用凭证只维护一份。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 不加 UTM。注意TaoToken 在这里的角色是统一管理模型调用凭证的通道不是替代本地 ONNX Runtime 推理YOLOv8 的 onnx 推理仍然在本地 C 进程里跑TaoToken 负责的是需要远程模型能力时的鉴权统一。为什么姿态估计项目会用到远程模型调用常见场景是本地 C 做实时关键点检测但动作分类、异常行为描述、报告生成这类任务需要调用大模型Python 端做数据清洗和验证时也要调同一批模型。如果两边 Key 不一致日志对不上。用 TaoToken 统一后Python 和 C 都读同一个环境变量或配置文件。操作步骤先到模型对话页面确认可用模型再到 API Keys 页面创建 Key。deep link 如下模型对话 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite API Keys https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。创建后把 Key 写进环境变量Python 和 C 都从环境变量读不要写进代码仓库。Python 侧读取方式import os TAOTOKEN_API_KEY os.environ.get(TAOTOKEN_API_KEY) TAOTOKEN_BASE_URL https://taotoken.net/apiC 侧读取方式#include cstdlib #include string std::string getEnv(const char* key) { const char* val std::getenv(key); return val ? std::string(val) : std::string(); } std::string apiKey getEnv(TAOTOKEN_API_KEY); std::string baseUrl https://taotoken.net/api;这样 Python 验证脚本和 C 生产服务用的是同一份凭证。轮换时只改环境变量两边同时生效。如果你做的是长期编码或 Agent 类任务可以看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。Claude Code 相关接入参考 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 。需要强调TaoToken 的 Key 用于远程模型调用鉴权本地 YOLOv8 ONNX 推理不需要联网。两者是配合关系不是替代关系。把凭证统一后Python 端做批量图片验证、C 端做视频流推理日志里都能追溯到同一个 Key 的调用记录排查问题方便很多。3. 可复制配置Python 推理脚本、ONNX 导出参数与 C 会话配置这一节给可直接复制的配置。先 Python 推理。安装依赖pip install ultralytics onnx onnxruntime opencv-python命令行推理单张图或摄像头yolo taskpose modepredict modelyolov8n-pose.pt source0 showtrue导出 ONNXyolo export modelyolov8n-pose.pt formatonnx导出成功输出类似Ultralytics YOLOv8.0.94 Python-3.8.13 torch-2.0.0cu117 CPU YOLOv8n-pose summary (fused): 187 layers, 3289964 parameters, 0 gradients, 9.2 GFLOPs PyTorch: starting from yolov8n-pose.pt with input shape (1, 3, 640, 640) BCHW and output shape(s) (1, 56, 8400) (6.5 MB) ONNX: starting export with onnx 1.13.1 opset 17... ONNX: export success, saved as yolov8n-pose.onnx (12.9 MB)用 netron 查看确认只有一个输出output0: float32[1,56,8400]。Python 端完整推理脚本import cv2 import numpy as np from ultralytics import YOLO model YOLO(yolov8n-pose.onnx, taskpose) img cv2.imread(test.jpg) results model(img, imgsz640, conf0.25, iou0.45) for r in results: if r.keypoints is None: continue kps r.keypoints.data.cpu().numpy() # [n,17,3] boxes r.boxes.xyxy.cpu().numpy() confs r.boxes.conf.cpu().numpy() for i in range(len(boxes)): print(box:, boxes[i], conf:, confs[i]) print(kps:, kps[i])C 侧配置。目录结构建议project/ include/ utils.h detect.h src/ utils.cpp detect.cpp main.cpp CMakeLists.txtCMakeLists.txt 关键片段cmake_minimum_required(VERSION 3.5) project(YOLOv8PoseOnnxRuntime LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) include_directories(/path/to/onnxruntime-linux-x64-1.14.1/include) include_directories(./include) aux_source_directory(./src SOURCES) find_package(OpenCV 4 REQUIRED) add_executable(${PROJECT_NAME} ${SOURCES}) target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS}) target_link_libraries(${PROJECT_NAME} /path/to/onnxruntime-linux-x64-1.14.1/lib/libonnxruntime.so)会话配置核心参数_netWidth 640、_netHeight 640、_batchSize 1、_anchorLength 56、_classThreshold 0.25、_nmsThrehold 0.45。这些必须和 Python 端conf0.25, iou0.45对齐否则两端结果不可比。会话创建时设置图优化级别_OrtSessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);输入张量 shape 从模型读取遇到 -1 动态维度时替换为 batchSize 和 640。输出节点名通过GetOutputNameAllocated获取注意 ONNX Runtime 1.14 之后 API 有变化旧版用GetOutputName新版用GetOutputNameAllocated代码里用#if ORT_API_VERSION区分。4. 验证请求与成功结果同一张测试图两端关键点对比这一节做一致性验证。准备一张包含单人的测试图test.jpg分别用 Python 和 C 推理打印 17 个关键点的 x、y、v逐点对比。Python 端输出格式box: [120.3 45.6 380.2 620.1] conf: 0.91 kps: [[210.5 80.2 0.99] [205.1 75.3 0.98] ...]C 端在OnnxDetect后打印同样格式。对比时注意Python 的 keypoints 已经是原图坐标C 端需要经过 letterbox 逆映射。逆映射公式float x (kps_x - params[2]) / params[0]; float y (kps_y - params[3]) / params[1];其中params[0]是宽度缩放比params[1]是高度缩放比params[2]是水平 paddingparams[3]是垂直 padding。如果两端 letterbox 实现一致同一张图的关键点坐标差应该在 1 到 2 个像素以内。实测下来YOLOv8n-pose 在 640 输入下单人图的关键点误差通常在 1 像素左右多人图因为 NMS 顺序可能不同需要按框的 IoU 匹配后再比。C 端成功运行输出read Net ok! read video ok! 1/300:find 1 person! 2/300:find 1 person! ...每帧打印检测到的人数绘制骨架后写入输出视频。验证成功的标志Python 和 C 对同一张图输出的框坐标、置信度、17 个关键点坐标在容差内一致视频推理时每帧耗时稳定CPU 上 YOLOv8n-pose 640 输入大约 30 到 60 毫秒每帧具体看机器。如果要做批量对比写一个 Python 脚本读 C 输出的 csv和 Python 推理结果做逐点差值统计import numpy as np py_kps np.load(py_kps.npy) cpp_kps np.load(cpp_kps.npy) diff np.abs(py_kps - cpp_kps) print(max diff:, diff.max(), mean diff:, diff.mean())max diff 小于 2 像素、mean diff 小于 1 像素基本可以认为两端一致。超过这个范围就回到第 5 节排查。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 与关键点偏移这一节列真实报错和排查路径。第一类鉴权相关。401 Unauthorized通常是 TaoToken Key 没读到或写错。检查环境变量echo $TAOTOKEN_API_KEYPython 里os.environ.get返回 None 就是没设置。C 里getEnv返回空字符串同理。注意不要把 Key 写进代码后提交到仓库用.env或系统环境变量。local proxy failed一般出现在本地网络配置异常时检查本机是否能正常访问https://taotoken.net/api用 curl 测curl -I https://taotoken.net/api如果返回 200 或 401 都说明网络通401 是 Key 问题。OAuth相关报错多见于 Claude Code 类工具接入检查是否用了正确的接入文档配置参考 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。reading choices报错通常是响应体解析失败检查请求是否发到了正确的 endpoint以及返回内容是否为预期 JSON。第二类ONNX Runtime 加载失败。Failed to load model检查 onnx 路径是否正确Linux 下路径区分大小写。GetInputNameAllocated编译报错说明 ONNX Runtime 版本低于 1.14改用GetInputName。libonnxruntime.so not found检查 CMake 里链接路径或者设置LD_LIBRARY_PATHexport LD_LIBRARY_PATH/path/to/onnxruntime/lib:$LD_LIBRARY_PATH第三类关键点偏移。最常见原因是 letterbox 参数不一致。Python Ultralytics 的 letterbox 用round取整C 如果用了int截断就会差 1 像素累积到关键点上可能差几个像素。统一用std::round。另一个原因是 BGR 和 RGB 顺序blobFromImages的swapRB参数要设为 true和 Python 一致。还有输出张量转置[1,56,8400]要转成[8400,56]再按行解析每行前 4 个是框第 5 个是分数第 6 个开始是 17×3 关键点。如果忘了转置解析出来的全是错位数据。第四类NMS 阈值不一致。Python 默认iou0.45C 里_nmsThrehold也要设 0.45。如果 C 设成 0.5多人场景下框的数量会不同导致关键点数量对不上。还有_classThreshold要和conf一致都是 0.25。第五类视频推理卡顿。检查是否每帧都重新创建 Session正确做法是 Session 只创建一次循环里只调Run。另外cv::dnn::blobFromImages每帧调用会有内存分配开销可以复用 blob 缓冲。如果用了 CUDA Execution Provider第一次推理前要 warm up否则第一帧特别慢。6. 把双语言链路固定成可复用工程习惯最后说几个实用习惯。第一把 Python 和 C 的预处理参数写进同一个配置文件比如config.json{ net_width: 640, net_height: 640, class_threshold: 0.25, nms_threshold: 0.45, num_keypoints: 17 }Python 和 C 都读这个文件避免两边手改不同步。第二每次导出新 onnx 后先用同一张测试图跑两端对比脚本max diff 超过 2 像素就不进入生产。第三TaoToken Key 用环境变量注入Python 和 C 共用一份轮换时只改一处。第四C 端把ReadModel和OnnxDetect分开Session 生命周期跟服务进程一致不要每帧重建。第五关键点后处理里 v 小于 0.5 的点直接跳过绘制避免画出图外的假点。这套链路跑通后Python 端负责快速验证新权重和新阈值C 端负责生产推理两端用同一份配置和同一份凭证出问题能快速定位是模型问题还是实现问题。需要进一步接入远程模型能力时从 API Keys 页面拿 Key接入文档看请求格式模型对话页面先验证模型可用性。长期做编码和 Agent 任务的话Coding Plan 页面有对应的方案说明。
返回列表