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.17.tar.gz (13.0 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.17-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: wunderscout-0.1.17.tar.gz
  • Upload date:
  • Size: 13.0 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.17.tar.gz
Algorithm Hash digest
SHA256 b2c47513b77e266f2753176b0d5535d704d9a948191867b9bb44edcf4fe40780
MD5 606a34f8b9b85aef15aacf03c1d42076
BLAKE2b-256 dfffcbeb3b9876a6d40a2303abc7495f82b23581a73ac0ad780cac6bcc5e7ca2

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: wunderscout-0.1.17-py3-none-any.whl
  • Upload date:
  • Size: 15.9 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.17-py3-none-any.whl
Algorithm Hash digest
SHA256 259ca4e4eb68af3fc7c4f6c0ac682fdbe1d5fec14754cbf3a520d325b13e7d33
MD5 722892645d98026d2fa0f12c12584e47
BLAKE2b-256 da8d284c0e8f8de2a2854961d57cb3694a8540e4c35f1bd613171786cd6fb42e

See more details on using hashes here.

Provenance

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