はじめに
こんにちは。 @dcm_yamaya です。
この記事は、NTTドコモ R&D Advent Calendar 2022 23日目の記事です。 ことしはカレンダーがドコモ開発者ブログ上に展開されています!
筆者は普段の業務では、画像認識やエッジコンピューティングの技術領域を担当しています。
アドベントカレンダーも4年目でして過去記事も(ちょっと情報が古いかもしれませんが)ぜひ。
最近では、NVIDIA がリリースしている DeepStream SDK にふれることが多く、今回は動画に対する分析をやってみようと思います。
対象とする人
動画認識に興味がある
NVIDIA の GPU が利用できる(ゲーミングノートPCなどもOK)
やること
DeepStream SDK とは
動画像認識できる環境づくり
カスタマイズの方法を見てみる
やってみよう動画像認識
と、いうことで早速やっていきましょう。
DeepStream SDK とは
まず最初に、今回利用する DeepStream SDK ですが、これは NVIDIA がリリースしている動画分析のためのフレームワークです(参考文献1)。
DeepStream SDK | NVIDIA Developer
NVIDIA のGPU上でパイプラインを構成し、入力から出力までを設定ファイルで定義しながら動画に対する画像認識を搭載GPUで最適にかつ高速に実行できます。
GStreamer という汎用のマルチメディアフレームワークがありますが、これをベースに開発をされていて、GStreamerを触ったことがあると、かなり理解がはやいと思います。
主に以下の構成で動画の分析をしていくことになります。
入力対象はmp4などの動画ファイル、RTSPで配信されているストリーミング動画、Webカメラなどのv4l2デバイスがあります。これらの映像に対してGPU上でデコードをかけ、必要に応じて前処理を行います。このあとに、最初の1段階目として推論(画像認識)を実施します。以降はまた必要に応じてその結果に対するトラッキングをし、2段階目の推論(画像を分類するなど)を実施します。
そうした処理結果を出力する方法としては、mp4ファイルへの書き出し、RTSPでの再配信、画面上にウィンドウ表示、message配信(jsonなど)のような形を取ることができます。
今回はもっとも結果が見えてわかりやすい例として、「動画を入力」とし、AIの認識結果を「画面上にウィンドウ表示」することにトライしてみます。
以下の節では、Ubuntuの環境でDeepStream SDKを動作させ、動画を分析するところをやってみたいと思います。
実行すると以下のような出力が得られます。1つのmp4ファイルを4つのストリームとして流し、これらそれぞれに対し画像認識をリアルタイムにかけることを目標にやっていきます。
筆者の動作環境
Ubuntu 20.04
DeepStream SDK 6.1.1
Docker 19.03 以降
GPUノートPC(ROG Zephyrus G15 GA503 RTX3060搭載モデル)
動画像認識できる環境づくり
用意するものとしては、Ubuntuをインストールした、nvidia の GPU が搭載されたPCがあればよいです。経験談として、Ubuntu のマシンにVNCなどでリモートでログインしたり、WindowsマシンにおいてWSL2を活用する手もありますが、ウィンドウ表示はなかなかうまくいかないことが多いです。(ファイル出力やRTSPでの配信などの用途であれば問題なくいけそうです。)
ということで、まずはGPUを利用するためにCUDAドライバをインストールしていきます。Ubuntu が用意するコマンドでインストールしていく方法もありますが、CUDA公式の方法が間違いないので、こちらをおすすめします。
以下のように環境を選んでいくと、GPUインストールのコマンドが現れます。これはdebファイルをダウンロードしてきてインストールするパターンです。
CUDA Toolkit 12.0 Downloads | NVIDIA Developer
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600 wget https://developer.download.nvidia.com/compute/cuda/12.0.0/local_installers/cuda-repo-ubuntu2004-12-0-local_12.0.0-525.60.13-1_amd64.deb sudo dpkg -i cuda-repo-ubuntu2004-12-0-local_12.0.0-525.60.13-1_amd64.deb sudo cp /var/cuda-repo-ubuntu2004-12-0-local/cuda-*-keyring.gpg /usr/share/keyrings/ sudo apt-get update sudo apt-get -y install cuda
このあとCUDAが読めるようにパスを通し、再起動します。
export PATH="/usr/local/cuda/bin:$PATH" export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
このあと、nvidia-smiコマンドでGPUの情報が表示されれば、インストール成功です。
deviceQuery というCUDAの中にあるより詳細な情報を取得できるプログラムもあります。最新のCUDA12では、demo_suiteディレクトリにありました。(以前は自分でmakeする必要があったと思いますが、実行可能プログラムとして含まれていました)
cd /usr/local/cuda/extras/demo_suite/ sudo ./deviceQuery ./deviceQuery Starting... CUDA Device Query (Runtime API) version (CUDART static linking) Detected 1 CUDA Capable device(s) Device 0: "NVIDIA GeForce RTX 3060 Laptop GPU" CUDA Driver Version / Runtime Version 12.0 / 12.0 CUDA Capability Major/Minor version number: 8.6 Total amount of global memory: 5938 MBytes (6226378752 bytes) (30) Multiprocessors, (128) CUDA Cores/MP: 3840 CUDA Cores GPU Max Clock rate: 1425 MHz (1.42 GHz) Memory Clock rate: 7001 Mhz Memory Bus Width: 192-bit L2 Cache Size: 3145728 bytes Maximum Texture Dimension Size (x,y,z) 1D=(131072), 2D=(131072, 65536), 3D=(16384, 16384, 16384) Maximum Layered 1D Texture Size, (num) layers 1D=(32768), 2048 layers Maximum Layered 2D Texture Size, (num) layers 2D=(32768, 32768), 2048 layers Total amount of constant memory: 65536 bytes Total amount of shared memory per block: 49152 bytes Total number of registers available per block: 65536 Warp size: 32 Maximum number of threads per multiprocessor: 1536 Maximum number of threads per block: 1024 Max dimension size of a thread block (x,y,z): (1024, 1024, 64) Max dimension size of a grid size (x,y,z): (2147483647, 65535, 65535) Maximum memory pitch: 2147483647 bytes Texture alignment: 512 bytes Concurrent copy and kernel execution: Yes with 2 copy engine(s) Run time limit on kernels: Yes Integrated GPU sharing Host Memory: No Support host page-locked memory mapping: Yes Alignment requirement for Surfaces: Yes Device has ECC support: Disabled Device supports Unified Addressing (UVA): Yes Device supports Compute Preemption: Yes Supports Cooperative Kernel Launch: Yes Supports MultiDevice Co-op Kernel Launch: Yes Device PCI Domain ID / Bus ID / location ID: 0 / 1 / 0 Compute Mode: < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) > deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 12.0, CUDA Runtime Version = 12.0, NumDevs = 1, Device0 = NVIDIA GeForce RTX 3060 Laptop GPU Result = PASS
これで、ホスト側の環境が整いました。
DeepStream SDK の構築
DeepStream SDK の利用方法として、ホストPCに直接インストールしていく方法もありますし、NVIDIA が用意しているDockerコンテナを利用する方法もあります。今回は後者の方法をやってみます。
ということで、まずはDockerのインストールです。公式のスクリプトが便利です。
Installation Guide — NVIDIA Cloud Native Technologies documentation
curl https://get.docker.com | sh \ && sudo systemctl --now enable docker
次に、Docker から GPU を見えるようにするために、nvidia-docker2をインストールします。
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \ && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \ && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-docker2 sudo systemctl restart docker
これで利用可能になりました。試しに、Ubuntuのイメージをpullしてきて、その中でGPUが見えているか実行してみる場合は以下でできます。nvidia docker 19.03以降はGPU利用の際に --gpus all
のような指定の仕方に変わっています。
sudo docker run --rm --gpus all nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi +-----------------------------------------------------------------------------+ | NVIDIA-SMI 525.60.13 Driver Version: 525.60.13 CUDA Version: 12.0 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 NVIDIA GeForce ... On | 00000000:01:00.0 Off | N/A | | N/A 32C P8 10W / N/A | 338MiB / 6144MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| +-----------------------------------------------------------------------------+
続いて、NGCというNVIDIAのコンテナがある場所からDeepStream SDKのイメージをpullしてきます。タグ名で含まれるライブラリの豊富さが変わりますが、今回はdevelタグを利用してみます。(参考文献2)
sudo docker pull nvcr.io/nvidia/deepstream:6.1.1-devel
今回は画面表示をしたいので、Xサーバーが接続を受け入れるコンピューターのリストに、ホスト名を追加しておきます。
xhost +
これでdocker runコマンドを実行し、コンテナの中に入ります。 -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY
もウィンドウを表示する際には必要なマウントオプションになります。
sudo docker run --gpus all -it --rm --net=host --privileged -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY -w /opt/nvidia/deepstream/deepstream-6.1 nvcr.io/nvidia/deepstream:6.1.1-devel
無事bashに入ることができたら、deepstream-appコマンドで動画を認識するプログラムを起動します。
cd /opt/nvidia/deepstream/deepstream-6.1/samples/configs/deepstream-app deepstream-app -c source4_1080p_dec_infer-resnet_tracker_sgie_tiled_display_int8.txt 0:00:01.083060958 156 0x555822c3f390 INFO nvinfer gstnvinfer.cpp:646:gst_nvinfer_logger:<secondary_gie_2> NvDsInferContext[UID 6]: Info from NvDsInferContextImpl::deserializeEngineAndBackend() <nvdsinfer_context_impl.cpp:1909> [UID = 6]: deserialized trt engine from :/opt/nvidia/deepstream/deepstream-6.1/samples/configs/deepstream-app/../../models/Secondary_CarMake/resnet18.caffemodel_b16_gpu0_int8.engine INFO: ../nvdsinfer/nvdsinfer_model_builder.cpp:610 [Implicit Engine Info]: layers num: 2 0 INPUT kFLOAT input_1 3x224x224 1 OUTPUT kFLOAT predictions/Softmax 20x1x1 ...中略 Runtime commands: h: Print this help q: Quit p: Pause r: Resume NOTE: To expand a source in the 2D tiled display and view object details, left-click on the source. To go back to the tiled display, right-click anywhere on the window. ** INFO: <bus_callback:194>: Pipeline ready **PERF: FPS 0 (Avg) FPS 1 (Avg) FPS 2 (Avg) FPS 3 (Avg) **PERF: 48.75 (47.75) 48.75 (47.75) 48.75 (47.75) 48.75 (47.75) **PERF: 30.00 (33.51) 30.00 (33.51) 30.00 (33.51) 30.00 (33.51) **PERF: 30.00 (31.95) 30.00 (31.95) 30.00 (31.95) 30.00 (31.95) **PERF: 29.99 (31.35) 29.99 (31.35) 29.99 (31.35) 29.99 (31.35) **PERF: 30.01 (31.03) 30.01 (31.03) 30.01 (31.03) 30.01 (31.03) **PERF: 29.99 (30.83) 29.99 (30.83) 29.99 (30.83) 29.99 (30.83) **PERF: 29.99 (30.70) 29.99 (30.70) 29.99 (30.70) 29.99 (30.70) **PERF: 30.00 (30.60) 30.00 (30.60) 30.00 (30.60) 30.00 (30.60) **PERF: 30.00 (30.53) 30.00 (30.53) 30.00 (30.53) 30.00 (30.53) **PERF: 30.00 (30.47) 30.00 (30.47) 30.00 (30.47) 30.00 (30.47) ** INFO: <bus_callback:217>: Received EOS. Exiting ... Quitting [NvMultiObjectTracker] De-initialized App run successful
となり、以下のようにリアルタイムに動画の認識が実行されます!
今回指定しているのは、1つの動画を4つのストリームとして流し、それに対し物体検出とトラッキング、その後に分類を行っているパイプラインを実行する設定ファイルになります。左クリックをすることで、検出している枠の表示に加え、どんなものと認識したのかのクラス名(person, carなど)とトラッキングのIDも表示できるように切り替えられます。
こうした形で、動画を入力とし、リアルタイムに分析をしながらその結果を目で見て確かめることができました。
実行中のログには、4つのストリームに対してどの程度のフレームレートで処理できているかが表示されていて、今回はおよそ30FPS前後で安定して処理できていることがわかります。裏でnvidia-smiコマンドなどで継続的に見ていくことで、GPUの使用率やメモリの利用率もわかると思います。
deepstream-app のコマンドを打ってから実行されるまでに数分(場合によっては10分以上)かかるのは、物体検出のモデルをTensorRTという形式に変換を行っているためです。DeepStreamのパイプライン上でAIモデルを動作させるためにはこの変換ができることが必須になります。1度変換し実行するGPUが変わらなければ、コンテナの中からengineファイルという形式になっているものをローカルなどに持ってきて、docker run時にマウントすることで、実行のたびに変換処理すること無く変換後のTensorRTモデルを読み込むことができるので、実行時間短縮にはおすすめです。
実行時の仕組みとカスタマイズのやり方
deepstream-app コマンドで動画認識を実行していますが、これには-cオプションをつけて設定ファイルを指定しています。この設定ファイルにどのような入力とするのか、出力とするのか、などの情報を記載していきます。 /opt/nvidia/deepstream/deepstream-6.1/samples/configs/deepstream-app
に他にもサンプルがあるので、見ていくとどのように記載をするとカスタムできていくのかが少しずつわかってきます。ここでは少しだけ中身を見てみます。すべてのconfigの解説は以下にあります。(参考文献3)
DeepStream Reference Application - deepstream-app — DeepStream 6.1.1 Release documentation
まずは入力をする部分です。ここは[source]というグループで記載をします。例えば、USBカメラやWebカメラを利用する場合には、以下のように記載をしていきます。enableというプロパティはほかのグループでも登場しますが、1で有効、0で無効となります。typeにはどの入力をしたいのかをdeepstream側に伝えます。1だとv4l2デバイス、2だとmp4ファイル、のような形です。あとは解像度やFPSの指定などができます。最後にホストPC上で /dev/video
いくつにマウントしているのか、をcamera-v4l2-dev-nodeで指定してあげます。
[source0] enable=1 type=1 camera-width=1920 camera-height=1080 camera-fps-n=30 camera-fps-d=1 camera-v4l2-dev-node=0
ほかにもグループはたくさん存在するのでここまでで割愛し次回以降にしたいと思いますが、このような形で入出力、モデルの設定、画面表示のカスタマイズなどを行っていきます。分類、セグメンテーション、物体検出など一般的なタスクであればTensorRT化もできますし、出力形式もサンプルから大きく変わることはないので、自分で学習したモデルを載せることもできます。さらなる詳細は以下のリンクなどが参考になります。(参考文献4)
Sample Configurations and Streams — DeepStream 6.1.1 Release documentation
おわりに
本記事では、DeepStream SDK を用いて動画像認識をする流れを紹介してみました。
今後はカスタムのモデルを動作させる際にはどうするとよいのか、より多段にしていく構成は取れるのか、などの実験をしてみたいと思っています。