← All work
Analytics & ML · 2025–26

Football player detection and match analytics (YOLOv8)

0.83mAP@0.5, all classes
Role

Solo

Year

2025–26

Type

Analytics & ML

Tools

YOLOv8, PyTorch, OpenCV

The answer

A small model can pull usable detections straight from broadcast frames on an ordinary CPU. Choosing the inference resolution on validation data lifted ball detection from 0.29 to 0.43 without retraining, and the rebuilt model matches v1 overall while beating it on 3 of 4 classes.

Detections on a broadcast clip
0.83test mAP@0.5, all classes (960 px)
0.29 → 0.43ball mAP@0.5, 640 → 960 px (validation)
~4 fps244 ms a frame on a 2-core CPU

01The problem

Tracking data normally needs expensive camera systems or manual tagging. Can a small model get usable detections straight from broadcast frames, on an ordinary CPU?

Cognitive Sciences and Analytics course, Semester 7, 2025 · v1 built on Roboflow; rebuilt end to end in Python in 2026 (v2) · Solo

02What I did

  • Fine-tuned YOLOv8-nano on 372 labelled frames (Roboflow public dataset, CC BY 4.0).
  • Chose the inference resolution on validation data, which lifted ball accuracy by half without retraining.
  • Built team identification (jersey-colour clustering) and a possession estimate (nearest player to the ball).

03Results

Players are easy. The ball is the hard part.
Playerplayer, v2: YOLOv8-nano (2026): 0.990.99player, v1: Roboflow (2025): 0.980.98Refereereferee, v2: YOLOv8-nano (2026): 0.950.95referee, v1: Roboflow (2025): 0.910.91Goalkeepergoalkeeper, v2: YOLOv8-nano (2026): 0.930.93goalkeeper, v1: Roboflow (2025): 0.910.91Ballball, v2: YOLOv8-nano (2026): 0.430.43ball, v1: Roboflow (2025): 0.470.47Allall, v2: YOLOv8-nano (2026): 0.830.83all, v1: Roboflow (2025): 0.820.82

v2: YOLOv8-nano (2026)v1: Roboflow (2025)

Test-set mAP@0.5 by class. Test frames share clips with training frames, so these are an upper bound for unseen matches.

Source: outputs/test_metrics.csv; v1 from notebook cell 16

960 px finds the ball best, at a cost in speed
640: 0.29640 px110.8 ms a frame on CPU0.29960: 0.43960 px280.1 ms a frame on CPU0.431280: 0.331280 px529.4 ms a frame on CPU0.33

Ball mAP@0.5 on the validation set, by inference size.

Source: notebook cell 12 (resolution sweep)

Setup
ModelYOLOv8-nano, 3,133,436 parameters, fine-tuned from COCO weights
Labelled frames (train / validation / test)298 / 49 / 25
Labelled objects (train / validation / test)7,133 / 1,174 / 598
Source clips56 Bundesliga broadcast clips
Training43 epochs at 640 px, 1.75 h on CPU
Inference size960 px (chosen on validation set)
Speed244 ms per frame at 960 px on a 2-core CPU (about 4 fps), no GPU
Test mAP@0.50.826 at 960 px (0.787 at 640 px)

Source: notebook cell 12; notebook cell 4; notebook cell 23; notebook cell 9; notebook cell 19; notebook cell 15

Speed is about 4 frames per second on a 2-core CPU.

04The ball is the bottleneck

When the model calls a ball it is right 95% of the time, but it finds only 38% of them, so possession can be estimated in only 47% of frames.

05Files

Next project

Adani vs Reliance: an ESG scorecard

5–3 →