DeepStream SDKの物体検出結果をKafka経由でNode-REDに連携してみた



# はじめに
この記事では、次の構成図のようにJetson TX2上にKafkaとNode-REDを構成し、DeepStreamの出力をKafkaとNode-REDに連携させる手順を説明しています。

#### 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/2b933a07-e329-f360-8296-a0ddf651d86f.png)

#### 完成イメージ
物体検出したラベルと位置情報をNode-REDのコンソールに出力しています。
![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/917bd1ba-55d6-9baf-c611-8cabc7a5463c.gif)


# 環境情報
Jetson TX2
Jetpack 4.3
カメラ：LOGICOOL HD PRO WEBCAM C920
Kafka 2.4.0
Node-RED 1.0.3
Node.js 12.14.1

# 手順概要
1. Kafkaセットアップ 
2. Node-REDセットアップ
3. DeepStreamサンプルアプリ起動


# 1. Kafkaセットアップ
## 1-1. Javaの導入
Kafkaを動かすためにはJavaが必要なため、導入します。

```bash
$ sudo apt install openjdk-11-jre
$ java --version
```

## 1-2. Kafkaの導入と起動

以下のKafkaのquickstartの手順を参考にシングルノードのKafkaを導入し、起動します。
https://kafka.apache.org/quickstart

以下のサイトからKafkaのコードをDownloadします。
https://www.apache.org/dyn/closer.cgi?path=/kafka/2.4.0/kafka_2.12-2.4.0.tgz

解凍します。

```bash
$ tar -xzf kafka_2.12-2.4.0.tgz
```

zookeeperを起動します。zookeeperを起動したターミナルは起動したままにしておきます。

```bash
$ cd kafka_2.12-2.4.0
$ bin/zookeeper-server-start.sh config/zookeeper.properties
```

kafkaサーバーを起動します。kafkaサーバーを起動したターミナルは起動したままにしておきます。

```bash
$ cd kafka_2.12-2.4.0
$ bin/kafka-server-start.sh config/server.properties
```

トピックを作成します。この記事では”test”という名前のトピックを作成しています。

```bash
$ cd kafka_2.12-2.4.0
$ bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test
$ bin/kafka-topics.sh --list --bootstrap-server localhost:9092
```

# 2.Node-REDセットアップ
## 2-1. Node.jsインストール

Node.jsのインストールは以下の記事が参考になります。
TX2でも同様の手順でインストールできました。

https://qiita.com/seibe/items/36cef7df85fe2cefa3ea

以下のコマンドを実行してNode.jsを導入します。

```bash
$ sudo apt install -y nodejs npm
$ sudo npm install n -g
$ sudo n stable
$ sudo apt purge -y nodejs npm
$ exec $SHELL -l
$ node -v
$ npm -v
```

## 2-2. Node-REDの導入と起動
Node-REDの導入は以下のNode-REDのDocumentにある以下のコマンドを実行します。

Node-RED Document
https://nodered.org/docs/getting-started/local#installing-with-npm

以下のようにコマンドを実行し、Node-REDを導入します。

```bash
sudo npm install -g --unsafe-perm node-red
```
以下のコマンドでNode-REDを起動します。

```bash
$ node-red
```

ブラウザーでlocalhost:1880にアクセスすると以下のような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/816dab45-47a5-4813-b112-7f551e1f3373.png)

Kafka接続用のNodeを追加します。
右上のメニューをクリックし、「Manage palette」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/2a191cd0-c0e3-b110-49d8-877edbf4d842.png)

「Install」のタブを選択し、「kafka」で検索し、「node-red-contrib-kafka-manager」をインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/55a1aa3e-2a82-ca83-fad1-76ebe359e159.png)

左側のパレットに「Kafka Consumer」や「Kafka Producer」が追加されているのが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/64b8c4e0-0fc0-9b0f-f236-4075c4ab462d.png)

## 2-3. Node-REDのフロー作成

「Inject」「Kafka Producer」「Kafka Consumer」「Debug」のノードを以下のキャプチャー画面のように構成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/973f2053-bc79-c503-d11a-07dc32f5b8dc.png)

「Kafka Consumer」のノードの設定は以下のキャプチャー画面のようになります。
Topicには、前のステップで作成した「test」を設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/1fcaefba-c0e4-24d4-063e-10b6fb98cd3f.png)

Kafka Brokerの設定に、今回はローカルに構成しているためNameに「127.0.0.1」,Portに「9092」を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/8617518c-305e-339e-7767-e7d7a4f4ba8b.png)

「Kafka Comsumer」のノードの設定は以下のキャプチャー画面のようになります。
Topicの設定には、前のステップで作成した「test」を設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/fe82e033-3f19-c50a-b72b-81aaf0a5f42b.png)

右上の「デプロイ」ボタンを押すと作成したフローがデプロイされ、Kafka Producer、Kafka ConsumerのノードからKafka Serverへの接続が行われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/a944a990-93e0-68f1-c478-9235b110d1f1.png)

Injectノード（以下の図の水色のノード）のボタンを押して、Kafkaサーバーへの書き込みと読み込みができることを確認します。挿入されたタイムスタンプが、Kafka Server経由でDebugノードに渡され右側のデバッグタブに出力されるのが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/a8f0c799-f7a0-5741-d27f-3781e59040b5.png)

# 3. DeepStreamサンプルアプリの起動

## 3-1. DeepStreamのサンプルコード、構成ファイルをコピー
サンプルコードと構成ファイルを作業ディレクトリにコピーします。

```bash
$ mkdir ~/work
$ cp -r /opt/nvidia/deepstream/deepstream-4.0/samples ~/work
$ cp -r /opt/nvidia/deepstream/deepstream-4.0/sources/ ~/work
```
## 3-2. Webカメラの確認
l4l2-ctlコマンドでWebカメラが接続されていることを確認します。
下の例では/dev/video0で接続されていることがわかります。

```bash
$ v4l2-ctl --list-devices
vi-output, ov5693 2-0036 (platform:15700000.vi:2):
	/dev/video1

HD Pro Webcam C920 (usb-3530000.xhci-2):
	/dev/video0
```
## 3-3. 構成ファイル
サンプルの構成ファイルtest5_config_file_src_infer.txtを参考にしてtest5_config_usb_src_infer.txtという名前で構成ファイルを作成します。

```bash
$ cd ~/work/sources/apps/sample_apps/deepstream-test5/configs/
$ ls
$ vi test5_config_file_src_infer.txt
```

構成ファイルは以下のような内容で作成します。
注意点が2点あります。
・「camera-v4l2-dev-node」に設定する番号は、前のステップで確認したWebカメラの/dev/videoXの番号
・camera-fps-nを30に設定すると処理が遅延するため、カメラ入力は15FPSに設定

```config
[application]
enable-perf-measurement=1
perf-measurement-interval-sec=5
#gie-kitti-output-dir=streamscl

[tiled-display]
enable=1
rows=1
columns=1
width=640
height=360
gpu-id=0
#(0): nvbuf-mem-default - Default memory allocated, specific to particular platform
#(1): nvbuf-mem-cuda-pinned - Allocate Pinned/Host cuda memory, applicable for Tesla
#(2): nvbuf-mem-cuda-device - Allocate Device cuda memory, applicable for Tesla
#(3): nvbuf-mem-cuda-unified - Allocate Unified cuda memory, applicable for Tesla
#(4): nvbuf-mem-surface-array - Allocate Surface Array memory, applicable for Jetson
nvbuf-memory-type=0

[source0]
enable=1
#Type - 1=CameraV4L2 2=URI 3=MultiURI
type=1
camera-width=640
camera-height=360
camera-fps-n=15
camera-fps-d=1
camera-v4l2-dev-node=0

[sink0]
enable=1
#Type - 1=FakeSink 2=EglSink 3=File
type=2
sync=1
source-id=0
gpu-id=0
nvbuf-memory-type=0

[sink1]
enable=1
#Type - 1=FakeSink 2=EglSink 3=File 4=UDPSink 5=nvoverlaysink 6=MsgConvBroker
type=6
msg-conv-config=dstest5_msgconv_sample_config.txt
#(0): PAYLOAD_DEEPSTREAM - Deepstream schema payload
#(1): PAYLOAD_DEEPSTREAM_MINIMAL - Deepstream schema payload minimal
#(256): PAYLOAD_RESERVED - Reserved type
#(257): PAYLOAD_CUSTOM   - Custom schema payload
msg-conv-payload-type=0
msg-broker-proto-lib=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_kafka_proto.so
#Provide your msg-broker-conn-str here
msg-broker-conn-str=127.0.0.1;9092;test
topic=test
#Optional:
#msg-broker-config=../../deepstream-test4/cfg_kafka.txt

[sink2]
enable=0
type=3
#1=mp4 2=mkv
container=1
#1=h264 2=h265 3=mpeg4
## only SW mpeg4 is supported right now.
codec=3
sync=1
bitrate=2000000
output-file=out.mp4
source-id=0

[osd]
enable=1
gpu-id=0
border-width=1
text-size=15
text-color=1;1;1;1;
text-bg-color=0.3;0.3;0.3;1
font=Arial
show-clock=0
clock-x-offset=800
clock-y-offset=820
clock-text-size=12
clock-color=1;0;0;0
nvbuf-memory-type=0

[streammux]
gpu-id=0
##Boolean property to inform muxer that sources are live
live-source=0
batch-size=4
##time out in usec, to wait after the first buffer is available
##to push the batch even if the complete batch is not formed
batched-push-timeout=40000
## Set muxer output width and height
width=1920
height=1080
##Enable to maintain aspect ratio wrt source, and allow black borders, works
##along with width, height properties
enable-padding=0
nvbuf-memory-type=0

[primary-gie]
enable=1
gpu-id=0
batch-size=4
## 0=FP32, 1=INT8, 2=FP16 mode
bbox-border-color0=1;0;0;1
bbox-border-color1=0;1;1;1
bbox-border-color2=0;1;1;1
bbox-border-color3=0;1;0;1
nvbuf-memory-type=0
interval=0
gie-unique-id=1
model-engine-file=../../../../../samples/models/Primary_Detector/resnet10.caffemodel_b4_int8.engine
labelfile-path=../../../../../samples/models/Primary_Detector/labels.txt
config-file=../../../../../samples/configs/deepstream-app/config_infer_primary.txt
#infer-raw-output-dir=../../../../../samples/primary_detector_raw_output/

[tracker]
enable=1
tracker-width=600
tracker-height=300
ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_mot_klt.so
#ll-config-file required for DCF/IOU only
#ll-config-file=tracker_config.yml
#ll-config-file=iou_config.txt
gpu-id=0
#enable-batch-process applicable to DCF only
enable-batch-process=0

[tests]
file-loop=0
```

## 3-4. DeepStreamサンプルアプリの起動
以下のコマンドでサンプルアプリを起動します。

```bash
cd ~/work/sources/apps/sample_apps/deepstream-test5/configs/
$ deepstream-test5-app -c test5_config_usb_src_infer.txt 
```

## 3-5. 結果の確認
ブラウザーでlocalhost:1880を開いて、Node-REDにデータが連携されていることを確認します。
以下のように車や人などが検出されるとNode-REDのdebugタブに検出結果が出力されるのが確認できると思います。
![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/917bd1ba-55d6-9baf-c611-8cabc7a5463c.gif)


# まとめ
簡単な設定を行うだけでDeepStream SDKとNode-REDの連携ができることを確認できました。