ModelBox
新手入门
【2022 ModelBox实战营】推理功能单元

发布于30个月以前

  • 2
  • 0
  • 1337

发布于30个月以前

ModelBox AI应用开发——推理功能单元

通过上两个教程的介绍,我们了解了ModelBox应用、通用功能单元的基本结构和开发流程,本文将使用ModelBox开发一个AI应用:读取摄像头,解码出视频帧,在画面中检测手部并框出来,再输出画面到本地屏幕。读者将了解到ModelBox中推理功能单元的基本结构和开发流程。

1. 传统开发模式

对于这样的应用,因为涉及到模型推理,除了OpenCV,我们还需要模型推理库,伪代码如下:

import cv2
import onnx_model

# 初始化摄像头,参数0表示设备的第一个摄像头
cap = cv2.VideoCapture(0)

# 判断初始化是否成功
if not cap.isOpened():
    print('failed to open camera 0')
    exit()

# 初始化手部检测模型
hand_det = onnx_model('./hand_det.onnx')

while True:
    # 读取一帧视频图像,ret表示读取是否成功
    ret, frame = cap.read()

    # 图像预处理
    img_pre = hand_det.preprocess(frame)

    # 手部检测推理
    output = hand_det.infer(img_pre)

    # 检测结果后处理与画框
    hand_bboxes = hand_det.postprocess(output)
    draw_hand_bboxes(frame, hand_bboxes)

    # 打开一个名为hand_det的窗口,显示图像
    cv2.imshow('hand_det', frame)

    # 阻塞等待键盘响应1ms,如果按下q键则跳出循环
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放摄像头资源
cap.release()

# 关闭所有窗口
cv2.destroyAllWindows()

这里我们以ONNXRuntime推理库为例,需要使用ONNXRuntime相关的api实现onnx模型的加载、推理等逻辑,另外模型推理前的预处理、推理后的后处理等逻辑也需要实现。也就是说,在传统开发模式中,开发者需要学习推理框架的使用,如果模型格式有变动,可能需要换用其他推理框架。

2. ModelBox Pipeline模式

这个应用对应的ModelBox版本已经做成模板放在华为云OBS中,可以用sdk中的solution.bat工具下载,接下来我们给出该应用在ModelBox中的完整开发过程:

1)下载模板

执行.\solution.bat -l可看到当前公开的技能模板:

PS ███> .\solution.bat -l
...

Solutions name:
mask_det_yolo3
...
hand_det_yolox
hand_tracking_yolox
single_hand_pose_yolox_mbv2
multi_hand_pose_yolox_mbv2

结果中的hand_det_yolox即为手部检测应用模板,可使用如下命令下载模板:

PS ███> .\solution.bat -s hand_det_yolox
...

solution.bat工具的参数中,-l 代表list,即列出当前已有的模板名称;-s 代表solution-name,即下载对应名称的模板。下载下来的模板资源,将存放在ModelBox核心库的solution目录下。

2)创建工程

ModelBox sdk目录下使用create.bat创建hand_det工程

PS ███> .\create.bat -t server -n hand_det -s hand_det_yolox
sdk version is modelbox-xxx
success: create hand_det in ███\modelbox\workspace

与之前创建工程的命令相比,末尾多了-s参数,表示将使用后面参数值代表的模板创建工程,而不是创建空的工程。
workspace目录下将创建出hand_det工程,工程内容如下所示:

hand_det
|--bin
│  |--main.bat:应用执行入口
│  |--mock_task.toml:应用在本地执行时的输入输出配置,此应用默认使用本地视频文件为输入源,最终结果输出到另一本地视频文件,可根据需要修改
|--CMake:存放一些自定义CMake函数
|--data:存放应用运行所需要的图片、视频、文本、配置等数据
│  |--hand.mp4:手部检测测试用视频文件
|--dependence
│  |--modelbox_requirements.txt:应用运行依赖的外部库在此文件定义
|--etc
│  |--flowunit:应用所需的功能单元存放在此目录
│  │  |--cpp:存放C++功能单元编译后的动态链接库,此应用没有C++功能单元
│  │  |--draw_hand_bbox:手部检测框画图功能单元
│  │  |--yolox_post:手部检测使用的是YOLOX模型,此处即为后处理功能单元
|--flowunit_cpp:存放C++功能单元的源代码,此应用没有C++功能单元
|--graph:存放流程图
│  |--hand_det.toml:默认流程图,使用本地视频文件作为输入源
│  |--hand_det_camera.toml:摄像头输入对应的流程图
│  |--modelbox.conf:modelbox相关配置
|--hilens_data_dir:存放应用输出的结果文件、日志、性能统计信息
|--model:推理功能单元目录
│  |--detect_hand:手部检测推理功能单元
│  │  |--detect_hand.toml:手部检测推理功能单元的配置文件
│  │  |--yolox_hand.onnx:手部检测onnx模型
|--build_project.sh:应用构建脚本
|--CMakeLists.txt
|--rpm:打包rpm时生成的目录,将存放rpm包所需数据
|--rpm_copyothers.sh:rpm打包时的辅助脚本

3)查看推理功能单元

hand_det应用包含模型推理部分,前面章节有介绍,在传统开发模式中,开发者需要学习推理框架的使用;而ModelBox内置了主流的推理引擎,如TensorFlow,TensorRT,LibTorch,Ascend ACL,MindSpore,以及Windows版本中所用的ONNXRuntime。在开发推理功能单元时,只需要准备模型文件,并配置对应的toml文件,即可完成推理功能单元的开发,无需掌握推理引擎的开发接口。

a. 模型训练与转换

hand_det_yolox模板中已经内置了手部检测模型,这个模型采用的是YOLOX网络结构,训练数据集用的是开源的100DOH,在ModelArtsNotebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。YOLOX是YOLO系列的优化版本,引入了解耦头、数据增强、无锚点以及标签分类等目标检测领域的优秀进展,拥有较好的精度表现,同时对工程部署友好。

模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本,以及详细的指导文档。开发者如果希望尝试自己训练模型,或者对模板中提供的模型效果不满意,可以进入 YOLOX手部检测模型训练与转换 页面,点击右上角的Run in ModelArts按钮,跟随教程一步步操作,也可以修改其中的代码、更换新的数据集训练出自己的模型。

b. 创建功能单元

本应用已经包含了推理功能单元,如果需要创建新的推理功能单元,命令如下:

PS ███> .\create.bat -t infer -n xxx_infer -p hand_det

create.bat工具使用时,-t infer 即表示创建的是推理功能单元;-n xxx_infer 表示创建的功能单元名称为xxx_infer-p hand_det 表示所创建的功能单元属于hand_det应用。

hand_det工程的model目录(此目录将作为推理功能单元的默认存放目录)下,生成了detect_hand文件夹,其中包含推理功能单元所需的模型文件和toml配置文件,即推理功能单元目录结构如下:

[flowunit_name]
|--[flowunit_name].toml    # 推理功能单元属性配置文件
|--[model_name].onnx       # 模型文件,此处以onnx模型为例,根据需求也可以使用其他模型格式

c. 设置功能单元属性

推理功能单元的模型路径和输入输出节点信息定义在配置文件中,打开detect_hand.toml,看到其内容为:

# 基础配置
[base]
name = "detect_hand"                           # 功能单元名称
device = "cpu"                                 # 功能单元运行的设备类型
version = "1.0.0"                              # 功能单元版本号
type = "inference"                             # 功能单元类型,推理功能单元此处为固定值inference
virtual_type = "onnx"                          # 推理引擎类型,Windows当前只支持onnx模型的推理
group_type = "Inference"                       # 功能单元分组信息, 推理功能单元默认为Inference
description = "yolox model for hand detection" # 功能单元的描述信息
entry = "./yolox_hand.onnx"                    # 模型文件路径,默认在当前路径下

# 模型输入节点描述:yolox onnx模型输入是图片经过预处理后的float数据
[input]
[input.input1]
name = "input"
type = "float"
device = "cpu"

# 模型输出节点描述:yolox模型输出是float格式的特征向量
[output]
[output.output1]
name = "output"
type = "float"

detect_hand推理功能单元使用同路径下的yolox_hand.onnx模型文件,该模型包含一个名为input、接收float格式数据的输入节点,一个名为output、输出float格式数据的输出节点。

d. 调试运行

推理功能单元无需编译,通常会直接将其应用到流程图中运行查看效果,推理功能单元路径需添加到流程图的扫描路径driver.dir中,流程图运行时,会自动扫描该路径并加载推理功能单元。

4)查看流程图

hand_det工程graph目录下包含两个流程图,其中与工程同名的hand_det.toml流程图,其内容为(以Windows版ModelBox为例):

[driver]
# 功能单元的扫描路径,包含在[]中,多个路径使用,分隔
# ${HILENS_APP_ROOT} 表示当前应用的实际路径
# ${HILENS_MB_SDK_PATH} 表示ModelBox核心库的实际路径
dir = [
    "${HILENS_APP_ROOT}/etc/flowunit",
    "${HILENS_APP_ROOT}/etc/flowunit/cpp",
    "${HILENS_APP_ROOT}/model",
    "${HILENS_MB_SDK_PATH}/flowunit",
]
skip-default = true

[profile]
# 通过配置profile和trace开关启用应用的性能统计
profile = false                       # 是否记录profile信息,每隔60s记录一次统计信息
trace = false                         # 是否记录trace信息,在任务执行过程中和结束时,输出统计信息
dir = "${HILENS_DATA_DIR}/mb_profile" # profile/trace信息的保存位置

[flow]
desc = "hand detection example using yolox for local video or rtsp video stream" # 应用的简单描述

[graph]
format = "graphviz" # 流程图的格式,当前仅支持graphviz
graphconf = """digraph hand_detection {
    node [shape=Mrecord]
    queue_size = 4
    batch_size = 1

    # 定义节点,即功能单元及其属性
    input1[type=input, flowunit=input, device=cpu, deviceid=0]
    data_source_parser[type=flowunit, flowunit=data_source_parser, device=cpu, deviceid=0]
    video_demuxer[type=flowunit, flowunit=video_demuxer, device=cpu, deviceid=0]
    video_decoder[type=flowunit, flowunit=video_decoder, device=cpu, deviceid=0, pix_fmt="bgr"]
    image_resize[type=flowunit, flowunit=resize, device=cpu, deviceid=0, image_width=320, image_height=320]
    color_transpose[type=flowunit, flowunit=packed_planar_transpose, device=cpu, deviceid=0]
    normalize[type=flowunit flowunit=normalize device=cpu deviceid=0 standard_deviation_inverse="1., 1., 1."]
    detect_hand[type=flowunit, flowunit=detect_hand, device=cpu, deviceid=0]
    yolox_post[type=flowunit, flowunit=yolox_post, device=cpu, deviceid=0]
    draw_hand_bbox[type=flowunit, flowunit=draw_hand_bbox, device=cpu, deviceid=0]
    video_out[type=flowunit, flowunit=video_out, device=cpu, deviceid=0]

    # 定义边,即功能间的数据传递关系
    input1:input -> data_source_parser:in_data
    data_source_parser:out_video_url -> video_demuxer:in_video_url
    video_demuxer:out_video_packet -> video_decoder:in_video_packet
    video_decoder:out_video_frame -> image_resize:in_image
    image_resize:out_image -> color_transpose:in_image
    color_transpose:out_image -> normalize:in_data
    normalize:out_data -> detect_hand:input
    detect_hand:output -> yolox_post:in_feat
    video_decoder:out_video_frame -> draw_hand_bbox:in_image
    yolox_post:out_data -> draw_hand_bbox:in_bbox
    draw_hand_bbox:out_image -> video_out:in_video_frame
}"""

除了之前教程中介绍过的inputdata_source_parservideo_out等功能单元,本应用还使用了如下ModelBox内置的功能单元:

  • video_demuxer:视频文件或者视频流的解封装,一般后面连接 video_decoder ,实现完整的视频解码功能
  • video_decoder:视频文件或者视频流的解码,一般在前面连接 video_demuxer ,实现完整的视频解码功能,可配置 pix_fmt 属性,表示解码图片的格式,取值范围为"nv12", “rgb”, “bgr”。
  • resize:对图片进行缩放操作,可配置的属性中包括缩放后的图像宽高(width/height)、缩放操作插值方法(interpolation,默认为"inter_nearest",此外还支持"inter_linear"、“inter_cubic”、"inter_area"等)
  • packed_planar_transpose:转换数据的布局类型,将HWC格式的数据转为CHW,或者相反
  • normalize:对数据进行归一化,可配置归一化参数standard_deviation_inverse,参数顺序和数据维度对应

除了这些内置功能单元,以及前文介绍的detect_hand推理功能单元外,还包括通用Python功能单元yolox_post,负责手部检测推理后的数据后处理,即从推理结果中解码出检测框;以及draw_hand_bbox,负责把检测框画到原始图像上。通用Python功能单元的开发前面的教程已有介绍,此处不赘述。

5)查看输入输出配置

查看任务配置文件bin/mock_task.toml,可以看到其中的任务输入和任务输出配置为如下内容::

[input]
type = "url"
url = "${HILENS_APP_ROOT}/data/hand.mp4"  # 表示输入源为本地视频文件

[output]
type = "local"
url = "${HILENS_APP_ROOT}/hilens_data_dir/hand_detection_result.mp4"  # 表示输出为本地视频文件

该流程图在本地运行时的逻辑过程是:data_source_parser解析bin/mock_task.toml文件中输入配置的data/hand.mp4文件,video_demuxervideo_decoder对该文件进行解码,resizepacked_planar_transposenormalize对原始图像进行缩放、转码、归一化等预处理,然后detect_hand在预处理后的图像上进行手部检测,yolox_post从推理结果中解码出检测框,draw_hand_bbox把检测框画到原始图像上,最后video_out将图像输出到bin/mock_task.toml文件中输出配置的hilens_data_dir/hand_detection_result.mp4文件中。

6)用启动脚本执行应用

启动应用前执行.\build_project.sh进行工程构建,该脚本将编译自定义的C++功能单元(本应用不涉及)、将应用运行时会用到的配置文件转码为Unix格式(防止执行过程中的格式错误):

PS ███> .\build_project.sh
...
PS ███>

然后执行.\bin\main.bat运行应用:

PS ███> .\bin\main.bat
...

运行结束后在hilens_data_dir目录下生成了hand_detection_result.mp4文件,可以打开查看:

7)查看camera流程图

除了测试视频文件外,我们还可以使用PC自带或者外接的USB摄像头进行手部实时检测,对应的流程图为hand_det_camera.toml,其内容如下:

[driver]
# 功能单元的扫描路径,包含在[]中,多个路径使用,分隔
# ${HILENS_APP_ROOT} 表示当前应用的实际路径
# ${HILENS_MB_SDK_PATH} 表示ModelBox核心库的实际路径
dir = [
    "${HILENS_APP_ROOT}/etc/flowunit",
    "${HILENS_APP_ROOT}/etc/flowunit/cpp",
    "${HILENS_APP_ROOT}/model",
    "${HILENS_MB_SDK_PATH}/flowunit",
]
skip-default = true

[profile]
# 通过配置profile和trace开关启用应用的性能统计
profile = false                       # 是否记录profile信息,每隔60s记录一次统计信息
trace = false                         # 是否记录trace信息,在任务执行过程中和结束时,输出统计信息
dir = "${HILENS_DATA_DIR}/mb_profile" # profile/trace信息的保存位置

[flow]
desc = "hand detection example using yolox for local USB camera" # 应用的简单描述

[graph]
format = "graphviz" # 流程图的格式,当前仅支持graphviz
graphconf = """digraph hand_detection {
    node [shape=Mrecord]
    queue_size = 4
    batch_size = 1

    # 定义节点,即功能单元及其属性
    input1[type=input, flowunit=input, device=cpu, deviceid=0]
    data_source_parser[type=flowunit, flowunit=data_source_parser, device=cpu, deviceid=0]
    local_camera[type=flowunit, flowunit=local_camera, device=cpu, deviceid=0, pix_fmt="bgr", cam_width=1280, cam_height=720]  
    image_resize[type=flowunit, flowunit=resize, device=cpu, deviceid=0, image_width=320, image_height=320]
    color_transpose[type=flowunit flowunit=packed_planar_transpose device=cpu deviceid=0]
    normalize[type=flowunit flowunit=normalize device=cpu deviceid=0 standard_deviation_inverse="1., 1., 1."]
    detect_hand[type=flowunit, flowunit=detect_hand, device=cpu, deviceid=0]
    yolox_post[type=flowunit, flowunit=yolox_post, device=cpu, deviceid=0]
    draw_hand_bbox[type=flowunit, flowunit=draw_hand_bbox, device=cpu, deviceid=0]
    video_out[type=flowunit, flowunit=video_out, device=cpu, deviceid=0]

    # 定义边,即功能间的数据传递关系
    input1:input -> data_source_parser:in_data
    data_source_parser:out_video_url -> local_camera:in_camera_packet
    local_camera:out_camera_frame -> image_resize:in_image
    image_resize:out_image -> color_transpose:in_image
    color_transpose:out_image -> normalize:in_data
    normalize:out_data -> detect_hand:input
    detect_hand:output -> yolox_post:in_feat
    local_camera:out_camera_frame -> draw_hand_bbox:in_image
    yolox_post:out_data -> draw_hand_bbox:in_bbox
    draw_hand_bbox:out_image -> video_out:in_video_frame
}"""

hand_det.toml相比,这个流程图使用local_camera替换了video_demuxervideo_decoder功能单元,其他部分是一致的。

8)修改输入和输出配置

打开工程目录下bin/mock_task.toml文件,修改其中的任务输入和任务输出配置为如下内容:

[input]
type = "url"
url = "0"  # 表示0号摄像头,即PC自带摄像头,若PC无摄像头需外接USB摄像头

[output]
type = "local"
url = "0:hand_det"  # 表示名为```hand_det```的本地窗口

即使用编号为0的摄像头(默认为PC自带的摄像头),输出画面显示到名为hand_det的本地屏幕窗口中。

9)运行应用

执行.\bin\main.bat camera运行应用,将会自动弹出实时的手部检测画面:

bin/main.bat脚本执行时,默认启动的是与工程同名的流程图,如果工程graph目录下带有多个流程图,配置文件modelbox.conf中的flow_path参数指定了技能运行时的流程图路径:

flow_path = "${HILENS_APP_ROOT}/graph/hand_det${HILENS_MB_GRAPH_TYPE}.toml"

其中环境变量${HILENS_APP_ROOT}在运行技能时将自动替换为当前工程的实际路径,而${HILENS_MB_GRAPH_TYPE}可以在执行bin/main.bat脚本时输入流程图后缀名作为参数进行配置,打开bin/main.bat脚本看到相关参数内容为:

if "%1" == "default" (
  set HILENS_MB_GRAPH_TYPE=
) else if "%1" == "" (
  set HILENS_MB_GRAPH_TYPE=
) else (
  set HILENS_MB_GRAPH_TYPE=_%1
)

即如果启动命令后跟上一个字符串xxx参数,则启动名为“工程名_xxx”的流程图。

如果PC带独立显卡,ModelBox将使用独显进行模型推理加速,可以打开“任务管理器”-“性能”版块查看(笔者的笔记本自带MX150独显,即图中的GPU1):

3. 小结

通过本教程,我们可以看到,使用ModelBox框架开发推理模块时,开发者不需要掌握推理库的使用方法,对于不同的推理引擎,ModelBox定义了统一的推理功能单元结构,开发者只需要准备模型文件,并配置对应的toml文件,即可实现推理功能。
另外,我们也可以看到功能单元作为可插拔的组件,可以自由组合构成不同的流程图,开发好的功能单元,也可以非常方便的用到不同的应用中,有助于沉淀功能模块,提升开发效率。

小提示1

如果自带独显的PC推理很慢,“任务管理器”中显示集成显卡占用很高,独显却占用为0或者很低,此时需要打开“NVIDIA控制面板”为ModelBox应用开启独显加速,设置完成后重启应用,将会看到更流畅的效果。

小提示2

如果想要关闭这个实时推理应用,关闭手部检测窗口是不行的,请点击右下角Terminal中的kill按钮结束此应用:

课程打卡

根据本次课程学习的内容,在自己的环境进行复现,并将复现的程序运行界面,连同网页右上方的华为云账号截图发至打卡贴上,详细打卡规则参见活动页面或参考2022ModelBox实战营活动打卡 (huaweicloud.com)

LLM初学者

作者相关内容

【2022 ModelBox实战营】第一个应用
发布于30个月以前
【2022 ModelBox实战营】通用Python功能单元
发布于30个月以前
【ModelBox客流分析实战营】ModelBox端云协同AI开发套件(Windows)SDK安装篇
发布于28个月以前
【2022 ModelBox实战营】推理功能单元
发布于30个月以前
ModelBox开发案例 - 使用YOLO v3做口罩检测
发布于34个月以前

暂无数据

热门内容推荐

ModelArts JupyterLab常见问题解决办法
ModelArts开发者 发布于45个月以前
为医生打造专属数字分身!华为云联合万木健康打造医疗医学科普和患者教育数字人引擎
HWCloudAI 发布于20个月以前
图数据库 | 聊聊超级快的图上多跳过滤查询
弓役是也 发布于23个月以前
ModelArts准备工作_简易版
ModelArts开发者 发布于46个月以前
”智蔗见智·向新而生”广西第二届人工智能大赛baseline使用教程
追乐小王子 发布于31个月以前

暂无数据