ModelBox开发指南 - 使用展开/合并功能单元实现多人人体关键点检测
ModelBox开发指南 - 展开/合并功能单元
本文将使用一个多人人体关键点检测的案例,介绍ModelBox中展开/合并功能单元的特性,案例效果如下所示:
本案例所需资源(代码、模型、测试数据等)已做成模板放到华为云上,查看和下载模板可以使用如下命令:
- Windows PC版本请使用
solution.bat
工具:
PS ███\modelbox>: .\solution.bat -l
...
Solutions name:
...
multi_person_pose_yolox_alpha_pose
...
结果中的multi_person_pose_yolox_alpha_pose即为多人人体关键点检测模板,可使用如下命令下载模板:
PS ███\modelbox>: .\solution.bat -s multi_person_pose_yolox_alpha_pose
...
- Linux开发板版本请使用
solution.py
脚本:
rock@rock-3a:~/███/modelbox$ ./solution.py -l
...
Solutions name:
...
multi_person_pose_yolox_alpha_pose
...
结果中的multi_person_pose_yolox_alpha_pose即为多人人体关键点检测模板,可使用如下命令下载模板:
rock@rock-3a:~/███/modelbox$ ./solution.py -s multi_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
创建multi_person_pose_alpha
工程(Windows PC版本请使用create.bat
):
rock@rock-3a:~/███/modelbox$ ./create.py -t server -n multi_person_pose_alpha -s multi_person_pose_yolox_alpha_pose
sdk version is modelbox-rk-aarch64
success: create multi_person_pose_alpha in /home/rock/███/modelbox/workspace
将会用多人人体关键点检测模板创建出一个multi_person_pose_alpha工程。
2)查看条件功能单元
本案例也需要条件功能单元,它根据当前帧的检测结果中是否包含人体检测框,在etc/flowunit/ped_condition
文件夹中:
关于条件功能ped_condition
的介绍,可以参考ModelBox开发指南 - 条件功能单元。
3)查看展开功能单元
展开功能单元,它将当前帧中包含的多个人体检测框,展开成多个流分别处理。它在etc/flowunit/expand_ped_images
文件夹中:
打开expand_ped_images.toml
配置文件,可以看到功能单元属性部分:
# 工作模式,以下配置项默认全为false,表示通用功能单元;且配置项之间互斥,即最多只能设置其中一个为true
stream = false # 是否是Stream类型功能单元
condition = false # 是否是条件功能单元
collapse = false # 是否是合并功能单元
expand = true # 是否是展开功能单元,此处设置为true,即表示expand_ped_images是一个展开功能单元
这些属性全部为false时表示通用功能单元,通用功能单元输出的数据与输入的数据属于同一层级,例如图像缩放功能单元Resize
,输入的是图片流,产生的也是图片流。
expand_ped_images
功能单元属性部分的expand
属性设置成了true
,即expand_ped_images
被设置成一个展开功能单元。打开expand_ped_images.py
,查看其process
函数,可以看到展开功能单元的输出是如何编码的:
def process(self, data_context):
# 从DataContext中获取输入输出BufferList对象
in_data = data_context.input("in_data")
out_image = data_context.output("roi_image")
# 循环处理每一个输入Buffer数据
for buffer_img in in_data:
# 获取输入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(), dtype=np.uint8, copy=False)
img_data = img_data.reshape(height, width, channel)
bboxes = buffer_img.get("bboxes")
bboxes = np.array(bboxes).reshape(-1,4)
# 业务处理,根据检测框裁剪出多个人体图像,展开成一个子数据流
# 展开功能单元的输出处理在for循环中,即展开为多个输出Buffer
for bbox in bboxes:
img_roi = self.crop_bbox_img(bbox, img_data)
h, w, c = img_roi.shape
img_roi = img_roi.flatten()
# 将业务处理返回的结果数据转换为Buffer,这里可以使用modelbox.Buffer构造函数
img_buffer = modelbox.Buffer(self.get_bind_device(), img_roi)
# 设置输出Buffer的Meta信息,此处拷贝输入Buffer的部分Meta信息,其余差异的部分再进行设置
img_buffer.copy_meta(buffer_img)
img_buffer.set("pix_fmt", "rgb")
img_buffer.set("width", w)
img_buffer.set("height", h)
img_buffer.set("channel", c)
img_buffer.set("width_stride", w)
img_buffer.set("height_stride", h)
# 将输出Buffer放入输出BufferList中
out_image.push_back(img_buffer)
# 返回成功标志,ModelBox框架会将数据发送到后续的功能单元
return modelbox.Status.StatusCode.STATUS_SUCCESS
可以看到对于每一个Buffer
(第一层for
循环内),展开功能单元将其展开为多个Buffer
进行输出(第二层for
循环),即单张图片中的多个人体检测框,裁剪出多张人体图片,作为多条数据分别输出,这样后面的人体关键点检测等功能单元就可以针对每个Buffer
分别处理。
关于展开功能单元的更细致介绍,详见ModelBox介绍。
4)关键点检测推理功能单元
人体关键点检测的模型推理功能单元,在工程目录的model
文件夹下:
5)合并功能单元
合并功能单元,它将当前帧中多个人体的关键点数据,合并到一起输出。它在etc/flowunit/collapse_multi_pose
文件夹中:
打开collapse_multi_pose.toml
配置文件,可以看到功能单元属性部分:
# 工作模式,以下配置项默认全为false,表示通用功能单元;且配置项之间互斥,即最多只能设置其中一个为true
stream = false # 是否是Stream类型功能单元
condition = false # 是否是条件功能单元
collapse = true # 是否是合并功能单元,此处设置为true,即表示collapse_multi_pose是一个合并功能单元
expand = false # 是否是展开功能单元
可以看到功能单元属性部分的collapse
属性设置成了true
,即collapse_multi_pose
被设置成一个合并功能单元。打开collapse_multi_pose.py
,查看其process
函数:
def process(self, data_context):
# 从DataContext中获取输入输出BufferList对象
in_feat = data_context.input("in_feat")
out_data = data_context.output("out_data")
# 循环处理每一个输入Buffer数据,但输出合并到一个对象中(multi_pose)
multi_pose = []
for buffer_feat in in_feat:
# 将输入Buffer转换为Python对象
feat_data = np.array(buffer_feat.as_object(), copy=False)
# 业务处理:将一帧图像中多人的关键点数据合并在一起
hm_shape = (self.pose_net_h // self.stride, self.pose_net_w // self.stride)
feat_data = feat_data.reshape((self.num_joints, hm_shape[0], hm_shape[1]))
pose_data = self.heatmap_to_coord_simple(feat_data)
multi_pose.append(pose_data)
# 将业务处理返回的结果数据转换为Buffer
multi_pose = np.array(multi_pose)
out_buffer = modelbox.Buffer(self.get_bind_device(), multi_pose)
# 将输出Buffer放入输出BufferList中
out_data.push_back(out_buffer)
# 返回成功标志,ModelBox框架会将数据发送到后续的功能单元
return modelbox.Status.StatusCode.STATUS_SUCCESS
可以看到它将接收到的所有Buffer
合并到一起,只产生一个Buffer
的输出,这样collapse_multi_pose
就将单张图片中的多个人体关键点信息合并到一条数据中,后面的画图功能单元就可以画出所有人的关键点。
关于合并功能单元的更细致介绍,详见ModelBox介绍。
6)画图功能单元
多人人体关键点画图功能单元在etc/flowunit/draw_multi_pose
文件夹中:
7)查看流程图
工程的默认流程图为graph/multi_person_pose_alpha.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 = "multi-person pose estimation example using yolox and alpha-pose for local video or rtsp video stream" # 应用的简单描述
[graph]
format = "graphviz" # 流程图的格式,当前仅支持graphviz
graphconf = """digraph multi_person_pose_alpha {
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]
expand_ped_images[type=flowunit, flowunit=expand_ped_images, device=cpu, deviceid=0]
pose_detection[type=flowunit, flowunit=pose_infer, device=rknpu, deviceid=0]
collapse_multi_pose[type=flowunit, flowunit=collapse_multi_pose, device=cpu, deviceid=0]
draw_multi_pose[type=flowunit, flowunit=draw_multi_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 -> expand_ped_images:in_data
expand_ped_images:roi_image -> pose_detection:input
pose_detection:output -> collapse_multi_pose:in_feat
ped_condition:has_pedestrian -> draw_multi_pose:in_image
collapse_multi_pose:out_data -> draw_multi_pose:in_pose
draw_multi_pose:out_image -> video_out:in_video_frame
}"""
该流程图对于某个视频流,经过视频解码、图像缩放、人体检测与后处理、人体检测结果判定、人体图像裁剪、多人人体关键点检测与后处理、关键点画图等一系列操作后,将结果保存下来。图中可以看到展开/合并功能单元在与其他功能单元连接时并没有不同,它们的输入输出不在同一层级,但这些都在框架内部处理。
工程的任务配置文件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/multi_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:multi_person_pose"
url = "${HILENS_APP_ROOT}/hilens_data_dir/multi_person_pose_result.mp4"
测试视频为data/multi_person_pose.mp4
,流程图使用这一视频进多人人体关键点检测,检测结果绘制后保存为hilens_data_dir/multi_person_pose_result.mp4
文件。
8)运行应用
在工程路径下执行build_project.sh
进行工程构建(以RK3568开发板版本为例):
rock@rock-3a:~/███/modelbox/workspace/multi_person_pose_alpha$ ./build_project.sh
build success: you can run main.sh in ./bin folder
rock@rock-3a:~/███/modelbox/workspace/multi_person_pose_alpha$
执行bin/main.sh
(Windows PC版本请使用bin\main.bat
)运行应用,运行结束后在hilens_data_dir
目录下生成了multi_person_pose_result.mp4
文件,可以下载到PC端查看。
- 点赞
- 收藏
- 关注作者
评论(0)