yolo26n-pose在lsp姿势估计数据集的训练预测流程(python/c++)

yolo26n-pose在lsp姿势估计数据集的训练预测流程(python/c++)

1.模型训练测试(python)

打开https://docs.ultralytics.com/zh/tasks/pose/#models,选择模型下载,这里选择yolo26n-pose.pt,下载后放到你创建的项目的model文件夹

前期python环境配置可参考《yolo11m端侧实验》

如果需要数据集,可以自行网上找找,或者可以通过网盘分享的文件:lsp.zip
链接: https://pan.baidu.com/s/1D_XUBfj0j_aXql0WE61G6g?pwd=cwkr 提取码: cwkr

若有兴趣知道用代码获取该数据集的方法,可以通过网盘分享的文件:LSPPoseCode.zip
链接: https://pan.baidu.com/s/1u0l_SADo_JrN-1oj11OUkQ?pwd=znph 提取码: znph

在项目创建cfg文件夹,放入配置文件lsp-pose.yaml

# cfg/lsp-pose.yaml path: datasets/lsp-pose train: train.txt val: val.txt test: test.txt kpt_shape: [14, 3] flip_idx: [0,1,2,10,11,12,3,4,5,6,7,8,9,13] # 根据 LSP 对称关键点 names: 0: person kpt_names: 0: [left_ankle, left_knee, left_hip, right_hip, right_knee, right_ankle, pelvis, thorax, upper_neck, head_top, left_wrist, left_elbow, left_shoulder, right_shoulder, right_elbow, right_wrist]

预期目录结构如下:

yolo # 项目根,根据你的项目改动 ├── model # 放 *.pt和*.onnx 的地方 ├── cfg # 专门放各种 yaml │ └── lsp-pose.yaml # 下面内容 ├── datasets # 专门放所有数据集 │ └── lsp-pose # 具体数据集 └── runs # 训练输出(自动生成)

通过终端在项目目录运行

yolo pose train \ data=cfg/lsp-pose.yaml \ model=model/yolo26n-pose.pt \ epochs=40 \ imgsz=640 \ batch=16 \ device=mps

epochs是轮数,imgsz是图片处理输入尺寸,batch是每次输入图像的数量,device是设备,没有cuda或mps的话用cpu

如果出现报错AttributeError: Can't get attribute 'Pose26' on <module 'ultralytics.nn.modules.head' from '/Users/Zhuanz/Desktop/work/yolo/.venv/lib/python3.10/site-packages/ultralytics/nn/modules/head.py'>,可通过uv pip install -U ultralytics升级 ultralytics 到 ≥ 8.4.0

训练结束

查看训练情况,模型在训练过程学到了特征,逐渐收敛,也没有出现过拟合现象,仍有较小的提升空间,下次继续训练和提高数据增强的强度可能提升指标

执行yolo export model=runs/pose/train/weights/best.pt format=onnx imgsz=640 simplify=True nms=True导出

把best.onnx文件移到model,并改名称为yolo26n-pose_best.onnx

编写代码eval-yolo-pose.py,看看在测试集的表现

""" Evaluate YOLO26 Pose ONNX model on test set - overall P / R / F1 - optional visualization """ from ultralytics import YOLO from pathlib import Path import argparse def main(args): model = YOLO(args.model) # ========================= # 1. 官方 Pose 验证(核心) # ========================= metrics = model.val( data=args.data, imgsz=args.imgsz, split="test", conf=args.conf, iou=args.iou, plots=args.vis, # 是否生成可视化 save_json=False, verbose=True ) # ========================= # 2. 输出整体指标 # ========================= print("\n===== Overall Pose Metrics =====") print(f"Precision: {metrics.box.mp:.4f}") print(f"Recall: {metrics.box.mr:.4f}") print(f"F1-score: {2 * metrics.box.mp * metrics.box.mr / (metrics.box.mp + metrics.box.mr + 1e-6):.4f}") print(f"mAP50: {metrics.box.map50:.4f}") print(f"mAP50-95: {metrics.box.map:.4f}") if args.vis: print(f"\nVisualization saved to: {metrics.save_dir}") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--model", type=str, default=r'model/yolo26n-pose_best.onnx', help="best.onnx or best.pt") parser.add_argument("--data", type=str, default=r'cfg/lsp-pose.yaml') parser.add_argument("--imgsz", type=int, default=640) parser.add_argument("--conf", type=float, default=0.25) parser.add_argument("--iou", type=float, default=0.65) parser.add_argument("--vis", action="store_true", help="enable visualization") args = parser.parse_args() main(args) 

执行uv run eval-yolo-pose.py

由此可以得出结论:

  • 微调适配效果优异:该训练后的yolo26n-pose模型在 LSP-pose测试集 上实现了 mAP50=0.7502、mAP50-95=0.5971 的成绩,超过同量级模型的通用基准,说明训练过程充分学习了该数据集的姿势特征,专属适配的效果到位;
  • 训练与泛化双健康:测试集指标和验证集高度吻合,无过拟合、无性能偏差,证明模型在 LSP-pose 数据集上的收敛性和泛化能力均为优秀,训练流程的可靠性无问题;
  • 速度 - 精度权衡达标:49.6ms 的单图推理速度保持了 YOLO26n-pose 的轻量化优势,同时在专属数据集上实现了高精度,完美匹配了 “LSP-pose 场景下边缘端实时姿势估计” 的核心需求;
  • 指标平衡合理:Precision=0.5515、Recall=0.8520 的取舍,是轻量化模型在姿势检测任务中的合理策略,且 F1-score=0.6695 实现了二者的良好平衡,无明显指标偏废。

2.模型预测展示(c++)

 mac电脑可通过brew install opencv安装c++版opencv

https://github.com/microsoft/onnxruntime/releases根据自己的系统下载库文件,比如mac可用https://github.com/microsoft/onnxruntime/releases/download/v1.23.1/onnxruntime-osx-arm64-1.23.1.tgz

编写纯cpu下c++的推理及展示预测结果图片的代码

/******************************************************* * YOLO26-Pose 单文件 C++ 推理(纯 CPU) * 功能:加载 ONNX 模型 → 读取图片 → 检测人体+14 个关键点 → 画框画骨架 → 保存结果 * 依赖:OpenCV4 + ONNX Runtime(CPU 版) * 编译:见文末命令 ******************************************************/ #include <onnxruntime_cxx_api.h> // ONNX Runtime C++ API #include <opencv2/opencv.hpp> // OpenCV 用于读写/画图 #include <iostream> #include <vector> #include <chrono> // 测速 /*=============== 1. 数据常量 ===============*/ // LSP 数据集 14 个关键点的人体骨架连接(用序号表示) const std::vector<std::pair<int, int>> SKELETON = { {0, 1}, {1, 2}, {2, 6}, {6, 3}, {3, 4}, {4, 5}, // 左腿→骨盆→右腿 {6, 7}, {7, 8}, {8, 9}, // 脊柱→头 {8, 12}, {12, 11}, {11, 10}, // 左臂 {8, 13}, {13, 14}, {14, 15} // 右臂 }; // 每根骨架颜色(这里全部用白色,可随意改) const cv::Scalar LINECOLOR(255, 255, 255); // 关键点颜色(循环用绿色) const cv::Scalar KPTCOLOR(0, 255, 0); // 单个人体的检测结果 struct BboxKpts { cv::Rect box; // 人体框 (x,y,w,h) float score; // 框置信度 std::vector<cv::Point2f> kpts; // 14 个关键点坐标(原图尺度) std::vector<float> conf; // 14 个关键点置信度 }; /*=============== 2. 图像预处理 ===============*/ // 将任意尺寸图片缩放到 640×640,保持宽高比,剩余区域用黑边填充 // 返回:8UC3 彩色图,大小 640×640 static cv::Mat letterbox(const cv::Mat& src, int target = 640) { int w = src.cols, h = src.rows; float scale = std::min(float(target) / w, float(target) / h); int nw = int(w * scale), nh = int(h * scale); cv::Mat resized; cv::resize(src, resized, cv::Size(nw, nh)); // 等比缩放 cv::Mat dst = cv::Mat::zeros(target, target, CV_8UC3); // 黑底 resized.copyTo(dst(cv::Rect((target - nw) / 2, (target - nh) / 2, nw, nh))); return dst; } /*=============== 3. 后处理 ===============*/ // 输入:ONNX 输出张量 float[1][300][57] // 57 = 4(box) + 1(obj置信度) + 14*3(关键点x,y,conf) // 输出:NMS 后的多人检测结果 static std::vector<BboxKpts> postprocess(const float* out, int rows, int cols, float confTh = 0.3f, float iouTh = 0.5f) { std::vector<cv::Rect> boxes; std::vector<float> objConf; std::vector<std::vector<cv::Point2f>> kpts; std::vector<std::vector<float>> kconf; // 逐行解析 300 个候选框 for (int i = 0; i < rows; ++i) { const float* row = out + i * cols; float objectness = row[4]; if (objectness < confTh) continue; // 先过滤低置信度框 // 1. 解析框 float x = row[0], y = row[1], w = row[2], h = row[3]; int x1 = int(x - w / 2), y1 = int(y - h / 2); boxes.emplace_back(x1, y1, int(w), int(h)); objConf.push_back(objectness); // 2. 解析 14 个关键点 std::vector<cv::Point2f> kp(14); std::vector<float> kc(14); for (int k = 0; k < 14; ++k) { kc[k] = row[5 + k * 3 + 2]; // 置信度 kp[k].x = row[5 + k * 3]; // x kp[k].y = row[5 + k * 3 + 1]; // y } kpts.push_back(kp); kconf.push_back(kc); } // NMS 去掉重复框 std::vector<int> indices; cv::dnn::NMSBoxes(boxes, objConf, confTh, iouTh, indices); std::vector<BboxKpts> final; for (int idx : indices) { BboxKpts b; b.box = boxes[idx]; b.score = objConf[idx]; b.kpts = kpts[idx]; b.conf = kconf[idx]; final.push_back(b); } return final; } /*=============== 4. 主函数 ===============*/ int main(int argc, char** argv) { if (argc < 2) { std::cerr << "用法:./test_pose <图片路径>\n"; return 1; } const std::string modelPath = "model/yolo26n-pose_best.onnx"; const std::string imgPath = argv[1]; /* 4.1 创建 ONNX Runtime 会话(纯 CPU)*/ Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "yolo26"); Ort::SessionOptions sessOpt; sessOpt.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); Ort::Session session(env, modelPath.c_str(), sessOpt); /* 4.2 获取输入/输出名字 */ Ort::AllocatorWithDefaultOptions alc; auto inputName = session.GetInputNameAllocated(0, alc); auto outputName = session.GetOutputNameAllocated(0, alc); const char* inputNames[] = {inputName.get()}; const char* outputNames[] = {outputName.get()}; /* 4.3 读取并预处理图片 */ cv::Mat raw = cv::imread(imgPath); if (raw.empty()) { std::cerr << "无法读取图片: " << imgPath << "\n"; return 1; } cv::Mat img640 = letterbox(raw); // 640×640 黑边 cv::Mat blob; cv::dnn::blobFromImage(img640, blob, 1.0 / 255.0, cv::Size(640, 640), cv::Scalar(), true, false); // NCHW, float32 /* 4.4 组装输入张量 */ std::vector<int64_t> inputDims{1, 3, 640, 640}; auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeDefault); Ort::Value inputTensor = Ort::Value::CreateTensor<float>( memoryInfo, blob.ptr<float>(), blob.total(), inputDims.data(), inputDims.size()); /* 4.5 推理 */ auto t0 = std::chrono::steady_clock::now(); auto outputTensors = session.Run(Ort::RunOptions{nullptr}, inputNames, &inputTensor, 1, outputNames, 1); auto t1 = std::chrono::steady_clock::now(); float latency = std::chrono::duration<double, std::milli>(t1 - t0).count(); std::cout << "[INFO] CPU 推理耗时: " << latency << " ms\n"; /* 4.6 后处理 */ float* outPtr = outputTensors[0].GetTensorMutableData<float>(); std::vector<int64_t> outShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape(); int candidates = outShape[1], attr = outShape[2]; // [1,300,57] auto dets = postprocess(outPtr, candidates, attr, 0.3f, 0.5f); /* 4.7 把坐标映射回原图并画图 */ float fx = float(raw.cols) / 640.f; float fy = float(raw.rows) / 640.f; for (const auto& d : dets) { // 画框 cv::Rect realBox(d.box.x * fx, d.box.y * fy, d.box.width * fx, d.box.height * fy); cv::rectangle(raw, realBox, cv::Scalar(0, 255, 0), 2); // 画关键点和骨架 for (int k = 0; k < 14; ++k) { if (d.conf[k] < 0.3f) continue; // 低置信度跳过 cv::Point2f p(d.kpts[k].x * fx, d.kpts[k].y * fy); cv::circle(raw, p, 3, KPTCOLOR, -1); } for (const auto& [s, t] : SKELETON) { if (d.conf[s] < 0.3f || d.conf[t] < 0.3f) continue; cv::Point2f ps(d.kpts[s].x * fx, d.kpts[s].y * fy); cv::Point2f pt(d.kpts[t].x * fx, d.kpts[t].y * fy); cv::line(raw, ps, pt, LINECOLOR, 2); } } /* 4.8 保存 & 显示 */ cv::imwrite("result_lsp.jpg", raw); cv::imshow("YOLO26-Pose 结果", raw); cv::waitKey(0); return 0; } /*=============== 5. 编译命令 =============== g++ test_pose.cpp -o test_pose \ -Ionnxruntime-osx-arm64-1.23.1/include \ -Lonnxruntime-osx-arm64-1.23.1/lib -lonnxruntime \ `pkg-config --cflags --libs opencv4` \ -std=c++17 -O3 \ -Wl,-rpath,@executable_path/onnxruntime-osx-arm64-1.23.1/lib =======================================*/

运行编译,注意onnxtime库文件路径要根据自己的版本改

g++ test_pose.cpp -o test_pose \     -Ionnxruntime-osx-arm64-1.23.1/include \     -Lonnxruntime-osx-arm64-1.23.1/lib -lonnxruntime \     `pkg-config --cflags --libs opencv4` \     -std=c++17 \     -Wl,-rpath,@executable_path/onnxruntime-osx-arm64-1.23.1/lib

执行./test_pose datasets/lsp-pose/images/test_0000.jpg,查看结果

创作不易,禁止抄袭,转载请附上原文链接及标题

Read more

TRAE vs Qoder vs Cursor vs GitHub Copilot:谁才是真正的“AI 工程师”?

引言:工具选择 = 成本 + 效率 + 风险 的综合权衡 2026 年,AI 编程工具已从“玩具”走向“生产主力”。但面对 TRAE、Qoder、Cursor、GitHub Copilot 等选项,开发者不仅要问: * 它能写 Rust 吗?支持中文需求吗? * 更要问:一个月多少钱?团队用得起吗?代码安全有保障吗? 本文将从 五大核心维度 深度剖析四大主流 AI IDE: 1. 核心理念与自主性 2. 多语言与跨生态支持能力 3. 工程化与交付闭环能力 4. 中文本地化与业务适配 5. 收费模式、定价策略与企业成本 帮你做出技术可行、经济合理、风险可控的决策。 一、核心理念:

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 husky 守卫鸿蒙项目的 Git 提交规范(前端工程化必备)

Flutter for OpenHarmony: Flutter 三方库 husky 守卫鸿蒙项目的 Git 提交规范(前端工程化必备)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 OpenHarmony 项目的团队协作中,我们最怕遇到“带病提交”的代码。比如:某位开发者提交的代码没经过 dart format 美化、或是包含明显的 lint 警告,甚至导致整个鸿蒙工程编译失败。如果在 CI(持续集成)阶段才发现,修复成本就太高了。 husky 是从前端生态圈引进的 Git Hooks 管理神器。它能让你极简地配置 Git 的各个钩子(如 pre-commit),在代码真正提交到远端(AtomGit)之前,强制执行格式化或单元测试,确保入库的代码永远是高质量的。 一、Git Hook 工作流模型 husky 在本地提交阶段建立了一道自动化的“安检门”。 通过 失败

By Ne0inhk
降本 100%!告别无限的 token 消耗 !OpenClaw (龙虾) 本地推理方案:基于 Ollama 部署开源模型替代云端 Token 消耗

降本 100%!告别无限的 token 消耗 !OpenClaw (龙虾) 本地推理方案:基于 Ollama 部署开源模型替代云端 Token 消耗

摘要 OpenClaw(社区昵称 “大龙虾”)作为 2026 年最火的 AI Agent 框架,凭借强大的自动化执行能力成为开发者标配。但随着使用频次提升,云端大模型 Token 消耗成本居高不下,成为个人开发者与中小企业的核心痛点。本文针对最新版 OpenClaw 2026.2.26,提供一套零成本、可复现的本地化解决方案:通过 Ollama 部署开源大模型,彻底摆脱云端依赖,解决命令行参数失效、认证配置错误等核心问题,实现 “本地推理 + 本地执行” 的全闭环,兼顾成本、隐私与性能。 关键词:OpenClaw;Ollama;本地部署;开源模型;Token 降本;AI Agent;2026.2.26 一、痛点直击:为什么你的

By Ne0inhk

开源承诺靠谱吗?CAM++项目可持续性观察

开源承诺靠谱吗?CAM++项目可持续性观察 在语音技术落地的实践中,我们常会遇到这样一种情况:一个功能惊艳的开源项目,部署起来简单直接,界面友好,文档清晰,但用着用着就担心——这个项目会不会哪天突然停止维护?作者会不会悄悄闭源?社区会不会无人响应问题?尤其是当它已经嵌入到你的业务流程中,这种隐忧就更真实。 CAM++说话人识别系统正是这样一个典型样本。它由开发者“科哥”构建并开源,提供开箱即用的说话人验证与声纹特征提取能力,基于达摩院开源模型 speech_campplus_sv_zh-cn_16k 二次开发,封装为 WebUI 界面,支持一键启动、本地运行、中文友好。更关键的是,它的文档里反复强调一句承诺:“永远开源使用,但请保留版权信息!” 这句承诺是否经得起推敲?它的开源是否只是“表面开源”,还是真正具备工程可持续性的实践?本文不谈高深模型原理,也不做横向性能评测,而是以一名实际部署者和长期使用者的身份,从代码可见性、交付完整性、维护活跃度、社区可替代性、法律约束力五个维度,

By Ne0inhk