ryryrymyg / kaggle_for_me

This is repository that help me to understand kaggle competition
0 stars 0 forks source link

🏈Tuning DeepSort + Helmet Mapping_high_score その2 #3

Open ryryrymyg opened 3 years ago

ryryrymyg commented 3 years ago

上記コードの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アルゴリズムを適用します。これにより、同じプレーヤー/ヘルメットであれば、フレーム間でヘルメットがクラスター化されます。 これらのディープソート・クラスターごとにグループ化し、そのクラスターに対して最も一般的なラベルを選びます。そして、そのヘルメットの予測をすべて、同じプレーヤーに上書きします。

ryryrymyg commented 3 years ago

Importing Deepsort from dataset

submissionノートはインターネットアクセスの使用が許可されていないので、添付のデータセットからDeepsortのコードベースを参照します。Deepsortはeasydictにも依存しており、これもデータセットとして追加しました。

ryryrymyg commented 3 years ago

DeepSortのデータセットをインポートするコードブロック

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
ryryrymyg commented 3 years ago

※コードに対しての補足 sys.path: importするモジュールを検索するパス sys.path.append: sys.pathはただのリスト型オブジェクトなので、そこにディレクトリを追加することで、モジュールを探索できるようになる

ryryrymyg commented 3 years ago

DeepSortコンフィグ

DeepSortはコンフィグyamlファイルを参照して設定を行うため、作成する必要がある。 次のyamlファイルはデフォルト値であるため、のちに設定し直す必要がある。

ryryrymyg commented 3 years ago

%%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
ryryrymyg commented 3 years ago

次の関数はdeepsortのラベルを書き出すためのyolov5のヘルパー関数

ryryrymyg commented 3 years ago
"""
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
ryryrymyg commented 3 years ago
ryryrymyg commented 3 years ago

ヘルメットボックスにdeepsortを適用するための関数

以下は、ビデオを横断して deepsort を実行する 2 つの関数 deepsort_helmets です。この関数には改良の余地がたくさんあります。deepsort のラベルを元のヘルメットボックスにマージするのは、現在のところ非常に雑な方法で行われています。

add_deepsort_label_col は、最も一般的なラベルを各 deepsort クラスタにマッピングします。

ryryrymyg commented 3 years ago
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')
ryryrymyg commented 3 years ago

DeepSortを、HelmetMappingで作成したBaselineデータに適応させる。

以下のコードによって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()
ryryrymyg commented 3 years ago

Check Submission & Save

作成したモデルによって出力されるsubmission.csvが提出要件に沿っているのか調べ、提出をします。 以下のステップを踏みます。

ryryrymyg commented 3 years ago
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)
ryryrymyg commented 3 years ago

Display video showing predictions

最後に、自分の予測を確認したい場合は、helmet_assignment helper packageのvideo_with_predictions関数を使って、予測を確認するためのビデオを作成します。

ryryrymyg commented 3 years ago

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))
           )
ryryrymyg commented 3 years ago

改善の余地があるとすればDeepSort部分。deepsortのラベルを元のヘルメットボックスにマージするのはとても雑な方法で行われているらしい。 読む必要があるノートとして、