☰
基于LSTM与MediaPipe的人体动作姿态识别Python源码实战解析
2026/9/28 17:18:04 网站建设 项目流程

简介:本资源是一套基于LSTM与MediaPipe实现人体动作姿态识别的Python项目源码,面向计算机、人工智能、通信、物联网等专业的在校学生与教师,可用于毕业设计、课程设计、大作业或项目立项演示。项目通过MediaPipe提取视频流中人体关键姿态信息,再交由LSTM网络完成动作分类识别,可应用于教育、运动分析、康复训练等场景。压缩包共280个文件,约22.09MB,包含16个py源码文件、240个npy数据文件、16个mp4示例视频、2个h5模型权重及说明文档等,覆盖数据处理、模型训练与推理演示的完整流程。目前已有184人学习下载。代码完整且功能验证通过,读者可据此理解姿态特征提取与序列建模的衔接方式,并在此基础上进行二次开发,替换数据集或调整网络结构以扩展其他动作识别功能。

1. 从一段 30 帧视频说起:这套 LSTM+MediaPipe 姿态识别源码到底能跑出什么

带过几届毕设之后,我总结出一个规律:凡是标题里同时出现「LSTM」和「MediaPipe」的项目,八成卡在同一个地方——骨架点提取出来了,但时序建模那一步接不上。你手里这份基于LSTM和mediapipe 实现人体动作姿态识别python源码就是冲着这个断点来的。它把 MediaPipe 的 Pose 模块当作前端特征提取器,把每帧的 33 个关键点坐标序列喂给 LSTM 做时序分类,最终输出动作类别。压缩包里能看到action.h5这个训练好的权重文件,还有l0.mp4、r0.mp4、r1.mp4三段示例视频,说明作者至少跑通了「视频输入 → 姿态序列 → 分类输出」这条链路。

适合谁?如果你正在做计算机相关专业的毕业设计、课程设计,或者需要一个能演示的初期立项原型,这套东西的性价比在于:它不要求你从零标注数据集,也不要求你手写 LSTM 单元。但前提是你得理解它每一环在干什么,否则改一个参数就翻车。下面我按「先看懂结构 → 再跑通流程 → 再避开坑 → 最后做二次开发」的顺序拆一遍。

2. 拆开压缩包:MediaPipe 提点与 LSTM 时序建模怎么咬合

2.1 为什么是 MediaPipe 而不是 OpenCV+DNN

人体姿态识别的前端方案常见有三类:OpenCV 的 DNN 模块加载 Caffe/TF 模型、MediaPipe、以及 YOLO-Pose 这类端到端方案。这份源码选 MediaPipe,理由很实际——它把「检测+关键点回归」封装成一个Pose对象,调用三行代码就能拿到 33 个归一化坐标,不需要你单独处理 NMS、anchor 匹配这些底层逻辑。

MediaPipe Pose 输出的 33 个关键点覆盖了面部(鼻子、眼睛、耳朵)、上肢(肩、肘、腕、手部关键点)、躯干(髋、脊柱)和下肢(膝、踝、脚)。每个点有x, y, z, visibility四个值,其中x, y是归一化到[0,1]的图像坐标,z是相对深度,visibility表示该点是否被遮挡。对动作分类来说,真正有用的是x, y和visibility,z的噪声偏大,我一般会先丢掉。

提示:MediaPipe 的z是以髋部中心为原点的相对值,不是真实深度,做精细动作区分时不要依赖它。

2.2 LSTM 在这里解决什么问题

单帧的 33 个点只能告诉你「这一刻人是什么姿势」,但「挥手」和「举手」在某一帧可能长得几乎一样。区别在于时间维度上的变化模式:挥手是左右往复,举手是单向抬升。LSTM 的门控机制(遗忘门、输入门、输出门)就是用来记住「前面几帧手在左边还是右边」这种上下文。

源码里的action.h5是一个 Keras/TensorFlow 格式的权重文件,从命名习惯推断,网络结构大概率是LSTM → Dropout → Dense(softmax)这种经典堆叠。输入张量的形状通常是(batch, time_steps, features),其中time_steps是每次喂入的帧数(常见 30 或 60),features是每帧的关键点维度。如果你用 33 个点的x, y两个坐标,features = 66;如果加上visibility,就是 99。

2.3 从视频到分类结果的完整数据流

把整条链路拆成可操作的步骤,大概是下面这样:

import cv2 import mediapipe as mp import numpy as np mp_pose = mp.solutions.pose pose = mp_pose.Pose(static_image_mode=False, model_complexity=1, min_detection_confidence=0.5, min_tracking_confidence=0.5) def extract_keypoints(video_path, max_frames=30): cap = cv2.VideoCapture(video_path) frames = [] while cap.isOpened() and len(frames) < max_frames: ret, frame = cap.read() if not ret: break rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) result = pose.process(rgb) if result.pose_landmarks: # 只取 x, y,丢掉 z 和 visibility 的噪声 kp = [[lm.x, lm.y] for lm in result.pose_landmarks.landmark] frames.append(np.array(kp).flatten()) # shape: (66,) cap.release() # 不足 max_frames 时用零填充,保证输入维度一致 while len(frames) < max_frames: frames.append(np.zeros(66)) return np.array(frames) # shape: (30, 66)

这段代码的逻辑说明:static_image_mode=False让 MediaPipe 在视频模式下启用跟踪,比逐帧检测快很多;model_complexity=1是精度和速度的折中,0 最快但精度低,2 最准但吃 CPU;min_detection_confidence和min_tracking_confidence都设 0.5 是保守值,光线差的时候可以降到 0.3,但会引入抖动。

参数说明:max_frames=30对应 LSTM 的time_steps,这个值必须和训练时一致,否则action.h5加载后会报维度错误。零填充是为了处理短视频,但填充过多会让模型把「静止」误判成某个动作,后面避坑章节会细说。

2.4 加载 action.h5 做推理

拿到(30, 66)的序列后,扩一维变成(1, 30, 66)就能喂给模型:

from tensorflow.keras.models import load_model model = load_model('action.h5') seq = extract_keypoints('l0.mp4') # (30, 66) seq = np.expand_dims(seq, axis=0) # (1, 30, 66) pred = model.predict(seq, verbose=0) class_id = np.argmax(pred, axis=1)[0] confidence = np.max(pred) print(f'预测类别: {class_id}, 置信度: {confidence:.3f}')

逻辑说明:load_model会同时恢复网络结构和权重,前提是你的 TensorFlow 版本和保存时兼容。np.argmax取最大概率对应的类别索引,confidence低于 0.6 时基本可以认为模型在瞎猜,这时候要检查输入序列是否有效(比如 MediaPipe 根本没检测到人)。

3. 环境搭建与首次运行:从 python 安装到跑通 l0.mp4

3.1 依赖版本是最大的玄学

MediaPipe 和 TensorFlow 的版本兼容性堪称血泪经验。我见过太多人pip install mediapipe tensorflow之后直接报DLL load failed或者AttributeError: module 'mediapipe' has no attribute 'solutions'。原因是 MediaPipe 对 protobuf 和 numpy 的版本有硬性要求,而 TensorFlow 2.x 又对 numpy 有另一套要求,两者打架。

我一般会这样锁版本(Python 3.8~3.10 最稳,3.11 以上 MediaPipe 支持还不完善):

python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install --upgrade pip pip install mediapipe==0.10.9 pip install tensorflow==2.13.0 pip install opencv-python==4.8.1.78 pip install numpy==1.24.3

参数说明:mediapipe==0.10.9是写这篇文章时比较稳定的版本,tensorflow==2.13.0对应 Keras 2.13,能正常加载大多数.h5文件。numpy==1.24.3是这两个库的交集。如果你用pycharm配置python环境,记得在项目解释器里选刚才建的 venv,不要用系统 Python。

注意:项目路径不要带中文,解压后重命名为英文,比如action_recognition。MediaPipe 在 Windows 下对中文路径的处理有 bug,会直接读不到模型文件。

3.2 验证 MediaPipe 是否正常工作

装完之后别急着跑主程序,先单独测 MediaPipe:

import mediapipe as mp import cv2 mp_pose = mp.solutions.pose pose = mp_pose.Pose() img = cv2.imread('test.jpg') # 随便一张有人物的图 result = pose.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) if result.pose_landmarks: print('检测到', len(result.pose_landmarks.landmark), '个关键点') else: print('未检测到人体')

如果这里报错,后面全白搭。常见错误是ImportError: cannot import name 'solutions',说明 MediaPipe 装成了残缺版,卸载重装。另一个是cv2显示不了图,那是 OpenCV 的 GUI 后端问题,服务器上跑就加cv2.imshow换成保存文件。

3.3 跑通示例视频并观察输出

用l0.mp4做端到端测试,把每一帧的预测结果打印出来:

import cv2 import numpy as np from tensorflow.keras.models import load_model model = load_model('action.h5') cap = cv2.VideoCapture('l0.mp4') buffer = [] while cap.isOpened(): ret, frame = cap.read() if not ret: break rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) result = pose.process(rgb) if result.pose_landmarks: kp = [[lm.x, lm.y] for lm in result.pose_landmarks.landmark] buffer.append(np.array(kp).flatten()) if len(buffer) == 30: seq = np.expand_dims(np.array(buffer), axis=0) pred = model.predict(seq, verbose=0) print('类别:', np.argmax(pred), '置信度:', np.max(pred)) buffer = buffer[15:] # 滑动窗口,每次移一半 cap.release()

逻辑说明:这里用了滑动窗口而不是每 30 帧切一次,好处是预测更连续,不会出现「第 30 帧才有输出」的卡顿感。buffer = buffer[15:]表示每次保留后 15 帧,新来 15 帧凑成下一个 30 帧窗口,相当于 50% 重叠。

参数说明:重叠比例可以调,buffer[10:]是 66% 重叠,预测更平滑但计算量翻倍;buffer[29:]几乎不重叠,适合对实时性要求高的场景。我一般用 50%,平衡得比较好。

4. 避坑与排查:五个让项目跑不起来的真实原因

4.1 现象:加载 action.h5 报ValueError: Input 0 is incompatible

原因:训练时的time_steps或features和你推理时不一致。比如训练用了 30 帧 × 99 维(含 visibility),你推理只给了 66 维。

解决:先打印模型输入形状确认:

print(model.input_shape) # 例如 (None, 30, 99)

然后调整你的特征提取逻辑,把visibility加回去,或者用model.input_shape动态决定max_frames。

4.2 现象:MediaPipe 检测不到关键点,result.pose_landmarks一直是 None

原因:三种可能——画面里没人、光线太暗、或者min_detection_confidence设太高。

解决:先把min_detection_confidence降到 0.3,model_complexity升到 2,然后保存一帧中间结果看看画面质量。如果画面本身模糊,再调参数也没用,得换视频源。

4.3 现象:预测结果全是同一个类别,置信度还很高

原因:零填充过多。如果视频只有 10 帧有效数据,剩下 20 帧全是零,LSTM 会把「静止」这个模式学成某个动作。

解决:要么保证输入视频至少 30 帧,要么在填充时用最后一帧复制而不是零填充:

while len(frames) < max_frames: frames.append(frames[-1] if frames else np.zeros(66))

4.4 现象:TensorFlow 和 MediaPipe 同时导入时崩溃

原因:protobuf 版本冲突。MediaPipe 依赖protobuf<4,而新版 TensorFlow 可能装了protobuf>=4。

解决:强制降级:

pip install protobuf==3.20.3

如果还不行,就分开两个进程跑——MediaPipe 提点存成 npy 文件,TensorFlow 单独读文件推理。虽然麻烦,但能绕开依赖地狱。

4.5 现象:视频播放卡顿,帧率掉到个位数

原因:model_complexity=2加上每帧都做model.predict,CPU 扛不住。

解决:把model_complexity降到 0 或 1,预测改成滑动窗口(每 15 帧才推理一次),中间帧复用上一次结果。如果还卡,考虑用python生成exe可执行文件打包后放到性能更好的机器上跑。

5. 二次开发与验证:换动作、调窗口、看混淆矩阵

5.1 换一套动作类别要改哪里

action.h5的输出维度是固定的,比如 5 类动作。如果你想识别自己的动作(比如深蹲、开合跳),必须重新训练。步骤是:用 MediaPipe 把你的视频转成(N, 30, 66)的序列数据集,标签用 one-hot,然后搭一个同样的 LSTM 结构训练。

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dropout, Dense model = Sequential([ LSTM(64, return_sequences=True, input_shape=(30, 66)), LSTM(32), Dropout(0.3), Dense(5, activation='softmax') # 5 类动作 ]) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.fit(X_train, y_train, epochs=50, batch_size=16, validation_split=0.2) model.save('my_action.h5')

参数说明:两层 LSTM 比单层能捕捉更复杂的时序模式,但数据量少于 500 条时容易过拟合,Dropout(0.3)就是用来压过拟合的。epochs=50配合validation_split=0.2可以观察验证集准确率是否还在涨,不涨就停。

5.2 用混淆矩阵验证模型是不是在瞎猜

准确率高不代表模型学到了东西。如果 5 类动作里 4 类都是「站立」,模型全预测成站立也能有 80% 准确率。我习惯跑一遍混淆矩阵:

from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt y_pred = model.predict(X_test) y_pred_labels = np.argmax(y_pred, axis=1) y_true_labels = np.argmax(y_test, axis=1) cm = confusion_matrix(y_true_labels, y_pred_labels) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues') plt.xlabel('预测') plt.ylabel('真实') plt.savefig('confusion_matrix.png')

看对角线是否明显深于其他格子。如果某一行全黑,说明那个动作模型完全没学会,要么数据太少,要么那个动作和别的动作在关键点层面区分度不够。

5.3 滑动窗口的重叠率怎么选

前面提过buffer[15:]是 50% 重叠。我做过一组对比:重叠率 0% 时,预测结果跳变明显,同一个动作中间会闪出别的类别;重叠率 50% 时,跳变减少但仍有;重叠率 80% 时,输出非常平滑,但推理频率是 0% 的五倍,实时性差。

我的习惯是:离线分析用 80%,实时演示用 50%,嵌入式设备用 0% 加后处理投票(连续 3 次预测取众数)。从那以后我每次调窗口参数都强制跑一遍l0.mp4和r0.mp4,对比两者的预测曲线是否稳定,再决定要不要上线。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询