Skip to content

feat: add SPEED mode with gated homography (PR 3) - #61

Open
AlexBodner wants to merge 17 commits into
feat/soccer-pr2-directionfrom
feat/soccer-pr3-speed
Open

feat: add SPEED mode with gated homography (PR 3)#61
AlexBodner wants to merge 17 commits into
feat/soccer-pr2-directionfrom
feat/soccer-pr3-speed

Conversation

@AlexBodner

@AlexBodner AlexBodner commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds SPEED analytics mode: per-player ground-speed badges (m/s), direction dots, and a translucent radar minimap.
  • Pitch keypoint detection + cache, and homography replay gating for stable m/s metrics.
  • Unified goalkeeper assignment (goalkeeper.py): defensive-block goal sides + nearer-goal GK when minimap H exists, centroid fallback otherwise.
  • Clip-level locks via derive_gk_locks / apply_goalkeeper_teams on VideoTrackingSession.team_locks and replay.

Demo

SPEED on 08fd33_0.mp4 (full-clip render, truncated preview):

All demos

Design notes for reviewers

  • Homography: ungated H for minimap; replay_gated_transforms for speed; gap_fill_speed_transforms for badge display when a gated frame is rejected (so badges still show).
  • GK / goals: defensive blocks (mean X of the N most defensive outfielders per side, default N=3), handshake uses the sign of the stronger |margin|. GK gets the team defending the nearer goal. Centroid fallback when H is missing.
  • Team colours: same SigLIP → UMAP → KMeans path as before; outfield locks vote player-class rows only and do not mutate under GK stabilization.

Scope

In: speed.py, pitch detect/cache, gated H, GK locks, SPEED wiring in main.py

Out: distance traces (PR4), combo/ALL (PR5), goal shading draw (PR6), ball/pass (later)

Test plan

  • python main.py --source_video_path data/08fd33_0.mp4 --target_video_path data/renders/speed.mp4 --mode SPEED --tracker bytetrack
  • Cache hit on second run (--cache)
  • DIRECTION unchanged; GK colours stable on replay
  • Goal sides not inverted on clips like c01561_3 (regression covered by unit tests)

AlexBodner and others added 13 commits July 1, 2026 20:49
Introduce pitch detection cache, speed badges, plain minimap, and
Mode.SPEED. Sequence gating lives on fit_pitch_homography via
HomographyGateState instead of a separate tracker class.

Co-authored-by: Cursor <cursoragent@cursor.com>
Pitch detector options belong in PR3 where keypoint cache and
homography-backed speed are wired.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep m/s computation in speed.py, dedupe model and min-speed constants,
reuse minimap transforms for gap-fill, drop unused homography helper,
and document SPEED in the soccer README.

Co-authored-by: Cursor <cursoragent@cursor.com>
fit_pitch_homography is ungated per-frame fit only. Temporal reproj and
jump gating uses local locked/last_was_accept vars in the replay loop.

Co-authored-by: Cursor <cursoragent@cursor.com>
Reuse DEFAULT_*_MODEL_ID in main.py, align pitch detector threshold
API with player detector, document gate/sprint constants, and type
_speed_by_tid transformer.

Co-authored-by: Cursor <cursoragent@cursor.com>
Restore local PITCH_DETECTION_MODEL_PATH for legacy modes and use
inline analytics model-id defaults instead of library constants.

Co-authored-by: Cursor <cursoragent@cursor.com>
Goal-distance when minimap homography exists; centroid fallback for DIRECTION and missing frames. Single derive_clip_locks and apply_goalkeeper_teams router.

Co-authored-by: Cursor <cursoragent@cursor.com>
Vote outfield team locks from player-class rows only, preserve pitch-based
gk_lock instead of overwriting from shirt votes, and let relock respect
gk_lock on goalkeeper rows. Set UMAP n_jobs=1 for stable classifier fit.

Co-authored-by: Cursor <cursoragent@cursor.com>
Set random_state=0 so SigLIP shirt clustering is reproducible across renders and PR demos.

Co-authored-by: Cursor <cursoragent@cursor.com>
Compute shirt-colour locks from unmutated player-class votes only; derive
gk_lock and goal-defender shading separately with mutate=False. Restore
derive_tracklet_team_lock for DIRECTION and drop fixed random_state seeds.

Co-authored-by: Cursor <cursoragent@cursor.com>
Adapt fit stride when max_frames would leave too few samples, and classify
from upper-torso jersey crops so green pitch no longer collapses clusters.
Also drop accidental session.tracks dependency from PR3 (DISTANCE-only API).

Co-authored-by: Cursor <cursoragent@cursor.com>
With denser short-clip sampling and torso crops, fixed UMAP/KMeans seeds
keep demo renders reproducible without collapsing kit clusters.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop jersey-crop sampling and fixed UMAP/KMeans seeds; use the original
full-box crops and classifier setup that produced the working PR3 demos.

Co-authored-by: Cursor <cursoragent@cursor.com>
AlexBodner and others added 4 commits July 21, 2026 11:51
Locks in the lab rule used by SPEED/radar locks: mean of the N most
defensive players per side, then assign each GK to the nearer goal's team.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comparing signed left/right margins flipped sides when both agreed on
orientation but the larger magnitude had the same sign (c01561_3).

Co-authored-by: Cursor <cursoragent@cursor.com>
Session team_locks already builds TeamLocks via derive_gk_locks; the
duplicate helper was never called.

Co-authored-by: Cursor <cursoragent@cursor.com>
Session team_locks already derives outfield majority + GK locks; these
wrappers were never called.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant