← Back to projects

Sports Field Video Analytics

Role
Freelance developer
Client
France-based sports technology startup
Period
2026 - Present
YOLOByteTrackOpenCVffmpegStreamlit
A goal detected automatically and stamped with its timestamp: a frame from the system output

Problem

Five-a-side pitches record matches with a single fixed wide-angle camera. What players actually want is not hours of raw footage but the 15-second clip of their own goal. Editing that by hand does not scale.

Solution

An end-to-end pipeline that detects players and the ball from a single camera, tracks them with persistent IDs, infers events from the ball trajectory and cuts clips automatically around those events. Output: an annotated match video, per-event clips and a combined highlight reel.

1
Camera · fixed wide angle
3
Output types · video, clip, reel
H.264
Browser-compatible encoding
YOLOByteTrackOpenCVPyTorch supervisionscikit-learnffmpegStreamlit

Detection, tracking and event inference

Player detection and persistent ID assignment (ByteTrack)
Shot candidate event: detected from a velocity spike in the ball trajectory
Team classification from jersey colour using KMeans
Ball trajectory tracking: linking positions across frames
System output, moving: player tracking, ball trajectory and the shot-candidate banner rendered onto the match video

Pipeline

  • Detection: players and ball with YOLO (Ultralytics)
  • Tracking: persistent player IDs with ByteTrack
  • Event: shot/goal candidates from sudden velocity change in the ball trajectory
  • Editing: clip cutting around events plus a combined reel
  • Output: browser-compatible H.264 via ffmpeg
  • Interface: a demoable front end in Streamlit

Engineering note

The sample videos were shot handheld/on a gimbal, so the goal area could not be calibrated; events are therefore flagged with a shot candidate heuristic rather than as goals. The event detection function was written to accept a goal-area parameter: on the fixed camera rig in production the same code becomes full goal detection. Declaring the limitation up front is cheaper than rearchitecting later.

YOLOByteTrackOpenCV PyTorchsupervisionscikit-learnffmpegStreamlit
def sut_adayi(hiz_gecmisi, esik_carpan=2.5):
    """Filtreli top hizinda ani sicrama = sut adayi.
    'Gol' demiyoruz: tek kamerayla kale duzlemini goremeyiz,
    o yuzden cikti durustce 'aday' olarak etiketlenir."""
    taban = median(hiz_gecmisi[:-3])          # yakin gecmisin tipik hizi
    simdiki = mean(hiz_gecmisi[-3:])          # son karelerdeki hiz
    return simdiki > esik_carpan * max(taban, MIN_HIZ)
Simplified, representative snippet: the shot-candidate check on the filtered ball velocity