Spaces:
Sleeping
A newer version of the Gradio SDK is available: 6.20.0
A15 β Filter ugly exercises, rescore good ones & cut exercise segments
Overview
This directory takes the ~180 2D exercise videos, filters out exercises with poor form (predicted "ugly" by a CNN), rescales the remaining good exercises onto a normalised 0β4 quality score, cuts each clip to only the frames between the predicted start and stop of the exercise movement using the A12 start/stop detection model, and finally augments the cut sequences to expand the training dataset for downstream models.
Pipeline
scores.csv (180 clips, raw quality scores)
β
βΌ filter_ugly_exercises.py
β β’ loads each clip from Datasets_all/kinect_good_preprocessed/
β β’ runs A_CNN.keras (sigmoid output: P(good))
β β’ threshold: β₯ 0.5 β GOOD, < 0.5 β UGLY
β
ββ a15_predictions.csv (179 clips processed, 1 missing Kinect file)
ββ a15_good_list.csv (171 clips predicted GOOD)
ββ a15_ugly_list.csv ( 8 clips predicted UGLY)
ββ a15_summary.txt (text summary of the filter run)
β
βΌ rescore_good.py
β’ min-max normalisation to 0β4
β’ excludes A1_kinect (score=0.0) from
the [min, max] range calculation
β’ clips result to [0, 4]
β
ββ a15_good_rescaled.csv (171 clips)
β
βΌ cut_with_a12.py
β’ loads each clip from kinect_good_preprocessed/
β’ engineers same 70 features as A12 training
(distances, velocities, accelerations)
β’ runs A_Kinect_Dense_relu_adam_bs64
frame-by-frame β binary prediction
β’ first 0β1 transition β START,
last 1β0 transition β STOP
β’ deletes sequences shorter than 30 frames
β
ββ a15_cut/ (148 cut CSVs)
ββ a15_cut_summary.csv (per-clip metadata)
β
βΌ augment_cut_data.py
β’ loads each cut CSV from a15_cut/
β’ applies 4 spatial augmentations:
β mirror on y-axis
β rotate Β±10Β° on y-axis
β stretch/compress x,y,z
β’ each variant inherits the original score
β
ββ a15_cut_augmented/ (740 CSVs)
ββ a15_augmented_data.csv (740 rows)
Files
| File | Rows | Description |
|---|---|---|
scores.csv |
180 | Original raw scores from upstream (Var1=clip, Var2=score) |
a15_predictions.csv |
179 | Per-clip original scores + model good_probability + predicted_label |
a15_good_list.csv |
171 | Subset predicted GOOD |
a15_ugly_list.csv |
8 | Subset predicted UGLY |
a15_good_rescaled.csv |
171 | Good clips with min-max normalised score (0β4) |
a15_summary.txt |
β | Human-readable summary of the filter step |
a15_cut_summary.csv |
171 | Per-clip cutting metadata (status, start/stop frames, lengths) |
Directories
| Directory | Contents |
|---|---|
a15_cut/ |
148 cut CSV files (β₯ 30 frames each), frames trimmed to predicted exercise segment |
a15_cut_augmented/ |
740 CSV files (148 original + 592 augmented), each with full frame-by-frame skeleton data |
Utility scripts
| Script | Purpose |
|---|---|
filter_ugly_exercises.py |
Connects scores.csv with Kinect CSVs, runs A_CNN inference, splits good/ugly |
rescore_good.py |
Min-max normalises good-clip scores to [0, 4] |
rescore.py |
(Original reference) β simple score / max * 4 rescale on all clips |
cut_with_a12.py |
Cuts each good clip to the exercise segment using the A12 start/stop classifier |
augment_cut_data.py |
Augments cut sequences via mirror, rotation, and stretch/compress transformations |
Rescaling method (rescore_good.py)
The original rescore.py uses a simple linear rescale:
score_rescaled = (score / max_score) * 4
This clustered most good clips between 3 and 4, providing poor
discrimination. The updated rescore_good.py uses min-max
normalisation, which spreads scores evenly across the full range:
score_rescaled = ((score - min_score) / (max_score - min_score)) * 4
β clamped to [0, 4]
Calibration set: 170 good clips (all except A1_kinect, which has score=0.0 β an artifact, not a meaningful observation).
| Statistic | Value |
|---|---|
| Min (maps to 0) | 0.8879 (A24_kinect) |
| Max (maps to 4) | 1.3354 (B2_kinect) |
| Range width | 0.4475 |
A1_kinect is kept in the output with its clipped score of 0.0, but excluded from the min/max calculation so it does not compress the range for all other clips.
Cutting method (cut_with_a12.py)
After filtering and rescoring, each clip undergoes frame-level start/stop
detection using the best-performing model from A12:
A_Kinect_Dense_relu_adam_bs64 (Dense architecture, 70 input features,
3-fold CV F1 = 0.949).
Steps
- Feature engineering β joint distances (hand-to-shoulder, head-to-hip, etc.), velocities, and accelerations are computed for every frame, matching the exact feature set used in A12 training.
- Frame classification β the Dense model predicts exercise (1) vs non-exercise (0) for each frame independently.
- Segment detection β the first 0β1 transition marks the start frame, and the last 1β0 transition marks the stop frame.
- Cut β frames outside [start, stop] are discarded.
- Length filter β sequences shorter than 30 frames (β 1 s at 30 fps) are considered too short and are removed.
Results
| Metric | Count |
|---|---|
| Good clips processed | 171 |
| Cut OK (β₯ 30 frames) | 148 |
| Too short, removed (< 30 frames) | 23 |
| Avg. cut length |
Output columns (a15_cut_summary.csv)
| Column | Description |
|---|---|
clip |
Exercise clip identifier |
status |
ok, too_short_N_frames, or missing_kinect_csv |
n_frames_input |
Number of frames in the original (uncut) CSV |
n_frames_cut |
Number of frames after cutting |
start_frame |
Predicted start frame (FrameNo value) |
stop_frame |
Predicted stop frame (FrameNo value) |
cut_path |
Path to the cut CSV file (empty if too short) |
Augmentation method (augment_cut_data.py)
After cutting, each sequence is augmented in-place to produce 4 additional variants per original clip, for a 5Γ expansion of the dataset (148 β 740). Only spatial augmentations that preserve the exercise label and quality score are applied.
Augmentations
| Augmentation | Suffix | Description |
|---|---|---|
| Mirror on y-axis | _mirror |
Negate all x-coordinates, mirroring left β right |
| Rotate +10Β° on y-axis | _rotate_pos |
3D rotation matrix around y-axis (+10Β°) |
| Rotate β10Β° on y-axis | _rotate_neg |
3D rotation matrix around y-axis (β10Β°) |
| Stretch/compress | _stretch |
Scale x by +5%, y by β5%, z by +2% |
Each augmented variant keeps its original score_rescaled and
good_probability β the augmentation changes only the skeleton
coordinates, not the underlying quality of the exercise.
Input / output
| Item | Path |
|---|---|
| Input directory | a15_cut/ β 148 frame-by-frame skeleton CSVs |
| Output directory | a15_cut_augmented/ β 740 CSVs |
| Metadata | a15_augmented_data.csv β clip, score_rescaled, good_probability |
Output columns (a15_augmented_data.csv)
| Column | Description |
|---|---|
clip |
Exercise clip identifier (e.g. A100_kinect_mirror) |
score_rescaled |
Min-max normalised quality score in [0, 4] (same as original) |
good_probability |
CNN sigmoid output β model confidence that the exercise is GOOD (same as original) |
Final output columns (a15_good_rescaled.csv)
| Column | Description |
|---|---|
clip |
Exercise clip identifier (e.g. A100_kinect) |
score_rescaled |
Min-max normalised quality score in [0, 4] |
good_probability |
CNN sigmoid output β model confidence that the exercise is GOOD |
Supplementary output (a15_augmented_data.csv)
See the Augmentation method section above. This file combines original and augmented clips (740 rows) and is the primary training input for downstream models that benefit from a larger, spatially varied dataset.