Skip to main content

Scouting and vision tools for YOLO and sports analytics.

Project description

wunderscout

A Python library for extracting player and ball tracking data from soccer match footage using YOLO, Siglip embeddings, and homography.

Features

  • Detection & Tracking: Uses YOLO for player/ball/pitch-keypoint detection and ByteTrack for temporal consistency.
  • Automated Team Clustering: Groups players into teams using Siglip vision transformer embeddings and K-Means clustering via UMAP dimensionality reduction.
  • Pitch Mapping: Transforms 2D image coordinates to a normalized 0-1 coordinate system using pitch keypoint homography.
  • Goalkeeper Attribution: Assigns goalkeepers to teams based on proximity to team centroids.
  • Data Export: Generates Team1 and Team2 CSV files containing frame-by-frame XY coordinates.
  • Heatmap Generation: Creates histogram and KDE-based spatial density maps for individual players and teams.

Installation

pip install wunderscout

Quick Start

from wunderscout import Detector, DataExporter, HeatmapGenerator

# Initialize with paths to trained YOLO weights
detector = Detector(
    player_weights="players.pt",
    field_weights="pitch.pt"
)

# Run processing
result = detector.run(
    video_path="input_match.mp4",
    output_video_path="output_match.mp4"
)

# Export tracking data to CSV
exporter = DataExporter()
exporter.save_csvs(result, "output/tracking.csv")

# Generate heatmaps
heatmap_gen = HeatmapGenerator()
player_heatmap = heatmap_gen.generate_player_heatmap(result, player_id=5, method="both")
heatmap_gen.save_heatmap(player_heatmap, "output/player_5.json")

Core Components

Detector

Main pipeline coordinator that orchestrates detection, tracking, and classification.

Parameters:

  • player_weights (str): Path to YOLO player detection model
  • field_weights (str): Path to YOLO field keypoint detection model

Methods:

  • run(video_path, output_video_path=None): Process video and return TrackingResult

Models

Handles YOLO detection and SiGLIP embedding extraction.

Key Methods:

  • detect_players(frame, conf=0.3): Detect players, goalkeepers, referees, and ball
  • detect_field(frame, conf=0.3): Detect pitch keypoints for homography
  • get_embeddings(pil_crops, batch_size=32): Extract visual embeddings for team classification
  • get_calibration_crops(video_path, stride=30): Sample player crops for calibration

Detection Class IDs:

  • 0: Ball
  • 1: Goalkeeper
  • 2: Player
  • 3: Referee

TeamClassifier

Clusters players into teams using UMAP + K-Means with temporal smoothing.

Methods:

  • fit(embeddings): Train clustering model on calibration embeddings
  • get_consensus_team(tracker_id, embedding): Assign team ID with 50-frame rolling consensus
  • resolve_goalkeepers_team_id(players, goalkeepers): Assign goalkeepers by centroid proximity
  • get_final_assignments(): Get final team assignments (dict: tracker_id → team_id)

PitchMapper

Projects pixel coordinates to normalized pitch coordinates [0, 1] × [0, 1] using homography.

Methods:

  • get_matrix(keypoints_xy, keypoints_conf): Compute homography from detected pitch keypoints (requires ≥4 keypoints with conf > 0.5)
  • transform(points, H=None): Transform points to normalized pitch coordinates

HeatmapGenerator

Generates spatial density maps using histogram binning and/or Gaussian KDE.

Parameters:

  • pitch_length (float): Pitch length in meters (default: 105.0)
  • pitch_width (float): Pitch width in meters (default: 68.0)
  • histogram_bins (tuple): Histogram resolution (default: (50, 34))
  • kde_grid_size (tuple): KDE grid resolution (default: (100, 68))
  • min_samples_for_kde (int): Minimum samples for KDE (default: 10)

Methods:

  • generate_player_heatmap(result, player_id, method="both"): Generate heatmap for one player
  • generate_team_heatmap(result, team, method="both"): Generate aggregated team heatmap
  • generate_all_players_heatmaps(result, method="both"): Generate heatmaps for all players
  • save_heatmap(heatmap_data, output_path, pretty=False): Save heatmap to JSON

DataExporter

Exports tracking data to CSV format.

Methods:

  • save_csvs(result, output_path): Export tracking data (creates {base_name}_Team1.csv and {base_name}_Team2.csv)

TrackingResult

Data class containing tracking results.

Attributes:

  • frames (dict): Frame-by-frame tracking data {frame_idx: {"players": {player_id: (x, y)}, "ball": (x, y) or None}}
  • team_assignments (dict): Player ID → Team ID mapping
  • total_frames (int): Total number of processed frames
  • fps (float): Video frame rate

Methods:

  • get_team_players(team): Get player IDs for team 0 or 1
  • get_all_player_ids(): Get all tracked player IDs
  • get_player_trajectory(player_id): Get position history for one player
  • get_ball_trajectory(): Get ball position history

Training Custom Models

from wunderscout import Trainer

trainer = Trainer(api_key="your_roboflow_api_key")

# Train player detection model
trainer.train_players(
    workspace="soccer-analytics",
    project="player-detection",
    version=1,
    epochs=300,
    output_dir="runs/training/player"
)

# Train field keypoint model
trainer.train_field(
    workspace="soccer-analytics",
    project="field-keypoints",
    version=1,
    epochs=300,
    output_dir="runs/training/field"
)

Output Formats

CSV Export

Two CSV files (one per team) with structure:

,,,Team1,Team1,Team1,Team1,,
,,,3,3,7,7,,
Period,Frame,Time [s],Player3_X,Player3_Y,Player7_X,Player7_Y,Ball_X,Ball_Y
1,0,0.00,0.234,0.567,0.789,0.345,0.500,0.500
1,1,0.04,0.235,0.568,NaN,NaN,0.501,0.499

Coordinate System:

  • Range: [0, 1] × [0, 1]
  • (0, 0) = Top-left corner of pitch
  • (1, 1) = Bottom-right corner of pitch
  • Missing data: "NaN"

Heatmap JSON

{
  "player_id": 5,
  "sample_count": 1500,
  "histogram": {
    "xedges": [0.0, 2.1, 4.2, ..., 105.0],
    "yedges": [0.0, 2.0, 4.0, ..., 68.0],
    "values": [[count_00, count_01, ...], [count_10, count_11, ...], ...]
  },
  "kde": {
    "x": [0.0, 1.05, 2.1, ..., 105.0],
    "y": [0.0, 1.0, 2.0, ..., 68.0],
    "values": [[density_00, density_01, ...], [density_10, density_11, ...], ...]
  }
}

Pitch Coordinate System

Normalized coordinate system mapping to standard football pitch (105m × 68m).

Coordinate Range:

  • X-axis: 0.0 (left goal line) → 1.0 (right goal line)
  • Y-axis: 0.0 (top sideline) → 1.0 (bottom sideline)

Key Landmarks:

Landmark X Y Description
Left goal (top post) 0.000 0.365 Top edge of left goal
Left goal (bottom post) 0.000 0.635 Bottom edge of left goal
Left penalty spot 0.105 0.500 11m from goal line
Center circle 0.500 0.500 Pitch center
Right penalty spot 0.895 0.500 11m from goal line
Right goal (top post) 1.000 0.365 Top edge of right goal
Right goal (bottom post) 1.000 0.635 Bottom edge of right goal

Dependencies

  • ultralytics
  • supervision
  • transformers
  • umap-learn
  • scikit-learn
  • opencv-python
  • scipy
  • more-itertools
  • torch
  • numpy
  • roboflow

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wunderscout-0.1.19.tar.gz (13.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wunderscout-0.1.19-py3-none-any.whl (16.1 kB view details)

Uploaded Python 3

File details

Details for the file wunderscout-0.1.19.tar.gz.

File metadata

  • Download URL: wunderscout-0.1.19.tar.gz
  • Upload date:
  • Size: 13.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for wunderscout-0.1.19.tar.gz
Algorithm Hash digest
SHA256 d00782f67dafe3e5d5595e310c9a7213e9314c2aff3d7e73b177995cc0ac0435
MD5 548534b3eb70e1b864756fd77bf24f9b
BLAKE2b-256 6d388bd2fccb8f535e6cb3d383a073f5b65f9a6a0b706719071641fb62a65059

See more details on using hashes here.

Provenance

The following attestation bundles were made for wunderscout-0.1.19.tar.gz:

Publisher: publish.yml on qhuboo/wunderscout

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wunderscout-0.1.19-py3-none-any.whl.

File metadata

  • Download URL: wunderscout-0.1.19-py3-none-any.whl
  • Upload date:
  • Size: 16.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for wunderscout-0.1.19-py3-none-any.whl
Algorithm Hash digest
SHA256 5d32d7758d7f5ae4c896d2a18b079f0e0b6dad4966e46643aa77aaa6be55919d
MD5 d1af8d7f08b8db7b80bdc9ce83e9fea7
BLAKE2b-256 f07c82e48a85bdd165b9451f4538b90fd22484a6aa46668fa860c8de02f77ef0

See more details on using hashes here.

Provenance

The following attestation bundles were made for wunderscout-0.1.19-py3-none-any.whl:

Publisher: publish.yml on qhuboo/wunderscout

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page