ARTICLE DETAIL

建站实战干货

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

基于OpenPose与PyQt5的太极拳姿态识别系统实现

2026/8/31 19:22:56 拓冰建站 浏览量
基于OpenPose与PyQt5的太极拳姿态识别系统实现 简介这是一套面向计算机专业本科生毕业设计与课程实践的太极拳姿态识别系统完整实现聚焦运动姿态分析场景为毕设学生、Python初学者及AI视觉方向学习者提供可直接运行的实战项目。资源基于PyQt5构建交互式可视化界面集成OpenPose人体关键点检测能力支持实时视频流或静态图像中的太极拳动作识别与关节轨迹展示兼顾教学演示与算法验证需求。压缩包共116个文件含13个核心Python源码含GUI主程序、姿态处理逻辑、配置加载模块、80张标注太极拳动作的JPG样本图像、4个XML标注文件用于数据校验以及配置、说明与构建相关文件整体仅1.74MB轻量易部署。已有102人学习下载代码经导师评审获99分高分结构清晰、注释完整附带数据清洗脚本与典型动作样本小白可快速理解流程、调试运行并拓展改进。 做这个太极拳姿态识别系统起因挺直接的——家里长辈练太极动作到底标不标准、哪里歪了光靠肉眼真是看不出细节。我就想着能不能用OpenPose把人体关键点骨架抠出来再用程序判断动作偏差。折腾了一段时间最后用PyQt5把OpenPose的检测结果包成了一个带界面的桌面工具Python源码和整理好的数据集也都打包在一起方便复现和继续改。这篇文章就把整个系统从设计、环境搭建、核心代码到排坑过程完整过一遍适合正在学PyQt5或想入门姿态识别的同学参考。1. 项目整体设计与技术选型1.1 为什么选OpenPose这条路线姿态识别这个方向最早的难点不是画框而是“把骨头找出来”。OpenPose是卡内基梅隆大学开源的方案它的核心思路是基于Part Affinity Fields也就是部位亲和场。简单说网络会同时输出两个东西一个是每个关键点的置信度热图另一个是相邻关键点之间属于同一个人的亲和向量场。有了这两路输出再把关键点连成骨架就可靠很多尤其在多人场景下不容易串人。我选择OpenPose还有一个现实原因它对环境要求相对低Python API用起来直接模型权重文件也不大。对比MediaPipe或MMPoseMediaPipe在移动端效率高但输出关键点数量和精度在复杂动作上不如OpenPose稳定MMPose是训练和评估全家桶功能强但学习曲线陡。太极拳的特点是重心低、膝关节弯曲明显、两臂缓慢转换这些都对关键点定位要求较高OpenPose在这类非正面、有遮挡的场景下表现更稳。当然如果你只是想快速做Demo用OpenPose官方C版本在Windows上编译确实比较劝退。所以我最后采用的是OpenPose的轻量化思路利用现成的TensorFlow或ONNX版本把人体关键点检测作为一个独立模型来调用。这样既保留了OpenPose的精度优势也绕开了从源码编译的坑。1.2 PyQt5做可视化层的原因界面层选PyQt5首先是它成熟稳定网上资料多遇到问题基本都能搜到。PyQt5本质是Qt5的Python绑定写起来比C版本省事得多又能用到Qt的完整控件体系。对姿态识别这种需要高频刷新画面的工具来说PyQt5的信号槽机制特别合适检测线程算完一帧后就发信号通知主线程界面更新不会因为画面刷新把逻辑搞乱。Tkinter虽然零依赖但画图和控件美化实在捉急做关键点骨架可视化会很吃力Web方案像Flask前端虽然灵活但要额外起本地服务对非网络背景的人来说多一层复杂度。PyQt5里的QPainter可以很方便地在图像上画点、画线、画圆这在骨架绘制时几乎是量身定做的。再加上PyQt5可以直接打包成exe给不会装Python的家人使用也方便。我在项目里也实现了摄像头实时画面和视频文件两种输入方式这个用OpenCV读取然后转成QImage塞进QLabel展示。整个过程没用什么高端UI框架纯手工布局好处是代码清晰改起来快初学者也能看懂。1.3 系统架构与模块划分整个系统按功能拆成了四个模块解压源码后结构一目了然。输入模块支持打开本地视频文件和摄像头实时画面用OpenCV的VideoCapture统一处理检测模块负责对每一帧图像做人体关键点推理输出关键点坐标和置信度后处理模块把关键点转成角度特征、计算动作相似度识别太极拳动作是否标准界面模块PyQt5负责视频显示、骨架叠加、角度数据和识别结果的展示这种分层设计的好处是每个模块的依赖关系很清楚。比如你不想要OpenPose了想换成MediaPipe只需要改检测模块的数据接口界面和后处理逻辑都不动。我一开始也是把所有代码堆在main.py里结果改一个参数就要全局找后来重构了一次写代码的心情完全不一样了。2. 环境搭建与数据集准备2.1 Python版本与依赖安装避坑指南先说版本。我用的Python 3.8这个版本兼容性最稳PyQt5、TensorFlow、OpenCV都有现成的轮子。Python 3.10以上也能跑但OpenPose相关的旧版本包有时会编译报错如果你不想折腾建议直接用3.8。依赖清单我放在requirements.txt里核心就几个PyQt5、opencv-python、numpy、tensorflow或onnxruntime再加一个google protobuf。这些都是常规包直接pip安装就行pip install pyqt5 opencv-python numpy onnxruntime如果你走TensorFlow路线再加一句pip install tensorflow2.4.0这里有两个坑要注意。第一个坑是opencv-python的版本新版OpenCV对某些摄像头格式兼容性会和旧代码冲突建议锁定4.5.5.64。第二个坑是protobuf版本OpenPose模型加载经常要求3.20.x以下新版本protobuf会把一些旧模型初始化函数删掉导致报错。我在requirements.txt里直接锁了protobuf3.20.0就再也没遇到过初始化失败的问题。另外建议创建虚拟环境别把包直接怼进系统环境不然项目多了之后依赖冲突会让你想砸电脑。2.2 OpenPose模型文件18点还是25点OpenPose常见的有两种预训练模型COCO模型检测18个关键点BODY_25模型检测25个关键点。两者的差异主要在细节上BODY_25多了脚部的关键点对单纯的太极动作识别来说18点其实已经够用但如果你要判断“马步”里脚掌有没有完全落地那25点的脚部信息就有用了。我的建议是优先使用18点模型它更轻量推理速度更快。太极拳最重要的几个角度特征主要集中在肩、肘、膝、髋这四对关键点在两种模型里都有。如果需要脚部信息再切换到25点模型。模型文件放在models目录下结构是这样的models/ ├── pose/coco/pose_iter_440000.caffemodel └── pose/coco/pose.prototxt如果你用的是ONNX版本的OpenPose那只需要一个onnx文件加载起来比caffemodel方便很多也不用管prototxt的配置。我在代码里写了模型路径的自动探测逻辑会优先找onnx找不到再找caffemodel这样两种用法都兼容。2.3 数据集内容与预处理流程项目里的数据集文件我整理成了两类。第一类是原始图片帧按动作名称分目录存放比如qishi起势、yemafenpi野马分鬃、baiheliangchi白鹤亮翅、loukiaobu搂膝拗步等每个动作下都有几十张带人体姿态的图片。第二类是已经跑过OpenPose处理的关键点数据存成CSV一行对应一张图片字段是所有关键点的x、y坐标和置信度。这里我觉得最花时间的其实是数据整理不是写代码。从公开视频网站截帧筛选出画面干净、人形完整的帧再统一缩放到固定尺寸这一步我建议用脚本批量处理别手动一张张来import cv2 import pandas as pd cap cv2.VideoCapture(yangshi.mp4) frames [] idx 0 while True: ret, frame cap.read() if not ret: break # 每5帧取1帧避免相邻帧过于重复 if idx % 5 0: frame cv2.resize(frame, (640, 480)) cv2.imwrite(fdata/qishi/frame_{idx:04d}.jpg, frame) idx 1 cap.release()之后再用OpenPose跑一遍这些图片把关键点坐标写进CSV作为动作模板。训练模板和真实测试数据的来源最好分开不然同一个人的同一段动作测出来分数虚高没有参考价值。3. 核心代码实现与界面开发3.1 视频帧读取与姿态检测线程视频推理如果直接放在主线程里界面会发生啥就是窗口整个卡死拖动都没反应因为每帧推理要几百毫秒甚至更久。PyQt5界面必须在主线程跑事件循环所以检测逻辑必须丢到QThread里。我定义了一个DetectionThread类继承QThread在run方法里循环读帧、推理、发信号。信号带了QImage和关键点数据两个参数界面槽函数收到后直接刷新QLabelimport cv2 import numpy as np from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage class DetectionThread(QThread): change_pixmap_signal pyqtSignal(QImage) keypoints_signal pyqtSignal(list) def __init__(self, source0): super().__init__() self.source source self._run_flag True def run(self): cap cv2.VideoCapture(self.source) while self._run_flag: ret, frame cap.read() if not ret: break # 推理关键点 kps, frame_out openpose_infer(frame) # 转换颜色格式并转成QImage rgb cv2.cvtColor(frame_out, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape bytes_per_line ch * w qimg QImage(rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) self.keypoints_signal.emit(kps) self.change_pixmap_signal.emit(qimg) cap.release()这里要注意QImage和OpenCV的Mat共享内存的问题如果你在循环里不断修改frame的bufferQImage显示可能花屏。最稳妥的办法是qimg.copy()牺牲一点拷贝开销换显示稳定。3.2 关节角度提取与动作特征构建姿态识别一般不会直接拿关键点坐标比对同样的动作离镜头近的人和远的人坐标差异巨大。正确做法是把坐标转成角度角度对尺度、平移不敏感这样才能泛化到不同身高、不同距离的人。我的源码里写了一个通用的三点夹角函数传入三个关键点坐标返回以中间点为顶点的角度值import math def calc_angle(a, b, c): 计算三点夹角顶点是 b a, b, c: (x, y) 坐标 返回角度值0~180 ba (a[0] - b[0], a[1] - b[1]) bc (c[0] - b[0], c[1] - b[1]) dot ba[0] * bc[0] ba[1] * bc[1] norm_ba math.hypot(ba[0], ba[1]) norm_bc math.hypot(bc[0], bc[1]) if norm_ba 0 or norm_bc 0: return 0.0 cos_theta max(-1.0, min(1.0, dot / (norm_ba * norm_bc))) return math.degrees(math.acos(cos_theta))基于这个函数我提取了8个关键角度左肘角、右肘角、左肩角、右肩角、左膝角、右膝角、左髋角、右髋角。这一组角度向量就是当前帧的动作特征后面所有比对都基于它。实际用下来这8个角度已经能区分出大部分太极基本动作的差异。3.3 太极拳动作比对识别逻辑动作比对这块我用了两种方法第一种是“关键帧模板匹配法”第二种是“DTW动态时间规整”。关键帧模板法是入门做法每个太极动作取若干标准帧比如“起势”可以拆成预备、抬手、屈膝三个关键帧每帧记录8个角度的标准区间。识别时计算当前帧角度向量与各标准帧的欧氏距离距离最小的那个就判定为最接近的动作同时把距离映射成0到100的相似度分数。实现简单对静止姿态的比对很有效。但太极拳是动态动作同一个动作在不同节奏下帧数不一样单纯逐帧比较容易错位。这时候用DTW更合适它可以压缩或拉伸两条序列找到最优对齐方式后再计算相似度。我源码里实现了一个基础版DTW输入是标准动作的关键点角度序列和当前动作序列输出一个归一化的距离值。距离越小代表动作越接近标准。一个容易忽略的点DTW的标准序列要做平滑处理。我从录制视频里提取的关键点坐标有抖动直接生成角度序列噪声很大。我用了滑动窗口均值滤波窗口大小取5把角度曲线磨平识别率能提高10个百分点以上。3.4 PyQt5界面布局与骨架绘制界面我分了三个区域左侧是视频画面区右侧是数据显示区底部是操作按钮区。视频画面用QLabel数据区用QTableWidget展示关键点坐标和角度按钮区放“打开视频”、“打开摄像头”、“暂停检测”、“退出”四个按钮。骨架绘制是在OpenCV层面做的先用OpenPose得到关键点坐标再用cv2.line把相邻关键点连起来最后把处理完的画面转成QImage显示。这样比在Qt层面绘制简单得多也避免了QPainter坐标换算的麻烦。核心绘制代码是这样的SKELETON_PAIRS [ (0, 1), (1, 2), (1, 5), (2, 3), (3, 4), (5, 6), (6, 7), (1, 8), (8, 9), (9, 10), (10, 11), (8, 12), (12, 13), (13, 14) ] def draw_skeleton(frame, kps, threshold0.3): for pair in SKELETON_PAIRS: a, b pair if kps[a][2] threshold and kps[b][2] threshold: pt_a (int(kps[a][0]), int(kps[a][1])) pt_b (int(kps[b][0]), int(kps[b][1])) cv2.line(frame, pt_a, pt_b, (0, 255, 0), 2) return frame界面在角度数据刷新时我用一个字典引用保存当前帧的8个角度然后更新表格里的单元格。这个刷新频率不要和视频帧率完全一致默认是每秒刷新10次不然表格闪烁太厉害。4. 实操运行与优化记录4.1 从解压到跑通的完整流程拿到zip包之后我建议按下面的顺序操作解压到全英文路径别放桌面或者带中文的目录创建虚拟环境并激活安装requirements.txt里的依赖把OpenPose模型权重放在models目录下对应的位置运行main.py打开界面点“打开视频”选择data/sample_videos下的测试视频我遇到过很多人卡在第四步因为模型权重没下载完整OpenPose加载时直接报错。检查方式是打开界面后随便点一下“打开摄像头”如果窗口能正常显示画面且不崩溃就说明模型加载成功了。如果直接闪退八成是模型文件和prototxt版本不匹配。运行起来后你会在右侧表格看到每一帧检测到的关键点坐标左侧画面上绿色骨架会跟着人动。底部状态栏会显示当前识别出来的动作名称和相似度比如“野马分鬃 85%”这个反馈还是很直观的。4.2 推理速度优化GPU/CPU与输入分辨率实测下来这条链路最耗时的就是OpenPose推理。在纯CPU环境下640x480输入帧大约需要200到500毫秒意味着每秒只能处理2到5帧画面看起来会有点不连续。GPU环境会好很多但也不是所有机器都有NVIDIA显卡。我做的优化主要有三个方向。第一个是降低输入分辨率OpenPose推理分辨率从640x480降到320x240速度快了接近3倍关键点精度损失对动作识别来说可以接受。第二个是跳帧处理推理每隔一帧跑一次上一帧的关键点直接复用画面刷新率翻倍。第三个是设置torch.backends.cudnn.benchmark为True让cudnn自动选择最优卷积算法在GPU上能额外快一点点。如果你是CPU环境我强烈建议用ONNX Runtime代替TensorFlow速度能提升一倍左右。ONNX Runtime的线程数我设了4再高速度提升有限反而会明显占用其他程序资源。4.3 识别准确率提升实测这个项目最让我花时间的地方是“动作模板库”的质量。刚开始我拿自己录的一段视频做模板换个不同身高的朋友来测识别率直接降了30%。原因是角度特征虽然对平移缩放不敏感但对拍摄角度非常敏感不同人同一动作的同一帧角度能差出10度以上。后来我调整了两件事。第一模板库做成多套按身高或者摄像高度分类比如“165cm以下”“165到175cm”“175cm以上”各一套模板识别时自动选择一个匹配模板库。第二给角度特征增加了一个权重向量膝关节和髋关节的权重调高因为太极拳下盘稳定与否更多体现在这两个角度肘关节和肩关节权重略低。这两步改完我自己家庭成员之间的交叉测试准确率从刚过60%提升到了85%左右。还有一个细节是置信度阈值。OpenPose对每个关键点会输出一个置信度我代码里默认是0.3。在实际使用中如果光线不好阈值可以调低到0.2避免大量关键点被过滤掉如果检测的人距离镜头远置信度普遍偏低这个参数非常值得反复试。5. 常见问题与排查技巧实录5.1 安装与环境类问题问题现象可能原因解决方法pip安装PyQt5速度极慢网络源问题改用清华镜像源pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple安装opencv时提示版本冲突已安装其他OpenCV版本先pip uninstall opencv-python再装指定版本启动程序报错No module named google.protobuf依赖未装全pip install protobuf3.20.0虚拟环境里能跑双击外部运行不了OpenPose库未打包直接用命令python main.py运行别用双击方式环境类问题最头疼但也是最好解决的。我强烈建议所有依赖版本先按requirements.txt装一遍不要图省事自己手动装。特别是protobuf版本不匹配出的问题很有迷惑性报错内容可能指向model初始化但你根本想不到是protobuf的锅。5.2 模型加载与检测效果问题问题现象可能原因解决方法模型加载失败提示File not found权重文件缺失或路径不对确认models目录下有caffemodel文件且与prototxt对应检测不到人画面里没骨架输入尺寸太小或阈值太高把输入分辨率调回640x480置信度阈值降到0.2骨架线条错乱连到不同人身上OpenPose多人关联错误升级到BODY_25模型或调整PAF阈值检测总是慢半拍CPU推理太慢跳帧处理或改用ONNX Runtime或降低分辨率我在这里踩过印象最深的一个坑是Windows下路径分隔符。模型加载路径我一开始写的是硬编码的“models/pose/coco/pose_iter_440000.caffemodel”在Windows上也能跑但有时候复制到别的机器就找不到了。后来我改成用os.path.join拼接路径彻底解决了。你要是从zip解压直接用先跑一次自带的demo.py确认环境OK再改自己的代码。5.3 界面卡顿与交互问题问题现象可能原因解决方法打开摄像头后界面卡死检测逻辑写在了主线程把检测代码移到QThread里画面有拖影或花屏QImage与Mat内存共享导致用qimg.copy()做深拷贝关闭窗口后程序还在后台跑子线程没停止重写closeEvent先停止线程再关闭窗口按钮点了没反应信号槽连接错误检查connect里的槽函数签名是否一致界面卡顿这个问题一定要在开发初期就处理好不然后面再加功能会越来越痛苦。我的思路是主线程只负责界面刷新和接收信号检测线程只负责推理和发信号两者之间用队列或信号传递数据不要共享Mutex锁避免死锁。关闭窗口时closeEvent里先set_flag为False然后wait线程结束再super().close()这样进程能干净退出。还有一个很多人会忽略的小坑视频文件路径和摄像头序号不要冲突。如果你用了0作为摄像头序号又想打开一个叫0.mp4的视频文件代码会优先认为0是摄像头索引。我UI上区分了“打开视频”和“打开摄像头”两个按钮源码里分别传入文件路径或整型索引这样可以避免这种混淆。我在最后总结一点个人体会做类似的项目真正耗时的地方其实不是写代码而是数据准备和动作标准的定义——什么样的角度区间才算“标准”的野马分鬃真要到操作层面去抠细节的时候才会发现动作视频里的关键帧和角度分布差异有多大。源码和数据都整理在一个包里你可以按照文章里的步骤先跑通再慢慢替换自己的动作模板。如果只是拿来学习重点看检测线程和界面信号槽的配合这套结构放到其他姿态识别项目里一样能用。本文还有配套的精品资源点击获取