基于DL Streamer与YOLOv8模型实现多路视频流实时分析

openlab_4276841a 更新于 6月前

作者:杨亦诚

作为众多AI应用场景的基座,基于流媒体的视觉分析一直是传统AI公司的核心能力之一。但想要搭建一套完整的视频分析系统其实并不容易,其中会涉及多个图像处理环节的开发工作,例如视频流拉取、图像编解码、AI模型前后处理、AI模型推理,以及视频流推送等常见任务模块。其中每一个模块都需要领域专家在指定的硬件平台进行开发和优化,并且如何高效地将他们组合起来也是一个问题。在这篇文章中,我们将探讨如何利用Intel的DL Streamer工具套件打造一套支持多路视频流接入的视频分析系统,利用OpenVINO™部署并加速YOLOv8推理任务。

示例代码:https://github.com/OpenVINO-dev-contest/dlstreamer/tree/yolov8-2023.0/demo



图:典型的视频分析系统流水线

Intel® DL Streamer工具套件

DL Streamer是一套开源的流媒体分析系统框架,它基于著名的GStreamer多媒体框架所打造,可以帮助开发者快速构建复杂的媒体分析流水线。开发者只需要通过命令行的方式就可以轻松完成一套支持多路的分析系统搭建。此外,在这个过程中,DL Streamer会帮助我们将每个模块部署在指定的硬件加速平台,以获得更好的性能与更高的资源利用率。



图:DL Streamer架构图

· Intel® DL Streamer Pipeline Framework用于构建最基础的视频分析流水线,其中利用VA-API库提升GPU的硬件编解码能力,基于OpenVINO™实现对于AI推理任务的加速。此外还支持C++ 和 Python API接口调用方式,便于开发者与自有系统进行集成。
· Intel® DL Streamer Pipeline Server可以将构建好的视频分析流水线以微服务的方式部署在多个计算节点上,并提供对外的REST APIs接口调用。
· Intel® DL Streamer Pipeline Zoo则被作为性能评估与调式的工具,其中集成了一些即开即用的示例,方便开发者测试。
本文中分享的demo是一个基于DL Streamer的最小化示例,仅使用Intel® DL Streamer Pipeline Framework进行任务开发。

开发流程

1. YOLOv8模型优化与转换

首先我们需要对模型进行性能优化,这里我们才利用量化技术来压缩模型体积。由于目前Ultralytics库已经直接支持OpenVINO IR格式的模型导出,所以我们可以直接调用以下接口将YOLOv8预训练权转化为OpenVINO IR,并通过NNCF工具进行后训练量化。

det_model = YOLO(f"../model/{DET_MODEL_NAME}.pt")

out_dir = det_model.export(format="openvino", dynamic=True, half=True)

完整代码可参考:https://github.com/openvinotoolkit/openvino_notebook***lob/recipes/recipes/intelligent_queue_management/docs/convert-and-optimize-the-model.ipynb

此外由于我们需要使用vaapi-surface-sharing backend,来实现从解码-前处理-推理在GPU设备上的zero-copy,标准YOLOv8模型的部分前处理任务没有办法支持vaapi-surface-sharing(https://dlstreamer.github.io/dev_guide/model_proc_file.html#pre-processing-description-input-preproc),因此我们需要将部分前处理任务以模型算子的形式提前集成到模型结构中,这里可以利用OpenVINO的Preprocessing API来进行前处理任务中转置归一化操作的集成。具体方法如下:

input_layer = model.input(0)

model.reshape({input_layer.any_name: PartialShape([1, 3, 640, 640])})

ppp = PrePostProcessor(model)

ppp.input().tensor().set_color_format(ColorFormat.BGR).set_layout(Layout("NCHW"))

ppp.input().preprocess().convert_color(ColorFormat.RGB).scale([255, 255, 255])

model = ppp.build()

2. 集成YOLOv8后处理任务

由于DLStreamer目前没有直接支持YOLOv8的后处理任务,所以我们需要在其源码中新增YOLOv8后处理任务的C++实现(https://github.com/OpenVINO-dev-contest/dlstreamer/blob/yolov8-2023.0/src/monolithic/gst/inference_elements/common/post_processor/converters/to_roi/yolo_v8.cpp),并重新编译DLStreamer源码。相较之前YOLO系列的模型,YOLOv8模型的原始输出会一些特殊,他的输出数据结构为(1, 84, 8400),其中8400代表识别对象的数量,84代表4个坐标信息+80种类别,而通常情况下坐标信息和类别信息都是在最后一个维度里,所以为了在C++应用中更方便的地模型输出进行遍历,我们首先需要做一个维度转置的操作,将其输出格式变为(1, 8400, 84),接下来就可以通过常规YOLO模型的后处理方式,来解析并过滤YOLOv8模型输出。

cv::Mat outputs(object_size, max_proposal_count, CV_32F, (float *)data);

cv::transpose(outputs, output*****r/>

DL Streamer源码编译方式可以参考:

https://dlstreamer.github.io/get_started/install/install_guide_ubuntu.html#id3

3. 构建DL Streamer Pipeline

其实DL Streamer Pipeline的构建非常简单,我们只需要记住每一个element模块的功能,并按从“输入->解码->推理->编码/输出”的次序将他们组合起来就可以了,以下就是一个单通道的示例。

gst-launch-1.0 filesrc location=./TownCentreXVID.mp4 ! decodebin ! video/x-raw\(memory:VASurface\) ! gvadetect model=./models/yolov8n_int8_ppp.xml model_proc=./dlstreamer_gst/samples/gstreamer/model_proc/public/yolo-v8.json pre-proces***ackend=vaapi-surface-sharing device=GPU ! queue ! meta_overlay device=GPU preprocess-queue-size=25 process-queue-size=25 postprocess-queue-size=25 ! videoconvert ! fpsdisplaysink video-sink=ximagesink sync=false

除推理部分的任务外,DL Streamer中大部分的模块都是复用GStreamer的element,这里需要特别注意的是,为了实现在GPU硬解码和推理任务之间的zero-copy,解码的输出需要为video/x-rawmemory:VASurface格式,并且推理的任务的前处理任务需要调用vaapi-surface-sharing backend,以此来将前处理的任务负载通过GPU来进行加速。

此外这边也会用到DL Streamer 2.0 API中新增的meta_overlay模块将结果信息,以bounding box的形态添加在原始视频流中,并利用fpsdisplaysink模块统计实时FPS性能后,一并作为结果可视化进行输出展示。如果本机不支持可视化播放,我们也可以通过拼接以下指令:

vaapih264enc ! h264parse ! mpegt**ux ! rtpmp2tpay ! udpsink host=192.168.3.9 port=5004
将结果画面编码后,通过udp协议推流,并用例如VLC这样的工具,在另一台设备播放。

如果不需要可视化呈现,我们也可以通过gvametapublish模块将原始结果输出到一个json文件中,或通过MQTT协议推送这些原始的结果数据。Gvametapublish模块的使用方法可以查询: https://dlstreamer.github.io/elements/gvametapublish.html

4. 多通道Pipeline优化

为了方便多通道任务同屏展示,我们通过compositor模块将多个通道的检测结果进行拼接。在多路推理性能优化方面,可以利用以下指令,将多个同一时刻内的多个stream输入,打包为一个batch,送入GPU进行推理,以激活GPU在吞吐量上的优势,而infer request的数量则会根据接入视频的通道数动态调整。

nireq=$((${STREAM}*2)) gpu-throughput-streams=${STREAM} batch-size=${NUM_PANES} model-instance-id=1
此外,为了避免重复创建模型对象,可以将每个通道里的model-instance-id都设为统一值,这样OpenVINO™只会为我们初始化一个模型对象。

如何运行示例

为了方便在不同硬件平台进行移植,同时降低部署门槛,这里我们已经将所有的示例代码打包进了docker镜像内,大家可以通过以下几条简单的指令,快速复现整个方法。

1. 初始化环境

这一步主要为了可以在容器内访问host的GPU资源,以及开启视频流展示的权限,如果当前硬件中存在多个GPU设备,我们可以通过修改GPU driver的编号来调整映射到容器内的GPU资源,例如这可以可以把renderD128修改为renderD129

$ xhost local:root
 
$ setfacl -m user:1000:r ~/.Xauthority
 
$ DEVICE=${DEVICE:-/dev/dri/renderD128}
 
$ DEVICE_GRP=$(ls -g $DEVICE | awk '{print $3}' | xargs getent group | awk -F: '{print $3}')

2. 拉取docker镜像

$ docker pull snake7gun/dlstreamer-yolov8-2023.0:latest
3. 运行容器

将之前设置host环境映射到容器内,并初始化容器内环境

$ docker run -it --rm --net=host -e no_proxy=$no_proxy -e https_proxy=$https_proxy -e socks_proxy=$socks_proxy -e http_proxy=$http_proxy -v ~/.Xauthority:/home/dlstreamer/.Xauthority -v /tmp/.X11-unix -e DISPLAY=$DISPLAY --device $DEVICE --group-add $DEVICE_GRP snake7gun/dlstreamer-yolov8-2023.0 /bin/bash

$ source /home/dlstreamer/dlstreamer_gst/scripts/setup_env.sh

4. 执行多路示例

运行示例,这里可以将shell脚本后的视频文件替换为IP摄像头RTSP地址,或是webcam的编号。由于gvawatermark模块在iGPU上的性能表现要优于meta_overlay,而在dGPU上则相反,因此这里准备了两套pipeline,分别为pipeline-igpu.sh以及pipeline-dgpu.sh,大家可以根据自己的硬件环境进行切换。

$ cd dlstreamer_gst/demo/

$ ./pipeline-igpu.sh ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4 ~/TownCentreXVID.mp4

效果展示

本方案已经在2023年度的Intel Innovation大会上进行了展示,该demo在9路1080p h.265摄像头输入的情况下,保证每路的实时分析性能可以达到15fps,也就是大部分摄像头的帧率上限。­­­­



图:Demo展示效果

参考资料
DL Streamer 文档
Developer Guide — Intel® Deep Learning Streamer (Intel® DL Streamer) documentation

YOLOv8 C++部署
https://github.com/ultralytics/ultralytics/tree/main/examples/YOLOv8-CPP-Inference

0个评论