SportVision
Real-time sports analytics toolkit built on the Roboflow ecosystem. It detects players and the ball, tracks them with ByteTrack, clusters teams by jersey color, and computes possession, speed, distance, and heatmaps. Detection works with any COCO-compatible model, YOLOv8 and RF-DETR included.
Features
Detection. Works with any COCO-compatible detector, YOLOv8 and RF-DETR included.
Tracking. ByteTrack via supervision, with sequential IDs as the fallback when trackers is missing.
Team classification. KMeans on HSV jersey histograms.
Homography. Pixel to field coordinate mapping via cv2.findHomography.
Analytics. Possession, speed, distance, and heatmap generation.
Annotation. Team-colored bounding boxes, a stats overlay, and player trails.
Quick Start
pip install sportvision
from sportvision.pipeline import SportVisionPipeline
pipeline = SportVisionPipeline()
result = pipeline.process_frame(frame)
Detection needs a backend: pip install "sportvision[inference]" for RF-DETR, or install ultralytics for YOLO. Without one the pipeline returns empty detections and logs a warning once.
Two knobs make it run real-time on a GPU. detect_every=N runs detection every Nth frame and reuses the last result in between. infer_size caps the detector's input size (default 640) so the model sees fewer pixels; detection quality barely moves and boxes are mapped back to full frame coordinates.
pipeline = SportVisionPipeline(device="auto", detect_every=3, infer_size=640)
Roboflow Workflows Plugin
SportVision ships as a Roboflow Workflows plugin. Install with inference and activate:
pip install "sportvision[workflows]"
export WORKFLOWS_PLUGINS="sportvision.workflows"
This registers 4 blocks you can use in any Roboflow Workflow:
| Block | Type Identifier | Description |
|---|---|---|
| Team Classifier | sportvision/team_classifier@v1 |
Clusters players into teams by jersey color. refit_every=N to periodically refit KMeans. |
| Possession Tracker | sportvision/possession_tracker@v1 |
Tracks ball possession per team over time. Warns when team_id is missing. |
| Distance Calculator | sportvision/distance_calculator@v1 |
Cumulative distance per tracked player. Supports homography_matrix for field-unit distances. |
| Sports Detection Filter | sportvision/sports_detection_filter@v1 |
Filters COCO detections to sports classes. |
Example: Using blocks directly in Python
import cv2
import numpy as np
import supervision as sv
from sportvision.workflows.team_classifier.v1 import TeamClassifierBlockV1
from sportvision.workflows.possession_tracker.v1 import PossessionTrackerBlockV1
from sportvision.workflows.distance_calculator.v1 import DistanceCalculatorBlockV1
from sportvision.workflows.sports_detection_filter.v1 import SportsDetectionFilterBlockV1
# --- Filter COCO detections to sports classes ---
det_filter = SportsDetectionFilterBlockV1()
# Assume `raw_detections` comes from a COCO model (person=0, sports_ball=32)
result = det_filter.run(detections=raw_detections)
detections = result["detections"] # now player=0, ball=1
# --- Classify players into teams ---
team_block = TeamClassifierBlockV1()
# `image` must have a .numpy_image attribute (or use WorkflowImageData)
# refit_every=10 refits KMeans every 10 frames (0 = fit once, default)
result = team_block.run(image=image, detections=detections, n_teams=2, refit_every=10)
detections = result["detections"] # detections.data["team_id"] is now set
# --- Track possession ---
possession_block = PossessionTrackerBlockV1()
result = possession_block.run(
detections=detections,
ball_class_id=1,
ball_proximity_threshold=100.0,
)
print(result["possession_stats"]) # {0: 0.6, 1: 0.4}
print(result["possessing_team"]) # 0
print(result["warning"]) # "" or warning if team_id missing
# --- Compute distances ---
distance_block = DistanceCalculatorBlockV1()
# Optional: pass a 3x3 homography matrix for field-unit distances (e.g. meters)
result = distance_block.run(detections=detections, homography_matrix=[[0.01,0,0],[0,0.01,0],[0,0,1]])
print(result["detections"].data["distance"]) # cumulative distance per tracker
Example: Workflow JSON definition
{
"steps": [
{
"type": "sportvision/sports_detection_filter@v1",
"name": "filter",
"detections": "$steps.model.predictions"
},
{
"type": "sportvision/team_classifier@v1",
"name": "teams",
"image": "$inputs.image",
"detections": "$steps.filter.detections",
"n_teams": 2,
"refit_every": 10
},
{
"type": "sportvision/possession_tracker@v1",
"name": "possession",
"detections": "$steps.teams.detections",
"ball_proximity_threshold": 100.0
},
{
"type": "sportvision/distance_calculator@v1",
"name": "distance",
"detections": "$steps.teams.detections",
"homography_matrix": [[0.01,0,0],[0,0.01,0],[0,0,1]]
}
]
}
Try it on Colab
Architecture
src/sportvision/
├── detection.py # SportsDetector, wraps COCO detectors, maps to sports classes
├── tracking.py # SportsTracker, ByteTrack via supervision
├── teams.py # TeamClassifier, KMeans on HSV jersey histograms
├── homography.py # FieldHomography, pixel to field coords
├── analytics/
│ ├── possession.py # PossessionTracker, nearest player to the ball per frame
│ ├── speed.py # SpeedEstimator, displacement over time to km/h
│ ├── distance.py # DistanceCalculator, cumulative path length
│ └── heatmap.py # HeatmapGenerator, 2D histogram plus gaussian blur
├── annotators.py # TeamColorAnnotator, StatsOverlayAnnotator, TrailAnnotator
├── pipeline.py # SportVisionPipeline, orchestrates all modules
└── workflows/ # Roboflow Workflows plugin
├── _compat.py # Inference compatibility shim
├── kinds.py # Custom kind definitions
├── team_classifier/ # Team classification block
├── possession_tracker/ # Possession tracking block
├── distance_calculator/ # Distance calculation block
└── sports_detection_filter/ # COCO→sports filter block
Sports Class IDs
| ID | Class |
|---|---|
| 0 | Player |
| 1 | Ball |
| 2 | Referee |
| 3 | Goalkeeper |
Dependencies
| Package | Purpose | Required |
|---|---|---|
| numpy | Arrays | Yes |
| opencv-python | Image processing, annotation | Yes |
| supervision | Detection/tracking data structures | Yes |
| scikit-learn | KMeans for team classification | Yes |
| pydantic | Workflow block manifests | Yes |
| inference | Roboflow Workflows engine | Optional ([workflows]) |
| ultralytics | YOLOv8 detection | Optional |
| rfdetr | RF-DETR detection | Optional ([inference]) |
Development
git clone https://github.com/MohibShaikh/sportvision.git
cd sportvision
pip install -e ".[all]"
# Tests
pytest tests/ -v
# Lint
ruff check src/ tests/ && ruff format --check src/ tests/
License
Apache-2.0
Metadata
Release files for sportvision 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sportvision-0.3.1.tar.gz | 996.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sportvision-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.0 MB
Release files / sportvision-0.3.1.tar.gz
| Download URL | sportvision-0.3.1.tar.gz |
|---|---|
| Size | 996.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8515d75f6d183b3906151eefd41166f8a25baddd96030894dccd082547e08855
|
|
BLAKE2b-256 checksum How to use checksums |
efbcd348209b9ffb34e724c5d637b4b070b8149dcce8b3277809925815b9807c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency logRelease files / sportvision-0.3.1-py3-none-any.whl
| Download URL | sportvision-0.3.1-py3-none-any.whl |
|---|---|
| Size | 27.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
838362c3086ae7c987072978e02af6820da2dc4137463c7d7fe19055e95b1a61
|
|
BLAKE2b-256 checksum How to use checksums |
c555254dad6e59f27bfed6f86006d75c0aca5878b2dfef87907634cf98ffea67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency log