Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,7 @@ import json
|
|
| 3 |
import math
|
| 4 |
import tempfile
|
| 5 |
from pathlib import Path
|
| 6 |
-
from typing import Dict,
|
| 7 |
|
| 8 |
import cv2
|
| 9 |
import numpy as np
|
|
@@ -11,7 +11,7 @@ import mediapipe as mp
|
|
| 11 |
import gradio as gr
|
| 12 |
|
| 13 |
# ------------------------------
|
| 14 |
-
# Configuration
|
| 15 |
# ------------------------------
|
| 16 |
REFERENCE_POSES_FILE = "reference_poses.json"
|
| 17 |
DEFAULT_TOLERANCE = 15.0
|
|
@@ -112,20 +112,6 @@ def compare_angles(detected, reference, tolerance=DEFAULT_TOLERANCE):
|
|
| 112 |
return overall, per_joint_score, per_joint_diff
|
| 113 |
|
| 114 |
|
| 115 |
-
def suggest_corrections(per_joint_diff, tol=DEFAULT_TOLERANCE):
|
| 116 |
-
tips = []
|
| 117 |
-
for joint, diff in per_joint_diff.items():
|
| 118 |
-
if diff is None:
|
| 119 |
-
tips.append(f"{joint}: not detected clearly.")
|
| 120 |
-
continue
|
| 121 |
-
if abs(diff) <= tol:
|
| 122 |
-
tips.append(f"{joint}: good (within Β±{tol}Β°).")
|
| 123 |
-
elif diff > 0:
|
| 124 |
-
tips.append(f"{joint}: decrease angle by {abs(diff):.0f}Β° (bend more).")
|
| 125 |
-
else:
|
| 126 |
-
tips.append(f"{joint}: increase angle by {abs(diff):.0f}Β° (straighten more).")
|
| 127 |
-
return tips
|
| 128 |
-
|
| 129 |
# ------------------------------
|
| 130 |
# Video processing
|
| 131 |
# ------------------------------
|
|
@@ -195,9 +181,14 @@ def process_video(input_path: str, pose_name: str, tolerance: float = DEFAULT_TO
|
|
| 195 |
pose.close()
|
| 196 |
|
| 197 |
avg_score = float(np.mean(aggregate_scores)) if aggregate_scores else 0.0
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
-
return out_path, {"pose": pose_name, "score_percent": avg_score, "suggestions": suggestions}
|
| 201 |
|
| 202 |
# ------------------------------
|
| 203 |
# Gradio Interface
|
|
@@ -206,42 +197,31 @@ ref_poses = load_reference_poses()
|
|
| 206 |
pose_list = list(ref_poses.keys())
|
| 207 |
|
| 208 |
with gr.Blocks(title="Yoga Pose Correctness Checker") as demo:
|
| 209 |
-
gr.Markdown(
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
with gr.Tab("Upload Video"):
|
| 220 |
-
video_in = gr.Video(label="Upload a video file (MP4/MOV)")
|
| 221 |
-
|
| 222 |
-
with gr.Tab("Record with Webcam"):
|
| 223 |
-
webcam_in = gr.Camera(label="Record using webcam")
|
| 224 |
-
|
| 225 |
pose_dropdown = gr.Dropdown(choices=pose_list, value=pose_list[0], label="Select Pose")
|
| 226 |
tol_slider = gr.Slider(5, 40, value=DEFAULT_TOLERANCE, step=1, label="Tolerance (degrees)")
|
| 227 |
run_btn = gr.Button("Analyze Pose")
|
| 228 |
output_video = gr.Video(label="Annotated Video Output")
|
| 229 |
output_json = gr.JSON(label="Results and Suggestions")
|
| 230 |
|
| 231 |
-
def analyze(video_path,
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
annotated_path, result = process_video(path, pose_name, tolerance)
|
| 236 |
if annotated_path is None:
|
| 237 |
return None, {"error": result}
|
| 238 |
return annotated_path, result
|
| 239 |
|
| 240 |
-
run_btn.click(
|
| 241 |
-
analyze,
|
| 242 |
-
inputs=[video_in, webcam_in, pose_dropdown, tol_slider],
|
| 243 |
-
outputs=[output_video, output_json]
|
| 244 |
-
)
|
| 245 |
|
| 246 |
if __name__ == "__main__":
|
| 247 |
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|
|
|
|
| 3 |
import math
|
| 4 |
import tempfile
|
| 5 |
from pathlib import Path
|
| 6 |
+
from typing import Dict, Tuple
|
| 7 |
|
| 8 |
import cv2
|
| 9 |
import numpy as np
|
|
|
|
| 11 |
import gradio as gr
|
| 12 |
|
| 13 |
# ------------------------------
|
| 14 |
+
# Configuration
|
| 15 |
# ------------------------------
|
| 16 |
REFERENCE_POSES_FILE = "reference_poses.json"
|
| 17 |
DEFAULT_TOLERANCE = 15.0
|
|
|
|
| 112 |
return overall, per_joint_score, per_joint_diff
|
| 113 |
|
| 114 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
# ------------------------------
|
| 116 |
# Video processing
|
| 117 |
# ------------------------------
|
|
|
|
| 181 |
pose.close()
|
| 182 |
|
| 183 |
avg_score = float(np.mean(aggregate_scores)) if aggregate_scores else 0.0
|
| 184 |
+
return out_path, {
|
| 185 |
+
"pose": pose_name,
|
| 186 |
+
"score_percent": avg_score,
|
| 187 |
+
"suggestions": [
|
| 188 |
+
f"Try maintaining stability. Overall correctness: {avg_score:.1f}%."
|
| 189 |
+
]
|
| 190 |
+
}
|
| 191 |
|
|
|
|
| 192 |
|
| 193 |
# ------------------------------
|
| 194 |
# Gradio Interface
|
|
|
|
| 197 |
pose_list = list(ref_poses.keys())
|
| 198 |
|
| 199 |
with gr.Blocks(title="Yoga Pose Correctness Checker") as demo:
|
| 200 |
+
gr.Markdown("""
|
| 201 |
+
# π§ Yoga Pose Correctness Checker
|
| 202 |
+
Upload a short video of your yoga pose.
|
| 203 |
+
The app will analyze:
|
| 204 |
+
- β
Pose correctness percentage
|
| 205 |
+
- π Joint-by-joint feedback
|
| 206 |
+
- π‘ Suggestions for improvement
|
| 207 |
+
""")
|
| 208 |
+
|
| 209 |
+
video_in = gr.Video(label="Upload a video (MP4/MOV)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
pose_dropdown = gr.Dropdown(choices=pose_list, value=pose_list[0], label="Select Pose")
|
| 211 |
tol_slider = gr.Slider(5, 40, value=DEFAULT_TOLERANCE, step=1, label="Tolerance (degrees)")
|
| 212 |
run_btn = gr.Button("Analyze Pose")
|
| 213 |
output_video = gr.Video(label="Annotated Video Output")
|
| 214 |
output_json = gr.JSON(label="Results and Suggestions")
|
| 215 |
|
| 216 |
+
def analyze(video_path, pose_name, tolerance):
|
| 217 |
+
if not video_path:
|
| 218 |
+
return None, {"error": "Please upload a video first."}
|
| 219 |
+
annotated_path, result = process_video(video_path, pose_name, tolerance)
|
|
|
|
| 220 |
if annotated_path is None:
|
| 221 |
return None, {"error": result}
|
| 222 |
return annotated_path, result
|
| 223 |
|
| 224 |
+
run_btn.click(analyze, inputs=[video_in, pose_dropdown, tol_slider], outputs=[output_video, output_json])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
|
| 226 |
if __name__ == "__main__":
|
| 227 |
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|