
1. 从一张 COCO 图说起飞桨版 OpenPose 到底能跑出什么多人姿态关键点检测要解决的问题很具体一张 RGB 图里可能站着好几个人每个人身上要标出十几个到二十几个骨骼点还要把属于同一个人的点连成骨架不能把 A 的左手接到 B 的右肩上。OpenPose 是这条路线里最经典的方案它用热力图heatmap预测每个关键点的位置再用 PAFPart Affinity Fields部位亲和场描述“哪个点和哪个点属于同一个人”最后通过后处理把点连成骨架。飞桨复现版的价值在于它把原版 Caffe 的推理流程用 Paddle 重写了一遍模型权重也做了转换你不需要装 Caffe 那一套老依赖直接用 Paddle 就能加载 15、18、25 点的人体模型以及 70 点人脸和 21 点手部模型。适合谁适合想在自己机器上从零搭一套推理环境、拿 COCO 样例图验证关键点输出、再往业务里接的开发者。这篇就按“装依赖 → 建网络 → 加载权重 → 单图推理 → 批量推理 → 可视化 → 排错”的顺序走一遍命令和脚本骨架都能直接复制。需要说明的是这个复现项目只做推理不含训练代码代码结构大量参考了 pytorch-openpose。所以你的目标不是训模型而是把已有权重跑通、验证输出、再封装成可调用的接口。2. 前置准备环境依赖清单与模型权重放置2.1 依赖清单飞桨版 OpenPose 的依赖不复杂核心是 paddlepaddle、opencv-python、scikit-image、matplotlib、numpy、scipy。建议用 Python 3.7 及以上Paddle 用 GPU 版还是 CPU 版取决于你的机器。GPU 版推理一张图通常几十毫秒到几百毫秒CPU 版会慢一些但完全能跑通验证。# 建议先建独立环境避免和已有 paddle 版本冲突 conda create -n openpose-paddle python3.8 -y conda activate openpose-paddle # 安装飞桨GPU 版示例CUDA 11.2CPU 版把 paddlepaddle-gpu 换成 paddlepaddle pip install paddlepaddle-gpu2.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple # 安装其余依赖 pip install opencv-python scikit-image matplotlib numpy scipy tqdm -i https://pypi.tuna.tsinghua.edu.cn/simple如果你拿到的是项目自带的 requirements.txt也可以直接cd openpose pip install -r ./requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple2.2 目录结构与权重放置复现项目的目录大致是这样权重文件单独放在一个 data 目录里openpose/ ├── models/ # 网络结构代码body、hand、face ├── postprocess/ # PAF 后处理、关键点连接 ├── detectors/ # PoseDetector、Body、Hand、Face 等入口类 ├── images/ # 演示图含 COCO 样例 ├── util.py # draw_pose / draw_bodypose 等可视化函数 └── requirements.txt data/ └── data66164/ # 转换好的模型参数文件权重文件通常按模型分开命名比如 body 的 18 点、25 点权重hand 的 21 点权重face 的 70 点权重。加载时路径要对得上否则会报找不到参数。建议在代码里用一个统一的MODEL_DIR变量管理别把路径写死在每个文件里。注意权重是转换后的 Paddle 格式不要拿原版 Caffe 的 caffemodel 直接加载格式不兼容。3. 可复制配置建网络、加载权重、单图推理脚本骨架3.1 用 PoseDetector 一把梭最省事的入口是PoseDetector它内部会把 body、hand、face 三个模型都建好你只要指定关键点数量和是否检测脸/手。import sys sys.path.append(/path/to/openpose) import cv2 from openpose.detectors.detector import PoseDetector # pose_points 可选 15 / 18 / 25detect_face、detect_hand 按需开 detector PoseDetector( pose_points25, detect_faceTrue, detect_handTrue ) img_path /path/to/openpose/images/COCO_val2014_000000000241.jpg ori_img cv2.imread(img_path) print(image shape:, ori_img.shape) output detector(ori_img) print(output type:, type(output)) print(output keys:, output.keys() if isinstance(output, dict) else not a dict)跑通后你会看到类似image shape: (h, w, 3)的输出output里一般包含candidate候选关键点、subset属于同一个人的点索引组合以及可选的face_keypoints、hand_keypoints。这一步的意义是确认网络建起来了、权重加载成功了、前向能出结果。3.2 单独用 Body 模型方便调试如果你只想验证人体关键点不想被脸和手干扰直接建 Body 模型更清晰import sys sys.path.append(/path/to/openpose) import cv2 from openpose.detectors.body import Body body_estimation Body(25) # 25 点模型 ori_img cv2.imread(/path/to/openpose/images/COCO_val2014_000000000241.jpg) candidate, subset body_estimation(ori_img) print(candidate shape:, candidate.shape) # (num_points, 4): x, y, score, id print(subset shape:, subset.shape) # (num_persons, 202) print(detected persons:, subset.shape[0])candidate的每一行是[x, y, score, point_id]subset的每一行代表一个人前若干列是这个人各关键点在candidate里的索引最后两列是整体得分和人数相关字段。subset.shape[0]就是这张图里检测到的人数这是验证多人检测是否生效最直接的指标。3.3 关键参数对照参数含义常用取值影响pose_points人体关键点数量15 / 18 / 25点数越多骨架越细25 点含脚部detect_face是否检测人脸True / False开启后多 70 点输出耗时增加detect_hand是否检测手部True / False开启后多 21 点输出耗时增加scale输入缩放默认按原图缩小可提速但小目标关键点会丢thre1热力图阈值0.1 左右调高减少误检调低召回更多点thre2PAF 阈值0.05 左右影响点与点之间的连接判定这些阈值在后处理里起作用调参时建议固定一张图反复看可视化结果别一次改好几个。4. 验证请求COCO 样例图跑通与可视化结果4.1 单图可视化拿到output之后用draw_pose把骨架画到原图上import matplotlib.pyplot as plt from openpose.util import draw_pose canvas draw_pose(ori_img, output, pose_points25) plt.rcParams[figure.dpi] 150 plt.axis(off) plt.imshow(canvas[:, :, [2, 1, 0]]) # BGR - RGB plt.savefig(pose_result.png, bbox_inchestight) plt.show()如果只想画人体骨架用draw_bodyposefrom openpose.util import draw_bodypose canvas draw_bodypose(ori_img, candidate, subset, 25) plt.rcParams[figure.dpi] 150 plt.axis(off) plt.imshow(canvas[:, :, [2, 1, 0]]) plt.savefig(body_result.png, bbox_inchestight) plt.show()成功的标志是图上每个人的肩、肘、腕、髋、膝、踝被连成骨架多人之间没有串线。如果出现串线多半是 PAF 阈值或后处理参数的问题不是权重坏了。4.2 批量推理脚本骨架单图跑通后批量推理就是把读图、推理、保存结果包一层循环import os import cv2 import glob from openpose.detectors.detector import PoseDetector from openpose.util import draw_pose detector PoseDetector(pose_points25, detect_faceFalse, detect_handFalse) img_dir /path/to/images out_dir /path/to/outputs os.makedirs(out_dir, exist_okTrue) img_paths glob.glob(os.path.join(img_dir, *.jpg)) print(total images:, len(img_paths)) for i, img_path in enumerate(img_paths): ori_img cv2.imread(img_path) if ori_img is None: print(skip unreadable:, img_path) continue output detector(ori_img) canvas draw_pose(ori_img, output, pose_points25) name os.path.splitext(os.path.basename(img_path))[0] save_path os.path.join(out_dir, name _pose.jpg) cv2.imwrite(save_path, canvas) print(f[{i1}/{len(img_paths)}] saved {save_path})批量跑的时候注意内存如果图很大建议先 resize 到长边 640 或 960 再推理速度和显存都更友好。4.3 验证 PAF 中间输出想确认网络内部真的在学 PAF可以把 body 模型各 stage 的输出画出来import numpy as np import paddle.fluid as F import paddle.fluid.dygraph as dg from openpose.models import build_body_model body_model build_body_model(18) body_model.eval() ori_img cv2.imread(/path/to/openpose/images/COCO_val2014_000000000241.jpg) inp np.float32(ori_img[:, :, :, np.newaxis]).transpose((3, 2, 0, 1)) / 255. - 0.5 inp dg.to_variable(inp) output body_model(inp) print(num stages:, len(output)) print(stage0 shape:, output[0].shape)output是一个列表每个 stage 对应一组热力图和 PAF。把output[stage].numpy()[0][:18].sum(axis0)画出来能看到关键点热力图随 stage 逐渐收敛把output[stage].numpy()[0][19]画出来能看到 PAF 的向量场。这一步是排查“网络到底有没有正常前向”的硬证据。5. 本篇常见错排查5.1 np.int 弃用警告刷屏你会看到大量DeprecationWarning: np.int is a deprecated alias。这是 NumPy 1.20 之后的正常提示不影响结果。想彻底消掉把代码里的np.int改成int或np.int64即可。项目里detector.py和util.py都有这类写法批量替换一下就行。5.2 找不到模型参数文件报错通常是FileNotFoundError或ValueError: cannot load parameter。原因一般是权重路径不对或者权重文件名和代码里写的不一致。检查MODEL_DIR指向的目录里是否真的有对应模型的.pdparams文件文件名大小写也要对上。5.3 检测不到人 / 关键点全丢先确认图片本身能正常读入ori_img is None就是读图失败多半是路径含中文或格式不支持。如果图能读但没结果把thre1、thre2调低一点再试。还有一种情况是输入尺寸太小人只占几十个像素热力图峰值不明显放大输入或换张人更大的图验证。5.4 多人骨架串线这是 PAF 后处理阈值不合适。把thre2适当调高让连接判定更严格同时检查subset里的人数是否和肉眼看到的一致。如果人数对但连线错重点看后处理里 PAF 积分那一段的参数。5.5 GPU 显存不足批量推理时最容易遇到。解决办法推理前 resize 输入、把detect_face和detect_hand关掉、减小 batch这个项目默认单图一般不会 batch 爆显存。如果还是不够换 CPU 版跑验证或者分批处理图片。5.6 可视化颜色不对plt.imshow默认按 RGB 解释而 OpenCV 读进来是 BGR所以要用canvas[:, :, [2, 1, 0]]转一下。忘了转的话红蓝会互换骨架颜色看着别扭但关键点位置是对的。6. 把推理接进你的工程接口封装与长期调用验证跑通之后下一步通常是把它包成一个可复用的服务。最简单的做法是写一个infer(image_bytes) - list[person_keypoints]的函数内部复用同一个PoseDetector实例避免每次请求都重新加载权重。权重加载是耗时大头实例化一次反复用单图延迟能降不少。如果你打算长期跑编码类或 Agent 类任务需要频繁调用模型接口做验证和对比可以了解下 Coding Plan 这类按周期计费的方式适合高频调试场景只是偶尔验证模型输出用模型对话页面直接试更省事。接入相关的 Key 和文档在 API Keys 和接入文档里按需取用即可。最后给一个实用建议把pose_points、thre1、thre2、输入 resize 尺寸这几个参数做成配置文件不同场景比如监控远景 vs 近距离人像用不同预设比每次改代码高效得多。验证阶段先用 COCO 样例图固定住基线结果之后任何改动都拿这张图对比能快速定位是参数问题还是代码问题。