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.22.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.22-py3-none-any.whl (16.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: wunderscout-0.1.22.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.22.tar.gz
Algorithm Hash digest
SHA256 45f3d33593a643ba60ed5455b3293702e5a6de64de43bacee9b6eee3908674de
MD5 7776cc97b42966c5c318ccfc0e402499
BLAKE2b-256 6d080a12fc766a407ec40b74ceb34eb6ed946b160cd0b1a0bb2b42723e1617b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for wunderscout-0.1.22.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.22-py3-none-any.whl.

File metadata

  • Download URL: wunderscout-0.1.22-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.22-py3-none-any.whl
Algorithm Hash digest
SHA256 d3b246a5e893743a7016f1669a83c9abe4d9232b4e00163ffe146803e575a260
MD5 ce22c375c66352637f213689e3052d9f
BLAKE2b-256 62c69495651b56353bc807681a8210eae6622f4115f2d1f8f047910f607dab93

See more details on using hashes here.

Provenance

The following attestation bundles were made for wunderscout-0.1.22-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