前回の続きで、物体検知&セグメンテーションのライブラリ調査です。
最終的にはモバイルARで使う想定で評価しています。
はじめに
Mask R-CNNをカメラでキャプチャしたリアルタイムの動画に適用したところ、すごくカクカクで思うように速度が出ませんでした。
GPU性能を活かせば速くなるらしいのですが、モバイルARで使うとなると、マシンスペックに頼るのは難しいです。
そこで代替のライブラリを探したところ、以下の動画を見つけました。
右下のDeepLabは、Mask R-CNNと同様に検知した物体のセグメンテーションができていて、リアルタイム動画にも追従できそうな雰囲気です。
人や車だけでなく、建物などの背景もマスクしている点も気になったので、試してみることにしました。
DeepLabの特徴
TensorFlowで実装・学習されており、オープンソースとしてGithubで公開されています。
また、Googleの紹介記事があります。
記事内では、Semantic image segmentation(セマンティック・イメージ・セグメンテーション)と書かれており、画像を"ピクセル単位"で意味的に分割するアルゴリズムのようです。
Mask R-CNNは、画像内の物体を検知して、検知した物体をマスクしていたので、その点で若干の違いがありそうです。ここは、実際に結果を見てみるとイメージしやすいです。
DeepLabの環境設定
まず、Tensorflowのモデルが提供されているGithubからリポジトリごとcloneして、DeepLabのディレクトリに移動します。
$ git clone https://github.com/tensorflow/models.git $ cd models/research/deeplab/
ライブラリのインストール
前回と同じで、Python3、Jupyter Notebookを使います。今回はMask R-CNNの実行時に構築した環境をそのまま使いました。
また、tensorflow、matplotlib、numpyなどのライブラリも必要になります。requirement.txt
が見当たらないので、不足分はpip install [パッケージ名]
で追加します。Mask R-CNNの環境を先に作ってあれば、大体インストールされていると思います。
サンプル実行
Mask R-CNNのときと同様に、Jupyter Notebookで可視化されたサンプルを動かします。
$ jupyter notebook
deeplab_demo.ipynb
を選択して、"Run"をクリックしていくと処理の流れが見られます。
結果のサンプルとしては、以下のような画像が表示されます。
コードを読んでみる
重要な部分だけ抜き出して見ていきます。
コードの最後に書かれているrun_visualization()
がメイン処理です。
def run_visualization(url): """Inferences DeepLab model and visualizes result.""" try: f = urllib.request.urlopen(url) jpeg_str = f.read() original_im = Image.open(BytesIO(jpeg_str)) except IOError: print('Cannot retrieve image. Please check url: ' + url) return print('running deeplab on image %s...' % url) resized_im, seg_map = MODEL.run(original_im) vis_segmentation(resized_im, seg_map) image_url = IMAGE_URL or _SAMPLE_URL % SAMPLE_IMAGE run_visualization(image_url)
image_url
から画像を読み込んでoriginal_im
にセットし、MODEL.run()
を実行すると、リサイズされた元画像resized_im
とセグメンテーション結果seg_map
が得られます。
その結果をvis_segmentation()
に渡して、画面に表示しています。
MODELはコードの前半で定義されているDeepLabModel
クラスのインスタンスで、http://download.tensorflow.org/models/
からダウンロードしたモデルを使ってTensorflowのSessionを実行しています。
MODEL = DeepLabModel(download_path)
seg_map
からマスク画像を生成する処理は、vis_segmentation()
内に記述されています。
seg_image = label_to_color_image(seg_map).astype(np.uint8) plt.imshow(seg_image)
具体的な処理はlabel_to_color_image()
、create_pascal_label_colormap()
に書かれていますが、長くなりそうなのでここでは割愛。
テストスクリプトの作成
例によって、deeplab_demo.ipynb
のコードを参考にして作りました。
静止画(ローカルファイル)
改変した箇所は以下です。
元画像を固定のURLから取得するようになっているので、ファイルパスに変更します。
#image_url = IMAGE_URL or _SAMPLE_URL % SAMPLE_IMAGE image_path = '[ローカルのファイルパス]' run_visualization(image_path)
Mask R-CNNのサンプルに倣って、ディレクトリ内の画像をランダムに表示する場合は以下のようにします。
import random IMAGE_DIR = './test_image' file_names = next(os.walk(IMAGE_DIR))[2] image_path = os.path.join(IMAGE_DIR, random.choice(file_names)) run_visualization(image_path)
image_urlで使っていた定数は要らないので、コメントアウトします。
''' SAMPLE_IMAGE = 'image1' # @param ['image1', 'image2', 'image3'] IMAGE_URL = '' #@param {type:"string"} _SAMPLE_URL = ('https://github.com/tensorflow/models/blob/master/research/' 'deeplab/g3doc/img/%s.jpg?raw=true') '''
最後に、画像の読み込み部分を変更します。
ローカルファイルを読み込む場合、 Image.open()
にパスをそのまま渡せばOKです。引数名はurl
じゃなくてpath
だけど直してません。
def run_visualization(url): """Inferences DeepLab model and visualizes result.""" try: # f = urllib.request.urlopen(url) # jpeg_str = f.read() # original_im = Image.open(BytesIO(jpeg_str)) original_im = Image.open(url) except IOError: print('Cannot retrieve image. Please check url: ' + url) return
完成版のコードサンプルです。
リアルタイム動画
OpenCVのVideoCapture()
を使って、取得したフレーム画像にマスクをかける処理をループさせれば実現できます。
import cv2 import time capure = cv2.VideoCapture(0) def run_visualization(): while(True): ret, frame = capure.read() original_im = cv2.resize(frame,(480,320)) start_time = time.time() resized_im, seg_map = MODEL.run(original_im) vis_segmentation(resized_im, seg_map) elapsed_time = time.time() - start_time print(elapsed_time) if cv2.waitKey(1) == 27: break capure.release() cv2.destroyAllWindows() run_visualization()
画像の表示部分もOpenCVに変更しています。かなりシンプルに書けます。
def vis_segmentation(image, seg_map): seg_image = label_to_color_image(seg_map).astype(np.uint8) result = cv2.add(image, seg_image) cv2.imshow("camera window", result)
動かすとこんな感じ。
DeepLabの動画テスト pic.twitter.com/J0BEdXP7GJ
— jyuko (@jyuko49) 2018年11月17日
完成版のサンプルコードはこちら。
性能比較
実行環境
- Macbook Pro 2017
処理結果は4パターンの画像をそれぞれ比較。 処理時間の方はMacbookのカメラでキャプチャしたリアルタイムの映像を480 x 320にリサイズして連続処理した際の時間を計測。
Mask R-CNN
処理結果
検知した物体をかなり正確にセグメンテーションできています。
処理時間(sec)
2.493619203567505 2.4120068550109863 2.4120633602142334 2.4788520336151123 2.5836117267608643 ...
1回の検知に2.4-2.5 secかかっています。
ということは、フレームレートは0.4fps。1fpsを切っているため、リアルタイム動画ではラグが相当大きいです。
DeepLab
処理結果
正面や横を向いている物体はキレイに検出できていますが、後ろ向きや座っている人の検出がイマイチです。この点は追加学習させれば改善する部分なのかもしれません。
明らかな差異としては物体の形状が途切れやすい点で、画像のピクセル単位でセグメンテーションを行なっている結果のようです。
処理時間(sec)
0.42249274253845215 0.45106005668640137 0.44458889961242676 0.42955899238586426 0.4366567134857178 ...
0.40-0.45 sec前後なので、Mask R-CNNの1/5程度の処理時間です。決して速くはないですが、2.5fpsくらいは出ます。
冒頭で紹介したYoutubeの動画内でも平均2.5fpsと表示されているので、妥当な結果と思われます。
結果まとめ
Mask R-CNNとDeepLabの比較。
— jyuko (@jyuko49) 2018年11月16日
検知精度はMask R-CNNの方がいいけど、DeepLabは5倍くらい速い。 pic.twitter.com/E9zp5cRMKL
- リアルタイム性を重視するならDeepLab
- リアルタイム性が重要でなく、精度重視ならMask R-CNN
という使い分けになりそうです。
今後
DeepLabの処理速度であれば、モバイルARでもなんとか使えそうという感触です。
実際に同様の研究はされていて、モバイル相当のスペックでもリアルタイム動画に追従はできる様子。
静止画のRGBに加えて前フレームで生成したマスクを使って、高速に処理しつつ精度を上げてるんですね。なるほど。
一旦は学習済みモデルをそのまま使うことにして、TensorFlowSharp in Unityを先に試そうかなと思っています。