ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

OpenPose+YOLOv5人体姿态识别实战:从PAF原理到多人场景避坑

2026/10/5 9:28:48 拓冰建站 浏览量
OpenPose+YOLOv5人体姿态识别实战:从PAF原理到多人场景避坑 简介项目以OpenPose与YOLOv5的融合为主线提供从数据预处理、模型训练到后处理输出的人体姿态识别完整方案适合具备计算机视觉与机器学习基础的开发者和研究者可应用于运动分析、人机交互、安全监控等场景。压缩包内共716个文件整体约85.59MB包含C/CUDA源码hpp、cpp、编译后的库与可执行程序obj、dll、exe、CMake构建配置、Python辅助脚本、Markdown说明文档以及示例mp4目录按模块划分便于快速定位与检索。项目细致展示了网络结构设计、特征提取、关键点预测等核心步骤并提供了数据标注、结果可视化、模型评估等实用工具使开发者能完整复现姿态识别流程并根据自身需求调整算法。文档与注释覆盖了环境配置、编译安装到运行示例的完整链路降低了上手门槛适合系统学习与二次开发。目前已有382人学习下载是深入理解姿态识别技术并开展工程实践的优质参考。1. 人体姿态识别为什么是 OpenPose YOLOv5 双保险一次多人场景实测之前帮别人调一个室内监控项目画面里只要同时出现三个人单人姿态模型就会把 A 的手接到 B 的肩上骨架线乱成一团。后来把方案换成 YOLOv5 先做人体目标检测再用 OpenPose 在裁剪出的人体区域上做关键点提取骨架才各归各位。这套基于 OpenPose YOLOv5 的人体姿态识别源码做的就是同一件事目标检测把多人问题拆成单人问题OpenPose 的 PAF 机制再稳定输出 25 个身体关键点。它适合已有 PyTorch 基础、想在企业项目里落地姿态识别的开发者也适合打算用自定义数据集训练关键点模型的研究者。全文按编译、参数调整、多人排错的顺序把能复现的步骤和踩过的坑一次写清楚。2. 把 OpenPose 跑通PAF 原理、CMake 编译与推理参数选择2.1 PAF 是怎么把关键点连成人的两个分支与 BODY_25 输出OpenPose 采用的思路和多数姿态估计模型相反。YOLOv5 这类目标检测是“自顶向下”的先验框回归而 OpenPose 属于“自底向上”它不先判断图里有几个人而是先在全图所有位置找到可能是“手腕”“肩膀”“脚踝”的关键点候选再决定这些候选点怎么连成一条条骨架。关键就在 PAFPart Affinity Fields。网络结构上OpenPose 有两个输出分支。一个分支输出每个关键点的 confidence map告诉模型“鼻子大概在哪些像素附近”另一个分支输出 PAF 向量场在每个像素位置编码相邻两个关键点之间的方向和距离。后处理阶段对每个候选连接沿着向量场积分分数超过阈值就保留低于阈值就断开。这样即使两个人站得很近只要 PAF 的朝向不一致也能把关键点区分开。BODY_25 输出 25 个关键点包含脚底和脚跟如果只需要躯干和手臂可以换用 COCO 模型关键点数量降到 18 个速度会快一点。选型上我一般把 BODY_25 作为默认虽然模型文件更大、推理略慢但多出脚部关键点后走路姿态、跌倒检测这类场景的可用性高很多。只做手势或上半身交互时再退回 COCO。要改关键点类别重点动两个地方模型配置里的 keypoint 定义以及源码里骨架连接关系表。这两个地方没对齐画出来的线就是乱的属于初学者最容易忽略的“黑匣子”。2.2 从源码包到 openpose.binCMake 构建与首次运行拿到源码包后先别急着跑。里面那些openpose_generated_bodyPartConnectorBase.cu.obj.Release.cmake、openpose_generated_resizeAndMergeBase.cu.obj.Debug.cmake一类的文件是 GPU 编译时 Nvcc 生成的中间产物不是源码本体后面会专门讲这类文件怎么处理。真正决定推理能力的是bodyPartConnectorBase.cu、resizeAndMergeBase.cu、renderPose.cu这些 CUDA 源文件它们分别负责 PAF 连接解析、尺度合并、关键点渲染。OpenPose 的构建坑主要在环境匹配。建议直接用 Ubuntu 18.04/20.04 CUDA 10.2 或 11.xCMake 3.12 以上。Windows 也能编但 MSVC 和 CUDA 版本一定要对齐否则报错信息很晦涩。常见做法是先在 Linux 上跑通Windows 再单独适配。构建命令我一般这样写cd openpose mkdir -p build cd build cmake .. \ -DUSE_CUDNNON \ -DGPU_MODECUDA \ -DBUILD_PYTHON_APION \ -DDOWNLOAD_BODY_25_MODELON \ -DCMAKE_BUILD_TYPERelease make -j$(nproc)逻辑说明-DGPU_MODECUDA是 OpenPose 新版本里切换 CUDA / CPU 推理的主开关老版本可能用-DUSE_CPU_ONLYON来切 CPU 模式两个方向相反看清楚再设。-DDOWNLOAD_BODY_25_MODELON让 CMake 阶段自动拉取 BODY_25 权重并放到models/pose/body_25/下离线环境需要手动把权重放进对应目录。-DBUILD_PYTHON_APION会编译 Python 接口方便后面接数据预处理脚本。编译第一次一般 20 到 40 分钟日志里出现Built target openpose才算成功。之后可以跑一张官方测试图./build/examples/openpose/openpose.bin \ --image_dir examples/media/ \ --write_json output/ \ --model_pose BODY_25 \ --net_resolution 368x368 \ --number_people_max 5参数说明--image_dir指定输入图片目录--write_json把每张图的关键点坐标落到 JSON 文件这是后面做坐标还原和评估的基础--net_resolution 368x368设置网络输入分辨率越高越准但越慢--number_people_max 5限制最大人数多人场景下能明显加速后处理。第一次跑通后看到output/里生成带 25 个关键点坐标的 JSONOpenPose 这半边就算过了。2.3 推理参数怎么调net_resolution、model_pose 与人数上限调参之前先理解几个关键参数之间的关系不然只能靠玄学试。net_resolution直接决定网络前向计算量OpenPose 的论文实验里 368x368 和 656x368 的精度差距并没有想象中大但推理耗时可能差一倍。监控画面里人比较小的情况下我一般先用-1x368这种写法高度固定 368宽度按原图比例自动算避免拉伸变形导致关键点偏移。参数常用值作用与注意点--model_poseBODY_25/COCO/MPII关键点数量和模型体积不同BODY_25 最全--net_resolution368x368/-1x368输入分辨率-1x表示等比缩放--scale_number1默认多尺度推理精度更高时间近似翻倍--scale_gap0.15多尺度之间的缩放间隔--number_people_max5限制人数能明显减少后处理耗时--paf_score_threshold0.05PAF 连接阈值串线时往上调--nms_threshold0.05关键点热图 NMS 抑制阈值--render_pose1是否在原图上绘制骨架调试时建议打开scale_number是个容易被忽略的坑。默认1表示不做多尺度想提升精度改成3代价是同一张图要推理多次帧率直接掉下来。实际项目中建议调试阶段开多尺度看精度上限上线时关掉只保留单尺度。paf_score_threshold的默认值在干净场景下没问题但多人靠近、互相遮挡时调高到0.1甚至0.2能滤掉不少错误连接。还有一点开了--hand和--face后显存占用会增加BODY_25 的输出点会额外带手部和面部关键点显存偏小的卡建议一次只开一个。3. YOLOv5 做人框预处理自顶向下管线拆解与脚本3.1 为什么不用 OpenPose 自带的人体检测拥挤场景的召回率差距OpenPose 的原始 pipeline 里也有一步人体检测但在人群密集、目标小、严重遮挡时它把关键点候选和 PAF 连接同时做的代价是误检率上升。YOLOv5 把“找人在哪”这件事单独拎出来COCO 预训练模型对person类的召回率在常规监控场景下比 OpenPose 内部检测更稳。结构上YOLOv5 的 CSPDarknet backbone 加 PANet neck对多尺度目标的特征融合做得比较充分小目标人框不容易漏。这套管线的核心变化是从“自底向上”改成“自顶向下”YOLOv5 先出人框再把人框区域作为 OpenPose 的输入。这样 OpenPose 只需要关心“这个框里的人的骨架”PAF 跨人连接的错误天然被框隔离开。之前在厂房监控里三个人并排走纯 OpenPose 偶尔会把两个人的手臂连成一条线加 YOLOv5 框选后这类串线问题基本消失。另一个理由是便于量化。YOLOv5 的人框可以直接送给跟踪算法做 ID 绑定姿态结果跟着人框走后续做行为分析时逻辑更清晰。如果画面里有大量非人物体比如椅子、球、货架上的箱子只保留person类还能省掉 OpenPose 在背景上浪费的计算量。这就是我坚持在 OpenPose 前面加 YOLOv5 的原因不是技术炫技是把误检维度和管理维度拆开。3.2 用 YOLOv5 批量裁出人体 ROIPython API 与数据集训练入口源码里最常用的入口是写一个 Python 脚本加载 YOLOv5 官方权重后批量跑人框。注意这里用的是 Python API不是命令行detect.py因为后续还要把坐标缓存起来供 OpenPose 消费直接走 API 更稳。脚本如下import cv2 import torch import json # 加载YOLOv5官方COCO预训练权重如果用自己的数据集训练把pt路径替换过来 model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.classes [0] # 只保留person类避免把椅子、球也框进来 model.conf 0.45 # 置信度阈值调低容易漏检小目标调高误检更多 model.iou 0.45 # NMS的IoU阈值值越小重复框越少 frame cv2.imread(scene.jpg) results model(frame) # 推理一次返回Results对象 boxes results.xyxy[0].cpu().numpy() # 每行是 x1,y1,x2,y2,conf,cls person_boxes [] for x1, y1, x2, y2, conf, cls in boxes: # 边界clip防止裁框时y1:y2越出图像边界导致OpenCV报错 x1 max(0, int(x1)); y1 max(0, int(y1)) x2 min(frame.shape[1], int(x2)); y2 min(frame.shape[0], int(y2)) person_boxes.append({x1: x1, y1: y1, x2: x2, y2: y2, conf: float(conf)}) person_crop frame[y1:y2, x1:x2] cv2.imwrite(fperson_{len(person_boxes):02d}.jpg, person_crop) with open(boxes.json, w) as f: json.dump(person_boxes, f, indent2)逻辑说明model.classes [0]是把检测目标限定为 COCO 的第 0 类也就是person。model.conf控制置信度阈值监控画面里目标比较小的时候我会把conf降到0.3同时调高 NMS 的 IoU防止同一个人的重叠框过多。results.xyxy[0]返回的是绝对像素坐标格式为[x1, y1, x2, y2, conf, cls]直接写入 JSON 后下一步 OpenPose 读这个 JSON 就能按框裁剪。如果要用自己的数据集训练 YOLOv5关键点是标注格式。YOLO 格式要求每行写成class x_center y_center width height其中坐标都是归一化到 0 到 1 的浮点数不是像素值。源码里的数据预处理部分如果包含标注转换脚本一般就是把 VOC 或 COCO 的坐标换算成这种归一化形式。训练时hyp.scratch.yaml里的学习率、mosaic 增强等超参数会影响人框精度实际训练时不要照着 COCO 的默认值盲目跑先用小数据集验证 loss 是否正常下降。3.3 抽帧、缩放与归一化把视频变成 OpenPose 能吃的张量YOLOv5 出框后OpenPose 吃的是图片或图片目录。视频流场景要先抽帧再把人框区域缩放成 OpenPose 的网络输入尺寸。最容易翻车的是直接cv2.resize拉伸导致宽高比变化、PAF 积分时关键点相对位置被扭曲。常见做法是 letterbox也就是等比缩放后补边import cv2 import numpy as np def letterbox(img, size(368, 368)): h, w img.shape[:2] target_h, target_w size scale min(target_w / w, target_h / h) nw, nh int(w * scale), int(h * scale) resized cv2.resize(img, (nw, nh)) canvas np.zeros((target_h, target_w, 3), dtypenp.uint8) start_x (target_w - nw) // 2 start_y (target_h - nh) // 2 canvas[start_y:start_y nh, start_x:start_x nw] resized return canvas, scale, start_x, start_y逻辑说明函数返回三个值补边后的图、缩放系数、以及补边偏移量。scale和start_x/start_y在最后一步把关键点坐标还原到原图时要用如果这一步少了返回值后面无论怎么调 OpenPose 参数画出来的骨架位置都是飘的。抽帧时还要注意 OpenCV 默认读进来的是 BGR 顺序OpenPose 的 Caffe 模型按 BGR 处理但 PyTorch 系模型大多数按 RGB 训练。如果换成 YOLOv5 的 Python 接口torch.hub内部会处理色彩顺序一旦自己写数据管线就要在喂给 OpenPose 前明确当前张量的通道顺序。归一化方面常见做法是直接除以255.0缩放到[0,1]而不是套 ImageNet 的 mean/std因为 OpenPose 自带的数据层内部有自己的均值处理外面再减一遍等于双重标准化关键点响应会变弱。4. 避坑实录从编译到多人推理的五个翻车点4.1 CUDA 算力不匹配bodyPartConnectorBase.cu 编译失败现象编译执行到bodyPartConnectorBase.cu或resizeAndMergeBase.cu时Nvcc 直接报错提示Unsupported gpu architecture或者compute_XX is not supported。原因显卡的 CUDA 算力和 CMake 里预设的TARGET_ARCHITECTURE没对上。比如 RTX 3060 算力是 8.6CMake 默认可能只生成了sm_50、sm_61这类老架构代码新卡跑不了。解决在 CMake 命令里显式加-DTARGET_ARCHITECTURE86对应sm_86。RTX 30 系用86RTX 20 系用7510 系用61。如果用的是 40 系新卡老版 OpenPose 自带的 Caffe 对sm_89支持不全常见做法是把 Caffe 升级到有 Ada Lovelace 支持的社区分支或者暂时用 CPU 模式验证算法等环境就绪再切 GPU。4.2 源码包里的 .cu.obj.Release.cmake 是中间产物不是源码现象解压源码包后看到大量openpose_generated_bodyPartConnectorBase.cu.obj.Release.cmake、openpose_generated_renderPose.cu.obj.Debug.cmake文件以为源码损坏或者缺文件。原因这些文件是 CMake 在编译 CUDA 文件时生成的中间依赖描述不是 OpenPose 工程本身的源码。同一份.cu文件编译 Release 和 Debug 两种配置就会对应生成两个.cmake临时文件所以看起来特别多。解决这类文件可以放心忽略或者全部删掉。CMake 重新执行 configure 时会根据.cu源文件自动重新生成它们。判断源码完整性的依据是.cu、.cpp、.hpp源文件是否齐全而不是有没有这些中间产物。我刚拿到项目时也一度以为少了东西后来对比原仓库目录结构才发现只是打包时把构建目录的东西一并收了进来。4.3 CPU 跑不出“实时”网络分辨率与 scale 参数要按硬件降级现象文档里写着“实时人体姿态识别”结果 CPU 上跑一张图要一两秒视频帧率只有 0.5 FPS。原因OpenPose 的真实瓶颈在卷积和多阶段 PAF 积分。CPU 上跑完整 BODY_25 前向单帧耗时是几百毫秒到几秒的量级所谓“实时”是相对 GPU 环境说的。解决先确认当前推理设备。GPU 环境优先把scale_number设回1net_resolution降到320x320能明显提速。如果在边缘设备如 RK3568、树莓派上部署常见做法是先对 YOLOv5 做量化再让 OpenPose 只在裁剪出的小图上推理配合隔帧处理。不要把整张 1080p 图直接喂给 OpenPose先在 YOLOv5 那里把 ROI 框小收益比调任何 OpenPose 参数都大。4.4 YOLOv5 版本迭代导致 detect.py 脚本行为不一致现象同一段推理命令在一台机器上能跑换到另一台机器输出格式完全不同比如坐标变成归一化的、--conf-thres参数直接报错。原因YOLOv5 从早期版本到 v6.0、v7.0CLI 参数和 Python API 都有过多次调整。教程里写的--conf在新版本里叫--conf-thres--save-txt输出的坐标有的是像素值有的是归一化值。解决固定版本。用torch.hub.load时显式指定branch或tag不要默认拉最新代码。工程里不要依赖detect.py的命令行输出直接把 YOLOv5 当 Python 库调用自己统一读取results.xyxy字段。这样以后升级版本也只需要改模型加载部分后处理逻辑不会被破坏。4.5 多人场景关键点串线PAF 阈值与 NMS 一起调现象三个人站在画面里A 的右手接到 B 的左肩骨架连线跨人OpenPose 输出的人数和实际人数对不上。原因paf_score_threshold默认值太低低置信度连接也被接受另一个常见原因是重复的人检测框导致 NMS 没有正确去重同一个人的关键点被拆成两份。解决先调paf_score_threshold从默认0.05往0.1、0.2试串线明显减少后再看是否漏检。nms_threshold也同步检查同一片区域出现两个重合度极高的框时NMS 阈值要稍微调大。还有一个小技巧YOLOv5 裁剪人框时不要贴着人身体裁上下左右各向外扩 10 到 20 像素避免 OpenPose 把手腕、脚踝切在边界外导致关键点丢失。5. 坐标映射回原图骨架绘制、跳帧策略与 PCK 验证5.1 从网络坐标回到原图坐标OpenPose 输出的关键点坐标是在网络输入分辨率下的不是原图坐标。如果用了 letterbox还原公式是(x - start_x) / scale再取整回原图。直接 resize 则按scale_x和scale_y分别换算。这一步不写好后续所有可视化都是错位而你很难判断是模型错了还是坐标错了。BODY_25 的骨架连接顺序以源码里POSE_BODY_25_PAIRS为准画线时按连接表逐条绘制。我习惯把连接表单独拎出来配置不写死在渲染代码里这样换 COCO 模型时只改一个数组。5.2 视频处理跳帧与缓冲队列监控场景动作变化不快时每两到三帧推理一次中间帧沿用上一帧骨架画面感官上完全可接受。采集线程和推理线程分开中间用队列缓冲避免视频 I/O 阻塞推理。跳帧数不要超过三帧否则快速挥手、跌倒这类动作的关键点轨迹会明显不连续。5.3 用 PCK 验证是否真的可用判断这套组合值不值得上线我用 PCK 指标做快速验证预测关键点与标注点距离小于头部框尺寸一定比例就算正确。实际使用中阈值设 0.5跑几百帧标注数据PCK 在 85% 以上再考虑上线。我从这些坑里养成的习惯是每拿到一套姿态识别工程先写一个十行脚本把坐标映射关系验一遍随便喂一张图确认关键点能落回原图再上视频调参。看到源码包里的.cu.obj.Release.cmake也知道只是编译中间产物不用慌。这套 OpenPose YOLOv5 的组合按先检测后人框的顺序跑通再回头看所谓“实时性”“准确率”问题都比想象的更可控。希望帮到你。本文还有配套的精品资源点击获取