|
18 | 18 | gap_fill_speed_transforms, |
19 | 19 | replay_gated_transforms, |
20 | 20 | ) |
| 21 | +from sports.common.kinematics import ( |
| 22 | + PlayerTrack, |
| 23 | + collect_tracks, |
| 24 | + compute_kinematics, |
| 25 | +) |
21 | 26 | from sports.common.goalkeeper import apply_goalkeeper_teams, derive_gk_locks |
22 | 27 | from sports.common.team import ( |
23 | 28 | TeamLocks, |
@@ -64,6 +69,7 @@ class VideoTrackingSession: |
64 | 69 | _speed_transforms: dict | None = field(default=None, repr=False) |
65 | 70 | _gap_filled_transforms: dict | None = field(default=None, repr=False) |
66 | 71 | _minimap_transforms: dict | None = field(default=None, repr=False) |
| 72 | + _tracks: dict[int, PlayerTrack] | None = field(default=None, repr=False) |
67 | 73 |
|
68 | 74 | def team_locks(self) -> TeamLocks: |
69 | 75 | """Return clip-level team locks, cached.""" |
@@ -112,6 +118,19 @@ def minimap_transforms_by_frame(self) -> dict: |
112 | 118 | self._minimap_transforms = build_minimap_transform_map(self.kp_by_frame) |
113 | 119 | return self._minimap_transforms |
114 | 120 |
|
| 121 | + @property |
| 122 | + def tracks(self) -> dict[int, PlayerTrack]: |
| 123 | + """Per-track cumulative distance from gated homography (not gap-filled).""" |
| 124 | + if self._tracks is None: |
| 125 | + raw = collect_tracks(self.iter_tracked()) |
| 126 | + self._tracks = compute_kinematics( |
| 127 | + raw, |
| 128 | + self.fps, |
| 129 | + mode="homography", |
| 130 | + frame_transforms=self._gated_speed_transforms(), |
| 131 | + ) |
| 132 | + return self._tracks |
| 133 | + |
115 | 134 | def iter_tracked(self): |
116 | 135 | """Yield frame_idx and tracked detections with blocked ids removed.""" |
117 | 136 | for frame_idx, dets in self.frames: |
|
0 commit comments