
简介这套基于OpenPose与YOLOv5的人体姿态识别项目面向计算机视觉入门及进阶开发者提供从关键点检测到目标定位的完整落地方案可应用于运动分析、交互设计、安防监控等场景。压缩包为zip格式内含716个文件约85.59MB以C头文件、源代码、Python脚本、构建配置与工程文件、预编译可执行程序和动态库、说明文档为主并附有示例图片和演示视频目录结构完整便于对照学习压缩包中还含有编译中间文件可辅助排查构建细节。已有382人浏览学习适合具备一定深度学习框架基础、希望深入理解姿态识别原理的读者实战演练。资源不仅包含网络结构设计、特征提取与关键点预测等核心算法实现还提供数据预处理、训练及后处理环节的脚本与工具可按需调整优化。通过阅读源码和运行演示能够系统掌握两种算法的协同工作方式提升在实际场景中部署姿态识别系统的能力。1. 人体姿态识别为什么是OpenPoseYOLOv5而不是单模型硬扛我最早做视频行为分析时拿到的场景是商场监控里三个人前后交错行走单靠OpenPose在整图上跑关键点经常串到隔壁人身上单靠YOLOv5又只能得到框和类别给不出关节坐标。这套基于OpenPoseYOLOv5的姿态识别工程解决的就是这个问题YOLOv5先以高召回把人一个个框出来OpenPose在剪裁后的区域里做关键点回归两者接力之后多人场景的串人问题明显减少帧率也比直接对整图跑OpenPose高出一截。源码包里既有关键点检测的核心算法又有数据预处理、训练和后处理环节适合正在做行为识别、人机交互或者安全监控的开发者也适合刚学姿态估计、想找一份能完整复现的工程来入门的研究者。这套组合的后端逻辑我下面逐层拆开讲。2. 原理与选型OpenPose的PAF机制和YOLOv5检测头怎么接力2.1 OpenPose为什么能处理多人关键点从置信度图到PAF向量场整套系统里姿态估计真正干活的是OpenPose。它属于bottom-up路线不先检测人而是先在整张图上回归出所有关键点位置再把关键点连成骨架。骨干网络用的是VGG19的前10层做特征提取之后分叉成两个分支。第一个分支输出置信度图confidence map也叫热力图每个关键点类别对应一张图图上数值最高的位置就是该类关键点最可能出现的地方。第二个分支输出PAFPart Affinity Fields部位亲和场这是OpenPose最关键的创新它把“哪两个关键点属于同一个人”这个问题建模成向量场每个像素位置输出一个二维向量指向该骨骼段的走向。比如左手肘到左手腕这一段PAF会在这条线段附近编码一个方向向量告诉后续匹配算法“这里存在一条左前臂”。后处理阶段OpenPose先把热力图上的局部极大值取出来作为候选关键点再用PAF作为权重对相邻两类关键点的候选做二分图匹配求解最大权重匹配。得益于PAF的向量先验即使两个人靠得很近、关键点重叠匹配算法也有依据判断哪两个点该连起来而不是靠距离最近硬拼。这就是它能做多人姿态估计的根本原因也是后续和YOLOv5搭配时的逻辑基础。OpenPose对单人场景完全够用但到了密集人群网络在整图上做关键点回归计算量大且匹配复杂度随人数上升。而且它自带的人体检测“粒度”比较粗用的是早期版本的检测器在遮挡和密集场景里召回率不够稳。这时候就需要一个专门训练的目标检测器先把人分开。2.2 YOLOv5的检测头为什么适合做前置人框CSPDarknet、PANet与anchor策略YOLOv5是典型的top-down检测器和OpenPose的bottom-up哲学正好相反。它先用CSPDarknet骨干网络提取多尺度特征再用PANet结构把高层语义信息和底层空间细节融合起来最后通过三个不同尺度的检测头输出预测框。三个检测头分别对应小、中、大目标anchor预设也按数据集的尺寸分布做了聚类所以对人这类中等大小的目标默认anchor就够用。工程上选YOLOv5做前置人框主要看中三点。第一是推理速度快YOLOv5s在640×640输入下在我的机器上跑单帧大约在10到20毫秒之间不会成为整个姿态识别管线的瓶颈。第二是模型体积小、导出门类全PyTorch训练完可以直接转ONNX或TensorRT部署链路成熟。第三是生态完善torch.hub加载权重、训练自定义数据集都有现成脚本后面要换成自己场景里的人形检测器时不用改太多代码。实际使用时我会把检测类别过滤条件写成只保留person这一类然后取置信度最高的前N个框N由场景人数上限决定。YOLOv5的NMS后处理默认已经做掉了一部分重叠框对于密集人群我会把iou_thres调低到0.4左右让重叠的框少留一些避免同一个人被OpenPose重复计算两套关键点。2.3 两阶段接力带来的实际收益剪裁ROI、减小搜索空间、缓解串人两阶段串联的核心收益在于YOLOv5把人框出来之后OpenPose只需要在这个ROI区域内做姿态估计而不是对整张图做全图推理。OpenPose在整幅1080p画面上跑一次内存开销和耗时都非常可观但把输入裁剪成几个人框之后送入OpenPose的单帧面积大幅缩小推理时间能下降一个量级实测帧率可以从不到5fps提升到20fps以上具体数字取决于显卡型号和人数。另一个更关键的收益是防串人。多人交叉行走时关键点在全局热力图上是混在一起的哪怕有PAF加持匹配算法在极端遮挡下还是会出错。但YOLOv5已经把每个“人”在像素空间上做了隔离只要两个人没有完全重叠OpenPose在每个框内各自跑一次就不会发生跨人的关键点错误连接。实现的时候要注意把检测框往外扩一部分避免裁掉四肢末端这部分我放到第4章的代码里细说。3. 环境与工程结构CUDA、PyTorch和CMake三件套怎么配齐3.1 conda环境下装依赖先锁CUDA版本再决定PyTorch这个项目我最想先强调的一点是依赖版本别乱配。很多人拿到压缩包解压后直接pip install按最新的装结果CUDA、PyTorch、cuDNN三者版本对不上编译OpenPose时各种报错。我一般会先创建一个独立conda环境把基础版本固定好再往里装东西。conda create -n pose python3.8 conda activate pose conda install cudatoolkit11.3 cudnn8.2.1 pip install torch1.10.0 torchvision0.11.0 pip install opencv-python protobuf这段命令的逻辑是先把NVIDIA的CUDA运行时和cuDNN固定在11.3和8.2.1再装对应的PyTorch 1.10.0。PyTorch和CUDA版本有严格的对应关系官方在安装命令里给了每个版本对应的cuXX标签比如cu113就对应CUDA 11.3。如果你机器上预装的驱动是CUDA 12.x也没必要换驱动只要conda环境里装了11.3运行时PyTorch只认环境里的CUDA Runtime API不直接依赖系统驱动版本但驱动不能太老。这里有一个容易踩的坑OpenPose编译时用到的CUDA编译器nvcc来自系统PATH里能找到的那套CUDA Toolkit。如果你系统里同时装了多个CUDA版本CMake会通过环境变量CUDA_HOME去定位nvcc定位错了版本编译出来的库和PyTorch里的CUDA版本不一致运行时就会报“CUDA driver version is insufficient”。所以在compile前我习惯先执行nvcc --version确认版本再执行python -c import torch; print(torch.version.cuda)两者必须能对上。3.2 解压后看到一堆.cu.obj.Release.cmake这是CMake构建残留不是源码本体项目压缩包解压后你会看到openpose_generated_bodyPartConnectorBase.cu.obj.Release.cmake、openpose_generated_resizeAndMergeBase.cu.obj.Debug.cmake这类文件还有renderPose、renderFace、pyramidalLK等读起来像是一堆乱码其实它们是OpenPose用CMake编译时生成的中间构建缓存文件。这些文件的名字拆开看是有规律的openpose_generated表示这是由CMake的custom command自动生成的源码目标bodyPartConnectorBase.cu.obj表示对应的源文件是CUDA写的、经过编译生成的obj中间产物Release.cmake/Debug.cmake表示CMake分别记录了Release和Debug两种配置下的编译命令。bodyPartConnectorBase是PAF关键点连接算法的CUDA实现resizeAndMergeBase负责把多尺度特征图缩放合并pyramidalLK是OpenPose自带的光流跟踪模块renderPose和renderFace则是可视化的渲染kernel。看到这些文件说明打包的人当初在Windows或者Linux上跑过完整的OpenPose编译流程但把这些构建中间产物一并塞进了压缩包。我的建议是这些文件不用留着直接删掉。它们既不能帮你跳过编译反而可能因为路径里带着原机器的绝对路径让CMake在二次构建时报缓存冲突。正确做法是新建一个干净的build目录按下面第3.3节的步骤重新配置。3.3 从源码编译OpenPose并生成Python APICMake参数逐个说OpenPose的Python API不是pip装完就有的必须自己编。编译之前确认你已经装好了CMake 3.12以上和Visual Studio 2019或GCC 7以上然后在工程根目录执行标准的CMake流程。git clone https://github.com/CMU-Perceptual-Computing-Lab/openpose cd openpose mkdir build cd build cmake -DBUILD_PYTHON_APION \ -DBUILD_EXAMPLESOFF \ -DUSE_CUDNNON \ -DDOWNLOAD_BODY_25_MODELON \ -DCMAKE_INSTALL_PREFIX../install .. make -j8BUILD_PYTHON_API这个开关必须在第一次配置时就打开它控制是否生成pyopenpose这个Python扩展模块编译完成后会在build/python/openpose/目录下生成pyopenpose.cpython-*.so文件。USE_CUDNN打开后会用cuDNN加速卷积速度能提升不少前提是你第3.1节已经装好了匹配的cuDNN。DOWNLOAD_BODY_25_MODEL会在首次编译时自动下载Body-25姿态模型这个模型对应25个关键点比COCO的17个关键点多出脚踝以下的点位后面代码里读到的poseKeypoints维度也是跟着它走的。编译过程大概要等10到20分钟期间可以观察输出里有没有Building CXX object ... /pyopenpose的字样。如果到最后在build/python/openpose下没有看到so文件八成是BUILD_PYTHON_API没生效这时候回到第3.1节检查Python路径是否是conda环境里的那个PythonCMake经常悄悄用上了系统自带的Python。4. 核心链路把YOLOv5检测框喂给OpenPose的Python实现4.1 整体数据流与模块划分检测、裁剪、回归、拼接坐标整个实时识别链路是这样一个数据流视频帧经cv2读取为BGR图送入YOLOv5做推理得到若干个person框每个框按一定比例向外扩边裁剪出ROIROI传给OpenPose的Wrapper做关键点回归得到该区域内每个人的关键点坐标和置信度最后把ROI坐标加回原图偏移量得到全图坐标系下的关键点再画骨骼连线和框写回视频帧。用模块化的眼光看这就是三个组件检测器YOLOv5、姿态估计器OpenPose、坐标对齐层我们自己写的offset逻辑。前两者是现成的真正要写好的就是坐标对齐层因为OpenPose返回的关键点坐标是ROI局部坐标系直接画到原图上会整体偏移必须在每个框处理完后把x和y分别加上框左上角的坐标x1和y1。整体主循环不需要多线程按帧顺序处理就行因为检测和姿态估计有依赖关系。想提速再考虑把OpenPose推理放到独立线程里异步执行但那会引入帧同步问题我建议先把单线程跑通再说。4.2 串联主代码如何正确调用pyopenpose并把坐标换回原图下面这段代码我把检测和姿态估计封装成一个函数可以直接粘贴到你的项目里跑通第一版。import cv2 import torch import numpy as np import pyopenpose as op # 初始化OpenPose params { model_folder: ./models/, hand: False, face: False, num_gpu: 1, number_people_max: 3, net_resolution: 656x368, } op_wrapper op.WrapperPython() op_wrapper.configure(params) op_wrapper.start() op_model op_wrapper.module # 加载YOLOv5 detector torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) detector.conf 0.3 detector.iou 0.45 detector.classes [0] # 只保留person类别 def detect_and_pose(frame): results detector(frame) boxes results.xyxy[0].cpu().numpy() person_boxes boxes[boxes[:, 5] 0] keypoints_list [] for box in person_boxes: x1, y1, x2, y2 box[:4].astype(int) # 向外扩10%边框避免裁掉手腕脚踝 pad_x int(0.1 * (x2 - x1)) pad_y int(0.1 * (y2 - y1)) x1 max(0, x1 - pad_x) y1 max(0, y1 - pad_y) x2 min(frame.shape[1], x2 pad_x) y2 min(frame.shape[0], y2 pad_y) roi frame[y1:y2, x1:x2] if roi.size 0: continue datum op.Datum() datum.cvInputData roi op_wrapper.emplaceAndPop([datum]) kps datum.poseKeypoints if kps is not None and kps.shape[0] 0: # 把ROI局部坐标转换回全图坐标 kps[..., 0] x1 kps[..., 1] y1 keypoints_list.append(kps[0]) return keypoints_list逻辑说明detect_and_pose函数先拿YOLOv5的检测结果把类别为person的框挑出来。每个框我都向外扩了10%的padding这个操作很关键因为人体检测框往往把四肢末端裁掉OpenPose对截断的关键点置信度很低甚至不输出扩边能明显改善手腕和脚踝的召回率。ROI送到OpenPose之后datum.poseKeypoints返回的是三维数组第一维是人数第二维是关键点索引第三维是x、y、置信度。因为我们已经按框裁剪理论上每帧只有一个人所以取kps[0]作为该人的关键点结果。参数说明net_resolution设置为656x368这是OpenPose要求的形式宽在前、高在后而且必须是16的倍数否则运行时会报错。这个分辨率是准确率和速度的折中点我的经验是如果GPU显存只有6G降到448x256更稳如果对精度要求高可以上832x496。number_people_max设为3限制了OpenPose在单个ROI里最多匹配3个人防止两个人框重叠时OpenPose一次性返回多套骨架导致坐标错乱。4.3 参数调优表与后处理细节conf、NMS、关键点置信度过滤整套系统的可调参数集中在两块YOLOv5的检测参数和OpenPose的推理参数。我整理了一张在实际项目中反复用到的参数表按推荐值给出参考参数推荐值作用与调整方向detector.conf0.25~0.4检测置信度阈值调低能找回被遮挡的人但误检也会变多detector.iou0.4~0.5NMS的IoU阈值调低能让重叠框合并得更干净detector.max_det10~20一帧最多保留的检测框数防止密集场景检测爆量net_resolution656x368OpenPose输入尺寸越大关键点越准但显存和时间成倍上涨number_people_max3~5每个ROI最多匹配人数高于实际人数反而容易串人keypoint置信度低于0.2置0后处理阶段把低置信度关键点置为(0,0)避免画骨架时出现飞点后处理阶段还有个小细节YOLOv5输出的坐标是xyxy格式即左上角和右下角的绝对坐标直接用于裁剪没问题。但如果你要保存成COCO标注格式就得把它们转成xywh中心点加宽高格式这个转换在数据自动标注场景里经常用到。另外关键点数组里置信度偏低的位置画连线前要单独置为无效否则OpenPose会把两个低置信度的点强行连出一条很长的“飞线”在可视化里非常难看也影响后续行为识别算法对骨架质量的判断。5. 避坑与排查五个最常翻车的环节5.1 CUDA out of memory一跑就崩现象程序启动后前几帧正常跑到第十几帧突然报RuntimeError: CUDA out of memory进程直接退出。原因OpenPose默认是fp32推理net_resolution如果设为832x496或更高单个ROI显存占用就能到2到3GBYOLOv5再占一份两模型同时驻留显存6GB的卡很容易触顶。我遇到过一次是OpenPose在初始化时把GPU显存全部预留了结果YOLOv5加载权重时无显存可用。解决先把net_resolution降到448x256这个尺寸下单人关键点精度损失不大。再用os.environ[CUDA_VISIBLE_DEVICES]0固定用单卡并在初始化YOLOv5后调用torch.cuda.empty_cache()释放缓存。如果还不行就检查是不是有别的进程占了显存nvidia-smi看一圈把不用的僵尸进程清掉。5.2 两个人交错行走时关键点串到对方身上现象A和B交叉走过OpenPose输出里A的左肘连到了B的左手腕骨架在交叉瞬间完全乱掉。原因我排查后发现是YOLOv5检测框在两个目标重叠时发生融合两个框合并成了一个更大的框OpenPose在同一个ROI里检测到两个人走了多人的匹配路径PAF在交叉点上歧义太大匹配算法选了错误连接。解决把detector.conf从0.3调到0.45减少置信度模糊的检测框同时把number_people_max从默认值改成1这样OpenPose在单个ROI内强制只输出置信度最高的一套骨架。这个改法对“单目标追踪”类项目很有效代价是两个人完全重叠时只保留一个人但总比两个人都串坏强。5.3 Windows下CMake编译报“找不到CUDA”现象在Windows的CMake GUI里配置OpenPose报Could not find CUDA但我明明装了CUDA 11.3。原因CMake缓存问题是最常见的。OpenPose第一次配置失败后CMakeCache.txt里残留了错误的CUDA路径后面无论怎么改配置都沿用旧值。项目压缩包里那些.cu.obj.Release.cmake残留文件也会干扰CMake对源码目录的判断。解决把build整个文件夹删掉重新mkdir build再执行一次cmake命令并且在命令行里显式指定CUDA根目录cmake -DCUDA_TOOLKIT_ROOT_DIRC:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.3 ..。从那以后我拿到任何项目包第一件事就是检查build目录里有没有残留的cmake缓存文件有就先清掉再编译这一下省了我很多时间。5.4 torch.hub.load下载权重失败一直卡在那里现象执行torch.hub.load(ultralytics/yolov5, yolov5s)时长时间卡在下载阶段网络稍有不稳就直接抛超时异常。原因torch.hub会到GitHub上拉取ultralytics/yolov5仓库代码再下载yolov5s.pt权重到本机缓存目录。国内网络访问不稳定时第一步clone仓库就失败重复失败很容易让人误以为是代码问题。解决提前手动把yolov5s.pt放到缓存目录~/.cache/torch/hub/checkpoints/yolov5s.pt再把YOLOv5源码clone到本地用detector torch.hub.load(local/path/to/yolov5, yolov5s, sourcelocal, path./yolov5)这种本地加载方式绕过网络依赖。权重文件版本也要注意yolov5s.pt对应的是COCO预训练权重能直接检测person类如果你换成了自己训练的权重记得把classes[0]改成你数据集中person类别的实际索引。5.5 帧率只有3fps卡得没法做实时应用现象整个pipeline跑起来只有3到5fps别说实时行为识别连预览都卡。原因很多人拿到项目直接跑根本没注意OpenPose在net_resolution默认值下对全图做了多尺度推理再加上YOLOv5又在CPU上跑所有瓶颈堆在一起。另一个隐藏点是OpenPose的renderPose默认开启它会把关键点可视化渲染结果写回datum这一步在CPU上做也非常耗时。解决关闭渲染把params里加上render_pose: 0关键点数据照常输出但不做绘制节省一大部分开销。YOLOv5那边确保用GPU推理detectortorch.hub.load(...).to(cuda)。再把net_resolution降到320x256整体帧率能翻一翻。如果还嫌慢就走第6章的TensorRT加速路线。6. 验证与进阶PCK评估、自定义数据训练以及TensorRT加速6.1 用PCK指标给关键点质量“体检”跑通之后我先会用PCKPercentage of Correct Keypoints来验证关键点回归质量。PCK0.5的意思是预测关键点与标注关键点之间的距离小于“参考长度×0.5”的比例参考长度一般取头部骨骼长度或躯干长度。这个指标能直观反映模型预测的关键点离真实位置有多远比肉眼看点准不准客观得多。你可以在项目里准备几十张标注好的图片跑一遍推理把每个关键点的预测坐标和标注坐标算归一化距离再统计阈值内的比例正常情况下PCK0.5应该到80%以上低于这个数就要检查ROI裁剪是不是裁掉了关键点。6.2 训练自己的数据集检测器微调与关键点模型如果项目要换到工厂、教室这类专属场景YOLOv5默认的person类不够用就该训练了。用labelimg把场景里的人标成person导出为YOLO格式放到datasets/your_dataset/labels和images下然后执行python train.py --img 640 --batch 16 --epochs 50 \ --data your_dataset.yaml --weights yolov5s.ptyour_dataset.yaml里只需改nc: 1和names: [person]两个字段其余保持默认。训练完替换掉第4章的权重文件即可。OpenPose端也可以用torch版OpenPose做关键点微调但成本较高实际项目中我更推荐把YOLOv5换成自己的检测器OpenPose保持官方预训练权重多数场景下效果已经够用。6.3 部署加速TensorRT与fp16推理最后是提速。OpenPose和YOLOv5转成ONNX之后再用TensorRT做fp16推理我实测在RTX 3060上能把单人姿态估计从15ms拉到6ms左右。转换的命令路径是YOLOv5用官方export.py导出end2end ONNXOpenPose用原生TensorRT封装或人工拆分一些层这部分工作量大但收益明显。部署到嵌入式设备时建议直接把net_resolution降到320x256并在Docker里固定CUDA 11.3环境避免现场机器环境差异导致API兼容问题。那次我为了省时间跳过版本检查结果现场整整调了一天环境从那以后我每次部署都强制走一遍版本核对流程跑通了再继续加功能。希望帮到你。本文还有配套的精品资源点击获取