Применение искусственного интеллекта к спорту — недавняя тенденция, но уже есть интересные материалы:
Лично мне ближе всего волейбольная тема. По ссылке выше находится сайт одного австрийского института, где занимаются разбором игры местной любительской лиги. Есть несколько документов на почитать, но что более важно — опубликован видео-датасет, который можно свободно использовать.
Сразу скажу, что с наскока распознать элементы игры получилось с невысокой точностью, так что пришлось придержать амбиции и пилить задачу по частям. И первая часть — про самый маленький, но необходимый объект.
Распознавание движущегося мяча (aka ball tracking) — довольно популярная тема и про нее написано немало статей. Однако, в основном это демо-информация про возможности технологий, чем про применение в реальной жизни (и в реальных игровых видах спорта).
Действительно, одно дело распознать ярко-зеленый (или красный) мяч в метре от камеры и совсем другое — пестрый, крохотный (с общепринятых ракурсов), быстро двигающийся и сливающийся с фоном мяч в игре.
Я поигрался с разными видео, но в этой статье я использовал видео из указанной выше австрийской лиги. Причина — те самые авторские права. Австрийцы выложили свои записи в открытый доступ именно для таких гиков, а вот видео с Youtube или еще откуда — как правило принадлежат какому-нибудь каналу и их использование и упоминание наверное может повлечь какие-нибудь санкции. Австрийское видео имеет свои особенности. Главных деталей три:
- статичная камера за кортом
- Уровень игры. Уровень игры непосредственно влияет на скорость мяча, ибо у серьезных людей мяч часто можно увидеть только на замедленном повторе
- Цвет мяча, желто-синий не сильно контрастирует с цветом пола, что делает бессмысленными практически все подходы через цветовые фильтры
Начал я с того, что мяч — движется, а значит его надо искать среди движущихся объектов.
Такие объекты будем искать с помощью функций удаления фона OpenCV, добавив к ним размытие и преобразование в бинарную маску:
mask = backSub.apply(frame) mask = cv.dilate(mask, None) mask = cv.GaussianBlur(mask, (15, 15),0) ret,mask = cv.threshold(mask,0,255,cv.THRESH_BINARY | cv.THRESH_OTSU)
Таким образом вот эта, например, картинка:




model = Sequential([ Convolution2D(32,(3,3), activation='relu', input_shape=input_shape), MaxPooling2D(), Convolution2D(64,(3,3), activation='relu'), MaxPooling2D(), Flatten(), Dense(64, activation='relu'), Dropout(0.1), Dense(2, activation='softmax') ]) model.compile(loss="categorical_crossentropy", optimizer=SGD(lr=0.01), metrics=["accuracy"])
Как я ни крутил модель, добиться лучшег чем 20% ложно-отрицательных и 30% ложно-положительных не удалось.
Это лучше чем ничего, но конечно не достаточно.
Если применить сеть в игре, мяч как правило распознается, но появляется немало ложных мячей.

В общем, на коленке пришлось сделать некий фреймворк управления траекториями. Вот записанные траектории за розыгрыш:

Добавив еще несколько небольших деталей (например, длинные траектории имеют приоритет и могут быть предсказаны, если вдруг прервались без явного преемника), получаем вполне реалистичные розыгрыши:
