Jetson nanoでDeepStreamを使ってみた
2020年1月7日に行われた第4回 Jetsonユーザー会 「Jetson Nano超入門」著者パネルディスカッション+LT大会に登壇させていただきました。
関係者のみなさま、ご参加いただいたみなさま、ありがとうございました。
で、資料をSlideShareにアップしたのですが、基本的には口頭で説明するためのベースとしての資料として作ったものですので、ブログ記事として解説をアップすることにしました。
まず、なんでDeepStreamを紹介しようと思ったかと言いますと、以下3つの理由からです。
- 何か話してよと言われたのが12月半ばで1か月未満でできることを考えた
ちなみに、依頼が来てから慌ててJetson nanoを買いました。 - DeepStreamに関して、NVIDIAさんのプレゼンでしか見たことないよという声があった
- 「Jetson Nano超入門」にはさらっと紹介程度にしか触れられていなかった
ということで、SlideShareの資料を別タブで見ながらでも読んでいただければと思います。
自己紹介は飛ばしまして、3ページ目から。
DeepStreamって何?
このあたりは「Jetson Nano超入門」に書かれている内容と一部重複します。
NVIDIA社が提供している映像解析向けのライブラリで、オープンソースのマルチメディアフレームワークであるGstreamerがベースとなっています。
Gstreamerのパイプライン(後述)の中でNVIDIA GPUを使った映像解析をするためのもので、スライドにあるとおり
- GPU(ハードウェア)を使ったエンコード/デコード
- TensorRTを使った高速な推論
- 各種メタデータのハンドリング
- トラッキング
- OSD(画面表示)
というように一般的な映像解析で行われるようなことは一通りできます。
Gstreamerの紹介と簡単な使い方
Gstreamerは前述のとおりオープンソースのマルチメディアフレームワークで、150以上のプラグインが提供されており、映像だけでなく音声の処理も含めてとても多くのシーンで活用されています。
エレメントと呼ばれるパーツ(プラグイン)で構成され、出力のみを持つソースエレメント、入出力を持つフィルターエレメント、入力のみを持つシンクエレメントをパイプラインで繋いで使用します。
スライドの図のようにソースエレメントで読み取ったカメラの映像などを、推論や解析等を行うフィルターエレメントを通し、その他のアプリケーションで使用したり画面に出力したりするシンクエレメントに流すという使われ方が一般的です。
OpenCVと組み合わせて使ったりもします。
一般的な映像解析フロー
4ページ目に行きまして、こちらちょっと図がよろしくないのですが、一般的なOpenCVを使った映像解析のフローを示しています。
解析情報がCVに戻っているように見えますが、OpenCVを使ってフレーム単位に分割した画像を解析し、またそれをOpenCVの機能を使い実行結果のレンダリングや画面への表示を行うということを表しています。
OpenCVじゃなぜダメなのか
5ページ目にOpenCVを使うデメリットを紹介させていただきました。
まず1点目は、時間軸の概念がないということです。
基本的にフレーム単位ぶ分解して処理を行うことになりますので、今処理しているのがいつのものかというような時間の概念がありません。
DeepStreamはもともと映像向けのライブラリであるため、ストリーミングパイプラインの中で処理が行われます。
続いて2点目は自分で詳細に調べて行ったわけではありませんが、検索したり聞きかじった情報から意外とCPUリソースを食ってそうという印象を持っています。
Jetsonデバイスではご存じのとおりそれほどCPUが強力なものではなく、CPUとGPUの間のやり取りも含めて結構なタイムロスになっているように思います。
DeepStreamにもデメリットがあるんじゃないか
これ、とても大事なところなのにスライドにも入れ忘れていますし、当日も説明していませんでした。
勘の良いかたならお気づきかと思いますが、パイプライン処理ですので1か所が遅延すると全体に影響を及ぼします。
DeepStreamのサンプルを見るとperfで各処理の計測をしているのは、このあたりのこともあるのかなと思います。
YOLOでの物体検出をやってみる
6ページ目に行きまして、ここからはDeepStreamを使ってみようという内容になっています。
DeepStream SDKをインストールすると、インストールディレクトリの配下に sources/ objectDetector_Yolo というディレクトリができます。
この中のREADMEに従い準備を進めます。
- ./prebuild.sh を実行し必要なweight等をダウンロード
- export CUDA_VER=10.0
ここでは使用するcudaのバージョンを指定します。資料作成時は10.0でした - make -C nvdsinfer_custom_impl_Yolo
続いて、deepstream_app_config_yoloV3_tiny.txtをベースに、USBカメラを使用できるように書き換えてみます。
[source0]のセクションが入力部分で、サンプルとして入っている映像をdetectするように書かれていますので、この部分を以下のようにカメラを使う記述に書き換えます。
type=1 num-sources=1 camera-width=640 camera-height=480 camera-fps-n=30 camera-fps-d=1 camera-v4l2-dev-node=0
ここまでできたら実行してみます。
実行コマンドは7ページに記載したとおり、
deepstream-app -c deepstream_app_config_yoloV3_tiny.txt
です。
会場では実際にUSBカメラを使ったデモを行いました。
カメラの性能もあるので精度はちょっと微妙だったかもしれませんが、24fps程度で動作していることを確認できました。
スライドに貼り付けた動画はこちらからご覧いただけます。
これは、USBカメラ用に書き換える前のサンプル映像をdetectした様子で、普通にコンパイルしたYOLOのdarknet.binとDeepStream版のYOLOを比較した動画となっています。
元動画:30fps
通常版:13fps程度
DeepStream版:24fps程度
です。
Jetson nanoではおそらくメモリの関係から残念ながらyolov3.weightですと動かず(推奨4GBなのでギリギリ?)、yolov3-tiny.weightを使用しました。
通常版(bin)のYOLOでは余分な検出枠が多く表示されていますが、DeepStream版ではTensorRT化によりネットワークの最適化も行われるためこのあたりも最適化されているように見えます。
ただ、もう少し拾ってほしいと思うところもあったりしますね。
一般的な推論のモデルは変換してDeepStreamでの処理に使用できるものが多いですが、元々のモデルと全く同じ結果になるというものではないということもわかります。