AI-система видеоанализа спортивных матчей

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
AI-система видеоанализа спортивных матчей
Сложная
от 1 недели до 3 месяцев
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

AI-анализ видеозаписей спортивных матчей

Профессиональные команды тратят десятки часов в неделю на ручной разбор видео матчей. AI-анализ автоматизирует детекцию игровых ситуаций, трекинг игроков и мяча, разбивку на эпизоды — аналитик вместо просмотра 90 минут видео работает с автоматически нарезанными ключевыми моментами с метриками.

Детекция и трекинг игроков на поле

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

class SportsVideoAnalyzer:
    def __init__(self, sport: str, model_path: str):
        # YOLOv8l дообученный на спортивных сценах
        # Классы: player_team_a, player_team_b, referee, ball, goalkeeper
        self.detector = YOLO(model_path)
        self.sport = sport

        # Гомография: пиксели → координаты поля в метрах
        self.homography = None
        self.field_width = 105.0   # футбол
        self.field_height = 68.0

        # Трекинг
        self.player_tracks = {}    # track_id → list of positions
        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())

Автоматическое обнаружение ключевых моментов

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:
        """Индекс прессинга: сколько игроков в радиусе 5м от владельца мяча"""
        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)

Тепловая карта активности игрока

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

Кейс: профессиональный футбольный клуб

Клуб Первой лиги. Задача: автоматический разбор 10–15 матчей в неделю (свои + соперники). Ранее: 1 видеоаналитик, 3–4 часа на матч.

После внедрения:

  • Автоматическая нарезка: удары по воротам, стандарты, смены владения — за 8 минут на матч
  • Тепловые карты всех игроков, статистика бега (км/матч, спринтов)
  • Аналитик тратит 30–45 мин на разбор вместо 3–4 часов
Метрика Точность
Детекция игроков 94–97%
Трекинг мяча (видимый) 88–93%
Определение команды по цвету 91–96%
Детекция ударов по воротам 86–92%
Тип проекта Срок
Базовый трекинг игроков + тепловые карты 5–8 недель
Полная аналитическая система 10–16 недель