环境配置
首先,确保你已安装所需的包:
python
pip install mediapipe opencv-python numpy pygame
代码实现:
python
import cv2
import mediapipe as mp
import time
import pygame
from PIL import ImageFont, ImageDraw, Image
import numpy as np
class PostureMonitor:
def __init__(self, camera_index=0, alert_sound="alert.MP3", alert_delay=2):
# MediaPipe初始化
self.mp_pose = mp.solutions.pose
self.pose = self.mp_pose.Pose(min_detection_confidence=0.5,
min_tracking_confidence=0.5)
self.mp_drawing = mp.solutions.drawing_utils
# 摄像头
self.cap = cv2.VideoCapture(camera_index)
# 提醒设置
pygame.mixer.init()
self.alert_sound = pygame.mixer.Sound(alert_sound)
self.alert_delay = alert_delay # 坐姿错误持续多久后提醒
# 状态
self.bad_posture_start = None
self.alert_played = False
# 中文字体(Windows 下可用微软雅黑 msyh.ttc,Linux/Mac 请改成系统有的字体)
self.font_path = "C:/Windows/Fonts/msyh.ttc"
self.font = ImageFont.truetype(self.font_path, 32, encoding="utf-8")
def check_posture(self, landmarks):
"""
根据关键点判断坐姿是否异常
返回: list of 异常信息
"""
issues = []
# 取出关键点
nose = landmarks[self.mp_pose.PoseLandmark.NOSE.value]
left_shoulder = landmarks[self.mp_pose.PoseLandmark.LEFT_SHOULDER.value]
right_shoulder = landmarks[self.mp_pose.PoseLandmark.RIGHT_SHOULDER.value]
left_hip = landmarks[self.mp_pose.PoseLandmark.LEFT_HIP.value]
right_hip = landmarks[self.mp_pose.PoseLandmark.RIGHT_HIP.value]
# 1. 左右肩高度差
shoulder_diff = abs(left_shoulder.y - right_shoulder.y)
if shoulder_diff > 0.05:
issues.append("身体左右倾斜")
# 2. 头部前倾(z值大表示更靠近摄像头)
if nose.z < -1.1:
issues.append("头部前倾/驼背")
# 3. 身体前倾(鼻子y比臀部低很多)
hip_y = (left_hip.y + right_hip.y) / 2
if (nose.y - hip_y) < -1.3:
issues.append("身体整体前倾")
return issues
def play_alert(self):
"""播放提醒音"""
if not self.alert_played:
self.alert_sound.play()
self.alert_played = True
def reset_alert(self):
"""重置提醒状态"""
self.bad_posture_start = None
self.alert_played = False
def add_chinese_text(self, img, text, position, color=(255, 0, 0)):
"""在OpenCV图像上绘制中文"""
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
draw.text(position, text, font=self.font, fill=color)
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
def run(self):
"""主循环"""
while self.cap.isOpened():
ret, frame = self.cap.read()
if not ret:
break
# 转换为RGB
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = self.pose.process(image_rgb)
# 可视化骨架
if results.pose_landmarks:
self.mp_drawing.draw_landmarks(
frame, results.pose_landmarks, self.mp_pose.POSE_CONNECTIONS)
# 检测坐姿
issues = self.check_posture(results.pose_landmarks.landmark)
if issues:
print(issues)
if self.bad_posture_start is None:
self.bad_posture_start = time.time()
elif time.time() - self.bad_posture_start > self.alert_delay:
frame = self.add_chinese_text(frame, f"警告: {', '.join(issues)}", (30, 50))
self.play_alert()
else:
self.reset_alert()
cv2.imshow("Posture Monitor", frame)
if cv2.waitKey(10) & 0xFF == ord('q'):
break
self.cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
monitor = PostureMonitor(camera_index=0, alert_sound="alert.MP3", alert_delay=2)
monitor.run()
运行效果
