NTTドコモR&Dの技術ブログです。

DeepStream SDKによる動画像認識事始め2022

はじめに

こんにちは。 @dcm_yamaya です。

この記事は、NTTドコモ R&D Advent Calendar 2022 23日目の記事です。 ことしはカレンダーがドコモ開発者ブログ上に展開されています!

筆者は普段の業務では、画像認識やエッジコンピューティングの技術領域を担当しています。

アドベントカレンダーも4年目でして過去記事も(ちょっと情報が古いかもしれませんが)ぜひ。

qiita.com

qiita.com

qiita.com

最近では、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の認識結果を「画面上にウィンドウ表示」することにトライしてみます。

DeepStream SDK のパイプライン(参考文献1より)

以下の節では、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

DeepStream | NVIDIA NGC

今回は画面表示をしたいので、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 を用いて動画像認識をする流れを紹介してみました。

今後はカスタムのモデルを動作させる際にはどうするとよいのか、より多段にしていく構成は取れるのか、などの実験をしてみたいと思っています。

参考文献

  1. DeepStream SDK | NVIDIA Developer

  2. DeepStream | NVIDIA NGC

  3. DeepStream Reference Application - deepstream-app — DeepStream 6.1.1 Release documentation

  4. Sample Configurations and Streams — DeepStream 6.1.1 Release documentation