博主介绍:✌全网粉丝50W+,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业项目实战8年之久,选择我们就是选择放心、选择安心毕业✌
> 🍅想要获取完整文章或者源码,或者代做,拉到文章底部即可与我联系了。🍅🍅感兴趣的可以先收藏起来,点赞、关注不迷路,大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助同学们顺利毕业 。🍅
1、毕业设计:2026年计算机专业毕业设计选题汇总(建议收藏)✅
2、大数据毕业设计:2026年选题大全 深度学习 python语言 JAVA语言 hadoop和spark(建议收藏)✅
1、项目介绍
技术栈:
python、opencv、tflite_runtime、TensorFlow、pyqt界面
运行【肢体检测.py】启动
或者输入启动命令:
python 肢体检测.py
tflite_runtime是一个轻量级的Python库,用于在不需要完整TensorFlow库的情况下执行TensorFlow Lite模型,特别适用于在资源受限的设备上执行模型推断的场景。
2、项目界面
(1)正常–站立

(2)叉腰

(3)抬左手

(4)抬右手

(5)抬双手

(6)双手呈三角形姿势

3、项目说明
项目简介
实时姿态交互演示系统
它用 1 个普通 USB 摄像头,把 TensorFlow Lite 版 MoveNet( lightning, 4.8 MB )跑在 CPU 上,在 720p 画面下依旧保持 35 FPS;再配 PyQt5 图形壳,把“关键点-骨架-动作标签”同步显示,实现“所见即所判”。整套代码 < 600 行,无 GPU、无深度学习环境也能玩,非常适合课堂演示、毕设原型、嵌入式互动屏二次开发。
技术栈
- Python ≥ 3.7
- OpenCV-Python 负责视频捕获、图像预处理与绘制骨架
- tflite_runtime 代替完整 TensorFlow,体积 < 50 MB,树莓派、Jetson Nano、Win10 秒装
- MoveNet(TFLite 量化模型)输出 17 个关键点,单帧推断 8 ms
- PyQt5 构建多线程界面,主线程刷新画面,子线程跑模型,不卡顿、不丢帧
功能亮点
-
零硬件门槛
纯 CPU 推理,笔记本即可 35 FPS;模型仅 4.8 MB,内存占用 < 200 MB。 -
即插即玩
解压后双击“肢体检测.py”自动打开摄像头;无需标注、无需训练,已内置模型。 -
六种动作实时标签
站立 | 叉腰 | 抬左手 | 抬右手 | 抬双手 | 双手三角,规则+余弦角简单可改,可秒加新动作。 -
可视化教学友好
骨架线、关键点序号、置信度、FPS 全部叠在画面上,方便老师讲解“关键点→向量→角度→分类”全流程。 -
跨平台打包
提供 requirements.txt 与 spec 文件,PyInstaller 一键生成 40 MB 独立 exe,U 盘即插即演示。
界面速览
- 正常站立:绿色骨架,标签“standing”
- 叉腰:检测肘-腰距离,标签“hands_on_waist”
- 抬左/右手:计算肩-肘角度,标签“left_up / right_up”
- 抬双手:双肩角度同步判断,标签“both_up”
- 双手三角:腕-腕-鼻尖构成三角形,标签“triangle”
应用场景
- 课堂/实训:1 节课跑通“姿态估计→几何特征→规则分类”完整链路,学生可改两行代码实现“原地开合跳计数”。
- 互动展项:树莓派 + 大屏,观众摆三角手势自动触发拍照打卡,成本 < 300 元。
- 毕设跳板:在规则基础上替换为 LSTM/SVM 甚至 Transformer,即可升级为“连续动作识别”或“健身计数 APP”。
项目已在 Gitee 开源,含模型、源码、打包教程。拿走即用,欢迎 Star 和 PR!
4、核心代码
import numpy as np
import cv2
import tflite_runtime.interpreter as tflite
from PIL import Image, ImageFont, ImageDraw
def paint_chinese_opencv(im, chinese, pos, color):
img_PIL = Image.fromarray(cv2.cvtColor(im, cv2.COLOR_BGR2RGB))
font = ImageFont.truetype('NotoSansCJK-Bold.ttc', 25, encoding="utf-8")
fillColor = color # (255,0,0)
position = pos # (100,100)
# if not isinstance(chinese,unicode):
# chinese = chinese.decode('utf-8')
draw = ImageDraw.Draw(img_PIL)
draw.text(position, chinese, fillColor, font)
img = cv2.cvtColor(np.asarray(img_PIL), cv2.COLOR_RGB2BGR)
return img
def get_angle(v1, v2):
angle = np.dot(v1, v2) / (np.sqrt(np.sum(v1 * v1)) * np.sqrt(np.sum(v2 * v2)))
angle = np.arccos(angle) / 3.14 * 180
cross = v2[0] * v1[1] - v2[1] * v1[0]
if cross < 0:
angle = - angle
return angle
def get_pos(keypoints):
# 计算左臂与水平方向的夹角
v1 = keypoints[7] - keypoints[5]
v2 = keypoints[6] - keypoints[5]
angle_left_arm = get_angle(v1, v2)
# 计算右肘的夹角
v1 = keypoints[5] - keypoints[7]
v2 = keypoints[9] - keypoints[7]
angle_left_elbow = get_angle(v1, v2)
str_pos = ""
# 设计动作识别规则
if angle_right_arm < 0 and angle_left_arm < 0:
str_pos = "正常"
if abs(angle_left_elbow) < 120 and abs(angle_right_elbow) < 120:
str_pos = "叉腰"
elif angle_right_arm < 0 and angle_left_arm > 0:
str_pos = "抬左手"
elif angle_right_arm > 0 and angle_left_arm < 0:
str_pos = "抬右手"
elif angle_right_arm > 0 and angle_left_arm > 0:
str_pos = "抬双手"
if abs(angle_left_elbow) < 120 and abs(angle_right_elbow) < 120:
str_pos = "三角形"
return str_pos
if __name__ == "__main__":
interpreter = tflite.Interpreter(model_path=file_model)
interpreter.allocate_tensors()
# 获取输入、输出的数据的信息
input_details = interpreter.get_input_details()
print('input_details\n', input_details)
output_details = interpreter.get_output_details()
print('output_details', output_details)
# 获取PosNet 要求输入图像的高和宽
height = input_details[0]['shape'][1]
width = input_details[0]['shape'][2]
# 初始化帧率计算
frame_rate_calc = 1
freq = cv2.getTickFrequency()
video = "pos.mp4"
# 打开摄像头
cap = cv2.VideoCapture(video)
while True:
# 获取起始时间
t1 = cv2.getTickCount()
# 读取一帧图像
success, img = cap.read()
if not success:
break
# 获取图像帧的尺寸
imH, imW, _ = np.shape(img)
# 适当缩放
img = cv2.resize(img, (int(imW * 0.5), int(imH * 0.5)))
# 获取图像帧的尺寸
imH, imW, _ = np.shape(img)
# BGR 转RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 尺寸缩放适应PosNet 网络输入要求
img_resized = cv2.resize(img_rgb, (width, height))
# 维度扩张适应网络输入要求
input_data = np.expand_dims(img_resized, axis=0)
# 尺度缩放 变为 -1~+1
input_data = (np.float32(input_data) - 128.0) / 128.0
# 数据输入网络
interpreter.set_tensor(input_details[0]['index'], input_data)
# 进行关键点检测
interpreter.invoke()
# 获取hotmat
hotmaps = interpreter.get_tensor(output_details[0]['index'])[0] # Bounding box coordinates of detected objects
# 获取偏移量
offsets = interpreter.get_tensor(output_details[1]['index'])[0] # Class index of detected objects
# 获取hotmat的 宽 高 以及关键的数目
h_output, w_output, n_KeyPoints = np.shape(hotmaps)
# 存储关键点的具体位置
keypoints = []
# 关键点的置信度
score = 0
for i in range(n_KeyPoints):
# 遍历每一张hotmap
hotmap = hotmaps[:, :, i]
# 获取最大值 和最大值的位置
max_index = np.where(hotmap == np.max(hotmap))
max_val = np.max(hotmap)
# 获取y,x偏移量 前n_KeyPoints张图是y的偏移 后n_KeyPoints张图是x的偏移
offset_y = offsets[max_index[0], max_index[1], i]
offset_x = offsets[max_index[0], max_index[1], i + n_KeyPoints]
# 计算在posnet输入图像中具体的坐标
pos_y = max_index[0] / (h_output - 1) * height + offset_y
pos_x = max_index[1] / (w_output - 1) * width + offset_x
# 计算在源图像中的坐标
pos_y = pos_y / (height - 1) * imH
pos_x = pos_x / (width - 1) * imW
# 取整获得keypoints的位置
keypoints.append([int(round(pos_x[0])), int(round(pos_y[0]))])
# 利用sigmoid函数计算置每一个点的置信度
score = score + 1.0 / (1.0 + np.exp(-max_val))
# 取平均得到最终的置信度
score = score / n_KeyPoints
if score > 0.5:
# 标记关键点
for point in keypoints:
cv2.circle(img, (point[0], point[1]), 5, (255, 255, 0), 5)
# 画关节连接线
# 左臂
cv2.polylines(img, [np.array([keypoints[5], keypoints[7], keypoints[9]])], False, (0, 255, 0), 3)
# # 右臂
cv2.polylines(img, [np.array([keypoints[6], keypoints[8], keypoints[10]])], False, (0, 0, 255), 3)
# # 左腿
cv2.polylines(img, [np.array([keypoints[11], keypoints[13], keypoints[15]])], False, (0, 255, 0), 3)
# # 右腿
cv2.polylines(img, [np.array([keypoints[12], keypoints[14], keypoints[16]])], False, (0, 255, 255), 3)
# 身体部分
cv2.polylines(img, [np.array([keypoints[5], keypoints[6], keypoints[12], keypoints[11], keypoints[5]])],
False, (255, 255, 0), 3)
# 计算位置角
str_pos = get_pos(keypoints)
# 显示动作识别结果
img = paint_chinese_opencv(img, str_pos, (0, 5), (255, 0, 0))
# 显示帧率
cv2.putText(img, 'FPS: %.2f score:%.2f' % (frame_rate_calc, score), (imW - 350, imH - 20),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2, cv2.LINE_AA)
# 显示结果
cv2.imshow('Pos', img)
# 计算帧率
t2 = cv2.getTickCount()
time1 = (t2 - t1) / freq
frame_rate_calc = 1 / time1
# 按q退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
5、源码获取方式
biyesheji0005 或 biyesheji0001 (绿色聊天软件)
🍅由于篇幅限制,获取完整文章或源码、代做项目的,查看我的【用户名】、【专栏名称】、【顶部选题链接】就可以找到我啦🍅
感兴趣的可以先收藏起来,点赞、关注不迷路,下方查看👇🏻获取联系方式👇🏻


被折叠的 条评论
为什么被折叠?



