在ARM盒子上使用IEF和Tensorflow运行边缘AI
引导式阅读
Python
在ARM盒子上使用IEF和Tensorflow运行边缘AI
作者
C***
上架时间
2022-08-02 12:01:51

在ARM盒子上使用IEF和Tensorflow运行边缘AI

0. 版本说明

本示例配套的SDK版本为:不涉及

1. 示例简介

由于视频流占用很多网络带宽,越来越多的场景下,希望在视频流产生的地方,对图像进行处理,在节省网络带宽的同时,能够快速处理,也能避免网络延迟和抖动带来的不确定性。典型应用场景为:化工厂内部通过图像检测火情,园区内人脸匹配进行安检,建筑工地施工人员是否佩戴安全帽。

在ARM盒子(以atlas500为例,arm64v8架构可以直接使用代码和镜像)上,通过网络接入网络摄像头,使用tensorflow框架对图像进行对象检测模型推理,检测结果通过MQTT协议推送到web页面上进行展示。

img

运行环境

  • 网络摄像头,rtsp协议,ipc网络地址格式
  • 智能小站(atlas500),EulerOS
  • tensorflow1.4.0(python2.7)
  • mosquitto 1.6.5
  • python: opencv-python 4.1.1, paho-mqtt 1.4.0
  • nodejs: mqtt 3.0.0, react 16.9.0

我使用atlas500智能小站,来看看是个什么样的设备,性能还是很强大的。(https://e.huawei.com/cn/material/enterprise/030106f2129145efa9c9bb472c7b0058)。

  • CPU:海思Hi3559A处理器

双核ARM Cortex A73@1.8GHz,32KB I-Cache, 64KB D-Cache /512KB L2 cache

双核ARM Cortex A53@1.2GHz,32KB I-Cache, 32KB D-Cache /256KB L2 cache

  • GPU:双核ARM Mali G71@900MHz,256KB cache
  • 处理器内存规格DDR4 4GB,64bit,2400Mbps
  • Atlas 200 AI加速模块

2个DaVinci AI Core

CPU:8核A55,max 1.6GHz

乘加计算性能:8TFLOPS/FP16,16TOPS/INT8

内存规格:LPDDR4X,128bit,容量8GB/4GB,接口 速率3200 Mbps

安装运行步骤

为了构建系统,分步来做

  • 通过IEF对智能小站进行纳管
  • 连接网络摄像头并测试视频流
  • 使用MQTT协议建立消息服务,并测试数据的发布和订阅
  • 通过tensorflow对图像进行推理
  • 对检测结果进行展示

通过IEF对智能小站进行纳管

1.打开华为云网站产品:智能边缘平台 https://www.huaweicloud.com/ ,使用自己账户登入,打开页面后,点击“注册边缘节点”,填写边缘几个信息。在“名称”里面可以填写名字。这次暂时不使用atlas500上AI加速卡。

img

2.接下来在“是否启用docker”选择“是”,在“系统日志”和“应用日志”中可以按照自己实际选择。

img

3.第三步,由于atlas500芯片是arm64架构,务必选择“arm64”方式,先下载文件。

img

下面在atlas500上进行安装

  1. 根据设备上标识的账户和密码,以及设备IP地址,从电脑上使用ssh方式登入到atlas500上。img

  2. 在atlas500上,使用命令:docker version,检查docker是否存在,如果未安装,参考arm64版本的docker ce进行安装 img

  3. 根据上述1.3步骤中命令安装,把atlas纳入IEF管理之中

sudo tar -zxvf edge-installer_1.0.5_arm64.tar.gz -C /opt sudo mkdir -p /opt/IEF/Cert; sudo tar -zxvf atlas500_training.tar.gz -C /opt/IEF/Cert cd /opt/edge-installer; sudo ./installer -op=install
  1. 在华为云智能边缘平台上查看运行状态,状态从“未纳管”显示为“运行中”

img

img

在atlas500上安装边缘计算平台,已经完成。


连接网络摄像头并测试视频流

  • 购买的摄像头为IPC格式,我把所有设备接入到同一个路由器下面,通过产品手册提供的内容,查看到摄像头相应的IP地址,可以获取到摄像头RTSP协议的URL为:"rtsp://admin:admin@192.168.1.164:554//Streaming/Channels/1"。

  • 在电脑上安装和打开VLC播放器,并点击导航栏“媒体”,“打开网络串流”,填入摄像头的地址"rtsp://admin:admin@192.168.1.164:554//Streaming/Channels/1",点击“播放”,我就可以看到摄像头拍摄的视频。表面摄像头可以使用,整个RTSP协议也是正常的。 img

  • 使用python连接网络摄像头。首先通过命令行安装所需的opencv库。

pip install opencv-python

首先我想确认下opencv是否安装正确,启动下面代码后,如果在界面上可以展示视频流,表面安装没有问题,ip和port填写本机的网卡ip和端口

#show_video.py import cv2 cap = cv2.VideoCapture("rtsp://<ip>:<port>/user=admin&password=&channel=1&stream=1.sdp?") ret,frame = cap.read() while ret: cv2.imshow("frame", frame) ret, frame = cap.read() if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows() cap.release()

也可以通过保存图像,或者录制成视频文件格式,查看摄像头是否工作正常

最后通过web页面查看摄像头是否工作正常,由于浏览器对rtsp协议支持并不好,当前只在IE11浏览器中查看到相应视频流,通过IE11加载下面页面,点击允许控件(根据自己实现情况修改下面的rtsp视频流地址),就可以看到摄像头视频流。

<html> <title>camera rtsp check</title> <meta charset="utf-8"> <body> <object type='application/x-vlc-plugin' id='Object1' width="500" height="400" events='True' pluginspage="http://www.videolan.org" codebase=" http://downloads.videolan.org/pub/videolan/vlc-webplugins/2.0.6/npapi-vlc-2.0.6.tar.xz" > <param name='mrl' value='rtsp://192.168.1.164:554/user=admin&password=&channel=1&stream=1.sdp?' /> <param name='autoplay' value='true' /> <param name='loop' value='false' /> <param value="transparent" name="wmode"> </object > </body> </html>

使用MQTT协议建立消息服务

  • 使用mosquitto程序做为mqtt的broker,使用python发布消息,以nodejs订阅消息,为下一步使用tensorflow检测结果并用mqtt发送检测结果,使用nodejs构建前端页面订阅消息并展示,建立消息服务的系统建立基础。

  • 使用mosquitto构建消息服务,构建的dockerfile如下,使用主机网络模式,以命令/usr/sbin/mosquitto启动,启动后默认使用1883的端口进行通信

FROM arm64v8/ubuntu:19.10 RUN set -ex; \ apt-get update; \ apt-get -y install mosquitto; \ rm -rf /var/lib/apt/lists/*
  • 使用nodejs订阅消息 使用mqtt的库,填写默认的端口号1883,订阅“object_detection”的topic,参考代码如下
//mqtt_sub.js const mqtt = require('mqtt'); const PORT = 1883; const HOST = 'localhost'; const defualtPubSettigs = {retain: true, qa: 0}; var options = { port: PORT, host: HOST, rejectUnauthorized: false, }; client = mqtt.connect('mqtt://localhost', options); client.subscribe('object_detection',{qos:1}); client.on('message',function(top,message) { console.log(message.toString()); });

以此为基础建立docker,dockerfile如下

FROM arm64v8/node:10.16.3 COPY mqtt_sub.js /root/ WORKDIR /root RUN set -ex; \ npm install --save mqtt
  • 使用python发送消息 使用paho.mqtt的库,填写默认的端口号1883,发布“object_detection”的topic,参考代码如下
#mqtt_pub.py import paho.mqtt.client as mqtt import json client = mqtt.Client() client.connect("127.0.0.1", 1883, 600) obj_ret = json.dumps({ "num_detections": 1, "detection_classes": 1, }) client.publish('object_detection', obj_ret, qos=0)

以此为基础建立docker,dockerfile如下

FROM arm64v8/python:2.7 RUN set -ex; \ pip install paho-mqtt COPY mqtt_pub.py /root/ ENTRYPOINT ["/usr/local/bin/python", "/root/mqtt_pub.py"]
  • 通过构建和下发镜像,之后可以通过docker logs查看镜像消息,如果能看到相应的log信息,表面整个消息服务已经成功建立

通过tensorflow对图像进行推理

使用Opencv,通过RTSP协议连接网络摄像头,再对图像帧进行对象检测,最终通过mqtt协议把检测结果发送到消息服务中。对于连接摄像头,参考上述步骤中的“连接网络摄像头并测试视频流“, 发送检测效果参考"使用MQTT协议建立消息服务",剩余部分主要针对使用tensorflow对图像进行检测。

参考https://github.com/tensorflow/models/tree/master/research/object_detection中的object_detection_tutorial.ipynb,添加使用opencv对rtsp协议进行解析,获取每一帧图像,针对图像数据,使用已经训练好的模型,对图像进行推理。检测模型可以从https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md中获取,制作好的镜像中包含了测试模型:ssd_mobilenet_v2_coco。推理的结果最终使用MQTT协议把结果发送出来。

  • 推理模型的参数:MODEL_NAME = 'ssd_mobilenet_v2_coco_2018_03_29'
  • 摄像头的参数:RTSP_URI = 'rtsp://192.168.1.164:554/user=admin&password=&channel=1&stream=1.sdp?'

参考代码od_atlas.py如下;

#od_atlas.py import numpy as np import os import sys import tensorflow as tf import cv2 import json import paho.mqtt.client as mqtt sys.path.append("..") from object_detection.utils import ops as utils_ops from utils import label_map_util client = mqtt.Client() client.connect("127.0.0.1", 1883, 600) cv2.setUseOptimized(True) MODEL_NAME = 'ssd_mobilenet_v2_coco_2018_03_29' PATH_TO_FROZEN_GRAPH = MODEL_NAME + '/frozen_inference_graph.pb' # List of the strings that is used to add correct label for each box. PATH_TO_LABELS = os.path.join('data', 'mscoco_label_map.pbtxt') print("PATH_TO_LABELS", PATH_TO_LABELS) detection_graph = tf.Graph() with detection_graph.as_default(): od_graph_def = tf.GraphDef() print("PATH_TO_FROZEN_GRAPH", PATH_TO_FROZEN_GRAPH) with tf.gfile.GFile(PATH_TO_FROZEN_GRAPH, 'rb') as fid: serialized_graph = fid.read() od_graph_def.ParseFromString(serialized_graph) tf.import_graph_def(od_graph_def, name='') category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS, use_display_name=True) cap = cv2.VideoCapture("rtsp://192.168.1.164:554/user=admin&password=&channel=1&stream=1.sdp?") fps = cap.get(cv2.CAP_PROP_FPS) cap.set(cv2.CAP_PROP_FPS, 1) width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) num = 0 with detection_graph.as_default(): with tf.Session() as sess: ops = tf.get_default_graph().get_operations() all_tensor_names = {output.name for op in ops for output in op.outputs} tensor_dict = {} for key in [ 'num_detections', 'detection_boxes', 'detection_scores', 'detection_classes', 'detection_masks' ]: tensor_name = key + ':0' if tensor_name in all_tensor_names: tensor_dict[key] = tf.get_default_graph().get_tensor_by_name( tensor_name) if 'detection_masks' in tensor_dict: detection_boxes = tf.squeeze(tensor_dict['detection_boxes'], [0]) detection_masks = tf.squeeze(tensor_dict['detection_masks'], [0]) real_num_detection = tf.cast(tensor_dict['num_detections'][0], tf.int32) detection_boxes = tf.slice(detection_boxes, [0, 0], [real_num_detection, -1]) detection_masks = tf.slice(detection_masks, [0, 0, 0], [real_num_detection, -1, -1]) detection_masks_reframed = utils_ops.reframe_box_masks_to_image_masks( detection_masks, detection_boxes, height, width) detection_masks_reframed = tf.cast( tf.greater(detection_masks_reframed, 0.5), tf.uint8) tensor_dict['detection_masks'] = tf.expand_dims( detection_masks_reframed, 0) image_tensor = tf.get_default_graph().get_tensor_by_name('image_tensor:0') while True: ret, image_np = cap.read() if ret == False: continue num = num + 1 print("frame num", num) image_np_expanded = np.expand_dims(image_np, axis=0) output_dict = sess.run(tensor_dict, feed_dict={image_tensor: image_np_expanded}) output_dict['num_detections'] = int(output_dict['num_detections'][0]) output_dict['detection_classes'] = output_dict[ 'detection_classes'][0].astype(np.int64) output_dict['detection_scores'] = output_dict['detection_scores'][0] output = {} num_detections = int(output_dict['num_detections']) for i in range(num_detections): detection_class = int(output_dict['detection_classes'][i]) class_name = category_index[detection_class]['name'] score = output_dict['detection_scores'][i] scores = "scores %.f%%" % (score * 100) output[class_name] = scores print("output ", output) if num_detections > 0 : obj_ret = json.dumps(output) client.publish('object_detection', obj_ret, qos=0) else: client.publish('object_detection', "", qos=0) cap.release()

完整的docker image可以参考braveyuyong/tf_on_atlas:tagname:0.2.1,参考dockerfile如下:

FROM python:2.7 ENV DEBIAN_FRONTEND=noninteractive RUN set -ex; \ apt-get update; \ apt-get -y install python-opencv; \ apt-get -y install protobuf-compiler; \ pip install numpy; \ pip install paho-mqtt; \ pip install pillow; \ apt-get -y install libhdf5-dev; \ rm -rf /root/.cache/; \ rm -rf /var/lib/apt/lists/* RUN ln -s /usr/lib/python2.7/dist-packages/cv2.aarch64-linux-gnu.so /usr/local/lib/python2.7/site-packages/cv2.aarch64-linux-gnu.so RUN curl -OL "https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.14.0-buster/tensorflow-1.14.0-cp27-none-linux_aarch64.whl" && \ pip install tensorflow-1.14.0-cp27-none-linux_aarch64.whl && \ rm tensorflow-1.14.0-cp27-none-linux_aarch64.whl RUN mkdir /models WORKDIR /models RUN git init && \ git remote add origin https://github.com/tensorflow/models.git && \ git config core.sparsecheckout true && \ echo "resarch/object_detection" >> .git/info/sparse-checkout && \ git pull --depth 1 origin master && \ rm -rf .git research/object_detection/test* WORKDIR /models/research/object_detection RUN protoc /models/research/object_detectionobject_detection/protos/*.proto --python_out=. COPY . /models/research/object_detection ENTRYPOINT ["/usr/local/bin/python", "od_atlas.py"]

通过web页面对结果进行展示

使用nodejs订阅主题:object_detection,之后通过websocket把推理结果推送到前端页面。前端页面把推理结果和网络摄像头的视频流在页面上展示出来。

  • 关于RTSP协议的插件,当前只支持IE11,注意在public/rtsp.html中设置rtsp地址(<param name='mrl'/>)
  • 在src\components\Visual\index.jsx中设置atlas500的IP参数。

代码片段如下:

//public/rtsp.html client.addListener('object_detection', (topic, message) => { if (sock) { sock.emit('object_detection', `${message.toString()}`); } }); //src\components\Visual\index.jsx this.state = { url: 'http://192.168.1.111:3001', }; socket.on('object_detection', (data) => { this.setState({ ob_ret: data.toString(), }); }); render() { const { ob_ret } = this.state; let component; if (ob_ret) { component = <p>{ob_ret}</p>; } else{ component = <p></p>; } }

最终效果

先把eclipse-mosquitto的镜像 (https://hub.docker.com/_/eclipse-mosquitto) 下载,并上传到华为云SWR上,并通过IEF新建应用下发到节点上,构建mqtt的broker。整个软件架构图如下:

img

把页面展示镜像 (已经制作好的镜像参考 docker pull braveyuyong/visual_rtsp:0.1) 进行同样操作,新建应用。

最后把TF推理镜像 (docker pull braveyuyong/tf_on_atlas:0.1.2) 进行同样操作,下发应用的时候我选择使用cpu 3core,内存2048M。

img

最终的三个应用状态如下:

img

通过浏览器IE11打开URL: http://192.168.1.111:3001 ,如果能看到摄像头视频流和推理结果,表明大功告成!

img

修订记录

发布日期 文档版本 修订说明
2021-11-29 1.0 文档首次发布