ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenPose骨架行为识别:模型文件选型与部署避坑指南

OpenPose骨架行为识别:模型文件选型与部署避坑指南 简介这份OpenPose模型压缩包面向姿态估计与行为识别开发者提供了OpenPose库中人体、面部、手部关键点检测所需的预训练模型文件适用于动作分析、人机交互、虚拟现实等场景。压缩包共16个文件包含6个prototxt网络配置、5个caffemodel模型权重以及脚本和示例文件整体约727.86MB。模型覆盖Face、Hand、Pose/Body_25、COCO和MPI等主流方案可分别实现人脸51点、手部21点和人体25点关键点检测开发者只需放入models目录即可调用。资源已吸引916人学习下载。对需要快速部署OpenPose或研究多模型差异的读者这份资源省去了逐一收集权重的时间配套的getModels脚本与xml示例也有助于理解目录结构与加载方式是上手骨架行为识别的高效基础包。1. openpose 骨架行为识别先过模型文件这一关做 openpose 骨架行为识别的人多半在模型文件这一环卡过壳源码编译通过一跑 demo 就报找不到 pose_iter_584000.caffemodel。OpenPose 开源仓库默认不含权重源码和模型是分开发布的缺文件、放错目录、prototxt 与权重不匹配都是新手期最容易踩的坑。这套 openpose-models 资源包把 openpose/models 目录下常用到的姿态、手部、面部模型按官方目录结构整理好了覆盖了完整推理所需的几套权重与网络定义文件拿到以后直接解压进源码树或者通过 --model_folder 指给它就能开始跑。适合正在做骨架行为识别、动作分析又不想在镜像站逐个比对文件字节数的从业者。2. 模型文件清单与选型body_25、coco、mpi 三套骨架的差异2.1 看懂官方 models 目录的标准结构想确认模型包是否完整拿官方源码树当参照系最省事。OpenPose 源码根目录下的 models/ 就是默认查找模型的地方里面按 pose、face、hand 三个子目录区分不同类型。这份 openpose-models 资源包正是按这个层级整理的解压后能直接对上源码树的目录结构。看完目录结构可以用一条命令快速核对文件缺不缺find ./models -type f \( -name *.caffemodel -o -name *.prototxt \) | sort逻辑说明这条命令会把 models 目录下所有 Caffe 权重文件和网络结构文件列出来方便对照官方 models 目录确认覆盖是否完整。我一般先跑这条命令再往下做部署避免后续推理报一堆找不到文件的错误。参数说明find 的-type f只输出文件过滤掉目录\( ... -o ... \)表示括号内的两个条件取或| sort让输出按路径排序方便人眼核对。正常情况下应看到 pose/body_25、pose/coco、pose/mpi 三组姿态模型以及 face 和 hand 组各一个权重加 prototxt。如果缺了 pose/body_25 下的文件而你又打算做下半身动作分析后面加载模型时会直接失败这个问题我们放到第 4 章细说。2.2 三套姿态模型怎么选骨架点数、prototxt 与训练权重对照OpenPose 官方常用的姿态模型有三套分别对应不同数据集和骨架点定义。选型直接决定行为识别的特征设计和最终准确率值得先花两分钟搞清差异。模型系列骨架点数prototxt训练权重适用场景body_2525 点0 为背景pose_deploy.prototxtpose_iter_584000.caffemodel脚部关键点完整适合步态、踢腿、下肢动作识别coco18 点0 为背景pose_deploy_linevec_fix.prototxtpose_iter_440000.caffemodel2D 姿态社区资料最多行为识别 demo 首选mpi16 点0 为背景pose_deploy_linevec.prototxtpose_iter_160000.caffemodel老版本模型预处理尺寸不同新项目不建议用参数说明body_25 的 25 个点里包含了左右大脚趾、小脚趾和足跟共 6 个脚部点所以对走路、跑步、跳跃类的行为识别更友好。coco 的 18 点定义与 COCO 数据集一致网上现成的骨架特征工程代码基本都是按它写的换模型时只要改关键点索引即可。mpi 的输入尺寸是 448x368和另外两套模型的 656x368 不一样如果不改 net_resolution 直接跑姿态质量会明显下降。我自己的选型习惯是做上半身动作举手、挥手、弯腰优先 coco做下肢动作和步态分析优先 body_25。如果你打算把关键点数据喂给自训练的时序分类器coco 的参考资料最多踩坑后能搜到的解决方案也最多。2.3 模型与源码版本的配套关系模型文件不是放到目录里就完事prototxt 与源码版本的配套关系经常被人忽略。coco 目录下有两份 prototxt一份是 pose_deploy_linevec.prototxt一份是 pose_deploy_linevec_fix.prototxt。新版本源码默认优先读取 fix 版本老版本源码只认原始版本如果两者混用启动时会提示找不到网络文件。这份 openpose-models 资源包把两份 prototxt 都收进去了算是省去了一个常见的版本匹配问题。你只需要记住一个原则不要从两个不同的模型包里各抽一个 prototxt 和一个 caffemodel 拼在一起用Caffe 加载网络结构时只检查维度是否一致而维度恰好一致但权重完全不匹配的情况也会发生后面输出全零就是典型症状。3. 部署与复现把模型包落进 models 目录的完整流程3.1 先确认源码树和默认模型路径部署前先确认你本地的 openpose 源码树长什么样。源码根目录下有 build 目录编译输出、examples 目录、include 目录还有一个 models 目录。CMake 配置里有个 MODEL_FOLDER 选项默认值指向这个 models 目录编译出来的可执行文件会去这里找权重。ls -d models/pose/body_25 models/pose/coco models/pose/mpi models/face models/hand逻辑说明这一步是在源码根目录确认默认模型子目录都存在。即使资源包没解压这些空目录在 clone 源码时也会生成后续直接把文件放进去即可。参数说明ls -d只看目录本身不展开内容避免目录不存在时输出一堆错误。如果这些目录不存在说明你的源码版本或拉取方式有差异建议先回源码根目录重新确认不要在别处手工建目录否则 CMake 默认路径找不到。3.2 解压放置模型文件含权限处理拿到 openpose-models 资源包后解压并合并到源码树里是第一步。我习惯用一条链路完成解压、拷贝、权限修正tar -xzf openpose-models.tar.gz cp -r openpose-models/* openpose源码目录/models/ chmod -R ar openpose源码目录/models/逻辑说明tar -xzf解压资源包cp -r把解压出来的文件按原有层级合并进 models 目录chmod -R ar给所有用户加读权限。参数说明-R是递归处理全部子目录ar表示所有用户owner、group、others都增加读权限这步在容器或 Web 服务场景里很关键Caffe 加载权重文件时如果遇到权限不足会直接抛异常。这里有个容易翻车的点解压出来的目录如果多套了一层比如实际路径变成 models/pose/body_25/pose_deploy.prototxt而把文件放成了 models/body_25/pose_deploy.prototxt运行时就会报找不到文件。放完后用第 2 章的 find 命令再对一遍比盲目开始推理更稳。3.3 编译后跑通单张图片推理模型文件就位后进入编译和推理环节。OpenPose 的编译依赖比较多这里只提两个关键 CMake 选项CMAKE_BUILD_TYPE 设 ReleaseBUILD_PYTHON_API 按需开启。cd openpose mkdir -p build cd build cmake -DCMAKE_BUILD_TYPERelease -DBUILD_PYTHON_APION .. make -j$(nproc)逻辑说明cmake负责生成构建配置make -j$(nproc)用全部 CPU 核心并行编译能显著缩短等待时间。参数说明-DBUILD_PYTHON_APION会编译出 pyopenpose 模块后面用 Python API 读骨架坐标必须开如果只打算跑命令行工具可以关掉减少编译时间。编译通过后用一张有人物的测试图跑推理./build/examples/openpose/openpose.bin \ --image /path/to/your_test.jpg \ --model_folder ../models/ \ --net_resolution 656x368 \ --write_json ./output_json/逻辑说明openpose.bin 是官方命令行工具--image指定输入图--model_folder显式告诉它模型在哪--write_json把骨架关键点坐标写成 JSON 文件。参数说明--net_resolution 656x368是宽 x 高不是高 x 宽设反了不报错但骨架点会整体偏移这点有点玄学建议直接用 16 的倍数。--write_json输出的 JSON 里会有 pose_keypoints_2d 数组一行包含 x、y、置信度三列。第一次跑通的关键是先看到日志里出现 keypoints 检测成功的信息再去打开输出图看骨架连线是否贴合人物。3.4 用 Python API 输出骨架坐标命令行产出的 JSON 适合做离线分析但接实时视频流或做二次开发时Python API 更顺手。注意 import 前要把编译出的 pyopenpose 路径加进 Python 环境export PYTHONPATHopenpose源码目录/build/python:$PYTHONPATH然后执行最小脚本import cv2 import openpose as op params { model_folder: ./models, net_resolution: 656x368, number_people_max: 1, keypoint_threshold: 0.25, } wrapper op.WrapperPython() wrapper.configure(params) wrapper.start() img cv2.imread(your_test.jpg) datum op.Datum() datum.cvInputData img wrapper.emplaceAndPop(op.VectorDatum([datum])) keypoints datum.poseKeypoints print(人数:, keypoints.shape[0]) print(关键点坐标与置信度:, keypoints)逻辑说明WrapperPython 是官方封装的 Python 接口Datum 是单帧数据的容器emplaceAndPop是同步推理入口一次处理一个 Datum结果写回同一个对象。参数说明number_people_max限制最大检测人数显存紧张时设为 1 很有效keypoint_threshold是关键点置信度阈值低于此值的点不会输出行为识别场景取 0.2 到 0.3 比较合理默认 0.05 会保留大量噪点。poseKeypoints 的维度是 (人数, 关键点数, 3)最后一位是置信度。拿到这个数组后后续做归一化和行为分类就都是纯 Python 的事了。4. 常见问题与避坑模型加载与推理异常的 5 条排查记录4.1 现象断言失败提示模型找不到运行 demo 时直接报[FATAL] Failed to load model: not found .../models/pose/body_25/pose_iter_584000.caffemodel。原因模型文件没放进默认目录或目录层级多套了一层程序按默认相对路径找不到权重。解决先用find ./models -type f -name *.caffemodel确认文件实际路径再在命令里加--model_folder指向绝对路径不要依赖相对路径因为不同工作目录下相对路径的解析结果不一样。4.2 现象caffemodel 加载即崩模型文件路径正确但程序加载到一半进程被终止或 Caffe 日志里出现 protobuf 解析相关错误。原因下载被中断或从资源站转存时字节损坏caffemodel 是二进制格式结尾少几个字节也加载不出来。解决先执行wc -c models/pose/body_25/pose_iter_584000.caffemodel看字节数正常应该在 200M 上下如果只有几 K 或几十 M直接重新解压覆盖。血泪经验校验文件大小永远排在调参数前面别在坏文件上浪费调优时间。4.3 现象不报错但关键点全零程序正常运行日志也没有错误但输出的 pose_keypoints_2d 数组全部为 0。原因prototxt 与 caffemodel 混用最常见的是从 body_25 目录取了 prototxt却加载了 coco 的权重文件网络层参数对不上时 Caffe 不一定崩溃而是静默输出零值。解决严格按目录配对使用prototxt 和 caffemodel 必须来自同一个模型包不要跨模型拼凑。检查输出时留意 JSON 里是否存在非零的 x、y 值比看运行日志更有用。4.4 现象手部/面部模型缺失启动时加了--hand参数程序不报错但输出里没有 handKeypoints 数据。原因新版本把手指和面部检测拆成独立开关默认关闭开启后若 models/hand 或 models/face 下没有对应权重文件日志只提示一次模型未找到后续继续执行姿态检测。解决确认models/hand/hand_pose_iter_102000.caffemodel和models/face/face_iter_116000.caffemodel存在再同时开启--hand --face两个参数。如果只做身体骨架行为识别没下载这两个模型文件也不影响主流程。4.5 现象显存溢出日志出现cudaErrorOutOfMemory或std::bad_alloc程序在初始化或推理中途退出。原因net_resolution 输入尺寸太大检测人数多渲染叠加也占显存。解决把--net_resolution降到368x288加--render_pose 0关闭可视化渲染再加--number_people_max把最大人数限制在 1 到 2。这三项同时调整后显存占用通常能降到原来的四分之一以下。5. 推理参数调优从骨架点到行为识别的关键设置5.1 影响行为识别质量的四个参数模型文件正确加载只是起点行为识别对推理参数的敏感度比姿态可视化高得多。我自己调参的固定顺序是从四个参数下手。参数名默认值作用行为识别建议keypoint_threshold0.05低于该置信度的点不输出0.2~0.3number_people_max-1最多检测人数-1 为不限制按场景固定为 1 或 2net_resolution656x368网络输入宽 x 高需 16 的倍数多人场景降到 368x288render_pose20 不渲染1 CPU 渲染2 GPU 渲染只取数据时设 0参数说明keypoint_threshold 调高可以减少抖动点但调太高会把真实关键点也滤掉导致行为分类输入缺维度我的经验是取 0.25 附近。number_people_max 固定后能避免画面里路过一个人就改变输出数组长度影响时序特征拼接。render_pose 设为 0 后不再绘制骨架图省下的显存可以反过来提高输入分辨率。5.2 坐标归一化消除身高与距离影响行为识别里最容易被忽略的一步是坐标归一化。同一动作人站得近和站得远骨骼点坐标绝对值差别很大直接喂给分类器会学到一堆和动作无关的噪声。import numpy as np def normalize_skeleton(skel): # skel: (25, 3)最后一列是置信度 neck skel[1] # body_25 的 1 号点是脖子 midhip skel[8] # body_25 的 8 号点是中髋 scale np.linalg.norm(neck[:2] - midhip[:2]) 1e-6 norm (skel[:, :2] - neck[:2]) / scale return norm, skel[:, 2]逻辑说明函数以脖子为原点把坐标系平移到人体躯干中心再除以“脖子到中髋”这段躯干长度消除身高和镜头距离带来的尺度差异。参数说明skel[:, :2]取所有点的 x、y 坐标1e-6防止躯干长度为 0 时除零返回值中skel[:, 2]保留置信度方便后续过滤低质量帧。如果用的是 coco 模型关键点索引不同1 号点是脖子但中髋没有直接定义需要用 8 号和 11 号的均值替代。换模型后第一件事是查关键点编号表别拿 body_25 的索引套 coco 的输出。5.3 关节角度比坐标更稳的行为特征坐标归一化后依然受人体姿态弯曲程度影响较小但关节角度是更稳定的特征同一个动作不同人做坐标差很多角度差很小。计算一个关节角度只需三个点def angle(a, b, c): ba a[:2] - b[:2] bc c[:2] - b[:2] cos_angle np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc) 1e-6) return np.degrees(np.arccos(np.clip(cos_angle, -1.0, 1.0))) # 右肘关节角度右肩(2)、右肘(3)、右腕(4) elbow_angle angle(skel[2], skel[3], skel[4])逻辑说明把三个关键点看成两条向量用点积公式求夹角余弦再反解出角度。参数说明np.clip把余弦值限制在 [-1, 1] 内避免浮点误差导致 arccos 返回 nan1e-6保护退化成一条线的情况。行为识别时把 25 点坐标和 14 个主要关节角度拼到一起作为特征比单独用坐标更抗噪。肘关节、膝关节、髋关节这几个大角度基本能区分站、坐、蹲、走这几类基础动作。5.4 时序行为识别的最小流程OpenPose 只负责单帧的姿态估计行为识别需要跨帧信息。常见做法是取连续 30 帧的归一化坐标每帧 25 个点只取 x、y展开成 50 维向量30 帧拼成 1500 维输入送进一层 LSTM 或两层全连接网络做分类。静态姿态站立、举手、弯腰用单帧特征就能分动态动作走路、蹲起、踢腿必须带时序。我一般先把单帧骨架存成 CSV用离线脚本拼接成样本再训练分类器。这样模型包只负责出骨架数据行为识别逻辑可以独立调试模型换版本也不影响分类代码。6. 进阶技巧用批量验证脚本给模型包做体检6.1 批量脚本的设计换了一套新的 openpose-models 资源包后不要只拿一张图测我会准备一个至少 20 张、不同分辨率和人物姿态的测试图目录跑一遍批量验证脚本用统计结果判断模型包是否完整、推理是否正常import sys, os, glob, cv2 import numpy as np import openpose as op model_folder sys.argv[1] img_dir sys.argv[2] threshold float(sys.argv[3]) params { model_folder: model_folder, net_resolution: 368x288, keypoint_threshold: threshold, number_people_max: -1, } wrapper op.WrapperPython() wrapper.configure(params) wrapper.start() total_points, total_conf, count 0, 0.0, 0 for p in sorted(glob.glob(os.path.join(img_dir, *.jpg))): datum op.Datum() datum.cvInputData cv2.imread(p) wrapper.emplaceAndPop(op.VectorDatum([datum])) kp datum.poseKeypoints if kp is None or kp.shape[0] 0: print(os.path.basename(p), no person) continue mask kp[0, :, 2] threshold total_points int(mask.sum()) total_conf float(kp[0, mask, 2].mean()) count 1 print(os.path.basename(p), persons:, kp.shape[0], points:, int(mask.sum())) print(avg points:, total_points / count) print(avg conf:, total_conf / count)逻辑说明脚本遍历目录下所有 jpg 图片逐张推理并统计每个图里置信度超过阈值的关键点数量和平均置信度。参数说明threshold从命令行传入建议用 0.2 验证人数设为 -1 是为了看到模型包在多人场景下是否还能跑通。如果连续多张图 person 为 0多半是模型路径或输入尺度有问题不只是图的内容问题。6.2 跑完脚本如何读结果平均关键点数接近模型定义的关键点数body_25 是 25、coco 是 18说明姿态检测基本正常。平均置信度低于 0.4 时先别怀疑模型文件坏了换成清晰、人体占画面比例大的图再测一轮如果换图后置信度仍然低把 net_resolution 调回 656x368 试试。这套脚本也适合验证手部模型只需要把输出换成 datum.handKeypoints 即可。从那以后我每次拿到一套新的 openpose 模型包都强制先跑一遍这个体检脚本再往下调行为识别的阈值。这个习惯帮我躲过了很多次“模型坏了”的假象希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进