Sports Performance Analysis · internal

Soccer player tracking

Germany vs Denmark U21 · broadcast feed · 30 second test window (t = 60 to 90 s)

Detections run at 10 Hz and are held across three frames for playback at 30 fps. The number on each box is its track ID. A trailing ? marks a low confidence team assignment, and boxes straddling two people are labelled unresolved rather than guessed.

Measured

Players detected
21–23
per frame, of 22 on the pitch
Ball width
10–19 px
detected by the stock model
Camera cuts
4
in 180 seconds of footage
Full match, one GPU
< 10 min
18 hours on a CPU box

Pipeline status

StageNotesState
Player detection Stock yolo11m at 1280 px. No fine tuning needed at this camera distance. Working
Pitch segmentation Turf hue sampled from the frame, so it should survive a different ground or a day game. Working
Team assignment Torso colour clustered in LAB space. Every one of the 110 stable tracks was checked by eye and all 110 were correct. Working
Track identity Fragments under camera pan, and separately swaps one ID between two players. Splitting and stitching in progress. Partial
Ball tracking Stock detector finds the ball; needs trajectory linking to survive occlusion. In progress
Pitch coordinates (metres) Line extraction works; deciding which line is which does not come free from geometry. Unblocked by hand: teach it on a keyframe, four clicks or more, and the fit appears over the photo. Manual
Identity across cuts Every cut resets identity. Not built. Not started

What cost the most time

The advertising boards are the same green as the grass

The standard “largest green region” pitch mask merges pitch and boards and runs up into the crowd. Hue alone does not separate them; hue tightness plus saturation does.

The brightest lines in the frame are not pitch markings

There are metres of run-off behind the goal line, so the strongest edge is the pitch-to-boards boundary, which is not on the pitch model at all. Any calibration that trusts line strength takes this bait and puts every player metres out.

White thresholding returns the shirts, not the lines

Under floodlights the painted markings are dimmer than the white kit. A tuned “white” threshold returned Germany’s shirts and the goal frame and almost no markings.

A broken homography scores perfectly

A degenerate fit collapses the pitch onto a single point; if that point lands on a line pixel the residual reads 0.00 px with every sample “in frame”. A one-way distance score also rewards shrinking the model onto any long line.

Recording only confirmed tracks hides the tracker’s own failure

An early version stored just the detections the tracker had accepted. On one frame it stored one player where the detector saw 23, and nothing about the output looked wrong. Every detection is now recorded, with a null ID when unconfirmed.

One track ID, two different players

Track 110 runs 34 frames on a Denmark player and then 66 on a Germany player, under the same ID. Sixteen tracks disagree with themselves about what colour their own player is. So the tracker has two opposite faults at once, and a count of distinct IDs cannot tell them apart: merging fragments lowers it, and so does swapping more players together.

Twelve goalkeepers

Ball crew and stewards in fluorescent bibs in front of the hoardings cluster with the keeper’s green kit. Colour alone reported twelve goalkeepers on a pitch that can hold two. They are now separated on saturation and on being too small for their distance from camera.

More pixels made the ball harder to see

Raising the input from 1280 to 1920 px dropped ball confidence from 0.57 to 0.11. The detector was trained at 640, so upscaling pushes the ball out of the size distribution it learned.