はじめに
今回はインスタンスセグメンテーションというAI技術をつかってみます。インスタンスセグメンテーションモデルとしては、以前紹介した物体検出アルゴリズムのYOLOから派生した、動画に特化した初のリアルタイムインスタンスセグメンテーションモデルであるYolactEdgeを使っていきます。インスタンスセグメンテーションを用いることで図-1のように1つの画像に複数の🐘が写っていた場合、それぞれを別々の物体と検出できます。
図-1 インスタンスセグメンテーションモデルによる検知例
インスタンスセグメンテーションとは
インスタンスセグメンテーションによって、重なっている物体を別々に検出できます。画像の中にある物体の領域を特定し、個体ごとに領域分割して物体の種類を認識できる手法です。各物体に対して、異なるIDを与えるため、図-2のように1つの画像に検知対象が複数写っている場合、それぞれを別々の物体と検出します。
さらに、空や道路などの物体ではないものはラベルを与えません。
図-2 Yolact Edgeによる検知例
YolactEdgeについて
YolactEdgeは、Real-time Instance Segmentation on the Edgeと呼ばれ、処理が高速でリアルタイムに動作可能な Instance Segmentation モデルです。MS COCOデータセットを用いた実験では、既存のリアルタイム手法に比べて3~5倍の速度向上を実現し、検出精度は見劣りしないことが検証されています。
YolactEdgeでは、Yolactから下記2点の改良をおこなっています。
①速度と精度を慎重にトレードオフしながらTensorRT最適化を適用。
②動画の時間的冗長性(時間的に近いフレームは相関が高い)を利用した新しい特徴ワープモジュール
キーフレーム(図-3の左)から非キーフレーム(図-3の右)に特徴のサブセットを変換することにより、バックボーン計算を削減しています。具体的には、非キーフレームにおいて、高解像度であるためマスク予測に重要なC3特徴量を容易に計算することができます。これにより、非キーフレームでの精度を維持したまま、大幅に高速化することができています。
図-3 Yolactからの拡張
FlowNetSは、下記の2段階でフロー推定をおこないますが、Yolact Edgeでは、①の代わりに、ResNetバックボーン(C3)から特徴を再利用し、より少ない畳み込み層を使用しています。
①RGB画像を入力として受け取り、特徴のスタックを計算しています。
②大きな動きと小さな動きの両方を運ぶ粗から細への特徴を生成するために、特徴マップを再帰的にサンプリングし連結して特徴のサブセットを精製する。
図-4 FlowNetSとYolact EdgeのFeatFlowNetの違い
YolactEdgeの実装
環境
Google Colabで簡単に動かせるようになっているため、それに添って説明していきます。自分で動かしてみたい方は、下記URLをクリックし表示されたページの先頭にあるOpen in Colabをクリックすると動かせます。
https://github.com/cedro3/yolact_edge/blob/master/YolactEdge_demo.ipynb
実行準備
ノートブックを開いたら、ファイルのタブから「ドライブにコピーを保存」をクリック。YolactEdge_demoのコピーを作れたら、ランタイムのタブから「ランタイムのタイプを変更」をクリック。そして、ハードウェアアクセラレータからGPUを選択します。その後、セットアップのコードを実行していきます。
セットアップ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# GPUの仕様チェック !nvidia-smi -L # Githubからコードをコピー !git clone https://github.com/cedro3/yolact_edge.git !git clone https://github.com/chentinghao/download_google_drive.git %cd yolact_edge !mkdir -p weights # 重みとCOCOデータのダウンロード ! pip install --upgrade gdown import gdown gdown.download('https://drive.google.com/uc?id=1-cyTqsIjmXKGzcToOQoEjE_uokn2vSoS', './weights/yolact_edge_54_800000.pth', quiet=False) gdown.download('https://drive.google.com/uc?id=15jyd5CRJxNiA41UMjGbaSnmaytfeILfI', './calib_images_coco.zip', quiet=False) ! unzip -q calib_images_coco.zip <span># </span>ライブラリーのインポート <span>import cv2 </span><span>from google.colab.patches import cv2_imshow </span><span>from IPython.display import HTML </span><span>from base64 import b64encode</span> |
静止画を物体検出
物体検出させたい画像をyolact_edgeフォルダの中のpicフォルダ下にアップロードします。今回検知させたい画像を下記のコードで表示します。なお、検知させたい画像は下記からダウンロードしました。←素材が豊富でおすすめです。
https://pixabay.com/ja/
1 2 3 |
# 検知させたい画像を表示 im = cv2.imread("/content/yolact_edge/pic/surfers.jpg") cv2_imshow(im) |
実行
さっそくインスタンスセグメンテーションをやってみましょう。下記のコードを実行してください。
1 2 3 4 5 |
# 静止画から物体検出 !python eval.py --trained_model=weights/yolact_edge_54_800000.pth\ --score_threshold=0.15 --top_k=100\ --image=/content/yolact_edge/pic/surfers.jpg: output_img.jpg \ --disable_tensorrt |
1 2 3 |
# 検出画像を表示 im = cv2.imread("/content/yolact_edge/output_img.jpg") cv2_imshow(im) |
結果
人、サーフボードを検知しています。一方、空や地面などの物体ではないものはラベルを与えていません。
ビデオから物体検知
物体検出させたい画像をyolact_edgeフォルダの中のvideoフォルダ下にアップロードします。今回検知させたい動画を下記のコードで表示します。なお、検知させたい動画はサーフィンが趣味である私が最近GoProで撮影したものです。
1 2 3 4 5 6 7 |
# 検知させたいビデオの確認 mp4 = open('/content/yolact_edge/video/surfing.mp4', 'rb').read() data_url = 'data:video/mp4;base64,' + b64encode(mp4).decode() HTML(f""" <video width="80%" height="80%" controls> <source src="{data_url}" type="video/mp4"> </video>""") |
実行
さっそくインスタンスセグメンテーションをやってみましょう。下記のコードを実行してください。
1 2 3 4 5 |
# ビデオから物体検出し、mp4動画を作成する !python eval.py --trained_model=weights/yolact_edge_54_800000.pth\ --score_threshold=0.15 --top_k=100\ --video=/content/yolact_edge/video/surfing.mp4:output_video.mp4\ --disable_tensorrt |
※今回、下記のコードを実行してもエラーが出て検知結果をGoogle Colab上で確認できなかったので、output_videoをダウンロードして手動で確認しました。
現在エラーについて調査中です…。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# コーデック変換 import os import shutil if os.path.exists('./output.mp4'): os.remove('./output.mp4') ! ffmpeg -i output_video.mp4 -vcodec h264 -pix_fmt yuv420p output.mp4 # mp4動画の再生 mp4 = open('./output.mp4', 'rb').read() data_url = 'data:video/mp4;base64,' + b64encode(mp4).decode() HTML(f""" <video width="80%" height="80%" controls> <source src="{data_url}" type="video/mp4"> </video>""") |
結果
上記の動画が、runs/track/expフォルダの中に生成されました。バウンディングボックスには、ラベルと信頼度が記載されています。
まとめ・考察
今回は、サーファーのインスタンスセグメンテーションをしてみました。検知する前と検知した後を比較してみると、かなり上手く検知してくれることがわかります。
一方で、下記2点が問題だと感じました。
・遠くの人物は検知されにくい
・水しぶきを鳥(bird)と検知してしまっているフレームがあった
2つ目の課題は、閾値(threshold)を上げる対策で解決できるのではないかと考えています。
人とサーフボードが近くで一緒に検知された時にそれをサーファーだと定義するスクリプトを書けば、アノテーション(大変な作業)をしなくてもサーファーを検知することも可能になると思いました。