Использование моделей компьютерного зрения с открытым исходным кодом для создания лучших моментов в баскетболе
Мы живем в мире быстрого потребления контента, во главе которого стоят TikTok, Snapchat, Instagram, Twitter, Facebook, Youtube и другие.
Молодые болельщики осваивают новые способы взаимодействия с лигами, при этом все меньше внимания уделяется просмотру игр в прямом эфире.
Согласно опросу Variety Intelligence Platform, среди американских спортивных болельщиков в возрасте от 18 до 34 лет 58 % болельщиков MLB, 54 % болельщиков NBA и 48 % болельщиков NFL говорят, что предпочитают смотреть лучшие моменты, а не полные игры. »).
Будучи преданным поклонником НБА, живущим на полпути от США, где эти игры проходят посреди ночи, нет большего потребителя этих ярких моментов, чем я.
Не нуждаясь в дополнительной мотивации, я решил создать автоматизированный процесс для создания лучших моментов игр NBA.
Что
Цель этого проекта состояла в том, чтобы создать эти основные моменты с использованием технологий с полностью открытым исходным кодом и максимально упростить их.
Как
Есть много способов попытаться найти точки интереса в играх — анализ звука, обнаружение движения и т. д. Есть компании, которые делают бизнес именно на этом, используя сложные модели для определения точек интереса и создания ярких клипов. Но эти сложные исходные данные и модели не обязательно дают более точные результаты. Вместо этого я решил положиться на устойчивый и четкий сигнал — отчет о каждой игре с отметкой времени, соответствующий игровым часам. Так просто, но так точно. Хотя это решение не обязательно будет работать в любом виде спорта, в НБА (и в баскетболе в целом) часы священны… так что надежность очень высока при минимальных усилиях или вообще без них, а уровень вычислений очень низок.
Это решение состоит из трех частей:
- Создание простого парсера play-by-play для получения данных об игре, для которой мы создаем хайлайт.
- Использование модели Tesseract OCR с открытым исходным кодом для поиска текущих игровых часов и четверти в игровом фильме.
- Сравнивая часы и четверть часа, которые мы извлекли из кадров, с нашими данными воспроизведения.
…и если у нас есть совпадение — вуаля! У нас есть изюминка!
Теперь давайте перейдем к техническим аспектам.
Этот проект был написан на Python, но его можно легко воспроизвести на любом языке.
Прежде чем мы начнем, вот библиотеки, которые использовались в этом проекте:
import pandas as pd import pytesseract import cv2 from moviepy.editor import * import json import requests
Создание пошагового парсера
Play-by-play предоставляет расшифровку игры в формате отдельных событий.
Некоторые примеры данных, которые можно найти в данных play-by-play:
- время владения (на игровых часах)
- квартал, в котором произошло владение
- игрок, инициировавший владение мячом (в случае перехвата или подбора при защите)
- игрок соперника, инициировавший владение мячом (в случае пропущенного броска или передачи мяча), включая местоположение на площадке, с которого был сделан бросок, и некоторые другие уникальные идентификаторы, которые мы используем для классификации типа владения мячом.
Данные, которые мы извлекаем, взяты из официальных API НБА (data.nba.net и cdn.nba.com). Данные хранятся в формате JSON. Чтобы получить данные в более традиционном формате, таком как кадр данных, нам просто нужно запустить несколько строк кода.
В первой части представлены игры, в которые играли в указанную дату:
jsn = f"https://data.nba.net/10s/prod/v1/{date}/scoreboard.json" page = requests.get(jsn) j = json.loads(page.content)
Затем мы проверяем, что введенные команды действительно играли в указанную дату. Если да, то мы берем идентификатор игры и вставляем его в следующий API.
Мы извлекаем данные и загружаем их в pandas dataframe
:
raw_game = f'https://cdn.nba.com/static/json/liveData/playbyplay/playbyplay_{game_id}.json' page = requests.get(raw_game) j = json.loads(page.content) df = pd.DataFrame(j['game']['actions'])
Результаты выглядят примерно так:
Отсюда мы можем фильтровать любые интересующие нас игры (корзины, перехваты, блоки и т. д.). В нашем случае я беру только забитые мячи и исключаю штрафные броски.
ndf = df[['clock', 'period', 'description', 'teamTricode', 'shotResult', 'actionType']] ndf = ndf[ndf['shotResult'] == 'Made'] ndf = ndf[ndf['actionType'] != 'freethrow']
Теперь, когда у нас есть набор данных обо всех точках интереса в игре, пришло время найти их в реальных игровых кадрах.
Обработка игрового ролика
Следующим шагом будет обработка игрового фильма в кадры и манипулирование ими с помощью модуля OpenCV.
cap = cv2.VideoCapture(video_path) ret, frame = cap.read() while cap.isOpened(): …
Затем каждый кадр проходит ряд манипуляций (предварительной обработки), чтобы подготовить его к распознаванию (подробнее об распознавании).
Шаги:
- Обрежьте исходный кадр и оставьте только нижнюю треть, где находятся игровые часы (чтобы не тратить время на чтение всего кадра).
- Преобразуйте изображение в оттенки серого.
- Преобразуйте изображение в градациях серого в черно-белое.
- Примените Размытие по Гауссу к черно-белому изображению.
height, width = frame.shape crop_img = frame[height-(height/3):height, 0:width] gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY) bw = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] blurred = cv2.GaussianBlur(bw, (5, 5), 0)
Тессеракт
Tesseract — это движок оптического распознавания символов с открытым исходным кодом. Это самая популярная и качественная OCR-библиотека.
OCR использует искусственный интеллект для поиска текста и распознавания изображений.
Tesseract находит шаблоны в пикселях, буквах, словах и предложениях.
Он имеет возможность распознавать более 100 языков из коробки и может быть обучен для распознавания других языков.
Таким образом, в основном движок ищет и извлекает текст из изображений.
В этом проекте мы используем python-tesseract (pytesseract
), который является оболочкой python для механизма Google Tesseract-OCR.
Существует 13 режимов настройки тессеракта. Мы будем использовать режим 11: Разреженный текст. Найдите как можно больше текста в произвольном порядке.
Реализация Tesseract
Следующим шагом будет прогон нашего обработанного кадра через движок tesseract.
data = pytesseract.image_to_string(blurred, lang='eng', config=' - psm 11')
Теперь, когда у нас есть весь текст во фрейме, мы можем искать нужную информацию — то есть текущий квартал и игровые часы.
Продолжая пример выше:
Четверть – «1-я», а игровые часы – «7:32». Однако в выводе Tesseract обнаружено «ist». Цифру «1» ошибочно приняли за букву «i».
Итак, следующий шаг — создать карту распространенных ошибок для каждого квартала.
firstQ = ['1st', 'ist', 'ast'] secondQ = ['2nd', '2n', 'znd'] thirdQ = ['3rd', '3r', '3r0', '3ro', '37d', '3fd', '31d'] fourth = ['4th', '4t', '47h', '41h', '4h']
Сопоставление данных по ходу игры с данными из видеозаписи игры
Давайте подытожим то, что у нас есть на данный момент: мы создали пошаговый парсер, обработали игровые кадры и прогнали их через движок Tesseract, чтобы извлечь текст из кадра.
Теперь пришло время объединить точки данных и найти основные моменты.
Мы делаем это, обрабатывая каждый кадр видеозаписи игры, как обсуждалось выше, и проверяя, совпадает ли извлеченная комбинация четверти и игровых часов с четвертью и игровыми часами основного момента.
Если у нас есть совпадение, мы сохраняем номер кадра.
curr_frame = cap.get(cv2.CAP_PROP_POS_FRAMES) frame_loc.append(curr_frame)
Соединяем кадры воедино
Как только мы закончим просматривать все кадры в игре и у нас будет список всех кадров, в которых произошли блики, все, что нам нужно сделать, это собрать их воедино.
Мы сделаем это с помощью модуля moviepy
.
Moviepy
позволяет нам вырезать подклипы из полных видео. Чтобы увидеть всю игру, которая привела к блику, мы берем несколько секунд до и пару секунд после той секунды, в которой произошел хайлайт.
clips = [] for frame in highlight_frames: clip_name = video.subclip(round(frame/fps) - 4, round(frame/fps)+ 2) clips.append(clip_name) final_clip = concatenate_videoclips(clips) final_clip.write_videofile(output_path, codec='libx264', audio_codec='aac', fps=fps)
Пример выделения выходных данных
Теперь, когда мы закончили создание бликов, давайте посмотрим на результат….
Повышение эффективности
Это базовый сценарий для поиска точек интереса и создания клипов с яркими моментами, но он далеко не эффективен.
Эффективность можно легко повысить различными способами, вот некоторые из них:
- Отсутствие проверки каждого кадра — переход на одну секунду вместо проверки каждого кадра уменьшает количество обрабатываемых кадров в двухчасовой игре (60 кадров в секунду) с 430 000 до 7 200.
- Определение координат игровых часов и запуск только этой области через движок OCR сократит время работы.
- Ищите изменения в счете вместо игровых часов (если вы хотите, чтобы забивались только корзины)
- Использование многопроцессорности для параллельной работы и ускорения процесса, а также использование графического процессора.
Подводя итог
Это всего лишь небольшой пример того, как можно использовать технологии с открытым исходным кодом для создания невероятных продуктов.
На этом пока все — надеюсь, вам было интересно, и, пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы или комментарии!