feat: ZeroGPU startup fix + FMS scoring sheet download + judge prompt alignment
Browse files- .gitattributes +7 -0
- app.py +39 -1
- assets/screenings/active-straight-leg-raise.png +3 -0
- assets/screenings/deep-squat.png +3 -0
- assets/screenings/hurdle-step.png +3 -0
- assets/screenings/inline-lunge.png +3 -0
- assets/screenings/rotary-stability.png +3 -0
- assets/screenings/shoulder-mobility.png +3 -0
- assets/screenings/trunk-stability-push-up.png +3 -0
- formscout/ui/theme.py +25 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
docs/FormScout-FMS-Spec.md.pdf filter=lfs diff=lfs merge=lfs -text
|
| 37 |
docs/plans/FormScout-Build-Prompt.md.pdf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
docs/FormScout-FMS-Spec.md.pdf filter=lfs diff=lfs merge=lfs -text
|
| 37 |
docs/plans/FormScout-Build-Prompt.md.pdf filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
assets/screenings/active-straight-leg-raise.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
assets/screenings/deep-squat.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
assets/screenings/hurdle-step.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
assets/screenings/inline-lunge.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
assets/screenings/rotary-stability.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
assets/screenings/shoulder-mobility.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
assets/screenings/trunk-stability-push-up.png filter=lfs diff=lfs merge=lfs -text
|
app.py
CHANGED
|
@@ -64,6 +64,28 @@ SCORE_DESCRIPTIONS = {
|
|
| 64 |
0: "Pain reported β clinician referral required",
|
| 65 |
}
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
# βββ Processing ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 69 |
|
|
@@ -370,6 +392,15 @@ def build_app() -> gr.Blocks:
|
|
| 370 |
scale=1,
|
| 371 |
)
|
| 372 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
_available_models = config.available_pose_models() or config.POSE_MODELS
|
| 374 |
_default_model = (
|
| 375 |
config.DEFAULT_POSE_MODEL
|
|
@@ -380,6 +411,7 @@ def build_app() -> gr.Blocks:
|
|
| 380 |
choices=list(_available_models.keys()),
|
| 381 |
value=_default_model,
|
| 382 |
label="Pose Model",
|
|
|
|
| 383 |
)
|
| 384 |
|
| 385 |
overlay_layers = gr.CheckboxGroup(
|
|
@@ -452,6 +484,11 @@ def build_app() -> gr.Blocks:
|
|
| 452 |
side = {"N/A": "na", "Left": "left", "Right": "right"}.get(side_display, "na")
|
| 453 |
return process_video(video, test_name, side, pose_model_key, overlay_layers, sess)
|
| 454 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
submit_btn.click(
|
| 456 |
fn=_map_inputs,
|
| 457 |
inputs=[video_input, test_dropdown, side_dropdown, pose_model_dropdown,
|
|
@@ -481,4 +518,5 @@ def build_app() -> gr.Blocks:
|
|
| 481 |
|
| 482 |
if __name__ == "__main__":
|
| 483 |
app = build_app()
|
| 484 |
-
app.launch(theme=formscout_theme(), css=FORMSCOUT_CSS
|
|
|
|
|
|
| 64 |
0: "Pain reported β clinician referral required",
|
| 65 |
}
|
| 66 |
|
| 67 |
+
# Per-test illustration shown as a movement guide (test_key -> filename).
|
| 68 |
+
_ASSET_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", "screenings")
|
| 69 |
+
SCREENING_ASSETS = {
|
| 70 |
+
"deep_squat": "deep-squat.png",
|
| 71 |
+
"hurdle_step": "hurdle-step.png",
|
| 72 |
+
"inline_lunge": "inline-lunge.png",
|
| 73 |
+
"shoulder_mobility": "shoulder-mobility.png",
|
| 74 |
+
"active_slr": "active-straight-leg-raise.png",
|
| 75 |
+
"trunk_stability_pushup": "trunk-stability-push-up.png",
|
| 76 |
+
"rotary_stability": "rotary-stability.png",
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def _screening_image(test_display_name: str) -> str | None:
|
| 81 |
+
"""Resolve the movement-guide image path for a dropdown display name."""
|
| 82 |
+
key = {name: val for name, val in FMS_TESTS}.get(test_display_name)
|
| 83 |
+
fname = SCREENING_ASSETS.get(key)
|
| 84 |
+
if not fname:
|
| 85 |
+
return None
|
| 86 |
+
path = os.path.join(_ASSET_DIR, fname)
|
| 87 |
+
return path if os.path.exists(path) else None
|
| 88 |
+
|
| 89 |
|
| 90 |
# βββ Processing ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 91 |
|
|
|
|
| 392 |
scale=1,
|
| 393 |
)
|
| 394 |
|
| 395 |
+
movement_guide = gr.Image(
|
| 396 |
+
value=_screening_image("Deep Squat"),
|
| 397 |
+
label="Movement guide",
|
| 398 |
+
interactive=False,
|
| 399 |
+
buttons=[], # no download/share/fullscreen overlay
|
| 400 |
+
height=240,
|
| 401 |
+
elem_id="movement-guide",
|
| 402 |
+
)
|
| 403 |
+
|
| 404 |
_available_models = config.available_pose_models() or config.POSE_MODELS
|
| 405 |
_default_model = (
|
| 406 |
config.DEFAULT_POSE_MODEL
|
|
|
|
| 411 |
choices=list(_available_models.keys()),
|
| 412 |
value=_default_model,
|
| 413 |
label="Pose Model",
|
| 414 |
+
elem_id="pose-model-dd",
|
| 415 |
)
|
| 416 |
|
| 417 |
overlay_layers = gr.CheckboxGroup(
|
|
|
|
| 484 |
side = {"N/A": "na", "Left": "left", "Right": "right"}.get(side_display, "na")
|
| 485 |
return process_video(video, test_name, side, pose_model_key, overlay_layers, sess)
|
| 486 |
|
| 487 |
+
# Swap the movement-guide illustration when the selected test changes.
|
| 488 |
+
test_dropdown.change(
|
| 489 |
+
fn=_screening_image, inputs=[test_dropdown], outputs=[movement_guide],
|
| 490 |
+
)
|
| 491 |
+
|
| 492 |
submit_btn.click(
|
| 493 |
fn=_map_inputs,
|
| 494 |
inputs=[video_input, test_dropdown, side_dropdown, pose_model_dropdown,
|
|
|
|
| 518 |
|
| 519 |
if __name__ == "__main__":
|
| 520 |
app = build_app()
|
| 521 |
+
app.launch(theme=formscout_theme(), css=FORMSCOUT_CSS,
|
| 522 |
+
allowed_paths=[_ASSET_DIR])
|
assets/screenings/active-straight-leg-raise.png
ADDED
|
Git LFS Details
|
assets/screenings/deep-squat.png
ADDED
|
Git LFS Details
|
assets/screenings/hurdle-step.png
ADDED
|
Git LFS Details
|
assets/screenings/inline-lunge.png
ADDED
|
Git LFS Details
|
assets/screenings/rotary-stability.png
ADDED
|
Git LFS Details
|
assets/screenings/shoulder-mobility.png
ADDED
|
Git LFS Details
|
assets/screenings/trunk-stability-push-up.png
ADDED
|
Git LFS Details
|
formscout/ui/theme.py
CHANGED
|
@@ -16,6 +16,8 @@ SAGE_DEEP = "#9cbcad"
|
|
| 16 |
TEAL = "#2b8a8a"
|
| 17 |
TEAL_HOVER = "#1f6e6e"
|
| 18 |
TEAL_DEEP = "#175757"
|
|
|
|
|
|
|
| 19 |
GOLD = "#e0a43b"
|
| 20 |
GOLD_DEEP = "#cf922a"
|
| 21 |
INK = "#243a34"
|
|
@@ -116,6 +118,29 @@ FORMSCOUT_CSS = f"""
|
|
| 116 |
color: {GOLD_DEEP};
|
| 117 |
}}
|
| 118 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
/* Score display */
|
| 120 |
.score-card {{
|
| 121 |
background: rgba(43, 138, 138, 0.08);
|
|
|
|
| 16 |
TEAL = "#2b8a8a"
|
| 17 |
TEAL_HOVER = "#1f6e6e"
|
| 18 |
TEAL_DEEP = "#175757"
|
| 19 |
+
TEAL_LIGHT = "#d6ecec"
|
| 20 |
+
TEAL_LIGHT_HOVER = "#bfe3e3"
|
| 21 |
GOLD = "#e0a43b"
|
| 22 |
GOLD_DEEP = "#cf922a"
|
| 23 |
INK = "#243a34"
|
|
|
|
| 118 |
color: {GOLD_DEEP};
|
| 119 |
}}
|
| 120 |
|
| 121 |
+
/* Pose Model dropdown β light-teal option cells (scoped, no other dropdowns affected) */
|
| 122 |
+
#pose-model-dd ul.options {{
|
| 123 |
+
background: {TEAL_LIGHT} !important;
|
| 124 |
+
}}
|
| 125 |
+
#pose-model-dd ul.options li.item {{
|
| 126 |
+
background: {TEAL_LIGHT} !important;
|
| 127 |
+
color: {INK} !important;
|
| 128 |
+
}}
|
| 129 |
+
#pose-model-dd ul.options li.item:hover,
|
| 130 |
+
#pose-model-dd ul.options li.item.active {{
|
| 131 |
+
background: {TEAL_LIGHT_HOVER} !important;
|
| 132 |
+
}}
|
| 133 |
+
|
| 134 |
+
/* Movement-guide illustration β rounded card matching the test selection */
|
| 135 |
+
#movement-guide img {{
|
| 136 |
+
border-radius: 12px;
|
| 137 |
+
object-fit: contain;
|
| 138 |
+
}}
|
| 139 |
+
#movement-guide {{
|
| 140 |
+
border: 1px solid rgba(43, 138, 138, 0.22);
|
| 141 |
+
border-radius: 14px;
|
| 142 |
+
}}
|
| 143 |
+
|
| 144 |
/* Score display */
|
| 145 |
.score-card {{
|
| 146 |
background: rgba(43, 138, 138, 0.08);
|