Національна футбольна ліга (НФЛ) є однією з найпопулярніших спортивних ліг у Сполучених Штатах найцінніша спортивна ліга у світі. NFL, BioCore та AWS прагнуть покращити людське розуміння діагностики, профілактики та лікування травм, пов’язаних зі спортом, щоб зробити гру у футбол безпечнішою. Додаткову інформацію щодо заходів із охорони здоров’я та безпеки гравців НФЛ доступно на Сайт НФЛ.
Команда Професійні послуги AWS Команда співпрацює з NFL і Biocore, щоб надати рішення на основі машинного навчання (ML) для ідентифікації ударів шолома на кадрах гри за допомогою методів комп’ютерного зору (CV). Маючи кілька видів камери, доступних у кожній грі, ми розробили рішення для ідентифікації ударів шолома з кожного з цих видів і об’єднання результатів удару шолома.
Мотивація використання кількох видів камери походить від обмеження інформації, коли події зіткнення фіксуються лише одним видом. Маючи лише одну перспективу, деякі гравці можуть закривати один одного або бути заблокованими іншими об’єктами на полі. Таким чином, додавання більшої кількості перспектив дозволяє нашій системі ML визначати більше впливів, які не видно в одному поданні. Щоб продемонструвати результати нашого процесу злиття та те, як команда використовує інструменти візуалізації, щоб допомогти оцінити продуктивність моделі, ми розробили кодову базу для візуального накладання результатів виявлення кількох переглядів. Цей процес допомагає визначити фактичну кількість впливів, які відчувають окремі гравці, видаляючи повторювані впливи, виявлені в кількох переглядах.
У цій публікації ми використовуємо загальнодоступний набір даних із NFL – конкурс Kaggle з виявлення ударів і показати результати для об’єднання двох переглядів. Набір даних включає обмежувальні рамки шолома на кожному кадрі та мітки ударів, знайдені в кожному відео. Зокрема, ми зосереджені на дедуплікації та візуалізації відео з ідентифікатором 57583_000082
у вигляді торцевої зони та бічної лінії. Ви можете завантажити відео з кінцевої та бічної лінії, а також метки істини землі.
Передумови
Рішення вимагає наступного:
Почніть роботу з SageMaker Studio Lab і встановіть необхідні пакети
Ви можете запустити блокнот з GitHub репозиторій або з SageMaker Studio Lab. У цій публікації ми запускаємо блокнот із середовища SageMaker Studio Lab. Ми обираємо SageMaker Studio Lab, тому що він безкоштовний, надає потужні сеанси користувача ЦП і ГП, а також 15 ГБ постійного сховища, яке автоматично збереже ваше середовище, дозволяючи продовжити з того місця, де ви зупинилися. Щоб використовувати SageMaker Studio Lab, подайте запит і створіть новий обліковий запис. Після схвалення облікового запису виконайте наступні кроки:
- Відвідати aws-samples GitHub repo.
- У
README
розділ, вибрати Відкрийте Studio Lab.
Це перенаправить вас до середовища SageMaker Studio Lab.
- Виберіть тип обчислення ЦП, а потім виберіть Запустіть Runtime.
- Після запуску часу виконання виберіть Копіювати в проект, що відкриває нове вікно з середовищем Jupyter Lab.
Тепер ви готові використовувати блокнот!
- відкритий
fuse_and_visualize_multiview_impacts.ipynb
і дотримуйтесь вказівок у зошиті.
Перша комірка блокнота встановлює необхідні пакети Python, такі як pandas і OpenCV:
%pip install pandas
%pip install opencv-contrib-python-headless
Імпортуйте всі необхідні пакети Python і встановіть параметри pandas для кращої візуалізації:
import os
import cv2
import pandas as pd
import numpy as np
pd.set_option('mode.chained_assignment', None)
Ми використовуємо pandas для отримання та аналізу файлу CSV із анотованими обмежувальними рамками шолома, а також ударами. Ми використовуємо NumPy в основному для роботи з масивами та матрицями. Ми використовуємо OpenCV для читання, запису та обробки даних зображень у Python.
Підготуйте дані, об’єднавши результати з двох переглядів
Щоб поєднати дві перспективи разом, ми використовуємо train_labels.csv
із змагання Kaggle як приклад, тому що він містить вплив правди на землю як з ендзони, так і з бічної лінії. Наступна функція приймає вхідний набір даних і виводить об’єднаний кадр даних, який дедуплікується для всіх відтворень у вхідному наборі даних:
def prep_data(df): df['game_play'] = df['gameKey'].astype('str') + '_' + df['playID'].astype('str').str.zfill(6) return df def dedup_view(df, windows): # define view df = df.sort_values(by='frame') view_columns = ['frame', 'left', 'width', 'top', 'height', 'video'] common_columns = ['game_play', 'label', 'view', 'impactType'] label_cleaned = df[view_columns + common_columns] # rename columns sideline_column_rename = {col: 'Sideline_' + col for col in view_columns} endzone_column_rename = {col: 'Endzone_' + col for col in view_columns} sideline_columns = list(sideline_column_rename.values()) # create two dataframes, one for sideline, one for endzone label_endzone = label_cleaned.query('view == "Endzone"') label_endzone.rename(columns=endzone_column_rename, inplace=True) label_sideline = label_cleaned.query('view == "Sideline"') label_sideline.rename(columns=sideline_column_rename, inplace=True) # prepare sideline labels label_sideline['is_dup'] = False for columns in sideline_columns: label_endzone[columns] = np.nan label_endzone['is_dup'] = False # iterrate endzone rows to find matches and dedup for index, row in label_endzone.iterrows(): player = row['label'] frame = row['Endzone_frame'] impact_type = row['impactType'] sideline_row = label_sideline[(label_sideline['label'] == player) & ((label_sideline['Sideline_frame'] >= frame - windows // 2) & (label_sideline['Sideline_frame'] <= frame + windows // 2 + 1)) & (label_sideline['is_dup'] == False) & (label_sideline['impactType'] == impact_type)] if len(sideline_row) > 0: sideline_index = sideline_row.index[0] label_sideline['is_dup'].loc[sideline_index] = True for col in sideline_columns: label_endzone[col].loc[index] = sideline_row.iloc[0][col] label_endzone['is_dup'].loc[index] = True # calculate overlap perc not_dup_sideline = label_sideline[label_sideline['is_dup'] == False] final_output = pd.concat([not_dup_sideline, label_endzone]) return final_output def fuse_df(raw_df, windows): outputs = [] all_game_play = raw_df['game_play'].unique() for game_play in all_game_play: df = raw_df.query('game_play ==@game_play') output = dedup_view(df, windows) outputs.append(output) output_df = pd.concat(outputs) output_df['gameKey'] = output_df['game_play'].apply(lambda x: x.split('_')[0]).map(int) output_df['playID'] = output_df['game_play'].apply(lambda x: x.split('_')[1]).map(int) return output_df
Щоб запустити функцію, ми запускаємо наступний блок коду, щоб визначити розташування train_labels.csv
даних, а потім виконайте підготовку даних, щоб додати додатковий стовпець і витягти лише впливові рядки. Після запуску функції ми зберігаємо вихідні дані у змінній кадру даних під назвою fused_df
.
# read the annotated impact data from train_labels.csv
ground_truth = pd.read_csv('train_labels.csv') # prepare game_play column using pipe(prep_data) function in pandas then filter the dataframe for just rows with impacts
ground_truth = ground_truth.pipe(prep_data).query('impact == 1') # loop over all the unique game_plays and deduplicate the impact results from sideline and endzone
fused_df = fuse_df(ground_truth, windows=30)
Наступний скріншот показує основну правду.
На наступному знімку екрана показано приклади об’єднаних кадрів даних.
Графік і відеокод
Після об’єднання результатів впливу ми використовуємо згенеровані fused_df
щоб накласти результати на наші відео з кінцевої та бічної лінії та об’єднати два види разом. Для цього ми використовуємо таку функцію, а необхідні вхідні дані – це шляхи до відео кінцевої зони, відео бічної лінії, fused_df
кадр даних і кінцевий вихідний шлях для щойно згенерованого відео. Функції, які використовуються в цьому розділі, описані в розділі розцінки блокнота, який використовується в SageMaker Studio Lab.
def get_video_and_metadata(vid_path): vid = cv2.VideoCapture(vid_path) total_frame_number = vid.get(cv2.CAP_PROP_FRAME_COUNT) width = int(vid.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = vid.get(cv2.CAP_PROP_FPS) return vid, total_frame_number, width, height, fps def overlay_impacts(frame, fused_df, game_key, play_id, frame_cnt, h1): # look for duplicates duplicates = fused_df.query(f"gameKey == {int(game_key)} and playID == {int(play_id)} and is_dup == True and Sideline_frame == @frame_cnt") frame_has_impact = False if len(duplicates) > 0: for duplicate in duplicates.itertuples(index=False): if frame_cnt == duplicate.Sideline_frame: frame_has_impact = True if frame_has_impact: cv2.rectangle(frame, #frame to be edited (int(duplicate.Sideline_left), int(duplicate.Sideline_top)), #(x,y) of top left corner (int(duplicate.Sideline_left) + int(duplicate.Sideline_width), int(duplicate.Sideline_top) + int(duplicate.Sideline_height)), #(x,y) of bottom right corner (0,0,255), #RED boxes thickness=3) cv2.rectangle(frame, #frame to be edited (int(duplicate.Endzone_left), int(duplicate.Endzone_top)+ h1), #(x,y) of top left corner (int(duplicate.Endzone_left) + int(duplicate.Endzone_width), int(duplicate.Endzone_top) + int(duplicate.Endzone_height) + h1), #(x,y) of bottom right corner (0,0,255), #RED boxes thickness=3) cv2.line(frame, #frame to be edited (int(duplicate.Sideline_left), int(duplicate.Sideline_top)), #(x,y) of point 1 in a line (int(duplicate.Endzone_left), int(duplicate.Endzone_top) + h1), #(x,y) of point 2 in a line (255, 255, 255), # WHITE lines thickness=4) else: # if no duplicates, look for sideline then endzone and add to the view sl_impacts = fused_df.query(f"gameKey == {int(game_key)} and playID == {int(play_id)} and is_dup == False and view == 'Sideline' and Sideline_frame == @frame_cnt") if len(sl_impacts) > 0: for impact in sl_impacts.itertuples(index=False): if frame_cnt == impact.Sideline_frame: frame_has_impact = True if frame_has_impact: cv2.rectangle(frame, #frame to be edited (int(impact.Sideline_left), int(impact.Sideline_top)), #(x,y) of top left corner (int(impact.Sideline_left) + int(impact.Sideline_width), int(impact.Sideline_top) + int(impact.Sideline_height)), #(x,y) of bottom right corner (0, 255, 255), #YELLOW BOXES thickness=3) ez_impacts = fused_df.query(f"gameKey == {int(game_key)} and playID == {int(play_id)} and is_dup == False and view == 'Endzone' and Endzone_frame == @frame_cnt") if len(ez_impacts) > 0: for impact in ez_impacts.itertuples(index=False): if frame_cnt == impact.Endzone_frame: frame_has_impact = True if frame_has_impact: cv2.rectangle(frame, #frame to be edited (int(impact.Endzone_left), int(impact.Endzone_top)+ h1), #(x,y) of top left corner (int(impact.Endzone_left) + int(impact.Endzone_width), int(impact.Endzone_top) + int(impact.Endzone_height) + h1 ), #(x,y) of bottom right corner (0, 255, 255), #YELLOW BOXES thickness=3) return frame, frame_has_impact def generate_impact_video(ez_vid_path:str, sl_vid_path:str, fused_df:pd.DataFrame, output_path:str, freeze_impacts=True): #define video codec to be used for VIDEO_CODEC = "MP4V" # parse game_key and play_id information from the name of the files game_key = os.path.basename(ez_vid_path).split('_')[0] # parse game_key play_id = os.path.basename(ez_vid_path).split('_')[1] # parse play_id # get metadata such as total frame number, width, height and frames per second (FPS) from endzone (ez) and sideline (sl) videos ez_vid, ez_total_frame_number, ez_width, ez_height, ez_fps = get_video_and_metadata(ez_vid_path) sl_vid, sl_total_frame_number, sl_width, sl_height, sl_fps = get_video_and_metadata(sl_vid_path) # define a video writer for the output video output_video = cv2.VideoWriter(output_path, #output file name cv2.VideoWriter_fourcc(*VIDEO_CODEC), #Video codec ez_fps, #frames per second in the output video (ez_width, ez_height+sl_height)) # frame size with stacking video vertically # find shorter video and use the total frame number from the shorter video for the output video total_frame_number = int(min(ez_total_frame_number, sl_total_frame_number)) # iterate through each frame from endzone and sideline for frame_cnt in range(total_frame_number): frame_has_impact = False frame_near_impact = False # reading frames from both endzone and sideline ez_ret, ez_frame = ez_vid.read() sl_ret, sl_frame = sl_vid.read() # creating strings to be added to the output frames img_name = f"Game key: {game_key}, Play ID: {play_id}, Frame: {frame_cnt}" video_frame = f'{game_key}_{play_id}_{frame_cnt}' if ez_ret == True and sl_ret == True: h, w, c = ez_frame.shape h1,w1,c1 = sl_frame.shape if h != h1 or w != w1: # resize images if they're different ez_frame = cv2.resize(ez_frame,(w1,h1)) frame = np.concatenate((sl_frame, ez_frame), axis=0) # stack the frames vertically frame, frame_has_impact = overlay_impacts(frame, fused_df, game_key, play_id, frame_cnt, h1) cv2.putText(frame, #image frame to be modified img_name, #string to be inserted (30, 30), #(x,y) location of the string cv2.FONT_HERSHEY_SIMPLEX, #font 1, #scale (255, 255, 255), #WHITE letters thickness=2) cv2.putText(frame, #image frame to be modified str(frame_cnt), #frame count string to be inserted (w1-75, h1-20), #(x,y) location of the string in the top view cv2.FONT_HERSHEY_SIMPLEX, #font 1, #scale (255, 255, 255), # WHITE letters thickness=2) cv2.putText(frame, #image frame to be modified str(frame_cnt), #frame count string to be inserted (w1-75, h1+h-20), #(x,y) location of the string in the bottom view cv2.FONT_HERSHEY_SIMPLEX, #font 1, #scale (255, 255, 255), # WHITE letters thickness=2) output_video.write(frame) # Freeze for 60 frames on impacts if frame_has_impact and freeze_impacts: for _ in range(60): output_video.write(frame) else: break frame_cnt += 1 output_video.release() return
Щоб запустити ці функції, ми можемо надати вхідні дані, як показано в наступному коді, який генерує виклик відео output.mp4
:
generate_impact_video('57583_000082_Endzone.mp4', '57583_000082_Sideline.mp4', fused_df, 'output.mp4')
Це генерує відео, як показано в наведеному нижче прикладі, де червоні обмежувальні прямокутники відображають удари, які спостерігаються як у перегляді кінцевої зони, так і в бічній лінії, а жовті обмежувальні прямокутники відображають удари, які спостерігаються лише в одному перегляді в кінцевій зоні чи бічній лінії.
Висновок
У цьому дописі ми продемонстрували, як команди NFL, Biocore та AWS ProServe працюють разом, щоб покращити виявлення впливу, поєднуючи результати з кількох переглядів. Це дозволяє командам налагодити та візуалізувати, як якісно працює модель. Цей процес можна легко масштабувати до трьох або більше переглядів; у наших проектах ми використовували до семи різних переглядів. Виявлення зіткнень шолома шляхом перегляду відео лише з одного ракурсу може бути складним через перешкоди для перегляду, але виявлення зіткнень з кількох ракурсів і об’єднання результатів дозволяє нам покращити продуктивність нашої моделі.
Щоб поекспериментувати з цим рішенням, відвідайте aws-samples GitHub repo і посилаються на fuse_and_visualize_multiview_impacts.ipynb блокнот. Подібні методи також можуть бути застосовані до інших галузей, таких як виробництво, роздрібна торгівля та безпека, де наявність кількох переглядів принесе користь системі ML для кращого визначення цілей із більш повним уявленням.
Для отримання додаткової інформації щодо здоров’я та безпеки гравців НФЛ відвідайте Сайт НФЛ та Пояснення НФЛ: інновації у сфері здоров’я та безпеки гравців.
Про авторів
Кріс Бумхауер є інженером машинного навчання в AWS Professional Services. Кріс має понад 6 років досвіду розробки контрольованих і неконтрольованих рішень машинного навчання в різних галузях. Сьогодні він витрачає більшу частину свого часу, допомагаючи клієнтам у сфері спорту, охорони здоров’я та сільського господарства розробляти та створювати масштабовані наскрізні рішення машинного навчання.
Бен Фенкер є старшим спеціалістом із обробки даних у AWS Professional Services і допомагав клієнтам створювати та розгортати рішення ML у різних галузях – від спорту до охорони здоров’я та виробництва. Має ступінь доктора філософії. ступінь магістра фізики в Техаському університеті A&M і 6 років досвіду роботи в галузі. Бен любить бейсбол, читає та виховує дітей.
Сем Хаддлстон є головним спеціалістом із обробки даних у Biocore LLC, який виконує функції технологічного керівника програми Digital Athlete NFL. Biocore — це команда інженерів світового класу, розташована в Шарлоттсвіллі, штат Вірджинія, яка надає клієнтам дослідження, тестування, експертизу з біомеханіки, моделювання та інші інженерні послуги, спрямовані на розуміння та зменшення травм.
Джарвіс Лі є старшим спеціалістом із обробки даних у AWS Professional Services. Він працює в AWS більше п’яти років, працюючи з клієнтами над проблемами машинного навчання та комп’ютерного зору. Поза роботою захоплюється їздою на велосипеді.
Тайлер Малленбах є керівником глобальної практики ML у AWS Professional Services. Він відповідає за стратегічний напрямок ML для професійних послуг і гарантує, що клієнти зможуть реалізувати трансформаційні досягнення бізнесу завдяки застосуванню технологій ML.
Кевін Сонг є спеціалістом з обробки даних в AWS Professional Services. Він має ступінь доктора філософії з біофізики та має понад 5 років досвіду роботи в галузі створення рішень для комп’ютерного зору та машинного навчання.
Бетті Чжан є науковцем з даних із 10-річним досвідом роботи з даними та технологіями. Її пристрасть полягає в створенні інноваційних рішень для машинного навчання, щоб стимулювати трансформаційні зміни для компаній. У вільний час вона любить подорожувати, читати та вивчати нові технології.
- Розповсюдження контенту та PR на основі SEO. Отримайте посилення сьогодні.
- Платоблокчейн. Web3 Metaverse Intelligence. Розширені знання. Доступ тут.
- джерело: https://aws.amazon.com/blogs/machine-learning/analyze-and-visualize-multi-camera-events-using-amazon-sagemaker-studio-lab/
- 1
- 10
- 100
- 202
- 7
- a
- МЕНЮ
- рахунки
- Досягнення
- через
- доданий
- Додатковий
- Прийняття
- після
- сільське господарство
- ВСІ
- дозволяє
- Amazon
- Amazon SageMaker
- Студія Amazon SageMaker
- Amazon SageMaker Studio Lab
- аналізувати
- та
- прикладної
- затверджений
- навколо
- Спортсмен
- автоматично
- доступний
- AWS
- Професійні послуги AWS
- бейсбол
- заснований
- оскільки
- за
- користь
- Краще
- Біофізика
- Блокувати
- блокований
- дно
- коробки
- Перерва
- будувати
- Створюємо
- бізнес
- званий
- кімната
- Зміни
- Вибирати
- Вибираючи
- клієнтів
- код
- Кодова база
- Колонка
- Колони
- вчинено
- Компанії
- конкурс
- повний
- всеосяжний
- обчислення
- комп'ютер
- Комп'ютерне бачення
- містить
- Кут
- створювати
- створення
- Клієнти
- дані
- Підготовка даних
- вчений даних
- присвячених
- продемонстрований
- розгортання
- описаний
- дизайн
- виявлено
- Виявлення
- розвиненою
- розвивається
- різний
- важкий
- цифровий
- напрям
- скачати
- управляти
- водіння
- дублікати
- кожен
- легко
- зусилля
- або
- дозволяє
- кінець в кінець
- інженер
- Машинобудування
- Інженери
- забезпечення
- Навколишнє середовище
- оцінювати
- Події
- Кожен
- приклад
- Приклади
- досвід
- експеримент
- експертиза
- пояснені
- витяг
- поле
- філе
- Файли
- фільтрувати
- остаточний
- знайти
- Перший
- Сфокусувати
- стежити
- після
- футбол
- знайдений
- кадрів в секунду
- FRAME
- Безкоштовна
- Заморожувати
- від
- функція
- Функції
- злиття
- гра
- генерується
- генерує
- отримати
- GitHub
- Глобальний
- GPU
- Земля
- має
- здоров'я
- охорона здоров'я
- висота
- допомога
- допоміг
- допомогу
- допомагає
- тримає
- Як
- HTTPS
- людина
- ID
- ідентифікувати
- ідентифікує
- зображення
- зображень
- Impact
- Вплив
- імпорт
- удосконалювати
- in
- includes
- індекс
- індивідуальний
- промисловості
- промисловість
- інформація
- інновація
- інноваційний
- вхід
- встановлювати
- інструкції
- IT
- тільки один
- ключ
- Діти
- lab
- етикетка
- етикетки
- вести
- Ліга
- ліги
- вивчення
- обмеження
- Лінія
- ліній
- LLC
- розташування
- подивитися
- машина
- навчання за допомогою машини
- зробити
- маніпулювання
- виробництво
- Злиття
- злиття
- метадані
- може бути
- ML
- режим
- модель
- модифікований
- більше
- найбільш
- Найбільш популярний
- мотивація
- множинний
- ім'я
- National
- необхідно
- Нові
- Нові технології
- NFL
- ноутбук
- номер
- нумпі
- об'єкти
- ONE
- OpenCV
- Відкриється
- Опції
- OS
- Інше
- поза
- пакети
- панди
- приватність
- партнерська
- пристрасть
- шлях
- Виконувати
- продуктивність
- виконанні
- перспектива
- перспективи
- Фізика
- вибирати
- plato
- Інформація про дані Платона
- PlatoData
- Play
- гравець
- гравці
- точка
- популярний
- пошта
- потужний
- практика
- Готувати
- Попередження
- Головний
- проблеми
- процес
- професійний
- програма
- проектів
- забезпечувати
- забезпечує
- публічно
- Python
- залучення
- ранжування
- RE
- Читати
- читання
- готовий
- реалізувати
- червоний
- про
- видалення
- Сховище
- вимагається
- Вимагається
- дослідження
- відповідальний
- результати
- роздрібна торгівля
- повертати
- верхова їзда
- ROW
- прогін
- біг
- безпечніше
- Безпека
- мудрець
- SageMaker Studio Lab
- зберегти
- масштабовані
- вчений
- другий
- розділ
- безпеку
- старший
- служить
- Послуги
- сесіях
- комплект
- сім
- Форма
- Показувати
- демонстрації
- показаний
- Шоу
- аналогічний
- один
- Розмір
- рішення
- Рішення
- деякі
- SPORTS
- стек
- укладання
- почалася
- починається
- Штати
- заходи
- зберігання
- Стратегічний
- студія
- такі
- система
- приймає
- цілі
- команда
- команди
- методи
- Технології
- Технологія
- Тестування
- Техас
- Команда
- отже
- три
- через
- час
- до
- сьогодні
- разом
- інструменти
- топ
- Усього:
- перетворювальний
- Подорож
- лікування
- правда
- розуміння
- створеного
- United
- Сполучені Штати
- університет
- us
- використання
- користувач
- використовувати
- використовує
- Цінний
- різний
- Відео
- Відео
- вид
- думки
- Віргінія
- видимий
- бачення
- візуалізації
- W
- спостереження
- який
- білий
- ВООЗ
- Вікіпедія
- волі
- windows
- Work
- робочий
- Світовий клас
- б
- письменник
- лист
- X
- років
- Ти
- вашу
- YouTube
- зефірнет