ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

Win10复现OpenPose+ST-GCN动作识别全流程:环境配置、数据转换与排错指南

Win10复现OpenPose+ST-GCN动作识别全流程:环境配置、数据转换与排错指南 动作识别这个方向近几年论文里的花样越来越多但很多人卡住的根本不是模型设计而是第一步的环境在Win10上把OpenPose 1.7.0和ST-GCN这两套老代码跑起来。OpenPose 1.7.0是CMU在2019年放出的最后稳定版ST-GCN是AAAI 2018的经典工作代码整体还停留在PyTorch 0.4时期的接口习惯上。这两个项目单独看都还好一旦要在Windows上串成一条动作识别的完整流程依赖、版本、数据格式的坑会一个个冒出来。这篇我根据自己的实操过程把Win10复现OpenPose ST-GCN的完整路线写清楚包括环境选型、OpenPose部署、数据转换、ST-GCN适配和最终的端到端测试适合只有Windows机器、又想复现动作识别完整链路的同学参考。1. 任务前拆解这个组合真正卡人的四道坎不谈清楚难点就动手基本是在给后面的自己挖坑。我先把这个组合在Win10上会遇到的四类问题摆出来后面每一章再逐个解决。1.1 OpenPose 1.7.0不是“pip install”就能完事的OpenPose底层依赖Caffe虽然官方仓库里带了3rdparty的Caffe源码但你要在Windows上把它编出来依然绕不开Visual Studio、CMake、CUDA、cuDNN这四件套。很多人在这一步就被劝退了因为Caffe是出了名的老派项目对编译器版本和CUDA版本都很敏感。还有一个容易忽略的问题OpenPose 1.7.0的Release里其实提供了Windows预编译包。也就是说如果只是想跑通流程、拿到骨架数据完全可以不碰Caffe编译。可惜很多人不知道这一点一上来就照着Linux教程从源码编译白白浪费一晚上。1.2 ST-GCN代码停在了老PyTorch时代兼容性需要自己收拾ST-GCN官方仓库的代码写于2018年前后依赖PyTorch 0.4.0。这个版本今天基本装不上了尤其在新显卡、新CUDA环境下。比较稳妥的思路是用PyTorch 1.6.0 Python 3.7这种当时过渡期的组合再做少量代码适配。需要说明的是ST-GCN的模型结构本身并不复杂核心就是图卷积加时间卷积适配新版PyTorch并不困难。难的是它的demo脚本会直接调用OpenPose的Python接口如果前面OpenPose没编好或者pyopenpose没被正确导入ST-GCN的demo连视频都读不进去。1.3 默认输出和模型输入之间还夹着一层“关节点顺序”这是最容易踩、也最坑的一处。OpenPose默认输出BODY_25也就是25个关节点而ST-GCN在Kinetics上的预训练模型用的是OpenPose的COCO模型输出18个关节点。如果你直接用BODY_25的骨架喂给ST-GCN关节点索引对不上动作识别自然是一塌糊涂。很多复现教程标题写得很热闹但恰恰把这一段跳过了。实际上跑OpenPose时加一个--model_pose COCO参数就能解决大部分问题换成自定义数据时再额外做一次关键点映射即可。1.4 Win10自身的小动作杀软、路径与运行库OpenPose编译出来的dll基本没有微软签名Windows Defender容易误报或直接隔离。另外Caffe对路径里的中文很敏感项目路径里一旦出现中文编译或运行阶段会莫名其妙报错。这些都是Windows环境特有的问题Linux教程里完全不会提到但实际折腾起来非常耽误时间。2. 先把GPU、CUDA、VS2019、Python3.7这一套钉死老项目组合最忌讳“版本差不多就行”。我的建议是先把我用的这套版本锁死这套组合在多个机器上验证过兼容性最好。2.1 为什么是这套版本组合组件推荐版本说明NVIDIA驱动441.x之后主要为了支持CUDA 10.2CUDA Toolkit10.2OpenPose 1.7.0源码对新版CUDA支持差cuDNN7.6.5与CUDA 10.2配套Visual Studio2019安装时勾选“使用C的桌面开发”CMake3.20.53.20之后对OpenPose的模块检测更友好Git任意较新版本拉取源码和子模块Python3.7AnacondaOpenPose预编译包和pyopenpose大多基于3.7FFmpegrelease最新版ST-GCN的demo抽帧要用需加入PATH这套组合的核心逻辑是CUDA 10.2和cuDNN 7.6.5是Caffe编译的老搭档VS2019能兼容Caffe的工程配置Python 3.7正好对上OpenPose预编译包里的Python绑定。当然如果你想用更新的CUDA也不是绝对不行但代价往往是需要手动改Caffe源码里的某些兼容判断不值当。2.2 一步步装齐环境先装NVIDIA驱动再用nvidia-smi确认驱动版本。接着安装CUDA 10.2 Toolkit安装时记得选择“自定义”不要勾选Visual Studio Integration因为后面用CMake和VS生成工程时不需要它。cuDNN装起来更简单解压后把bin、include、lib\x64三个目录里的文件分别复制到CUDA安装目录的对应文件夹下然后把C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\bin加入PATH。VS2019在Visual Studio Installer里勾选“使用C的桌面开发”默认会带上MSVC编译器和Windows SDK。CMake我建议用图形界面的CMake GUI版本虽然命令行也能用但GUI可以直观看到所有配置项对排查OpenPose的编译问题更友好。Anaconda建议创建两个独立环境一个给OpenPose的Python绑定用一个给ST-GCN用后面会解释为什么分开。FFmpeg的安装比较直接去官网下载Windows release包解压到D:\tools\ffmpeg把bin目录加入PATH在命令行执行ffmpeg -version验证即可。2.3 Windows安全中心对OpenPose的误报处理这里要特别说一句建议把OpenPose的整个项目目录加入Windows安全中心的“排除项”而不是直接关闭实时保护。网上很多教程喜欢让人关安全中心从个人经验看完全没必要反而会让电脑暴露在风险里。加入排除项后重新解压一次OpenPose预编译包基本就能避免dll被吞的问题。提示如果运行OpenPoseDemo.exe时闪退先看一眼Windows安全中心的“保护历史记录”十有八九能找到被隔离的OpenPose相关文件点“允许”即可不需要关闭安全中心。3. OpenPose 1.7.0的两种部署路线先跑通Demo再谈深度改造OpenPose部署有两条路线用官方Windows预编译包或者从源码编译。我的建议很直接先走预编译包把流程跑通确认视频输入、模型加载、JSON输出都没问题之后再决定要不要花时间自己编译。3.1 路线一官方Windows预编译包省掉九成编译痛苦在OpenPose 1.7.0的Release页面里找带WIN64、GPU、Python3.7标识的zip包。这个包里面已经带好了OpenPose的exe、dll、Caffe依赖和部分模型下载脚本。解压时注意两点路径要纯英文比如D:\repos\openpose不要出现中文或空格解压后第一件事就是把build\x64\Release目录加入PATH否则运行时会提示找不到dll。接着手动下载模型。在OpenPose解压目录的models文件夹里运行getModels.bat脚本会下载BODY_25、COCO、手部、脸部等几套模型。如果下载中断可以单独补下载pose/coco/pose_iter_440000.caffemodel这是跑COCO 18点必需的文件。跑第一个Demo时可以先用官方自带的示例视频bin\OpenPoseDemo.exe --video examples\media\couple.mp4 --model_pose COCO --write_json output_json --render_pose 0--model_pose COCO指定用18点模型--write_json output_json让OpenPose把每帧骨架关键点写入JSON文件--render_pose 0关闭渲染减少GPU压力。如果一帧能正常输出JSONOpenPose这部分就算通了。3.2 路线二从源码编译Caffe适合要改底层逻辑的人如果你要改OpenPose源码、加自定义后处理逻辑或者让OpenPose和ST-GCN共用同一套Python环境那还是从源码编译更靠谱。步骤不复杂但耗时较长。先拉源码注意要用--recursive把Caffe等子模块一起拉下来git clone --recursive https://github.com/CMU-Perceptual-Computing-Lab/openpose.git然后打开CMake GUIsource code填D:\repos\openposebuild目录填D:\repos\openpose\build点Configure。第一次Configure时会让你选生成器选择“Visual Studio 16 2019”平台选x64。接下来重点检查这些选项BUILD_CAFFEONOpenPose源码默认会一起编译CaffeBUILD_PYTHON_APION后面要用pyopenposeUSE_CUDAONGPU环境必须USE_CUDNNONPYTHON_EXECUTABLE填你准备用的Anaconda环境的python.exe路径比如C:\Users\你的用户名\anaconda3\envs\openpose\python.exeCMAKE_CONFIGURATION_TYPESRelease。Configure完成后点Generate生成openpose.sln。用VS2019打开这个工程右上角配置改成Release和x64右键ALL_BUILD项目选择生成。整个过程视机器性能可能需要20到40分钟。最后编译出的OpenPoseDemo.exe在build\x64\Release下pyopenpose对应的openpose目录在build\python下。编译时最常见的报错是Caffe老代码在VS2019下的编译错误比如某些宏或类型不兼容。这种时候我不建议硬啃源码先看是不是CUDA/cuDNN版本没对上如果版本没问题还报错那就直接回到路线一用预编译包。毕竟对绝大多数人来说OpenPose只是骨架提取工具没必要在Caffe编译上消耗太多精力。3.3 Python API把pyopenpose接进你的conda环境无论ST-GCN的官方demo还是自己写批量处理脚本最终都要通过pyopenpose调用OpenPose。在源码编译成功后进入build\python目录你会看到openpose文件夹里面是pyopenpose.cp37-win_amd64.pyd之类的文件。在Python里导入pyopenpose时光import openpose是不够的因为OpenPose的很多dll在build\x64\Release里。我的做法是在脚本开头显式加入路径import sys import os sys.path.append(rD:\repos\openpose\build\python) os.environ[PATH] r;D:\repos\openpose\build\x64\Release import pyopenpose as op params dict() params[model_folder] rD:\repos\openpose\models params[model_pose] COCO op_wrapper op.WrapperPython() op_wrapper.configure(params) op_wrapper.start() datum op.Datum() datum.cvInputData frame # frame是cv2读取的图像 op_wrapper.emplaceAndPop([datum]) keypoints datum.poseKeypoints # shape: (人数, 18, 3)如果import pyopenpose报错首先检查Python是不是3.7其次确认刚才的两个路径都没写错。这一步打通之后ST-GCN的demo就有地基了。4. 骨骼点顺序的坑ST-GCN要的是COCO 18点不是BODY_25这个坑我当时的印象太深了。第一次跑通OpenPose之后我直接拿默认的BODY_25骨架丢给ST-GCN结果识别结果完全没法看。后来对比两张骨架可视化图才发现关节点的索引顺序压根对不上。4.1 OpenPose两种姿态模型输出差异OpenPose的BODY_25模型输出25个关键点包含更多头部细节和脚部关键点COCO模型输出18个关键点更接近ST-GCN论文里使用的骨架格式。两者的坐标原点都是一样的但关键点索引完全不同比如COCO里0号点是鼻子而BODY_25里0号点是鼻子、1号点是脖子、2号点是右肩后面顺序也不一样。模型关键点数量输出数组长度典型用途BODY_252575x,y,score × 25OpenPose默认动作捕捉类任务COCO1854x,y,score × 182D姿态研究ST-GCN配套4.2 ST-GCN预训练模型使用的节点定义ST-GCN在Kinetics-Skeleton数据集上的预训练模型骨架关键点来自OpenPose的COCO模型。也就是说输入模型时关节点的顺序必须和训练时一致。如果你用BODY_25哪怕只取前18个关键点依然会因为顺序错位导致模型感受到的“左肩”实际上是“右肩”之类的错误。正确的处理方式有两个一是在跑OpenPose时直接用--model_pose COCO输出就是18点顺序二是如果必须用BODY_25那就要自己写索引映射把BODY_25的25点重新排列成COCO的18点再额外丢弃多余的点。第一种方式省事强烈推荐。4.3 错误的典型表现与验证方法用了错误顺序时ST-GCN的输出不是直接报错而是“看似在跑但结果很离谱”。比如一段标准的太极动作top1可能识别成打扫卫生而且连续几帧的预测类别跳来跳去。这时候不要急着调模型参数先检查骨架输入。我验证顺序是否正确的习惯是把OpenPose输出的某一帧关键点画在图像上并标出每个点的索引号然后和COCO 18点的定义表对比。鼻子是0、脖子是1、右肩是2、右肘是3、右手腕是4、左肩是5、左肘是6、左手腕是7、右髋是8、右膝是9、右踝是10、左髋是11、左膝是12、左踝是13、右眼是14、左眼是15、右耳是16、左耳是17。画图像时一目了然。5. 把OpenPose的JSON转成ST-GCN能吃的骨架序列OpenPose跑通只能算拿到了“骨架原材料”ST-GCN真正需要的是一整个视频帧序列的三维数组。这一步是OpenPose和ST-GCN之间的桥梁也是整套流程里最容易写错数据形状的地方。5.1 JSON里到底有什么OpenPose用--write_json输出后每一帧会生成一个JSON文件。里面包含people数组每个人的pose_keypoints_2d是一个一维列表按“x0,y0,score0,x1,y1,score1……”排列。如果使用COCO模型长度就是54。这里有个细节JSON里的坐标是图像像素坐标没有做归一化而ST-GCN的模型训练数据也没有强制归一化到固定范围所以直接使用原始坐标问题不大。但建议记录一下原视频的分辨率后面如果做数据增强或跨数据集测试会用到。5.2 ST-GCN的数据形状原版ST-GCN的Feeder读取的数据格式是train_data.npy和train_label.pkl。data数组的shape是(N, C, T, V, M)N样本数一个视频片段算一个样本C通道数这里是3分别是x、y、scoreT时间帧数也就是一个样本里的帧数量V每帧的关节点数COCO 18点就是18M人数Kinetics数据集最多保留了两个人所以M通常取2单人场景取1即可。label.pkl里面存的是一个字典关键是label字段它是一个一维数组长度等于N每个元素是对应样本的动作类别索引。5.3 转换脚本核心逻辑我自己写转换脚本时核心部分大概是这样的import numpy as np import json import pickle import glob frame_files sorted(glob.glob(rD:\output_json\*.json)) T len(frame_files) V 18 C 3 M 1 N 1 data np.zeros((N, C, T, V, M), dtypenp.float32) for t, f in enumerate(frame_files): with open(f, r) as fp: j json.load(fp) if len(j[people]) 0: continue # 取第一个人 kp j[people][0][pose_keypoints_2d] arr np.array(kp, dtypenp.float32).reshape(-1, 3) for v in range(V): data[0, 0, t, v, 0] arr[v, 0] data[0, 1, t, v, 0] arr[v, 1] data[0, 2, t, v, 0] arr[v, 2] np.save(rD:\val_data.npy, data) with open(rD:\val_label.pkl, wb) as fp: pickle.dump({label: np.array([0]), num_total: 1}, fp)这里有两个实际经验要分享。第一不同视频的帧数不一样而ST-GCN训练时一般会固定输入帧数。官方Feeder里有一个按时间维度resize的逻辑但如果你自己写数据转换最好先把所有样本统一到固定的T比如300帧。帧数不够就重复最后一帧帧数太多就均匀采样。第二多人场景的M比较麻烦。如果视频里同时出现两个人OpenPose的JSON里会有两个people对象你需要规定谁是第一个人、谁是第二个人并固定下来。最简单的规则是按画面中心距离排序离画面中心近的排前面。如果不固定同一个视频在不同时间跑出的“第一个人”不一致训练数据就废了。5.4 验证转换结果是否正常转换完之后不要急着训练或测试先做一次数据回读检查。用np.load加载val_data.npy取某一帧的二维关键点画出来确认五官、躯干、四肢的位置和原图对得上。尤其要看看最后一帧和第一帧的时间顺序是否正确因为ST-GCN的时间卷积对时间顺序非常敏感。6. ST-GCN在Win10上的实测Demo运行、老代码适配与自定义训练ST-GCN官方仓库本身就是给视频动作识别用的所以只要环境对了跑起来并不复杂。难点集中在老代码对新库的兼容性上。6.1 环境安装时直接把依赖锁死我建议用独立的conda环境Python 3.7conda create -n stgcn python3.7 -y conda activate stgcnPyTorch直接按官网给出的Windows pip CUDA 10.2命令安装1.6.0版本不要装最新的2.x因为老代码虽然没有特别复杂的高级API但某些内部行为在新版本下有变化排查起来更费劲。其余依赖建议这样装pip install numpy1.23.5 scipy1.10.1 opencv-python4.5.5.64 matplotlib tqdm h5py einopsnumpy版本卡在1.23.5不是手滑。ST-GCN代码里有一些老式写法比如np.bool、np.float这类别名在numpy 1.24之后被移除了直接报AttributeError。卡住版本能省很多无谓的修改。6.2 跑通视频Demo拉取ST-GCN仓库下载预训练模型到models目录后执行python main.py --demo examples/ta_chi.mp4 --model_path models/st_gcn.kinetics.pt这里要注意ST-GCN的demo脚本会自动调用OpenPose提取每帧骨架所以它要求当前Python环境能import pyopenpose比如把OpenPose build目录相关路径提前加进PYTHONPATH或直接在脚本里sys.path注入。如果import openpose失败demo会在读取视频后卡在姿态估计那一步而不是直接给出识别结果。如果在Windows上遇到BrokenPipeError这类和PyTorch DataLoader多进程相关的报错多半是num_workers大于0导致的把配置里的num_workers改成0即可。6.3 原版代码在新环境下的兼容修改点老代码在新环境下的兼容问题主要来自两个地方一个是numpy一个是ffmpeg。numpy的坑上面说了ffmpeg则是demo抽帧时会用命令行调用ffmpeg如果没装或没加入PATH会报找不到命令。我的做法是把FFmpeg装好并加入PATH然后在命令行执行ffmpeg -version确认无误再跑demo。还有一个比较隐蔽的问题OpenPose输出的JSON里某些帧可能检测不到人people数组是空。老代码遇到这种情况可能直接崩溃或产生全零骨架。我处理的方式是在数据转换时对空帧做“前向填充”也就是用上一帧的骨架填充当前帧如果连续多帧都为空就复制最近一帧保证时间维度完整。6.4 自定义数据训练怎么接如果不想只跑demo想用自己的视频训练ST-GCN流程是先用OpenPose批量提取所有视频的骨架JSON再按第5章的格式生成train_data.npy、train_label.pkl、val_data.npy、val_label.pkl最后修改config文件里的数据路径和类别数执行python main.py --train --config config/st_gcn/kinetics-skeleton/train.yaml有几个需要注意的细节。类别的顺序要和label数值对应好建议建一个classes.txt维护顺序。训练时ST-GCN会计算骨骼数据的均值和方差用于batch normalization不要在训练前自己手动归一化否则模型学到的分布和推理时不一致。如果场景中经常出现多人类别的标签必须明确是主样本还是交叉样本这部分处理起来比较复杂起步阶段先做单人视频最稳妥。7. 端到端动作识别流程跑通后的效果与性能参考到这里整条链路已经通了。我再从实测角度把你的预期拉到一个合理范围。7.1 完整串联链路端到端流程是这样的读取视频按帧或均匀抽帧用OpenPose的COCO模型提取每帧18个关键点把整个视频的所有帧关键点组装成ST-GCN输入格式然后交给ST-GCN模型推理得到动作类别和各个类别的置信度。如果只用官方demo其实不需要自己写抽帧逻辑ST-GCN的demo脚本会在内部调用OpenPose并完成组装。但如果是做批量数据集处理建议把OpenPose和ST-GCN分成两个阶段先统一产出一批JSON再统一转成npy/pkl最后做训练或测试。这样定位问题也更方便。7.2 实测效果我在GTX 1080 Ti上测试输入视频分辨率768×432OpenPose单帧推理大约40到60毫秒ST-GCN读入一个300帧的骨架序列并完成推理大约几十毫秒量级。也就是说端到端的瓶颈基本都在OpenPose上。如果你用的是CPU版本OpenPose单帧可能需要1到2秒整个流程跑起来会比较煎熬。识别效果取决于动作类别和训练数据来源。ST-GCN的Kinetics预训练模型覆盖400类动作对太极、挥拳、跑步这类常见动作表现不错。如果你要识别的是装配动作、工位操作这类工业场景预训练模型大概率不适用必须在自采数据上做微调。8. 排错实录Win10上出现过的问题和最终解决方式最后把我实际操作中遇到的高频问题列成一个速查表给后面再踩坑的同学一个直接索引。症状原因解决方式OpenPoseDemo.exe启动闪退VC运行库缺失或dll被安全中心隔离安装VC 2019运行库在安全中心排除项中添加项目目录提示找不到cudnn64_7.dllcuDNN版本不对或未加入PATH确认使用cuDNN 7.6.5将bin目录加入PATH后重启终端OpenPose输出JSON里关键点全是0模型文件未下载完整运行getModels.bat确认pose_iter_440000.caffemodel存在ST-GCN demo报No module named openpose当前Python环境没有导入pyopenpose将OpenPose的build\python和build\x64\Release加入路径识别结果乱跳、完全不符合常理用了BODY_25骨架而非COCO 18点运行OpenPose加--model_pose COCO或写映射函数转成COCO顺序numpy报bool/float属性不存在numpy版本太新老代码不兼容固定安装numpy 1.23.5DataLoader报BrokenPipeErrorWindows下多进程数据加载问题config中num_workers设为0视频demo提示ffmpeg not foundFFmpeg未安装或未加入PATH安装FFmpeg配置环境变量验证ffmpeg -versionOpenPose和ST-GCN同时跑时显存不足两个模型都占GPU显存先结束OpenPose进程再跑ST-GCN或降低OpenPose输入分辨率除了这张表还有一条很实际的建议把自己的依赖版本写成一个txt放在项目根目录再写一个scripts目录保存所有用过的命令和转换脚本。这种老项目组合半年后再打开你大概率会忘记某个坑当初是怎么解决的。我第一次折腾完没有记录换了一台机器重装环境时至少多花了两个小时才把版本重新对齐。后来每次配完一个环节就顺手把命令存下来重装成本就变得很低了。
RELATED READING

延伸阅读

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