利用 Video infer 服务实现摄像头图像采集与 AI 模型推断

声明

  • 本文测试所用设备系统为 Ubuntu 18.04
  • 本文测试所用摄像头为海康威视 IP 网络摄像头,其型号为 DS-IPC-B12-1
  • 本文测试的 AI 推断跑在 CPU 使用官方的 video-infer 模块
  • 本文测试采用的 AI 模型为 ssd_mobilenet_v1_coco_2017_11_17
  • 模拟 MQTT client 向本地 Hub 服务发送消息的客户端为 MQTTBox

操作流程

  • 步骤 1:在 Ubuntu18.04 上安装 Baetyl,相关内容可参考 快速安装 Baetyl
  • 步骤 2:依据本文测试需求,撰写各服务的配置文件,然后执行 sudo systemctl start baetyl 命令启动 Baetyl 服务,可以通过 sudo systemctl status baetyl 命令查看 Baetyl 服务的运行情况,关于各服务的配置情况请参考下文 各服务配置
  • 步骤 3:根据 Hub 服务的配置信息对 MQTTBox 进行连接配置,确保 MQTTBox 与 Hub 服务建立连接,更多可参考 与 Hub 服务建立连接
  • 步骤 4:MQTTBox 订阅主题 video/infer/result,观察其是否能够正常接收到模型的推断结果消息。上述步骤的操作流程示意图如下图所示。

../_images/video-infer-flow.png利用 video-infer 服务进行视频采集和 AI 模型推断

各服务配置

Baetyl 主程序的配置文件位置 var/db/baetyl/application.yml,配置信息如下:

  1. version: V0
  2. services:
  3. - name: localhub
  4. image: 'hub.baidubce.com/baetyl/baetyl-hub:latest'
  5. replica: 1
  6. ports:
  7. - '1883:1883'
  8. mounts:
  9. - name: localhub-conf
  10. path: etc/baetyl
  11. readonly: true
  12. - name: localhub-persist-data
  13. path: var/db/baetyl/data
  14. - name: demo-log
  15. path: var/log/baetyl
  16. - name: function-manager
  17. image: 'hub.baidubce.com/baetyl/baetyl-function-manager:latest'
  18. replica: 1
  19. mounts:
  20. - name: function-manager-conf
  21. path: etc/baetyl
  22. readonly: true
  23. - name: demo-log
  24. path: var/log/baetyl
  25. - name: function-python
  26. image: 'hub.baidubce.com/baetyl/baetyl-function-python36:0.1.6-opencv41'
  27. replica: 0
  28. mounts:
  29. - name: function-python-conf
  30. path: etc/baetyl
  31. readonly: true
  32. - name: function-python-code
  33. path: var/db/baetyl/code
  34. readonly: true
  35. - name: image-data
  36. path: var/db/baetyl/image
  37. - name: video-infer
  38. image: 'hub.baidubce.com/baetyl-beta/baetyl-video-infer:latest'
  39. replica: 1
  40. mounts:
  41. - name: infer-person-model
  42. path: var/db/baetyl/model
  43. readonly: true
  44. - name: image-data
  45. path: var/db/baetyl/image
  46. - name: demo-log
  47. path: var/log/baetyl
  48. - name: video-infer-conf
  49. path: etc/baetyl
  50. readonly: true
  51. volumes:
  52. - name: localhub-conf
  53. path: var/db/baetyl/localhub-conf
  54. - name: localhub-persist-data
  55. path: var/db/baetyl/localhub-persist-data
  56. - name: demo-log
  57. path: var/db/baetyl/demo-log
  58. - name: function-manager-conf
  59. path: var/db/baetyl/function-manager-conf
  60. - name: function-python-conf
  61. path: var/db/baetyl/function-python-conf
  62. - name: function-python-code
  63. path: var/db/baetyl/function-python-code
  64. - name: image-data
  65. path: var/db/baetyl/image-data
  66. - name: remote-mqtt-conf
  67. path: var/db/baetyl/remote-mqtt-conf
  68. - name: infer-person-model
  69. path: var/db/baetyl/infer-person-model
  70. - name: video-infer-conf
  71. path: var/db/baetyl/video-infer-conf

Hub 服务配置文件位置 var/db/baetyl/localhub-conf/service.yml,配置信息如下:

  1. listen:
  2. - tcp://0.0.0.0:1883
  3. principals:
  4. - username: test
  5. password: hahaha
  6. permissions:
  7. - action: 'pub'
  8. permit: ['#']
  9. - action: 'sub'
  10. permit: ['#']
  11. logger:
  12. path: var/log/baetyl/localhub-service.log
  13. level: "debug"

Function manager 服务配置文件位置 var/db/baetyl/function-manager-conf/service.yml,配置信息如下:

  1. server:
  2. address: 0.0.0.0:50051
  3. hub:
  4. address: tcp://localhub:1883
  5. username: test
  6. password: hahaha
  7. functions:
  8. - name: analyse
  9. service: function-python
  10. instance:
  11. max: 10
  12. logger:
  13. path: var/log/baetyl/func-service.log
  14. level: "debug"

Function python 服务配置文件位置 var/db/baetyl/function-python-conf/service.yml,配置信息如下:

  1. functions:
  2. - name: 'analyse'
  3. handler: 'analyse.handler'
  4. codedir: 'var/db/baetyl/code'
  5. logger:
  6. path: "var/log/baetyl/python-service.log"
  7. level: "debug"

Video infer 服务配置文件位置 var/db/baetyl/video-infer-conf/service.yml,配置信息如下:

  1. hub:
  2. address: tcp://localhub:1883
  3. username: test
  4. password: hahaha
  5. video:
  6. uri: "rtsp://admin:admin@192.168.1.2:554/Streaming/channels/1/"
  7. limit:
  8. fps: 1
  9. infer:
  10. model: var/db/baetyl/model/frozen_inference_graph.pb
  11. config: var/db/baetyl/model/ssd_mobilenet_v1_coco_2017_11_17.pbtxt
  12. process:
  13. before:
  14. swaprb: true
  15. width: 300
  16. hight: 300
  17. after:
  18. function:
  19. name: analyse
  20. functions:
  21. - name: analyse
  22. address: function-manager:50051
  23. logger:
  24. path: var/log/baetyl/infer-service.log
  25. level: "debug"

需要注意的是,这里 uri 配置代表的是 IP 网络摄像头的地址,其通用格式为 rtsp://<username>:<password>@<ip>:<port>/Streaming/channels/<stream_number>,其中,<username><password> 为激活成功后的 IP 摄像头的登录认证口令,<ip> 为该摄像头的IP地址,<port> 为 RTSP 协议的端口号,默认为 554,后面内容为信道,其中 <stream_number> 为 1 代表抓取主码流,为 2 代表抓取次码流。

此外,Video infer 服务除支持抓取 IP 网络摄像头图片信息外,还支持抓取 USB 摄像头采集图像信息和读取视频文件进行抽帧。相应地,若为 USB 摄像头,则 uri 配置为设备编号,通用性配置为 “0”,同时需要将设备地址 /dev/video0 映射进容器;若配置读取视频文件进行抽帧,则直接配置 uri 内容为视频文件的地址,同时将该视频文件以(自定义)存储卷形式挂载到 Video infer 服务即可,更多关于存储卷的内容可以参考 如何正确地引入存储卷

Video infer 服务捕获 USB 摄像头采集图像配置可参考:

  1. video:
  2. uri: "0"
  3. limit:
  4. fps: 1

同时在 application.yml 配置中将 /dev/video0 设备地址映射到容器中,相关配置可参考:

  1. version: V0
  2. services:
  3. - name: video-infer
  4. image: 'hub.baidubce.com/baetyl-beta/baetyl-video-infer:latest'
  5. replica: 1
  6. devices:
  7. - /dev/video0 # 将 USB 设备映射进容器
  8. mounts:
  9. - name: infer-person-model
  10. path: var/db/baetyl/model
  11. readonly: true
  12. - name: image-data
  13. path: var/db/baetyl/image
  14. - name: demo-log
  15. path: var/log/baetyl
  16. - name: video-infer-conf
  17. path: etc/baetyl
  18. readonly: true

不难发现,从容器内读取宿主机的 USB 、串口等设备,均可采用上述设备映射的方式配置。

测试及验证

如本文开头所述,本次测试我们采用 ssd_mobilenet_v1_coco_2017_11_17 模型,该模型可用于检测人、水果等多达 90 项物品。这里,我们给出用于检测人的 Python 脚本仅供参考,具体如下:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. """
  4. function to analyse video infer result in python
  5. """
  6. import numpy as np
  7.  
  8. location = "var/db/baetyl/image/{}.jpg"
  9. classes = {
  10. 1: 'person'
  11. }
  12.  
  13. def handler(event, context):
  14. """
  15. function handler
  16. """
  17. data = np.fromstring(event, np.float32)
  18. mat = np.reshape(data, (-1, 7))
  19. objects = []
  20. scores = {}
  21. for obj in mat:
  22. clazz = int(obj[1])
  23. if clazz in classes:
  24. score = float(obj[2])
  25. if classes[clazz] not in scores or scores[classes[clazz]] < score:
  26. scores[classes[clazz]] = score
  27. if score < 0.6:
  28. continue
  29. objects.append({
  30. 'class': classes[clazz],
  31. 'score': score,
  32. 'left': float(obj[3]),
  33. 'top': float(obj[4]),
  34. 'right': float(obj[5]),
  35. 'bottom': float(obj[6])
  36. })
  37.  
  38. res = {}
  39. res["imageDiscard"] = len(objects) == 0
  40. res["imageObjects"] = objects
  41. res["imageScores"] = scores
  42. res["publishTopic"] = "video/infer/result"
  43. res["messageTimestamp"] = int(context["messageTimestamp"]/1000000)
  44. if len(objects) != 0:
  45. res["imageLocation"] = location.format(context["messageTimestamp"])
  46.  
  47. return res

若检测其他物品,直接在这里添加或修改即可,可支持检测物品列表参考 mscoco_label_map

一切就绪后,启动 Baetyl 服务,然后通过 sudo systemctl status baetyldocker ps 命令查看 Baetyl 服务的运行状态及正在运行的容器列表。

../_images/running-status-of-baetyl.png Baetyl 服务运行情况

../_images/running-container-view.png 正在运行的容器列表

可以发现,Baetyl 服务处于 active (running) 状态,系统现在正在运行了 baetyl-hubbaetyl-video-inferbaetyl-function-managerbaetyl-function-python36 四个容器。

然后,我们启动 MQTTBox,根据 Hub 服务配置完连接信息后,可以发现 MQTTBox 已经与 Hub 服务建立连接。

../_images/MQTTBox-connect-to-hub.png MQTTBox 与 Hub 服务建立连接

然后,通过 MQTTBox 订阅主题 video/infer/result,若服务运行正常,则 MQTTBox 会收到消息。

../_images/model-infer-result-none-person.png MQTTBox 接收消息,未检测到人

../_images/model-infer-result-person.png MQTTBox 接收消息,检测到人

如上所示,MQTTBox 通过订阅主题 video/infer/result 正确收到了消息。对比上述两条消息,可以发现一个检测到了人,一个未检测到人,如果模型检测到了人,则会在接收到的信息中给出类别 class 类别信息,同时给出检测到的人在捕获图片中的位置信息,可以用于后续的画框标记操作,感兴趣的可以参考 代码

至此,我们已经基于 Video infer 服务实现了 IP 网络摄像头图像的采集和 AI 模型的推断。