[PYTHON] Notification LINE à partir de la reconnaissance vidéo en temps réel avec le SDK DeepStream

Notification LINE à partir de la reconnaissance vidéo haute vitesse avec le SDK DeepStream

Cet article est le 7ème jour du calendrier Docomo Dovent 2019.

Je m'appelle Sakai du département d'innovation du service NTT Docomo. Dans mon travail, je travaille sur la recherche et le développement d'un moteur de reconnaissance d'images utilisant le Deep Learning et en faisant un service. Cette fois, j'ai utilisé l'outil d'accélération de reconnaissance vidéo "DeepStream SDK" utilisant le GPU pour détecter les voitures à partir de vidéos à haute vitesse et notifier LINE des résultats. On a l'impression que vous pouvez créer quelque chose comme une caméra AI et l'utiliser pour détecter les personnes avec une caméra de surveillance ou détecter un stationnement illégal à partir d'images de caméra.

動画gif

J'ai également contesté la messagerie en utilisant le SDK DeepStream et AMQP, que je n'ai pas pu trouver beaucoup de cas sur le Web.

workflow.jpg

Motivation

Ces dernières années, à mesure que l'utilisation du Deep Learning dans la technologie de reconnaissance d'image s'est développée, la reconnaissance d'image des données vidéo est progressivement devenue plus populaire. Cependant, la reconnaissance vidéo utilisant le Deep Learning nécessite plus de spécifications de la machine que la reconnaissance d'image normale, et le temps de traitement a tendance à être plus lent.

Dans un tel cas, si vous utilisez le [SDK DeepSTtream] de NVIDIA (https://developer.nvidia.com/deepstream-sdk), vous pouvez accélérer la reconnaissance vidéo et accélérer le traitement vidéo à l'aide du GPU. Il peut être utilisé avec docker, et il est facile à installer, alors je l'ai essayé.

DeepStream SDK

Dans l'inférence Deep Learning (+ learning), le GPU est souvent utilisé pour accélérer. DeepStream SDK est un SDK pour le traitement vidéo haute vitesse à l'aide de GPU. Diverses fonctions requises pour le traitement vidéo sont fournies sous la forme de plug-ins Gstreamer qui sont courants dans le traitement de flux.

Méthode

environnement

Nous avons préparé un serveur GPU capable d'utiliser NVIDIA docker 2.0.

gpu était support natif dans docker, mais il semble que vous ne pouvez pas spécifier GPU dans docker-compose. , Adopté l'ancienne méthode de spécification des expressions d'exécution.

organisation des fichiers

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

Les données vidéo peuvent être trouvées sur le site de matériel vidéo gratuit Mixkit. J'utilise Chinatown street la nuit.

Puisqu'il est nécessaire d'en faire un flux h264, convertissez-le en utilisant ffmpeg.

ffmpeg -i ./2012-1080.mp4  -vcodec copy -an -bsf:v h264_mp4toannexb videos/test.h264

Démarrer le serveur AMQP

Le SDK DeepStream peut envoyer le résultat de la reconnaissance sous forme de message à Kafka, MQTT, AMQP, etc. Cette fois, j'ai décidé de lancer RabbitMQ en utilisant docker. RabbitMQ est un service de messagerie de type Pub / Sub qui se compose des éléments suivants.

rabbitmq.jpg

--producer: l'expéditeur du message. Cette fois, le SDK DeepStream est celui --exchange: attribue le message envoyé à une file d'attente appropriée --queue: file d'attente --consumer: utilisez le message en allant dans la file d'attente. Cette fois, un script python qui envoie une notification à LINE

Préparez le fichier de configuration AMQP. L'utilisateur et le mot de passe sont tous deux invités.

rabbitmq/custom_definitions.json


{
    "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": {}
    }]
}

Dans les files d'attente, le nom de la file d'attente est défini sur test. De plus, Exchange utilise amq.topic, qui est défini par défaut, et lorsqu'un message avec une rubrique «deepstream» arrive dans les liaisons, il est acheminé vers la file d'attente de test.

Préparez également un fichier conf pour lire le fichier json.

rabbitmq/rabbitmq.conf


loopback_users.guest = false
listeners.tcp.default = 5672
management.tcp.port = 15672
management.load_definitions = /etc/rabbitmq/custom_definitions.json

Vous pouvez maintenant échanger des messages sur le port 15672 lors du chargement du fichier json.

Démarrez RabbitMQ à l'aide de docker-compose. Lors de la récupération de l'image de base officielle de RabbitMQ, utilisez l'option volumes pour monter le conf / json afin qu'il puisse être vu de l'intérieur du conteneur.

docker-compose-amqp.yml


version: "2.3"
services:
  rabbitmq:
    image: rabbitmq:3.8-management
    hostname: rabbitmq
    container_name: rabbitmq
    expose: #Publication pour d'autres conteneurs
      - "5672"
      - "15672"
    ports: #
      - "5672:5672"
      - "15672:15672"
    volumes:
      - ./rabbitmq:/etc/rabbitmq
networks:
  default:

De plus, en définissant un réseau, il est possible de se connecter ultérieurement à partir du SDK DeepStream ou du conteneur consommateur. Commencez par la commande suivante.

docker-compose -f docker-compose-amqp.yml up -d

Préparation du SDK DeepStream

Pour le SDK DeepStream, nous utiliserons l'image officielle fournie par NVIDIA GPU CLOUD.

使うイメージはnvcr.io/nvidia/deepstream:4.0.1-19.09-samplesです。

Tirez-le à l'avance.

docker pull nvcr.io/nvidia/deepstream:4.0.1-19.09-samples

Cette image contient tout ce dont vous avez besoin, y compris le SDK DeepStream compilé, des exemples, TensorRT et Gstreamer. Cette fois, nous utiliserons DeepStream Test 4 dans les exemples d'applications inclus dans cette image de docker. Il s'agit d'une application qui peut détecter et reconnaître les voitures / personnes, puis envoyer le résultat de la reconnaissance sous forme de message toutes les 30 images.

Directory: /sources/apps/sample_apps/deepstream-test4 Description: This builds on top of the deepstream-test1 sample for single H.264 stream - filesrc, decode, nvstreammux, nvinfer, nvosd, renderer to demonstrate the use of "nvmsgconv" and "nvmsgbroker" plugins in the pipeline for IOT connection. For test4, user have to modify kafka broker connection string for successful connection. Need to setup analytics server docker before running test4. The DeepStream Analytics Documentation has more information on setting up analytics servers.

Vous pouvez vérifier le README et l'utilisation avec les commandes suivantes.

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
#READ ME s'affiche
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

Selon README, vous pouvez utiliser AMQP en définissant les options --proto-lib, --conn-str, --topic.

1.Use --proto-lib or -p command line option to set the path of adaptor library. Adaptor library can be found at /opt/nvidia/deepstream/deepstream-/lib

kafka lib - libnvds_kafka_proto.so azure device client - libnvds_azure_proto.so AMQP lib - libnvds_amqp_proto.so

2.Use --conn-str command line option as required to set connection to >backend server. For Azure - Full Azure connection string For Kafka - Connection string of format: host;port;topic For Amqp - Connection string of format: host;port;username. Password to be provided in cfg_amqp.txt

3.Use --topic or -t command line option to provide message topic (optional). Kafka message adaptor also has the topic param embedded within the connection string format In that case, "topic" from command line should match the topic within connection string

Dans [Démarrer le serveur AMQP](#AMQP Server Start), l'hôte est rabbitmq, le port est 15672 et l'utilisateur est l'invité, donc -c rabbitmq; 5672; le sujet invité doit être deepstream -c cfg_amqp.txt. Ce sera.

Libnvds_amqp_proto.so est stocké dans / opt / nvidia / deepstream / deepstream-4.0 / lib / libnvds_amqp_proto.so dans l'image du docker.

Préparation du consommateur

Préparez un conteneur pour recevoir les messages du serveur AMQP et envoyer des notifications à LINE. Utilisez LINE Notify pour envoyer des notifications à LINE. Cela signifie que lorsque vous vous connectez à un service Web, vous recevrez une notification du compte officiel "LINE Notify" fourni par LINE. Ecrivez l'équivalent de ce "Web service" en python. Vous pouvez envoyer une notification en acquérant un TOKEN dédié et en POSTANT un message à une URL spécifique lors de l'authentification à l'aide de TOKEN. Cette fois, j'ai obtenu TOKEN en me référant à cet article. Le TOKEN acquis sera utilisé dans [Execute](# Execution), alors notez-le quelque part.

docker/src/receive.py


import pika
import json
import requests
import os

LINE_URL = "https://notify-api.line.me/api/notify"
line_token = os.environ['LINE_TOKEN'] #Extraire TOKEN des variables d'environnement
message_format = '''
Heures du jour: {time:s}
endroit: {place:s}
caméra: {camera:s}
Objet détecté: {object:s}
'''


def main():
    #Connectez-vous à mqtt
    credentials = pika.PlainCredentials('guest', 'guest')
    connect_param = pika.ConnectionParameters(
        host='rabbitmq',  #docker-compsoe-Nom d'hôte donné au conteneur avec amqp
        credentials=credentials
    )
    connection = pika.BlockingConnection(connect_param)
    channel = connection.channel()

    #jeton pour l'authentification de ligne
    line_headers = {"Authorization": "Bearer {}".format(line_token)}

    for method_frame, properties, body in channel.consume(
        'test',
        inactivity_timeout=30  #Pause s'il n'y a pas de nouveau message pendant 30 secondes
    ):
        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)

    #Annuler le consommateur et revenir s'il y a un message en attente
    requeued_messages = channel.cancel()
    print('Requeued %i messages' % requeued_messages)
    connection.close()


if __name__ == '__main__':
    main()

À payload =, nous traitons le message envoyé depuis le SDK DeepStream via AMQP. Les messages de DeepStream Test 4 sont envoyés au format json une fois toutes les 30 images (DeepStream Test 4). Le message contient des informations sur l'un des objets découverts au format suivant: Nous avons décidé de récupérer les informations de lieu de prise de vue stockées sur place, les informations de caméra stockées dans la caméra, l'heure et les informations sur l'objet.

{
    "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": ""
}

L'image de conteneur qui exécute le consommateur ci-dessus est créée à l'aide de Dockerfile. Après avoir installé python3, nous avons installé le package python nécessaire à l'aide de pip. Enfin, copiez le code source et vous avez terminé.

docker/Dockerfile


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"]

requirements.txt


pika
requests

Courir

Lancez le SDK DeepStream et le consommateur à l'aide de docker-compose.

docker-compose.yml


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

"XXXXXXXX"Collez le JETON obtenu à la place. Vous recevrez désormais des notifications sur votre LINE.



 Le processus démarre lorsque vous exécutez ce qui suit.

```bash
xhost + #L'affichage des résultats peut être affiché à partir du menu fixe
docker-compose build #processus de construction du consommateur
docker-compose up -d

Cela suppose un environnement avec affichage, donc une légère modification [^ 2] est nécessaire pour l'exécuter sur un serveur ou autre.

résultat

Lorsque docker-compose up, le processus de reconnaissance vidéo démarrera sous la forme suivante et une notification sera envoyée à LINE.

result_all.gif

Ce qui précède est la version PC de LINE, mais si vous utilisez un smartphone, vous recevrez une notification comme celle-ci.

Screenshot_20191206-212712_LINE.jpg

à la fin

Nous avons présenté comment effectuer une reconnaissance vidéo haute vitesse et envoyer des notifications LINE à l'aide du SDK DeepStream. Cette fois, j'ai utilisé un serveur normal, mais en utilisant le conteneur deepstream-l4t, pour les GPU Edge tels que jetson Vous devriez pouvoir faire de même sur votre machine. Le rêve de créer quelque chose comme une caméra IA se propage.

De plus, cette fois, je n'ai pas pu utiliser le modèle de reconnaissance de mon choix dans l'application côté DeepStream SDK ni changer le format des données de sortie. Je voudrais le présenter si j'en ai l'occasion.

Jusqu'à la fin Merci d'avoir lu.

[^ 1]: une bibliothèque pour accélérer le traitement d'inférence Deep Learning sur les GPU NVIDIA. Auparavant, j'ai essayé d'utiliser TensorRT 5.0, [Accélérer l'inférence avec TensorRT 5.0.6 et JETSON NANO](https://qiita.com/104ki/ J'écris des articles tels que items / 17434efa7ce045f767dd). [^ 2]: Si vous voulez l'exécuter sur un serveur sans affichage, vous pouvez l'exécuter en supprimant ʻenvironment de deepstreamdans docker-compose.yml et en ajoutant l'option--no-display à command`. ..

Recommended Posts

Notification LINE à partir de la reconnaissance vidéo en temps réel avec le SDK DeepStream
Reconnaissance faciale en temps réel avec vidéo acquise par getUserMedia [HTML5, openCV]
Lire ligne par ligne à partir d'un fichier avec Python