Trí thông minh dữ liệu tạo

Phân tích và trực quan hóa các sự kiện nhiều camera bằng Amazon SageMaker Studio Lab

Ngày:

National Football League (NFL) là một trong những giải đấu thể thao phổ biến nhất tại Hoa Kỳ và là giải đấu thể thao giá trị nhất thế giới. NFL, BioCore và AWS cam kết nâng cao hiểu biết của con người về chẩn đoán, phòng ngừa và điều trị các chấn thương liên quan đến thể thao để giúp trận đấu bóng đá trở nên an toàn hơn. Thông tin thêm về các nỗ lực về Sức khỏe và An toàn của Người chơi NFL có sẵn trên Trang web của NFL.

Sản phẩm Dịch vụ chuyên nghiệp của AWS nhóm đã hợp tác với NFL và Biocore để cung cấp các giải pháp dựa trên máy học (ML) nhằm xác định tác động của mũ bảo hiểm từ cảnh quay trò chơi bằng kỹ thuật thị giác máy tính (CV). Với nhiều chế độ xem camera có sẵn từ mỗi trò chơi, chúng tôi đã phát triển các giải pháp để xác định tác động của mũ bảo hiểm từ từng chế độ xem này và hợp nhất các kết quả về tác động của mũ bảo hiểm.

Động lực đằng sau việc sử dụng nhiều chế độ xem camera xuất phát từ giới hạn thông tin khi các sự kiện tác động được ghi lại chỉ bằng một chế độ xem. Chỉ với một góc nhìn, một số người chơi có thể che khuất nhau hoặc bị chặn bởi các vật thể khác trên sân. Do đó, việc thêm nhiều góc nhìn hơn cho phép hệ thống ML của chúng tôi xác định được nhiều tác động hơn mà không thể nhìn thấy trong một chế độ xem. Để giới thiệu kết quả của quy trình hợp nhất của chúng tôi và cách nhóm sử dụng các công cụ trực quan để giúp đánh giá hiệu suất của mô hình, chúng tôi đã phát triển một cơ sở mã để phủ một cách trực quan các kết quả phát hiện nhiều chế độ xem. Quá trình này giúp xác định số lượng tác động thực tế mà từng người chơi gặp phải bằng cách loại bỏ các tác động trùng lặp được phát hiện trong nhiều chế độ xem.

Trong bài đăng này, chúng tôi sử dụng bộ dữ liệu có sẵn công khai từ NFL – Cuộc thi Kaggle Phát hiện Tác động và hiển thị kết quả để hợp nhất hai chế độ xem. Bộ dữ liệu bao gồm các hộp giới hạn mũ bảo hiểm ở mọi khung hình và nhãn tác động được tìm thấy trong mỗi video. Cụ thể, chúng tôi tập trung vào việc loại bỏ trùng lặp và trực quan hóa các video có ID 57583_000082 trong chế độ xem endzone và sideline. Bạn có thể tải về endzone và video bên lềvà cả nhãn sự thật mặt đất.

Điều kiện tiên quyết

Giải pháp yêu cầu như sau:

Bắt đầu trên SageMaker Studio Lab và cài đặt các gói cần thiết

Bạn có thể chạy sổ ghi chép từ GitHub kho lưu trữ hoặc từ SageMaker Studio Lab. Trong bài đăng này, chúng tôi chạy sổ ghi chép từ môi trường SageMaker Studio Lab. Chúng tôi chọn SageMaker Studio Lab vì nó miễn phí, cung cấp các phiên người dùng CPU và GPU mạnh mẽ cũng như 15GB dung lượng lưu trữ liên tục sẽ tự động lưu môi trường của bạn, cho phép bạn tiếp tục từ nơi bạn đã dừng lại. Để sử dụng SageMaker Studio Lab, yêu cầu và thiết lập một tài khoản mới. Sau khi tài khoản được phê duyệt, hãy hoàn thành các bước sau:

  1. Truy cập vào kho lưu trữ aws-samples GitHub.
  2. Trong tạp chí README phần, chọn Phòng thí nghiệm phòng thu mở.

sagemaker-studio-nút

Thao tác này sẽ chuyển hướng bạn đến môi trường SageMaker Studio Lab của bạn.

  1. Chọn loại tính toán CPU của bạn, sau đó chọn Bắt đầu thời gian chạy.
  2. Sau khi thời gian chạy bắt đầu, hãy chọn Sao chép vào dự án, mở ra một cửa sổ mới với môi trường Jupyter Lab.

Bây giờ bạn đã sẵn sàng để sử dụng sổ ghi chép!

  1. Mở fuse_and_visualize_multiview_impacts.ipynb và làm theo hướng dẫn trong vở.

Ô đầu tiên trong notebook cài đặt các gói Python cần thiết như pandas và OpenCV:

%pip install pandas
%pip install opencv-contrib-python-headless

Nhập tất cả các gói Python cần thiết và đặt tùy chọn gấu trúc để có trải nghiệm trực quan tốt hơn:

import os
import cv2
import pandas as pd
import numpy as np
pd.set_option('mode.chained_assignment', None)

Chúng tôi sử dụng gấu trúc để nhập và phân tích cú pháp thông qua tệp CSV với các hộp giới hạn mũ bảo hiểm được chú thích cũng như các tác động. Chúng tôi sử dụng NumPy chủ yếu để thao tác với mảng và ma trận. Chúng tôi sử dụng OpenCV để đọc, viết và thao tác dữ liệu hình ảnh trong Python.

Chuẩn bị dữ liệu bằng cách hợp nhất kết quả từ hai chế độ xem

Để hợp nhất hai quan điểm lại với nhau, chúng tôi sử dụng train_labels.csv từ cuộc thi Kaggle làm ví dụ vì nó chứa các tác động thực tế cơ bản từ cả khu vực cuối và khu vực bên lề. Hàm sau lấy tập dữ liệu đầu vào và xuất ra một khung dữ liệu hợp nhất được loại bỏ trùng lặp cho tất cả các lần phát trong tập dữ liệu đầu vào:

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

Để chạy chức năng, chúng tôi chạy khối mã sau để cung cấp vị trí của train_labels.csv dữ liệu và sau đó thực hiện chuẩn bị dữ liệu để thêm một cột bổ sung và chỉ trích xuất các hàng tác động. Sau khi chạy chức năng, chúng tôi lưu đầu ra vào một biến khung dữ liệu được gọi là 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)

Ảnh chụp màn hình sau đây cho thấy sự thật cơ bản.

Ảnh chụp màn hình sau đây hiển thị các ví dụ về khung dữ liệu hợp nhất.

Biểu đồ và mã video

Sau khi chúng tôi hợp nhất các kết quả tác động, chúng tôi sử dụng fused_df để phủ kết quả lên các video bên lề và vùng cuối của chúng tôi và hợp nhất hai chế độ xem lại với nhau. Chúng tôi sử dụng chức năng sau cho việc này và đầu vào cần thiết là đường dẫn đến video khu vực cuối, video bên lề, fused_df khung dữ liệu và đường dẫn đầu ra cuối cùng cho video mới được tạo. Các chức năng được sử dụng trong phần này được mô tả trong phần đánh dấu của sổ ghi chép được sử dụng trong 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

Để chạy các chức năng này, chúng tôi có thể cung cấp đầu vào như được hiển thị trong đoạn mã sau, tạo ra một video có tên output.mp4:

generate_impact_video('57583_000082_Endzone.mp4', '57583_000082_Sideline.mp4', fused_df, 'output.mp4')

Điều này tạo ra một video như được hiển thị trong ví dụ sau, trong đó các hộp giới hạn màu đỏ là các tác động được tìm thấy ở cả chế độ xem vùng cuối và bên lề và các hộp giới hạn màu vàng là các tác động chỉ được tìm thấy trong một chế độ xem ở vùng cuối hoặc bên lề.

Kết luận

Trong bài đăng này, chúng tôi đã trình bày cách các nhóm NFL, Biocore và AWS ProServe phối hợp với nhau để cải thiện khả năng phát hiện tác động bằng cách kết hợp các kết quả từ nhiều chế độ xem. Điều này cho phép các nhóm gỡ lỗi và hình dung mô hình đang hoạt động một cách định tính như thế nào. Quá trình này có thể dễ dàng được mở rộng lên đến ba hoặc nhiều chế độ xem; trong các dự án của mình, chúng tôi đã sử dụng tới bảy chế độ xem khác nhau. Việc phát hiện tác động của mũ bảo hiểm bằng cách xem video chỉ từ một chế độ xem có thể khó khăn do bị che khuất tầm nhìn, nhưng việc phát hiện tác động từ nhiều chế độ xem và kết hợp các kết quả cho phép chúng tôi cải thiện hiệu suất mô hình của mình.

Để thử nghiệm với giải pháp này, hãy truy cập kho lưu trữ aws-samples GitHub và tham khảo Fuse_and_visualize_multiview_impacts.ipynb sổ tay. Các kỹ thuật tương tự cũng có thể được áp dụng cho các ngành khác như sản xuất, bán lẻ và bảo mật, trong đó việc có nhiều chế độ xem sẽ mang lại lợi ích cho hệ thống ML để xác định mục tiêu tốt hơn với chế độ xem toàn diện hơn.

Để biết thêm thông tin về Sức khỏe và An toàn của Người chơi NFL, hãy truy cập Trang web của NFLGiải thích về NFL: Đổi mới về Sức khỏe & An toàn của Người chơi.


Giới thiệu về tác giả

Chris Boomhower là Kỹ sư máy học tại AWS Professional Services. Chris có hơn 6 năm kinh nghiệm phát triển các giải pháp Học máy có giám sát và không giám sát trong nhiều ngành khác nhau. Hiện nay, anh ấy dành phần lớn thời gian của mình để giúp khách hàng trong các ngành thể thao, chăm sóc sức khỏe và nông nghiệp thiết kế và xây dựng các giải pháp Máy học toàn diện, có thể mở rộng.

Ben Fenker là Nhà khoa học dữ liệu cấp cao trong Dịch vụ chuyên nghiệp của AWS và đã giúp khách hàng xây dựng và triển khai các giải pháp ML trong các ngành từ thể thao đến chăm sóc sức khỏe đến sản xuất. Anh ấy có bằng tiến sĩ. bằng vật lý của Đại học Texas A&M và 6 năm kinh nghiệm trong ngành. Ben thích bóng chày, đọc sách và nuôi dạy con cái.

Sam Huddleston là Nhà khoa học dữ liệu chính tại Biocore LLC, người đóng vai trò là Trưởng nhóm công nghệ cho chương trình Vận động viên kỹ thuật số của NFL. Biocore là một nhóm các kỹ sư đẳng cấp thế giới có trụ sở tại Charlottesville, Virginia, chuyên cung cấp dịch vụ nghiên cứu, thử nghiệm, chuyên môn về cơ chế sinh học, mô hình hóa và các dịch vụ kỹ thuật khác cho khách hàng nhằm mục đích hiểu và giảm thiểu thương tích.

Jarvis Lee là Nhà khoa học dữ liệu cấp cao của AWS Professional Services. Anh ấy đã làm việc cho AWS hơn XNUMX năm, làm việc với khách hàng về các vấn đề liên quan đến máy học và thị giác máy tính. Ngoài công việc, anh ấy thích đi xe đạp.

Tyler Mullenbach là Trưởng nhóm Thực hành Toàn cầu về ML với AWS Professional Services. Ông chịu trách nhiệm thúc đẩy định hướng chiến lược của ML cho các Dịch vụ Chuyên nghiệp và đảm bảo rằng khách hàng nhận ra những thành tựu kinh doanh mang tính chuyển đổi thông qua việc áp dụng các công nghệ ML.

Bài hát Kevin là Nhà khoa học dữ liệu tại AWS Professional Services. Ông có bằng Tiến sĩ về Vật lý sinh học và có hơn 5 năm kinh nghiệm trong ngành xây dựng các giải pháp máy học và thị giác máy tính.

Betty Trương là một nhà khoa học dữ liệu với 10 năm kinh nghiệm về dữ liệu và công nghệ. Niềm đam mê của cô là xây dựng các giải pháp máy học sáng tạo để thúc đẩy những thay đổi mang tính chuyển đổi cho các công ty. Khi rảnh rỗi, cô thích đi du lịch, đọc và tìm hiểu về các công nghệ mới.

tại chỗ_img

Tin tức mới nhất

tại chỗ_img