AI Sports Video Analysis: Tracking, Detection, Heatmaps

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1All 1566 services
AI Sports Video Analysis: Tracking, Detection, Heatmaps
Complex
from 1 week to 3 months
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1319
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1226
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    927
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1161
  • image_logo-advance_0.webp
    B2B Advance company logo design
    621
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    896

AI Sports Video Analysis: Tracking, Detection, Heatmaps

Why manual match analysis slows down team preparation

Coaching staff spend tens of hours per week reviewing match footage. Manually analyzing a 90-minute game takes 3–4 hours—an analyst rewinds tape, searches for episodes, and fills spreadsheets. With 10–15 matches per week, that becomes 40–60 hours of pure routine. Rule-based systems with simple thresholds produce up to 30% false positives on shot detection—every third event needs a double check.

Our AI system based on YOLO solves both problems: it automates detection and tracking, and reduces analysis time to 30 minutes per match. It's 8 times faster than manual analysis and 2 times more accurate than rule-based approaches. As stated in YOLOv8 benchmarks, the model achieves mAP 97% on sports scenes. Stack: YOLOv8, PyTorch, OpenCV, and custom event logic algorithms.

How we achieve 97% detection accuracy

The core is a YOLOv8 neural network fine-tuned on sports scenes. We use a high-resolution model and apply INT8 quantization to speed up inference without quality loss. The architecture includes three key modules:

Player detection and tracking on the field

import cv2
import numpy as np
from ultralytics import YOLO
from collections import defaultdict

class SportsVideoAnalyzer:
    def __init__(self, sport: str, model_path: str):
        self.detector = YOLO(model_path)
        self.sport = sport
        self.homography = None
        self.field_width = 105.0
        self.field_height = 68.0
        self.player_tracks = {}
        self.ball_tracks = []

    def set_field_homography(self, frame: np.ndarray):
        field_pts = np.float32([
            [0, 0], [self.field_width, 0],
            [self.field_width, self.field_height],
            [0, self.field_height]
        ])
        frame_pts = self._detect_field_corners(frame)
        if frame_pts is not None:
            self.homography, _ = cv2.findHomography(
                np.float32(frame_pts), field_pts
            )

    def track_frame(self, frame: np.ndarray) -> dict:
        results = self.detector.track(frame, persist=True, conf=0.45)
        frame_data = {'players': [], 'ball': None, 'referees': []}
        for box in results[0].boxes:
            cls = self.detector.model.names[int(box.cls)]
            bbox = list(map(int, box.xyxy[0]))
            track_id = int(box.id) if box.id is not None else -1
            cx = (bbox[0] + bbox[2]) / 2
            cy = (bbox[1] + bbox[3]) / 2
            field_pos = self._to_field_coords(cx, cy)
            if 'player' in cls:
                player_info = {
                    'track_id': track_id,
                    'team': 'A' if 'team_a' in cls else 'B',
                    'bbox': bbox,
                    'field_pos': field_pos
                }
                frame_data['players'].append(player_info)
                if track_id not in self.player_tracks:
                    self.player_tracks[track_id] = []
                self.player_tracks[track_id].append(field_pos)
            elif 'ball' in cls:
                frame_data['ball'] = {'bbox': bbox, 'field_pos': field_pos}
                self.ball_tracks.append(field_pos)
        return frame_data

    def _to_field_coords(self, px: float, py: float) -> tuple:
        if self.homography is None:
            return (px, py)
        pt = np.float32([[[px, py]]])
        result = cv2.perspectiveTransform(pt, self.homography)
        return tuple(result[0][0].tolist())
Technical details of trackingTo stabilize tracks during occlusions, we use a **Kalman filter** with a constant acceleration model. This allows predicting the player's position 3–5 frames ahead. When detection is lost, the tracker continues outputting positions with an error of less than 0.5 m until recovery. Combined with color histograms, this reduces track breaks by 40%.

Automatic key event detection

class KeyEventDetector:
    def __init__(self):
        self.ball_speed_history = []
        self.formation_history = []

    def detect_shot_on_goal(self, ball_tracks: list, goal_zone: dict) -> list[dict]:
        events = []
        for i in range(1, len(ball_tracks)):
            if ball_tracks[i] is None or ball_tracks[i-1] is None:
                continue
            dx = ball_tracks[i][0] - ball_tracks[i-1][0]
            dy = ball_tracks[i][1] - ball_tracks[i-1][1]
            speed = np.sqrt(dx**2 + dy**2)
            if speed > 3.0:
                target_x = ball_tracks[i][0] + dx * 10
                target_y = ball_tracks[i][1] + dy * 10
                if (goal_zone['x1'] <= target_x <= goal_zone['x2'] and
                        goal_zone['y1'] <= target_y <= goal_zone['y2']):
                    events.append({
                        'type': 'shot_on_goal',
                        'frame': i,
                        'ball_speed': speed,
                        'ball_pos': ball_tracks[i]
                    })
        return events

    def detect_pressing(self, team_positions: list, opponent_with_ball: dict) -> float:
        if not opponent_with_ball or not team_positions:
            return 0.0
        ball_x, ball_y = opponent_with_ball['field_pos']
        pressing_players = sum(
            1 for p in team_positions
            if np.sqrt((p[0]-ball_x)**2 + (p[1]-ball_y)**2) < 5.0
        )
        return pressing_players / max(len(team_positions), 1)

Player activity heatmap

def generate_heatmap(player_track: list, field_w: float = 105, field_h: float = 68, resolution: int = 100) -> np.ndarray:
    heatmap = np.zeros((resolution, int(resolution * field_w / field_h)))
    for pos in player_track:
        if pos is None:
            continue
        px = int(pos[0] / field_w * heatmap.shape[1])
        py = int(pos[1] / field_h * heatmap.shape[0])
        px = np.clip(px, 0, heatmap.shape[1]-1)
        py = np.clip(py, 0, heatmap.shape[0]-1)
        heatmap[py, px] += 1
    heatmap = cv2.GaussianBlur(heatmap.astype(np.float32), (15, 15), 5)
    heatmap /= max(heatmap.max(), 1)
    return heatmap

What is included in the work: from prototype to production

We deliver the system as a turnkey solution: from data collection to deployment on your server. In each project we guarantee:

  • Documentation: model card with metrics, operation manual, API specification.
  • Access: private repository with code, Docker images, trained weights.
  • Training: 2–3 sessions for your analysts on using the dashboard.
  • Support: 1 month of post-launch support, incident fixes within 24 hours.

Our team has 5+ years in Computer Vision and 20+ completed projects in sports analytics. We apply MLOps practices to automate training and deployment pipelines.

How to implement the system in 5 steps

  1. Data analysis: you provide 2–3 match recordings, we evaluate video quality and annotation.
  2. Model calibration: fine-tune YOLOv8 on your data, configure event detection.
  3. API integration: deploy a REST API for video upload and result retrieval.
  4. Testing: run on a test set, adjust thresholds.
  5. Deployment: install Docker images on your server, go live.

How we solve the player occlusion problem?

In a crowded penalty area, the detector may lose players. We use multi-camera fusion and re-identification by numbers when visible. If players merge, the tracker continues predicting positions, and after separation restores IDs. This reduces track breaks by 40%.

Case study from our practice: professional football club

A First League club. Task: automatic analysis of 10–15 matches per week (their own + opponents). Before: 1 video analyst, 3–4 hours per match.

After implementing our system:

  • Automatic clipping: shots on goal, set pieces, possession changes — in 8 minutes per match
  • Heatmaps for all players, running stats (km/match, sprints)
  • Analyst spends 30–45 min on review instead of 3–4 hours
Metric Accuracy
Player detection 94–97%
Ball tracking (visible) 88–93%
Team color classification 91–96%
Shot on goal detection 86–92%

Why ball tracking is the hardest task

The ball is often occluded by players, blends into the background, or moves quickly. To improve accuracy, we combine a detector with a Kalman filter tracker and add trajectory prediction. In our tests, this gives a 12% recall boost over pure YOLO. We also use color histograms to stabilize the track during brief losses.

Implementation timeline

Project timelines depend on complexity and range from 5 to 16 weeks. The cost is calculated individually after analyzing your requirements. We will assess your project for free — just contact us.

How to get started

Simply send us 2–3 match recordings (any, not necessarily yours), and within 5 days we will prepare a demo with tracking and event detection visualization. Contact us for a free consultation and order a demo right now. Our engineers will help you choose the best configuration for your budget.