ModelBox开发指南 - 使用条件功能单元实现单人人体关键点检测

举报
HWCloudAI 发表于 2022/12/21 16:24:52 2022/12/21
【摘要】 ModelBox开发指南 - 条件功能单元本文将使用一个单人人体关键点检测的案例,介绍ModelBox中条件功能单元的特性,案例效果如下所示:本案例所需资源(代码、模型、测试数据等)均可从single_person_pose_yolox_alpha_pose下载(提取码为modbox),该目录中的资源列表说明如下:desc.toml # 资源描述common.zip # 公共数据...

ModelBox开发指南 - 条件功能单元

本文将使用一个单人人体关键点检测的案例,介绍ModelBox中条件功能单元的特性,案例效果如下所示:

本案例所需资源(代码、模型、测试数据等)已做成模板放到华为云上,查看和下载模板可以使用如下命令:

  • Windows PC版本请使用solution.bat工具:
PS ███\modelbox>: .\solution.bat -l
...

Solutions name:
...
single_person_pose_yolox_alpha_pose
...

结果中的single_person_pose_yolox_alpha_pose即为单人人体关键点检测模板,可使用如下命令下载模板:

PS ███\modelbox>: .\solution.bat -s single_person_pose_yolox_alpha_pose
...
  • Linux开发板版本请使用solution.py脚本:
rock@rock-3a:~/███/modelbox$ ./solution.py -l
...

Solutions name:
...
single_person_pose_yolox_alpha_pose
...

结果中的single_person_pose_yolox_alpha_pose即为单人人体关键点检测模板,可使用如下命令下载模板:

rock@rock-3a:~/███/modelbox$ ./solution.py -s single_person_pose_yolox_alpha_pose
...

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

如果对ModelBox AI应用开发还不熟悉,请先阅读ModelBox 端云协同AI开发套件(RK3568)上手指南,或者ModelBox 端云协同AI开发套件(博时特EC02)上手指南

模型准备

本案例使用了两个模型:一是人体关键点检测模型AlphaPose,这是一个两阶段的模型(或者叫自上而下的人体关键点检测模型),需要搭配人体检测模型使用;另一个就是人体检测模型YOLOX,与ModelBox sdk中自带的car_det模板使用的模型相同。在下载的资源包中,已经包含了转换好的rknn模型或者onnx模型,可以在对应的平台上直接使用。当然,开发者也可以直接使用car_det模板中的YOLOX模型。
YOLOX原始模型的训练工程可参考此项目AlphaPose的训练工程可参考此项目,感兴趣的开发者可使用这些项目自行训练模型。
如果想体验rknn模型的转换过程,rknpu版本可参考RK1808模型转换验证案例,rknpu2版本可参考RK3568模型转换验证案例

应用开发

打开VS Code,连接到ModelBox sdk所在目录或者远程开发板,开始进行单人人体关键点检测应用开发。下面以RK3568(rknpu2)版本为例进行说明,其他版本与之类似。

1)创建工程

使用create.py创建single_person_pose工程(Windows PC版本请使用create.bat):

rock@rock-3a:~/███/modelbox$ ./create.py -t server -n single_person_pose -s single_person_pose_yolox_alpha_pose
sdk version is modelbox-rk-aarch64
success: create single_person_pose in /home/rock/███/modelbox/workspace

将会用单人人体关键点检测模板创建出一个single_person_pose工程。

2)查看条件功能单元

条件功能单元,它根据当前帧的检测结果中是否包含人体检测框,形成两个输出分支。它在工程目录的etc/flowunit/ped_condition文件夹下:

打开ped_condition.toml配置文件,可以看到功能单元属性部分:

# 工作模式,以下配置项默认全为false,表示通用功能单元;且配置项之前互斥,即最多只能设置其中一个为true
stream = false   # 是否是Stream类型功能单元
condition = true # 是否是条件功能单元,此处设置为true,即表示ped_condition是一个条件功能单元
collapse = false # 是否是合并功能单元
expand = false   # 是否是展开功能单元

这些属性全部为false时表示通用功能单元,通用功能单元在处理完数据后,会产生一到多个输出,后续功能单元在承接时,必须接收它的所有输出数据(当然接收后可以只处理其中一部分输出)。

ped_condition功能单元属性部分的condition属性设置成了true,即ped_condition被设置成一个条件功能单元。查看ped_condition.toml配置文件中的输出配置部分,可以看到有两个输出节点,分别代表检测到人体和未检测到人体两种状态:

# 输出端口描述
[output]
[output.output1] # 检测到人体时的输出数据
name = "has_pedestrian" # 原图 + 人体检测框(检测框以属性方式附加在原图上)
type = "uint8"          # 原图数据格式为 uint8

[output.output2] # 未检测到人体时的输出数据
name = "no_pedestrian" # 原图
type = "uint8"         # 原图数据格式为 uint8

打开ped_condition.py,查看其process函数,可以看到条件功能单元的输出是如何编码的:

    def process(self, data_context):
        # 从DataContext中获取输入输出BufferList对象
        in_image = data_context.input("in_image")
        in_bbox = data_context.input("in_bbox")

        has_ped = data_context.output("has_pedestrian")
        no_ped = data_context.output("no_pedestrian")

        # 循环处理每一个输入Buffer数据(实际上条件功能单元的batch size为1,此处循环中只有1条数据)
        for buffer_img, buffer_bbox in zip(in_image, in_bbox):
            # 获取输入Buffer的属性信息
            width = buffer_img.get('width')
            height = buffer_img.get('height')
            channel = buffer_img.get('channel')

            # 将输入Buffer转换为numpy对象
            img_data = np.array(buffer_img.as_object(), copy=False)
            img_data = img_data.reshape((height, width, channel))

            # 字符串数据可以直接用as_object函数转换
            bbox_str = buffer_bbox.as_object()

            # 解码出人体检测框数据
            ped_bboxes = self.decode_ped_bboxes(bbox_str, (height, width))

            # 此处是将输入Buffer直接作为输出Buffer向后传递
            # 此时Buffer的Data、Meta等全部内容都将保留,无需构建Buffer、设置Meta
            if ped_bboxes:  # 检测到人体时的输出分支
                buffer_img.set("bboxes", list(itertools.chain(*ped_bboxes)))   # 将人体检测框作为属性附在输出Buffer上
                has_ped.push_back(buffer_img)
            else:           # 未检测到人体时的输出分支
                no_ped.push_back(buffer_img)

        # 返回成功标志,ModelBox框架会将数据发送到后续的功能单元
        return modelbox.Status.StatusCode.STATUS_SUCCESS

这里也可以看到一个输出数据的设置技巧,在检测到人体时,我们是将人体检测框作为一个属性附在图片上,这样这个分支只需要一个输出。
关于条件功能单元的更细致介绍,详见ModelBox介绍

3)其他功能单元

人体关键点检测的模型推理功能单元,在工程目录的model/pose_infer文件夹中:

然后是人体图像裁剪、人体关键点后处理、关键点画图等模块,分别对应crop_ped_imagesingle_pose_postdraw_single_pose等通用功能单元:

4)查看流程图

工程的默认流程图为graph/single_person_pose.toml(以RK3568开发板版本为例):

# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.

[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 = "single person pose estimation example using yolox and alpha-pose for local video or rtsp video stream" # 应用的简单描述

[graph]
format = "graphviz" # 流程图的格式,当前仅支持graphviz
graphconf = """digraph single_person_pose {
    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=rknpu, deviceid=0, pix_fmt="bgr"]
    image_resize[type=flowunit, flowunit=resize, device=rknpu, deviceid=0, image_width=512, image_height=288]
    ped_detection[type=flowunit, flowunit=yolox_infer, device=rknpu, deviceid=0]
    yolox_post[type=flowunit, flowunit=yolox_post, device=cpu, deviceid=0]
    ped_condition[type=flowunit, flowunit=ped_condition, device=cpu, deviceid=0]
    crop_ped_image[type=flowunit, flowunit=crop_ped_image, device=cpu, deviceid=0]
    pose_detection[type=flowunit, flowunit=pose_infer, device=rknpu, deviceid=0]
    single_pose_post[type=flowunit, flowunit=single_pose_post, device=cpu, deviceid=0]
    draw_single_pose[type=flowunit, flowunit=draw_single_pose, device=cpu, deviceid=0]
    video_out[type=flowunit, flowunit=video_out, device=rknpu, 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 -> ped_detection:input
    ped_detection:output -> yolox_post:in_feat
    yolox_post:out_data -> ped_condition:in_bbox
    video_decoder:out_video_frame -> ped_condition:in_image
    ped_condition:no_pedestrian -> video_out:in_video_frame
    ped_condition:has_pedestrian -> crop_ped_image:in_data
    crop_ped_image:roi_image -> pose_detection:input
    pose_detection:output -> single_pose_post:in_feat
    ped_condition:has_pedestrian -> draw_single_pose:in_image
    single_pose_post:out_data -> draw_single_pose:in_pose
    draw_single_pose:out_image -> video_out:in_video_frame
}"""

该流程图对于某个视频流,经过视频解码、图像缩放、人体检测与后处理、人体检测结果判定、人体图像裁剪、单人人体关键点检测与后处理、关键点画图等一系列操作后,将结果保存下来。
图中,可以看到条件功能单元ped_condition的两个输出分别对接到不同的功能单元,在未检测到人体时,no_pedestrian分支的输出直接对接到video_out进行视频编码。

工程的任务配置文件bin/mock_task.toml中设置了输入输出源,内容为:

# 任务输入配置,当前支持以下几种输入方式:
# 1. rtsp摄像头或rtsp视频流:type="rtsp", url="rtsp://xxx.xxx"
# 2. 设备自带摄像头或者USB摄像头:type="url",url="${摄像头编号}" (需配合local_camera功能单元使用)
# 3. 本地视频文件:type="url",url="${视频文件路径}" (请使用${HILENS_APP_ROOT}宏,表示当前应用的实际路径)
# 4. http服务:type="url", url="http://xxx.xxx"(指的是任务作为http服务启动,此处需填写对外暴露的http服务地址,需配合httpserver类的功能单元使用)
[input]
type = "url"
# url = "0"
url = "${HILENS_APP_ROOT}/data/single_person_pose.mp4"

# 任务输出配置,当前支持以下几种输出方式:
# 1. rtsp视频流:type="local", url="rtsp://xxx.xxx"
# 2. 本地屏幕:type="local", url="0:xxx" (设备需要接显示器,系统需要带桌面)
# 3. 本地视频文件:type="local",url="${视频文件路径}" (请使用${HILENS_APP_ROOT}宏,表示当前应用的实际路径)
# 4. http服务:type="webhook", url="http://xxx.xxx"(指的是任务产生的数据上报给某个http服务,此处需填写上传的http服务地址)
[output]
type = "local"
# url = "0:single_person_pose"
url = "${HILENS_APP_ROOT}/hilens_data_dir/single_person_pose_result.mp4"

测试视频为data/single_human_pose.mp4,流程图使用这一视频进单人人体关键点检测,检测结果绘制后保存为hilens_data_dir/single_human_pose_result.mp4文件。

5)运行应用

在工程路径下执行build_project.sh进行工程构建(以RK3568开发板版本为例):

rock@rock-3a:~/███/modelbox/workspace/single_person_pose$ ./build_project.sh

build success: you can run main.sh in ./bin folder

rock@rock-3a:~/███/modelbox/workspace/single_person_pose$

执行bin/main.sh(Windows PC版本请使用bin\main.bat)运行应用,运行结束后在hilens_data_dir目录下生成了single_human_pose_result.mp4文件,可以下载到PC端查看。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。