Download src/pozify/exercises/push_up/analyzer.py from build-small-hackathon/Pozify: direct link, hf CLI and curl.
- Browser
- Download file 4.83 kB
-
https://huggingface.co/spaces/build-small-hackathon/Pozify/resolve/main/src/pozify/exercises/push_up/analyzer.py
- Command line
-
hf download hf://spaces/build-small-hackathon/Pozify/src/pozify/exercises/push_up/analyzer.py
-
curl -L -o analyzer.py https://huggingface.co/spaces/build-small-hackathon/Pozify/resolve/main/src/pozify/exercises/push_up/analyzer.py
4.83 kB
| from __future__ import annotations | |
| from pozify.contracts import PoseFrame | |
| from pozify.exercises.shared.analyzer import ( | |
| ExerciseMetricResult, | |
| max_optional, | |
| mean_optional, | |
| mean_pair, | |
| min_optional, | |
| range_optional, | |
| round_optional, | |
| safe_ratio, | |
| score, | |
| side_delta, | |
| std_optional, | |
| value_series, | |
| width, | |
| ) | |
| from pozify.steps.rep_signals import average_axis, body_line_score | |
| class PushUpAnalyzer: | |
| def metrics(self, frames: list[PoseFrame]) -> ExerciseMetricResult: | |
| elbow_angles = value_series( | |
| frames, | |
| lambda frame: mean_pair( | |
| frame, | |
| ("left_shoulder", "left_elbow", "left_wrist"), | |
| ("right_shoulder", "right_elbow", "right_wrist"), | |
| ), | |
| ) | |
| elbow_deltas = value_series( | |
| frames, | |
| lambda frame: side_delta( | |
| frame, | |
| ("left_shoulder", "left_elbow", "left_wrist"), | |
| ("right_shoulder", "right_elbow", "right_wrist"), | |
| ), | |
| ) | |
| body_line = value_series(frames, body_line_score) | |
| shoulder_y = value_series( | |
| frames, | |
| lambda frame: average_axis(frame, ("left_shoulder", "right_shoulder"), "y"), | |
| ) | |
| hip_y = value_series(frames, lambda frame: average_axis(frame, ("left_hip", "right_hip"), "y")) | |
| ankle_y = value_series( | |
| frames, | |
| lambda frame: average_axis(frame, ("left_ankle", "right_ankle"), "y"), | |
| ) | |
| hand_width = value_series(frames, lambda frame: width(frame, "left_wrist", "right_wrist")) | |
| shoulder_width = value_series( | |
| frames, | |
| lambda frame: width(frame, "left_shoulder", "right_shoulder"), | |
| ) | |
| elbow_width = value_series(frames, lambda frame: width(frame, "left_elbow", "right_elbow")) | |
| knee_y = value_series( | |
| frames, | |
| lambda frame: average_axis(frame, ("left_knee", "right_knee"), "y"), | |
| ) | |
| min_elbow = min_optional(elbow_angles) | |
| max_elbow = max_optional(elbow_angles) | |
| elbow_rom = 0.0 if min_elbow is None or max_elbow is None else max_elbow - min_elbow | |
| chest_depth = range_optional(shoulder_y) or 0.0 | |
| hand_width_ratio = safe_ratio(mean_optional(hand_width), mean_optional(shoulder_width)) | |
| elbow_flare = safe_ratio(mean_optional(elbow_width), mean_optional(shoulder_width)) | |
| body_line_mean = mean_optional(body_line) | |
| hip_sag_score = None | |
| if body_line_mean is not None: | |
| hip_sag_score = max(0.0, 1.0 - body_line_mean) | |
| knee_support_score = self._knee_support_score(hip_y, knee_y, ankle_y) | |
| rom_score = score((elbow_rom / 80.0) * 0.65 + (chest_depth / 0.16) * 0.35) | |
| hip_stability = 1.0 - min(1.0, (std_optional(hip_y) or 0.0) * 4.0) | |
| stability_score = score(((body_line_mean or 0.5) * 0.75) + hip_stability * 0.25) | |
| symmetry_score = score(1.0 - ((mean_optional(elbow_deltas) or 0.0) / 45.0)) | |
| metrics = { | |
| "min_elbow_angle_deg": round_optional(min_elbow), | |
| "max_elbow_angle_deg": round_optional(max_elbow), | |
| "body_line_score": round_optional(body_line_mean), | |
| "hip_sag_score": round_optional(hip_sag_score), | |
| "hip_pike_score": round_optional(max(0.0, ((body_line_mean or 1.0) - 1.0) * -1.0), 4), | |
| "chest_depth_proxy": round_optional(chest_depth, 4), | |
| "hand_width_ratio": round_optional(hand_width_ratio, 3), | |
| "elbow_flare_ratio": round_optional(elbow_flare, 3), | |
| "lockout_quality": score(((max_elbow or 120.0) - 120.0) / 55.0), | |
| "knee_support_score": knee_support_score, | |
| } | |
| hints = [] | |
| if hand_width_ratio is not None and hand_width_ratio > 1.45: | |
| hints.append("wide_grip_push_up") | |
| elif hand_width_ratio is not None and hand_width_ratio < 0.95: | |
| hints.append("close_grip_push_up") | |
| if knee_support_score >= 0.8: | |
| hints.append("knee_push_up") | |
| return metrics, rom_score, stability_score, symmetry_score, hints | |
| def _knee_support_score( | |
| self, | |
| hip_y: list[float | None], | |
| knee_y: list[float | None], | |
| ankle_y: list[float | None], | |
| ) -> float: | |
| scores: list[float] = [] | |
| for hip, knee, ankle in zip(hip_y, knee_y, ankle_y, strict=False): | |
| if hip is None or knee is None or ankle is None: | |
| continue | |
| hip_to_ankle = abs(ankle - hip) | |
| if hip_to_ankle <= 1e-6: | |
| continue | |
| knee_to_hip = abs(knee - hip) | |
| bent_leg_score = score(1.0 - knee_to_hip / max(hip_to_ankle * 0.55, 0.01)) | |
| scores.append(bent_leg_score) | |
| return score(mean_optional(scores) or 0.0) | |