PitchIQ turns a high school soccer match into stats a coach can use: tagged live from the sideline, read from the match video, and a private page for every player.
Sports & fitnessPython · JavaScriptGitHub @Vega-77OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗PitchIQ turns video of a high-school football (soccer) match into stats a
coach can use: possession, territory, shape, a top-down map of every player,
shots and xG. Players get reports of their own as well. A tablet on the
touchline tags what the camera can't settle by itself (kick-off,
substitutions, restarts). The computer-vision pipeline in pitchiq/ turns the
match video and that tag log into a report JSON, and the site (GitHub Pages
and Firestore) shows the report to the coach and the players.
PitchIQ is source-available under the PolyForm Noncommercial License 1.0.0: free for non-commercial use, see License.
Working on the team's own footage: a panning (Hudl) camera calibrated from the match itself, the Hybrid 90 ball tracker, and reports on the site for the coach and the players. It is a research tool used by one team, not a finished product. The ball tracker finds about three balls in four on held-out clips and abstains on the rest, by design, but under floodlights it loses the ball almost entirely. Players and ball together on one top-down map is the next piece, and real footage needs a CUDA GPU. ROADMAP.md has where things stand and what is next.
Python 3.12, from a clone of the repo (Setup has the rest):
pip install -e .[test]
python -m pitchiq demo
demo draws a nine-second scripted match (coloured boxes: a long pass, a
touch, a shot), fits a fixed-camera calibration to it and runs
analyse_match with a scripted detector standing in for YOLO. It needs no
footage, GPU or weights and finishes in seconds; --out DIR keeps the clip,
the calibration and the report JSON. The numbers show the chain working, not
how well it sees a real match. On real fixed-camera footage the same steps
are grab_frame, calibrate and match_report (see FOOTAGE_DAY.md).
Fixed camera (pitchiq/calibration.py). One homography per camera position
maps pixels to metres. It is fitted once from landmarks clicked on a single
frame. This is the pipeline FOOTAGE_DAY.md was first written for. A tripod,
a locked-off shot, and that is all it needs.
Panning camera (pitchiq/panning_camera.py + pitchiq/balltrack/). The footage the team
actually has is from a Hudl camera that follows the play. Every frame gets its
own frame-to-pitch homography:
pitchiq/autocal.py calibrates the camera from the match itself, using the
paint plus SIFT between frames, andpitchiq/balltrack/pitch_registration/register_frames.py registers frames to a venue map.The ball comes from the Hybrid 90 tracker in pitchiq/balltrack/, and
PannedCamera moves everything into one still reference view. After that,
pitchiq/pipeline.py runs unchanged. This example requires a GPU and
generated data, so it does not run from a fresh clone:
from pitchiq.panning_camera import PannedCamera
from pitchiq.pipeline import analyse_match
camera = PannedCamera.from_tracker('fr00') # needs hybrid.sh outputs, see below
report = analyse_match(video, panned=camera, start_s=..., end_s=...)
from_tracker reads files that sh pitchiq/balltrack/hybrid.sh <lo> <hi> <tag>
(one window) or pitchiq/balltrack/half.py (a whole half) writes. They are
generated outputs, not in git (see pitchiq/balltrack/README.md, "Generated, not
in git"). Accuracy figures for the tracker are in that README too, and
FOOTAGE_DAY.md ("Fixed or panning") sets out which pipeline a camera needs.
| path | what |
|---|---|
pitchiq/ | the pipeline: detection, tracking, identity, calibration, ball, possession, events, top-down map, report JSON, publishing to Firestore |
pitchiq/cli/ | the match-day commands, run as python -m pitchiq <command>: grab_frame, calibrate, event_report, match_report, compare_reports, publish_report, demo |
pitchiq/experiments/ | research tools and labs: spike_detect, track_report, speed_report, autocal_run, player_lab, ... |
pitchiq/balltrack/ | the ball tracker for panning footage (Hybrid 90: detector + classifier + TAPNext++ bridges + 3D flight chain), and the venue map and registration in pitch_registration/ |
shared/ | shared frontend modules and CSS (no build step, plain ES modules) |
index.html | landing page and sign-in |
coach/ | coach dashboard: season, roster, matches, staff, player reports |
player/ | a player's own reports |
halftime/ | the three-minute read for the touchline |
live-tagging/ | match-day tablet tool |
calibrate/ | landmark pins on a frame or a panorama, exported for both calibrators |
xg_training/ | xG model training (main.py, StatsBomb open data -> ONNX) |
baselines/ | committed report JSONs that compare_reports checks changes against |
tests/ | Python (pytest) and JavaScript (node --test, Firestore emulator) suites |
docs/ | architecture, glossary, report schema, conventions, per-module design notes, history and generated figures |
tools/ | repo maintenance (audit_metrics.py) |
Python 3.12 (requires-python = ">=3.12"; CI tests 3.12 only). Clone the
repo and install it editable into a virtual environment. That is the only
supported install: the ball tracker and the panning-camera path read data
files that sit beside the code (the venue map, the ball ranker and
classifier), and a wheel or a plain pip install . does not carry them.
This is the beginning of the README. Read the rest at the original. Read the full README ↗
.github/workflows/ | CI: tests.yml (pytest, pyflakes, ruff, the JS suites, the rules emulator) and static.yml (deploys the site to GitHub Pages from main) |