通过 MediaPipe 检测人体姿态,计算俯卧撑角度和计数,并在图像上进行可视化展示
需要有cv2库和mediapipe库
mediapipe库:
MediaPipe是Google开源的机器学习框架,用于构建实时音频、视频和多媒体处理应用程序。它提供了一组预训练的模型和工具,帮助开发人员快速构建和部署计算机视觉和音频处理应用。MediaPipe库的特点包括实时性能、多平台兼容性、灵活性和易用性。
MediaPipe库的主要功能包括:
1. 视频和图像处理:提供了各种预训练的模型和工具,用于视频流分析和处理,例如人脸检测、姿态估计、手势识别等。
2. 音频处理:提供了模型和工具,用于音频流的实时处理,例如语音识别、语音分割、声音增强等。
3. 数据流图:使用数据流图构建和组合多个模块,以实现复杂的音频、视频和多媒体处理应用程序。
4. 跨平台支持:支持多种平台,包括Android、iOS、Linux、Windows等。
5. 开发者工具:提供了一些工具,用于开发和调试MediaPipe应用程序,例如模型训练和调优、性能分析等。
总之,MediaPipe库是一个功能强大的机器学习框架,用于实时音频、视频和多媒体处理应用程序的开发和部署。它提供了丰富的预训练模型和工具,使开发人员能够快速搭建高性能的应用程序。
目录
定义计算角度的函数:
初始化MediaPipe Pose实例:
打开视频:
读取视频帧:
计算个数:
绘制图像:
完整代码:
定义计算角度的函数:
def calculate_angle(a, b, c):
# 将输入点转换为numpy数组
a = np.array(a)
b = np.array(b)
c = np.array(c)
# 计算两个向量的角度差(弧度)
radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
# 转换为角度(度)
angle = np.abs(radians * 180.0 / np.pi)
# 确保角度在0到360度之间
if angle > 180.0:
angle = 360 - angle
return angle
初始化MediaPipe Pose实例:
# 初始化MediaPipe Pose实例
pose = mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5)
'''
min_detection_confidence:表示检测到人体姿态的最小置信度阈值。当检测到的姿态置信度低于该阈值时,可能会被视为无效检测。
min_tracking_confidence:表示跟踪人体姿态的最小置信度阈值。在跟踪过程中,如果姿态的置信度低于该阈值,可能会重新进行检测。
'''
打开视频:
cap = cv2.VideoCapture("D:\\桌面\\1.mp4")
读取视频帧:
ret, frame = cap.read()
计算个数:
# 尝试获取姿态关键点
try:
landmarks = results.pose_landmarks.landmark
# 获取左肩、左肘和左腕的坐标
shoulder = [landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x,
landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y]
elbow = [landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x, landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y]
wrist = [landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x, landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y]
# 计算并显示肘部到肩部的角度
angle = calculate_angle(shoulder, elbow, wrist)
cv2.putText(image, str(angle),
tuple(np.multiply(elbow, [640, 480]).astype(int)),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
print(angle)
# 根据角度更新俯卧撑计数
if angle > max_angle:
stage = "down"
if angle < min_angle and stage == 'down':
stage = "up"
counter += 1
print(counter)
except:
pass
绘制图像:
# 在图像上绘制矩形框,用于显示计数和阶段
cv2.rectangle(image, (0, 0), (225, 73), (245, 117, 16), -1)
# 在矩形框内显示计数和阶段
cv2.putText(image, 'COUNTER', (15, 22),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(image, str(counter),
(35, 60),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.putText(image, 'STAGE', (135, 22),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(image, stage,
(130, 60),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
# 在图像上绘制关键点和连接线
mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS,
mp_drawing.DrawingSpec(color=(245, 117, 66), thickness=2, circle_radius=2),
mp_drawing.DrawingSpec(color=(245, 66, 230), thickness=2, circle_radius=2)
)
# 显示处理后的图像
cv2.imshow('Mediapipe Feed', image)
# 检查是否按下'q'键退出
if cv2.waitKey(10) & 0xFF == ord('q'):
break
完整代码:
#coding:utf-8
# 导入必要的库
import cv2
import mediapipe as mp #通过 MediaPipe 检测人体姿态,计算俯卧撑角度和计数,并在图像上进行可视化展示
import numpy as np
import logging
# 配置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 记录日志信息
logging.info('这是一条信息日志')
logging.warning('这是一条警告日志')
logging.error('这是一条错误日志')
# 定义计算角度的函数
"""
计算三个点之间的角度
参数:
a (list):第一个点的坐标
b (list):第二个点的坐标
c (list):第三个点的坐标
返回:
float:三个点之间的角度(度)
"""
def calculate_angle(a, b, c):
# 将输入点转换为numpy数组
a = np.array(a)
b = np.array(b)
c = np.array(c)
# 计算两个向量的角度差(弧度)
radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
# 转换为角度(度)
angle = np.abs(radians * 180.0 / np.pi)
# 确保角度在0到360度之间
if angle > 180.0:
angle = 360 - angle
return angle
# 导入MediaPipe的绘图工具和Pose解决方案
mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose
# 初始化MediaPipe Pose实例
pose = mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5)
'''
min_detection_confidence:表示检测到人体姿态的最小置信度阈值。当检测到的姿态置信度低于该阈值时,可能会被视为无效检测。
min_tracking_confidence:表示跟踪人体姿态的最小置信度阈值。在跟踪过程中,如果姿态的置信度低于该阈值,可能会重新进行检测。
'''
# 打开视频
cap = cv2.VideoCapture("D:\\桌面\\1.mp4")
# 计数器变量
counter = 0
# 当前动作阶段
stage = None
# 完成俯卧撑的最大角度
max_angle = 160
# 准备开始俯卧撑的最小角度
min_angle = 60
while cap.isOpened():
# 读取视频帧
ret, frame = cap.read()
# BGR图像转为RGB,便于MediaPipe处理
image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image.flags.writeable = False
# 使用MediaPipe进行姿态检测
results = pose.process(image)
# 重新转为BGR
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
# 尝试获取姿态关键点
try:
landmarks = results.pose_landmarks.landmark
# 获取左肩、左肘和左腕的坐标
shoulder = [landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x,
landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y]
elbow = [landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x, landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y]
wrist = [landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x, landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y]
# 计算并显示肘部到肩部的角度
angle = calculate_angle(shoulder, elbow, wrist)
cv2.putText(image, str(angle),
tuple(np.multiply(elbow, [640, 480]).astype(int)),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
print(angle)
# 根据角度更新俯卧撑计数
if angle > max_angle:
stage = "down"
if angle < min_angle and stage == 'down':
stage = "up"
counter += 1
print(counter)
except:
pass
# 在图像上绘制矩形框,用于显示计数和阶段
cv2.rectangle(image, (0, 0), (225, 73), (245, 117, 16), -1)
# 在矩形框内显示计数和阶段
cv2.putText(image, 'COUNTER', (15, 22),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(image, str(counter),
(35, 60),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.putText(image, 'STAGE', (135, 22),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(image, stage,
(130, 60),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
# 在图像上绘制关键点和连接线
mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS,
mp_drawing.DrawingSpec(color=(245, 117, 66), thickness=2, circle_radius=2),
mp_drawing.DrawingSpec(color=(245, 66, 230), thickness=2, circle_radius=2)
)
# 显示处理后的图像
cv2.imshow('Mediapipe Feed', image)
# 检查是否按下'q'键退出
if cv2.waitKey(10) & 0xFF == ord('q'):
break
# 释放视频资源,关闭所有OpenCV窗口
cap.release()
cv2.destroyAllWindows()