Refactor into modular app structure
Split monolithic files into focused modules: - app/core: settings, logging, lifecycle - app/signaling: websocket server, ICE parser, message models - app/webrtc: peer session, video receiver, frame source - app/vision: pose landmarker wrapper, model config, pose types - app/exercises/dead_bug: detector, metrics, rules, state machine, types - app/rendering: skeleton renderer, status overlay, window display - app/audio: rep announcer - app/diagnostics: perf timer, crash handler - configs: environment-based settings - tests: unit tests for rules, state machine, ICE parser - run.py: entry point
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from app.exercises.dead_bug.types import Point
|
||||
|
||||
|
||||
def angle(a: Point, b: Point, c: Point) -> float:
|
||||
ba = np.array([a.x - b.x, a.y - b.y], dtype=np.float32)
|
||||
bc = np.array([c.x - b.x, c.y - b.y], dtype=np.float32)
|
||||
denom = float(np.linalg.norm(ba) * np.linalg.norm(bc))
|
||||
if denom == 0:
|
||||
return 0.0
|
||||
cos_value = float(np.dot(ba, bc) / denom)
|
||||
return float(np.degrees(np.arccos(np.clip(cos_value, -1.0, 1.0))))
|
||||
|
||||
|
||||
def distance(a: Point, b: Point) -> float:
|
||||
return float(np.hypot(a.x - b.x, a.y - b.y))
|
||||
|
||||
|
||||
def calculate_metrics(
|
||||
lm: list[Point],
|
||||
*,
|
||||
left_shoulder: int,
|
||||
right_shoulder: int,
|
||||
left_elbow: int,
|
||||
right_elbow: int,
|
||||
left_wrist: int,
|
||||
right_wrist: int,
|
||||
left_hip: int,
|
||||
right_hip: int,
|
||||
left_knee: int,
|
||||
right_knee: int,
|
||||
left_ankle: int,
|
||||
right_ankle: int,
|
||||
visibility_threshold: float = 0.45,
|
||||
) -> dict:
|
||||
left_elbow_angle = angle(lm[left_shoulder], lm[left_elbow], lm[left_wrist])
|
||||
right_elbow_angle = angle(lm[right_shoulder], lm[right_elbow], lm[right_wrist])
|
||||
left_knee_angle = angle(lm[left_hip], lm[left_knee], lm[left_ankle])
|
||||
right_knee_angle = angle(lm[right_hip], lm[right_knee], lm[right_ankle])
|
||||
|
||||
shoulder_width = distance(lm[left_shoulder], lm[right_shoulder])
|
||||
hip_width = distance(lm[left_hip], lm[right_hip])
|
||||
scale = max(shoulder_width, hip_width, 0.08)
|
||||
|
||||
left_arm_extended = (
|
||||
left_elbow_angle >= 145
|
||||
and distance(lm[left_shoulder], lm[left_wrist]) >= scale * 1.15
|
||||
and lm[left_wrist].y <= lm[left_shoulder].y + scale * 0.35
|
||||
)
|
||||
right_arm_extended = (
|
||||
right_elbow_angle >= 145
|
||||
and distance(lm[right_shoulder], lm[right_wrist]) >= scale * 1.15
|
||||
and lm[right_wrist].y <= lm[right_shoulder].y + scale * 0.35
|
||||
)
|
||||
|
||||
left_leg_extended = (
|
||||
left_knee_angle >= 150
|
||||
and distance(lm[left_hip], lm[left_ankle]) >= scale * 1.55
|
||||
and lm[left_ankle].y >= lm[left_knee].y - scale * 0.2
|
||||
)
|
||||
right_leg_extended = (
|
||||
right_knee_angle >= 150
|
||||
and distance(lm[right_hip], lm[right_ankle]) >= scale * 1.55
|
||||
and lm[right_ankle].y >= lm[right_knee].y - scale * 0.2
|
||||
)
|
||||
|
||||
feedback: list[str] = []
|
||||
if left_arm_extended and left_elbow_angle < 160:
|
||||
feedback.append("Straighten left arm")
|
||||
if right_arm_extended and right_elbow_angle < 160:
|
||||
feedback.append("Straighten right arm")
|
||||
if left_leg_extended and left_knee_angle < 165:
|
||||
feedback.append("Straighten left leg")
|
||||
if right_leg_extended and right_knee_angle < 165:
|
||||
feedback.append("Straighten right leg")
|
||||
|
||||
return {
|
||||
"left_arm_extended": left_arm_extended,
|
||||
"right_arm_extended": right_arm_extended,
|
||||
"left_leg_extended": left_leg_extended,
|
||||
"right_leg_extended": right_leg_extended,
|
||||
"left_elbow_angle": left_elbow_angle,
|
||||
"right_elbow_angle": right_elbow_angle,
|
||||
"left_knee_angle": left_knee_angle,
|
||||
"right_knee_angle": right_knee_angle,
|
||||
"scale": scale,
|
||||
"feedback": feedback,
|
||||
}
|
||||
Reference in New Issue
Block a user