How PitchOverlay works
Everything runs locally in the browser. This page documents the actual pipeline — including the parts that are unreliable — so you can judge the numbers before you put them in a coaching report.
1. Video decoding and sampling
The file you drop in becomes an object URL and is played by a plain <video> element. The analysis loop seeks to evenly spaced timestamps and grabs each frame into an offscreen canvas at a reduced width (960px by default) for colour sampling. Sample rate is your choice: 1, 2, 5 or 10 samples per second.
Why not every frame? A 90-minute match at 25 fps is 135,000 frames. At 5 samples per second it is 27,000 — an order of magnitude less work for almost the same coaching value, because passes and possession phases last longer than 200 ms. Short clips can be analysed at 10/s when you want detail.
2. Detection: MediaPipe EfficientDet-Lite0 by default
Detection is delegated to MediaPipe Tasks Vision with the EfficientDet-Lite0 model — a 7 MB COCO detector served from Google's model CDN, running on WebGL with a WebAssembly fallback.
[1, 4+nc, N] or its transpose.
From the raw detections we keep the person class and, optionally, the sports-ball class, then apply non-maximum suppression and a filter that removes boxes that are too small, absurdly tall, mostly outside the frame, or taller than 90% of the frame.
3. Tracking: two-stage association with an ID revival pool
Each confirmed player is a track carrying a Kalman filter over [cx, cy, w, h, vx, vy, vw, vh]. Every frame:
- Tracks are predicted forward.
- High-confidence detections (score ≥ 0.45) are matched to predicted tracks with a Hungarian assignment whose cost is 1 − IoU, with a fallback gate on size-normalised centre distance for fast movement.
- Low-confidence detections (0.12–0.45) get a second pass to rescue tracks that detection briefly lost.
- Leftover detections create tentative tracks that are only reported after two hits.
A track that dies is kept for 240 frames in a revival pool. A new detection near its last position, with a similar size and a similar kit colour, inherits the old ID. In practice this is what keeps a shirt number attached to the same player across the moments where detection drops out.
4. Teams from kit colour
For every detection we sample the torso region (35–70% of the box height, the middle 60% of its width), discard grass-coloured pixels by hue, and accumulate a circular-mean hue per track. Tracks are then split with a 2-means clustering over those hue vectors. Tracks whose colour sits far from both centroids — typically the referee in black and a goalkeeper in a third kit — are pushed into an other bucket instead of being forced into a team.
Clusters are only a starting point: rename them (Home / Away), swap them, or override any individual player. An override locks that player so re-clustering will not undo your decision.
5. Pitch calibration and metric distances
You click four points that form a known rectangle — the penalty area (40.32 × 16.5 m) is the most useful — and the tool solves a homography from image coordinates to pitch coordinates with a direct linear transform. From then on, every player position also exists in metres, which is what makes these possible:
- distance covered (sum of consecutive metric positions, gaps longer than four samples are skipped so a lost track does not teleport),
- speed (median-filtered over a five-sample window, capped at 13 m/s),
- the bird's-eye radar map in the corner of the overlay,
- pass length in metres in
events.csv.
Without calibration you still get the whole overlay and the pass chain, but distances are reported in pixels and the unit column says so.
6. Possession, passes and turnovers
The ball’s controlling player is the nearest one whose size-normalised distance to the ball is within 0.75 player-heights. Normalising by the player's bounding-box height matters: a defender on the far touchline occupies 30 pixels while a player in the foreground occupies 120, and a raw pixel radius would hand every ball to the near side of the pitch.
Ownership does not change on a single sample. A challenger must stay within range for 0.3 s before the transfer is committed. Without that rule, a ball in flight passes briefly within range of third players and you get a burst of invented passes — the single biggest source of nonsense in naive implementations.
| Event | Meaning |
|---|---|
recovery | A player takes control of a ball nobody controlled. |
pass | Control moves to a player of the same team — counted as a completed pass. |
turnover | Control moves to the opposition. |
transfer | Control moves, but at least one of the two players has no known team (referee bucket, low-confidence kit). |
loose | Nobody has been near the ball for 0.9 s: a clearance, a throw, or the ball left the frame. |
Possession share is the summed duration of each team's control segments out of the attributed time. It excludes loose time, so the two numbers still add to 100% of attributed possession.
7. Corrections are first-class, not an afterthought
The scan (detect + track over the video) is expensive and runs once. Everything downstream — possession, events, statistics, the report — is recomputed from the stored per-frame records in milliseconds. That split is why a correction is instant:
- type a shirt number, pick a team, merge a broken track, ignore the referee — the records are rewritten and the chain recomputed;
- click two players to insert a pass the model missed;
- click the ball in any frame to override its position.
Corrected events are flagged in events.csv (manual = 1) and the report states how many there are, so a reader can tell machine output from human judgement.
8. Exports
Three CSVs, UTF-8 with BOM so Excel opens them without a character-set dialog: players.csv (one row per tracked player), teams.csv (aggregates per team) and events.csv (the possession chain in time order, with pixel and metre positions). The report is a self-contained printable HTML file — pass network, distance chart, team comparison, event log and limitations.
9. Known limitations, stated plainly
- Ball detection is the weak link. On a wide shot a ball is a handful of pixels; expect misses and expect to correct the important passages.
- Camera motion is not modelled. The homography is static, so panning or handheld footage degrades metric numbers. Calibrate a fixed camera.
- Identity switches happen. Crossing players with similar kits can swap. Merge the tracks and the statistics follow.
- Crowd and bench. If people sit inside the frame, some will be detected as players. The minimum-size filter and the pitch polygon (when you calibrate) reduce this, corrections finish the job.
- No identity without numbers. The tool does not read shirt numbers from the image; numbers are labels you assign, which is why "number" and "track ID" are the same field until you edit it.