AI-Coach / push_up /engine.py
anhlehong
feat:frontend-using-reflex
0f26f4e
import cv2
import mediapipe as mp
import numpy as np
class PoseEngine:
def __init__(self):
self.mp_pose = mp.solutions.pose
# Detector dành cho Tracking (quét video nhanh)
self.pose = self.mp_pose.Pose(static_image_mode=False, model_complexity=1,
min_detection_confidence=0.5, min_tracking_confidence=0.5)
# Detector dành cho Static (vẽ frame lỗi dính chặt)
self.pose_static = self.mp_pose.Pose(static_image_mode=True, model_complexity=1,
min_detection_confidence=0.5)
def extract_kinematics(self, frame, is_static=False):
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
detector = self.pose_static if is_static else self.pose
results = detector.process(rgb_frame)
if not results.pose_landmarks:
return None, None
lms = np.array([[lm.x, lm.y, lm.z]
for lm in results.pose_landmarks.landmark])
def get_angle_3d(p1, p2, p3):
v1, v2 = p1 - p2, p3 - p2
return np.degrees(np.arccos(np.clip(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)), -1.0, 1.0)))
# Tính toán riêng trái phải để đo độ lệch (Symmetry)
left_elbow = get_angle_3d(lms[11], lms[13], lms[15])
right_elbow = get_angle_3d(lms[12], lms[14], lms[16])
elbow_angle = (left_elbow + right_elbow) / 2
left_right_symmetry = abs(left_elbow - right_elbow)
# Đặc trưng 2: Độ sâu (Chiều dọc vai so với hông)
shoulder_y = (lms[11][1] + lms[12][1]) / 2
hip_y = (lms[23][1] + lms[24][1]) / 2
depth_sig = shoulder_y - hip_y
# Góc hông trung bình (để kiểm tra lưng thẳng)
hip_angle = (get_angle_3d(lms[11], lms[23], lms[25]) + get_angle_3d(lms[12], lms[24], lms[26])) / 2
# Góc vai trung bình (Khuỷu tay - Vai - Hông)
shoulder_angle = (get_angle_3d(lms[13], lms[11], lms[23]) + get_angle_3d(lms[14], lms[12], lms[24])) / 2
# Góc đường cơ thể (Vai - Hông - Mắt cá chân). Dùng max thay vì avg để khắc phục chân bị che khuất
body_line_angle = max(get_angle_3d(lms[11], lms[23], lms[27]), get_angle_3d(lms[12], lms[24], lms[28]))
# Góc đầu (Mắt - Vai - Hông)
head_angle = (get_angle_3d(lms[2], lms[11], lms[23]) + get_angle_3d(lms[5], lms[12], lms[24])) / 2
# Tính thêm Nose angle để debug
nose_angle = (get_angle_3d(lms[0], lms[11], lms[23]) + get_angle_3d(lms[0], lms[12], lms[24])) / 2
# Khoảng cách dọc Vai - Gót chân (Dùng cho lọc trạng thái đứng/plank)
heel_y = (lms[29][1] + lms[30][1]) / 2
shoulder_heel_y_dist = abs(shoulder_y - heel_y)
# Tính khoảng cách dọc giữa Tai và Vai (Âm = Đầu thấp hơn vai)
# Hệ trục toạ độ y hướng xuống dưới, y lớn = thấp hơn
ear_y = (lms[7][1] + lms[8][1]) / 2
ear_shoulder_y_diff = shoulder_y - ear_y # Dương = Đầu cao hơn vai, Âm = Đầu thấp hơn/Cúi gập
hip_center = (lms[23] + lms[24]) / 2
spine_dist = np.linalg.norm((lms[11]+lms[12])/2 - hip_center)
# head_drop_norm: Khoảng cách đầu rơi xuống (Dương = đầu thấp hơn vai) chuẩn hoá theo lưng
head_drop_norm = (ear_y - shoulder_y) / (spine_dist if spine_dist > 0 else 1)
# Chuẩn hóa Pose Embedding (Xoay và Scale về gốc Hông)
norm_lms = (lms - hip_center) / (spine_dist if spine_dist > 0 else 1)
return {
"pose_embedding": norm_lms,
"elbow_angle": elbow_angle,
"shoulder_angle": shoulder_angle,
"hip_angle": hip_angle,
"body_line_angle": body_line_angle,
"head_angle": head_angle,
"nose_angle": nose_angle,
"ear_shoulder_y_diff": ear_shoulder_y_diff,
"head_drop_norm": head_drop_norm,
"left_right_symmetry": left_right_symmetry,
"depth_sig": depth_sig,
"shoulder_heel_y_dist": shoulder_heel_y_dist,
"sig": [elbow_angle, depth_sig * 100] # Signature đa đặc trưng
}, results.pose_landmarks