Open ryryrymyg opened 3 years ago
submissionノートはインターネットアクセスの使用が許可されていないので、添付のデータセットからDeepsortのコードベースを参照します。Deepsortはeasydictにも依存しており、これもデータセットとして追加しました。
import sys
sys.path.append('../input/easydict-master/easydict-master/')
# https://github.com/mikel-brostrom/Yolov5_DeepSort_Pytorch
sys.path.append('../input/yolov5-deepsort-pytorch/Yolov5_DeepSort_Pytorch-master/Yolov5_DeepSort_Pytorch-master/deep_sort_pytorch/')
from deep_sort.deep_sort import DeepSort
from utils.parser import get_config
※コードに対しての補足 sys.path: importするモジュールを検索するパス sys.path.append: sys.pathはただのリスト型オブジェクトなので、そこにディレクトリを追加することで、モジュールを探索できるようになる
DeepSortはコンフィグyamlファイルを参照して設定を行うため、作成する必要がある。 次のyamlファイルはデフォルト値であるため、のちに設定し直す必要がある。
%%writefile deepsort.yaml
DEEPSORT:
REID_CKPT: "../input/yolov5-deepsort-pytorch/ckpt.t7"
MAX_DIST: 0.2
MIN_CONFIDENCE: 0.3
NMS_MAX_OVERLAP: 0.5
MAX_IOU_DISTANCE: 0.9
MAX_AGE: 15
N_INIT: 1
NN_BUDGET: 30
次の関数はdeepsortのラベルを書き出すためのyolov5のヘルパー関数
"""
Helper functions from yolov5 to plot deepsort labels.
"""
def compute_color_for_id(label):
"""
Simple function that adds fixed color depending on the id
"""
palette = (2 ** 11 - 1, 2 ** 15 - 1, 2 ** 20 - 1)
color = [int((p * (label ** 2 - label + 1)) % 255) for p in palette]
return tuple(color)
def plot_one_box(x, im, color=None, label=None, line_thickness=3):
# Plots one bounding box on image 'im' using OpenCV
assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to plot_on_box() input image.'
tl = line_thickness or round(0.002 * (im.shape[0] + im.shape[1]) / 2) + 1 # line/font thickness
color = color or [random.randint(0, 255) for _ in range(3)]
c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
cv2.rectangle(im, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
if label:
tf = max(tl - 1, 1) # font thickness
t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
cv2.rectangle(im, c1, c2, color, -1, cv2.LINE_AA) # filled
cv2.putText(im, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
return im
以下は、ビデオを横断して deepsort を実行する 2 つの関数 deepsort_helmets です。この関数には改良の余地がたくさんあります。deepsort のラベルを元のヘルメットボックスにマージするのは、現在のところ非常に雑な方法で行われています。
add_deepsort_label_col は、最も一般的なラベルを各 deepsort クラスタにマッピングします。
def deepsort_helmets(video_data,
video_dir,
deepsort_config='deepsort.yaml',
plot=False,
plot_frames=[]):
# Setup Deepsort
cfg = get_config()
cfg.merge_from_file(deepsort_config)
deepsort = DeepSort(cfg.DEEPSORT.REID_CKPT,
max_dist=cfg.DEEPSORT.MAX_DIST,
min_confidence=cfg.DEEPSORT.MIN_CONFIDENCE,
nms_max_overlap=cfg.DEEPSORT.NMS_MAX_OVERLAP,
max_iou_distance=cfg.DEEPSORT.MAX_IOU_DISTANCE,
max_age=cfg.DEEPSORT.MAX_AGE,
n_init=cfg.DEEPSORT.N_INIT,
nn_budget=cfg.DEEPSORT.NN_BUDGET,
use_cuda=True)
# Run through frames.
video_data = video_data.sort_values('frame').reset_index(drop=True)
ds = []
for frame, d in tqdm(video_data.groupby(['frame']), total=video_data['frame'].nunique()):
d['x'] = (d['left'] + round(d['width'] / 2))
d['y'] = (d['top'] + round(d['height'] / 2))
xywhs = d[['x','y','width','height']].values
cap = cv2.VideoCapture(f'{video_dir}/{myvideo}.mp4')
cap.set(cv2.CAP_PROP_POS_FRAMES, frame-1) # optional
success, image = cap.read()
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
confs = np.ones([len(d),])
clss = np.zeros([len(d),])
outputs = deepsort.update(xywhs, confs, clss, image)
if (plot and frame > cfg.DEEPSORT.N_INIT) or (frame in plot_frames):
for j, (output, conf) in enumerate(zip(outputs, confs)):
bboxes = output[0:4]
id = output[4]
cls = output[5]
c = int(cls) # integer class
label = f'{id}'
color = compute_color_for_id(id)
im = plot_one_box(bboxes, image, label=label, color=color, line_thickness=2)
fig, ax = plt.subplots(figsize=(15, 10))
video_frame = d['video_frame'].values[0]
ax.set_title(f'Deepsort labels: {video_frame}')
plt.imshow(im)
plt.show()
preds_df = pd.DataFrame(outputs, columns=['left','top','right','bottom','deepsort_cluster','class'])
if len(preds_df) > 0:
# TODO Fix this messy merge
d = pd.merge_asof(d.sort_values(['left','top']),
preds_df[['left','top','deepsort_cluster']] \
.sort_values(['left','top']), on='left', suffixes=('','_deepsort'),
direction='nearest')
ds.append(d)
dout = pd.concat(ds)
return dout
def add_deepsort_label_col(out):
# Find the top occuring label for each deepsort_cluster
sortlabel_map = out.groupby('deepsort_cluster')['label'].value_counts() \
.sort_values(ascending=False).to_frame() \
.rename(columns={'label':'label_count'}) \
.reset_index() \
.groupby(['deepsort_cluster']) \
.first()['label'].to_dict()
# Find the # of times that label appears for the deepsort_cluster.
sortlabelcount_map = out.groupby('deepsort_cluster')['label'].value_counts() \
.sort_values(ascending=False).to_frame() \
.rename(columns={'label':'label_count'}) \
.reset_index() \
.groupby(['deepsort_cluster']) \
.first()['label_count'].to_dict()
out['label_deepsort'] = out['deepsort_cluster'].map(sortlabel_map)
out['label_count_deepsort'] = out['deepsort_cluster'].map(sortlabelcount_map)
return out
def score_vs_deepsort(myvideo, out, labels):
# Score the base predictions compared to the deepsort postprocessed predictions.
myvideo_mp4 = myvideo + '.mp4'
labels_video = labels.query('video == @myvideo_mp4')
scorer = NFLAssignmentScorer(labels_video)
out_deduped = out.groupby(['video_frame','label']).first().reset_index()
base_video_score = scorer.score(out_deduped)
out_preds = out.drop('label', axis=1).rename(columns={'label_deepsort':'label'})
print(out_preds.shape)
out_preds = out_preds.groupby(['video_frame','label']).first().reset_index()
print(out_preds.shape)
deepsort_video_score = scorer.score(out_preds)
print(f'{base_video_score:0.5f} before --> {deepsort_video_score:0.5f} deepsort')
以下のコードによってDeepSortをBaselineデータに結合します。
# Add video and frame columns to submission.
submission_df['video'] = submission_df['video_frame'].str.split('_').str[:3].str.join('_')
submission_df['frame'] = submission_df['video_frame'].str.split('_').str[-1].astype('int')
if debug:
video_dir = '../input/nfl-health-and-safety-helmet-assignment/train/'
else:
video_dir = '../input/nfl-health-and-safety-helmet-assignment/test/'
# Loop through test videos and apply. If in debug mode show the score change.
out_ds = []
outs = []
for myvideo, video_data in tqdm(submission_df.groupby('video'), total=submission_df['video'].nunique()):
print(f'==== {myvideo} ====')
if debug:
# Plot deepsort labels when in debug mode.
out = deepsort_helmets(video_data, video_dir, plot_frames=[10, 150, 250])
else:
out = deepsort_helmets(video_data, video_dir)
out_ds.append(out)
out = add_deepsort_label_col(out)
outs.append(out)
if debug:
# Score
score_vs_deepsort(myvideo, out, labels)
submission_deepsort = pd.concat(outs).copy()
作成したモデルによって出力されるsubmission.csvが提出要件に沿っているのか調べ、提出をします。 以下のステップを踏みます。
ss = pd.read_csv('../input/nfl-health-and-safety-helmet-assignment/sample_submission.csv')
# Final Checks
submission_deepsort['label_deepsort'] = submission_deepsort['label_deepsort'] \
.fillna(submission_deepsort['label'])
submission_deepsort = submission_deepsort.drop('label', axis=1) \
.rename(columns={'label_deepsort':'label'})[ss.columns]
# Drop duplicate labels
submission_deepsort = submission_deepsort.loc[
~submission_deepsort[['video_frame','label']].duplicated()]
check_submission(submission_deepsort)
submission_deepsort.to_csv('submission.csv', index=False)
最後に、自分の予測を確認したい場合は、helmet_assignment helper packageのvideo_with_predictions関数を使って、予測を確認するためのビデオを作成します。
from helmet_assignment.video import video_with_predictions
from IPython.display import Video, display
if debug:
submission_deepsort['video'] = submission_deepsort['video_frame'].str.split('_').str[:3].str.join('_') + '.mp4'
debug_videos = submission_deepsort['video'].unique()
debug_labels = labels.query('video in @debug_videos')
scorer = NFLAssignmentScorer(debug_labels)
scorer.score(submission_deepsort)
# Create video showing predictions for one of the videos.
video_out = video_with_predictions(
f'../input/nfl-health-and-safety-helmet-assignment/train/{debug_videos[0]}',
scorer.sub_labels)
frac = 0.60 # scaling factor for display
display(Video(data=video_out,
embed=True,
height=int(720*frac),
width=int(1280*frac))
)
改善の余地があるとすればDeepSort部分。deepsortのラベルを元のヘルメットボックスにマージするのはとても雑な方法で行われているらしい。 読む必要があるノートとして、
上記コードのDeepSort部分をまとめる。
DeepSort
Deepsortは、ビデオ内のオブジェクトトラッキングのための人気のフレームワークです。
このISSUEでは、このフレームワークの活用例を紹介しています。 このノートブックでは、ヘルメットのデータセットにDeepsortを適用する方法を示しています: https://www.kaggle.com/s903124/nfl-helmet-with-yolov5-deepsort-starter また、Deepsortの論文はこちらからご覧いただけます: https://arxiv.org/pdf/1703.07402.pdf アプローチは非常にシンプルです。
動画の各フレームを見て、deepsortアルゴリズムを適用します。これにより、同じプレーヤー/ヘルメットであれば、フレーム間でヘルメットがクラスター化されます。 これらのディープソート・クラスターごとにグループ化し、そのクラスターに対して最も一般的なラベルを選びます。そして、そのヘルメットの予測をすべて、同じプレーヤーに上書きします。