使用DeepStream SDK进行实时视频识别,并发送通知给LINE
使用DeepStream SDK进行快速视频识别,然后通过LINE进行通知。
本文是NTT DOCOMO公司2019年度冒险日历第七天的文章。
我是NTT DoCoMo服务创新部的酒井。在工作中,我们致力于使用深度学习进行图像识别引擎的研究开发和服务化。这次,我们使用了基于GPU的视频识别加速工具”DeepStream SDK”,在高速识别视频中的车辆的同时,将结果通知到LINE上。我们可以制作类似AI摄像头的设备,用于监控摄像头中的人员检测,或者从摄像头画面中检测违停等。
我在网上找不到太多关于使用DeepStream SDK和AMQP进行消息传递的案例,因此我也尝试了这种方法。
动机
随着近年来深度学习技术在图像识别中的应用不断扩大,视频数据的图像识别也逐渐兴起。然而,利用深度学习进行视频识别相较于一般图像识别而言,需要更高的机器配置,并且处理时间往往会变慢。
在那种情况下,有传闻称使用英伟达的DeepStream SDK可以利用GPU快速进行视频识别和加速视频处理。而且现在还可以用于docker,安装变得更加简单,所以我试着使用了一下。
深流SDK
深度学习的推理(+学习)经常使用GPU来加速。DeepStream SDK是一个使用GPU来高速处理视频的软件开发工具包(SDK)。它以GStreamer插件的形式提供了各种在视频处理中所需的功能。
-
- GPUや、エッジGPUデバイスの専用ハードウェアを用いて、動画のエンコード/デコードを高速化
-
- Deep Learningを用いた物体検出、画像分類を、GPUおよびTensorRT1を用いて高速化
-
- トラッキング機能も提供
- 画像認識結果を、Kafka、MQTT、AMQPなどを用いて、送信可能
方式
环境
我已经准备好了支持NVIDIA Docker 2.0的GPU服务器。
-
- OS: Ubuntu 18.04
-
- nvidia-driver: 418.67
-
- docker: 19.03.4
-
- docker-compose: 1.24.1
- nvidia-docker: 2.0.3
尽管在docker中原生支持GPU,但在docker-compose中似乎无法指定GPU,因此我选择了使用较旧的runtime表达方式。
文件结构
deepstream-line
├── docker
│ ├── Dockerfile
│ ├── requirements.txt
│ └── src
│ └── receive.py
├── docker-compose-amqp.yml
├── docker-compose.yml
├── rabbitmq
├ ├── custom_definitions.json
├ └── rabbitmq.conf
└── videos
└── test.h264
我們使用了免費的動態素材網站Mixkit中的Chinatown夜景街道影片資料。
为了保持为H264格式的流,需要使用FFmpeg进行转换。
ffmpeg -i ./2012-1080.mp4 -vcodec copy -an -bsf:v h264_mp4toannexb videos/test.h264
启动AMQP服务器
DeepStream SDK可以将识别结果作为消息发送到Kafka、MQTT、AMQP等系统。
在这次实验中,我们选择使用docker来启动RabbitMQ。
RabbitMQ是一种发布/订阅类型的消息服务,由以下元素组成。
-
- producer: メッセージを送る側。今回は、DeepStream SDKがこれに当たる
-
- exchange: 送られてきたメッセージを適当なqueueに割り振る
-
- queue: キュー
- consumer: メッセージをqueueに取りに行って利用する。今回は、LINEに通知を送るpythonスクリプト
准备AMQP的配置文件。用户名和密码都设置为guest。
{
"rabbit_version": "3.8.0",
"users": [{
"name": "guest",
"password_hash": "CV/8dVC+gRd5cY08tFu4h/7YlGWEdE5lJo4sn4CfbCoRz8ez",
"hashing_algorithm": "rabbit_password_hashing_sha256",
"tags": "administrator"
}],
"vhosts": [{
"name": "/"
}],
"permissions": [{
"user": "guest",
"vhost": "/",
"configure": ".*",
"write": ".*",
"read": ".*"
}],
"topic_permissions": [],
"parameters": [],
"global_parameters": [{
"name": "cluster_name",
"value": "rabbit@rabbitmq"
}],
"policies": [],
"queues": [{
"name": "test",
"vhost": "/",
"durable": true,
"auto_delete": false,
"arguments": {
"x-queue-type": "classic"
}
}],
"exchanges": [],
"bindings": [{
"source": "amq.topic",
"vhost": "/",
"destination": "test",
"destination_type": "queue",
"routing_key": "deepstream",
"arguments": {}
}]
}
在queues中,我将队列的名称设置为test。另外,我决定使用默认设置的amq.topic作为交换机,并在bindings中设置当带有”deepstream”主题的消息到达时,将其路由到test队列。
为了读取先前的json文件,准备好conf文件。
loopback_users.guest = false
listeners.tcp.default = 5672
management.tcp.port = 15672
management.load_definitions = /etc/rabbitmq/custom_definitions.json
通过这个,可以在端口15672上进行消息的交流,并读取之前的JSON文件。
使用docker-compose启动RabbitMQ。使用RabbitMQ官方基础镜像,并通过volumes选项将先前的conf/json挂载到容器内可见。
version: "2.3"
services:
rabbitmq:
image: rabbitmq:3.8-management
hostname: rabbitmq
container_name: rabbitmq
expose: #他のコンテナ向けの公開
- "5672"
- "15672"
ports: #
- "5672:5672"
- "15672:15672"
volumes:
- ./rabbitmq:/etc/rabbitmq
networks:
default:
此外,我们通过定义网络,以便在以后可以从DeepStream SDK和消费者容器进行连接。使用以下命令启动。
docker-compose -f docker-compose-amqp.yml up -d
准备DeepStream SDK。
我打算使用NVIDIA GPU CLOUD提供的官方图像来使用DeepStream SDK。
使用的图像是nvcr.io/nvidia/deepstream:4.0.1-19.09-samples。
我会提前拉取。
docker pull nvcr.io/nvidia/deepstream:4.0.1-19.09-samples
这个镜像中包含编译好的DeepStream SDK、示例、TensorRT和Gstreamer等所需的一切。本次我们将使用此docker镜像中包含的示例应用程序之一,即DeepStream Test 4。该应用程序可以进行车辆/人员的检测和识别,并每30帧发送一次识别结果消息。
目录:/sources/apps/sample_apps/deepstream-test4
描述:此示例基于deepstream-test1样本,用于单个H.264流 – filesrc、decode、nvstreammux、nvinfer、nvosd、renderer,以展示在IOT连接中使用”nvmsgconv”和”nvmsgbroker”插件的使用。对于测试4,用户需要修改kafka broker连接字符串以实现成功连接。在运行test4之前,需要设置分析服务器docker。DeepStream分析文档中有关于设置分析服务器的更多信息。
您可以使用以下命令查看README和使用说明。
docker run --gpus 0 --rm -it \
nvcr.io/nvidia/deepstream:4.0.1-19.09-samples \
cat /root/deepstream_sdk_v4.0.1_x86_64/sources/apps/sample_apps/deepstream-test4/README
# READMEが表示される
docker run --gpus 0 --rm -it \
nvcr.io/nvidia/deepstream:4.0.1-19.09-samples \
/opt/nvidia/deepstream/deepstream-4.0/bin/deepstream-test4-app
# /opt/nvidia/deepstream/deepstream-4.0/bin/deepstream-test4-app \
# -i <H264 filename> -p <Proto adaptor library> --conn-str=<Connection string
根据这个README,通过设置–proto-lib、–conn-str和–topic选项,可以使用AMQP。
1. 使用 –proto-lib 或 -p 命令行选项设置适配器库的路径。
适配器库可以在 /opt/nvidia/deepstream/deepstream-/lib 找到。Kafka 库 – libnvds_kafka_proto.so
Azure 设备客户端 – libnvds_azure_proto.so
AMQP 库 – libnvds_amqp_proto.so2. 根据需要使用 –conn-str 命令行选项设置与后端服务器的连接。
对于 Azure – 完整的 Azure 连接字符串
对于 Kafka – 格式为:host;port;topic 的连接字符串
对于 Amqp – 格式为:host;port;username 的连接字符串。密码应在 cfg_amqp.txt 中提供。3. 使用 –topic 或 -t 命令行选项提供消息主题(可选)。
Kafka 消息适配器也可以在连接字符串格式中嵌入主题参数
在这种情况下,命令行中的 “topic” 应与连接字符串中的主题匹配。
在启动AMQP服务器时,您可以将主机设置为rabbitmq,端口设置为15672,用户设置为guest,然后执行-c rabbitmq;5672;guesttopic deepstream -c cfg_amqp.txt。
libnvds_amqp_proto.so的存储位置位于docker镜像内的/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_amqp_proto.so。
消费者的准备
准备一个容器来从AMQP服务器接收消息并发送通知到LINE。
为了发送通知到LINE,我们将使用LINE Notify。
当与Web服务进行协作时,通知将来自LINE提供的官方账号“LINE Notify”。
我们将使用Python编写用于这个“Web服务”的专用TOKEN。
获取并保存专用TOKEN,在执行时使用该TOKEN进行身份验证,并通过向特定URL发送POST请求来发送通知。
根据这篇文章,我们已经获取了TOKEN并将在执行过程中使用它,所以请记下在某个地方备忘。
import pika
import json
import requests
import os
LINE_URL = "https://notify-api.line.me/api/notify"
line_token = os.environ['LINE_TOKEN'] #環境変数からTOKENを持ってくる
message_format = '''
時刻: {time:s}
場所: {place:s}
カメラ: {camera:s}
検出された物体: {object:s}
'''
def main():
# mqttに接続する
credentials = pika.PlainCredentials('guest', 'guest')
connect_param = pika.ConnectionParameters(
host='rabbitmq', #docker-compsoe-amqpでcontainerにつけたホスト名
credentials=credentials
)
connection = pika.BlockingConnection(connect_param)
channel = connection.channel()
# line認証用のtoken
line_headers = {"Authorization": "Bearer {}".format(line_token)}
for method_frame, properties, body in channel.consume(
'test',
inactivity_timeout=30 # 30秒新規メッセージがなければbreakする
):
if method_frame is None:
break
message = json.loads(body)
if 'vehicle' in message['object'].keys():
obj_data = '{} {} {}'.format(
message['object']['vehicle']['make'],
message['object']['vehicle']['model'],
message['object']['vehicle']['color'],
)
elif 'person' in message['object'].keys():
obj_data = 'Person {} {}'.format(
message['object']['person']['age'],
message['object']['person']['gender']
)
else:
obj_data = ''
payload = {
"message": message_format.format(
time=message['@timestamp'],
place='{}_{}({})'.format(
message['place']['id'],
message['place']['name'],
message['place']['type'],
),
camera=message['sensor']['id'],
object=obj_data,
)
}
r = requests.post(
LINE_URL,
headers=line_headers,
params=payload
)
channel.basic_ack(method_frame.delivery_tag)
# consumerをキャンセルし、pending中のメッセージがあればreturn
requeued_messages = channel.cancel()
print('Requeued %i messages' % requeued_messages)
connection.close()
if __name__ == '__main__':
main()
载荷 = 在这里,我们正在处理通过DeepStream SDK从AMQP传递的消息。
来自DeepStream Test 4的消息以json格式每30帧发送一次(DeepStream Test 4的设置)。
消息中包含以下格式中存储的一个检测到的对象的信息:
保存在place中的拍摄地点信息,保存在camera中的相机信息,时间,提取物体信息。
{
"messageid": "a8c57c62-5e25-478d-a909-3ab064cbf11f",
"mdsversion": "1.0",
"@timestamp": "2019-11-17T13:42:39.807Z",
"place": {
"id": "1",
"name": "XYZ",
"type": "garage",
"location": {
"lat": 30.32,
"lon": -40.55,
"alt": 100.0
},
"aisle": {
"id": "walsh",
"name": "lane1",
"level": "P2",
"coordinate": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
},
"sensor": {
"id": "CAMERA_ID",
"type": "Camera",
"description": "\"Entrance of Garage Right Lane\"",
"location": {
"lat": 45.293701447,
"lon": -75.8303914499,
"alt": 48.1557479338
},
"coordinate": {
"x": 5.2,
"y": 10.1,
"z": 11.2
}
},
"analyticsModule": {
"id": "XYZ",
"description": "\"Vehicle Detection and License Plate Recognition\"",
"source": "OpenALR",
"version": "1.0",
"confidence": 0.0
},
"object": {
"id": "-1",
"speed": 0.0,
"direction": 0.0,
"orientation": 0.0,
"vehicle": {
"type": "sedan",
"make": "Bugatti",
"model": "M",
"color": "blue",
"licenseState": "CA",
"license": "XX1234",
"confidence": 0.0
},
"bbox": {
"topleftx": 585,
"toplefty": 472,
"bottomrightx": 642,
"bottomrighty": 518
},
"location": {
"lat": 0.0,
"lon": 0.0,
"alt": 0.0
},
"coordinate": {
"x": 0.0,
"y": 0.0,
"z": 0.0
}
},
"event": {
"id": "4f8436ab-c611-4257-8b83-b9134c6cab0d",
"type": "moving"
},
"videoPath": ""
}
使用Dockerfile创建包含上述consumer的容器映像。首先安装Python3,然后使用pip安装所需的Python包。最后将源代码复制进去即可。
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y python3-pip python3-dev \
&& cd /usr/local/bin \
&& ln -s /usr/bin/python3 python \
&& pip3 install --upgrade pip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt /tmp/requirements.txt
RUN pip3 install -r /tmp/requirements.txt
COPY src /opt/src
WORKDIR /opt/src
CMD ["python3", "./receive.py"]
pika
requests
执行
使用docker-compose启动DeepStream SDK和consumer。
version: "2.3"
services:
deepstream:
image: nvcr.io/nvidia/deepstream:4.0.1-19.09-samples
runtime: nvidia
hostname: deepstream
container_name: deepstream
command: >
/opt/nvidia/deepstream/deepstream-4.0/bin/deepstream-test4-app
-i /root/videos/test.h264
-p /opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_amqp_proto.so
-c cfg_amqp.txt
-t deepstream
--conn-str rabbitmq;5672;guest
cap_add:
- SYSLOG
working_dir: /root/deepstream_sdk_v4.0.1_x86_64/sources/apps/sample_apps/deepstream-test4
networks:
- deepstream-line_default
volumes:
- ./videos:/root/videos
- /tmp/.X11-unix:/tmp/.X11-unix
environment:
- DISPLAY=$DISPLAY
consumer:
build: ./docker
hostname: consumer
container_name: consumer
environment:
LINE_TOKEN: "XXX"
networks:
- deepstream-line_default
networks:
deepstream-line_default:
external: true
请将获取到的TOKEN粘贴到”XXXXXXXX”处。这样,您就可以收到通知了。
当执行以下操作时,处理程序将开始运行。
xhost + # dockerから結果のdisplay表示ができるように
docker-compose build # consumerのbuild処理
docker-compose up -d
由于这个假设是针对有显示屏的环境的,所以如果要在服务器或其他地方运行,需要进行一些简单的修正。
结果 (jié guǒ)
当运行docker-compose up时,会启动视频识别处理,并通过LINE发送通知。
在电脑版LINE上,通知会以以下方式传达给您,但在手机上则会如下所示。
最后
我們介紹了如何使用 DeepStream SDK 進行快速視頻識別並通過 LINE 進行通知的方法。雖然本次使用了普通伺服器,但是通過使用 deepstream-l4t 容器,應該也可以在像 Jetson 的邊緣 GPU 機器上進行相同處理。這擴大了我們自己製作 AI 相機之夢想的可能性。
另外,这次在DeepStream SDK的应用程序中,我们无法使用自己喜欢的识别模型,也无法更改输出数据的格式。如果有机会的话,我们将会介绍它。
非常感谢您一直阅读到最后。
为了加速在NVIDIA的GPU上进行深度学习推理处理而设计的库。我们之前有使用过TensorRT 5.0和Jetson Nano进行推理加速等方面的文章。↩
如果在没有显示器的服务器上运行,可以通过从docker-compose.yml文件的deepstream部分中移除环境变量并在命令行中添加–no-display选项来执行。↩