• [问题求助] 使用ModelBox SDK查看模板失败
    1.5.0版本ModelBox SDK,输入“.\solution.bat -l”,提示如下错误
  • [问题求助] Hilens控制台专业版已经注册的设备,提示离线了该如何处理?
    Hilens控制台专业版已经注册的设备,提示离线了该如何处理,如下图
  • [问题求助] ModelBox支持哪些AI框架?
    ModelBox支持哪些AI框架?
  • [技术干货] ModelBox推理真的高效吗?
    ModelBox推理真的高效吗?“高性能推理”是ModelBox宣传的主要特性之一,不信谣不传谣的我决定通过原生API和ModelBox实现相同案例进行对比,看一下ModelBox推理是否真的“高性能”。我们分别使用onnxruntime与ModelBox Windows SDK对相同的模型实现相同的推理逻辑进行端到端性能对比,为了防止测试视频帧率成为性能瓶颈,我们准备了120fps的视频作为测试输入。如果对Windows ModelBox SDK使用还不熟悉,可以参考我们的ModelBox 端云协同AI开发套件(Windows)上手指南。案例所需资源(代码、模型、测试数据等)均可从obs桶下载。案例说明为了充分考验不同框架的推理性能,我决定做一个稍微有那么一点点繁琐的双阶段单人人体关键点检测案例。案例具体流程如下:其中,人形检测使用开源YOLOV7-tiny预训练模型,关键点检测使用开源PP-TinyPose预训练模型,在进行人形跟踪后我们过滤得到最早出现的id的检测框进行关键点检测。onnxruntime推理原生API推理代码位于资源包的onnxruntime_infer目录下,具体的代码组织为:onnxruntime_infer ├─onnxruntime_infer.py # 推理入口脚本 ├─utils.py # 人形检测后处理 ├─sort.py # 跟踪 ├─hrnet_post.py # 关键点检测后处理 └─smooth.py # 关键点时序滤波其中,入口脚本onnxruntime_infer.py中指定了使用的模型文件与测试视频:cam = cv2.VideoCapture("../data/demo_120fps.mp4") size = (int(cam.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cam.get(cv2.CAP_PROP_FRAME_HEIGHT))) fourcc = cv2.VideoWriter_fourcc(*"mp4v") out_fps = cam.get(cv2.CAP_PROP_FPS) res_cam = cv2.VideoWriter("../hilens_data_dir/rt_result.mp4", fourcc, out_fps, size) det_sess = rt.InferenceSession("../model/det_human/yolov7-tiny.onnx", providers=["DmlExecutionProvider"]) pose_sess = rt.InferenceSession("../model/det_pose/pose.onnx", providers=["CPUExecutionProvider"])人形检测模型为gpu推理,关键点检测模型为cpu推理,在使用ModelBox Windows SDK推理时也保持了同样的硬件配置。fps取检测预处理开始到绘制关键点这一区间进行测试:class DetPre: def __init__(self, net_h=320, net_w=320): self.net_h = net_h self.net_w = net_w def __call__(self, img_data): buffer_meta = {} buffer_meta["time"] = time.time() resized_image, ratio, (dw, dh) = self.letterbox(img_data) ... total_time += time.time() - buffer_meta.get("time") idx += 1 res_img, pose_data = draw_pose(im, filter_box, pose_data, total_time / idx) ...结果视频rt_result.mp4保存在hilens_data_dir文件夹下,查看结果:可以看到,双阶段单人关键点技能在onnxruntime推理可以达到36fps左右。ModelBox Windows SDK推理ModelBox Windows SDK推理代码位于资源包的single_human_pose目录下,具体的代码组织为:single_human_pose ├─bin │ ├─main.bat // 启动脚本 │ └─mock_task.toml // 本地模拟运行输入输出配置文件 ├─data │ └─demo_120fps.mp4 // 测试视频 ├─etc │ └─flowunit // 功能单元目录 │ ├─det_post // 检测后处理 │ ├─det_pre // 检测预处理 │ ├─draw_pose // 关键点绘制 │ ├─expand_box // 单人图像切分 │ ├─object_tracker // 人形跟踪 │ └─pose_post // 姿态后处理 ├─graph │ └─single_human_pose.toml // 技能流程图 ├─hilens_data_dir // 运行结果目录 │ ├─log │ ├─mb_profile // 性能统计目录 │ │ ├─performance_[time].toml │ │ └─trace_[time].toml │ ├─dance_result.mp4 // 运行结果视频 │ └─rt_result.mp4 ├─model // 推理功能单元 │ ├─det_human // 人形检测推理 │ │ ├─det_human.toml // 推理配置文件 │ │ └─yolov7-tiny.onnx // 推理模型 │ └─det_pose // 关键点检测推理 │ ├─det_pose.toml │ └─pose.onnx └─build_project.sh我们可以查看技能流程图graph/single_human_pose.toml了解技能逻辑:fps在关键点绘制功能单元中进行计算,计算的为端到端全流程fps:class draw_poseFlowUnit(modelbox.FlowUnit): def open(self, config): ... self.start_time = time.time() self.idx = 0 ... def process(self, data_context): ... self.idx += 1 self.draw(out_img, bbox, pose_data, self.idx / (time.time() - self.start_time)) ...在bin/mock_task.toml中配置输入输出:# 任务输入,mock模拟目前仅支持一路rtsp或者本地url # rtsp摄像头,type = "rtsp", url里面写入rtsp地址 # 其它用"url",比如可以是本地文件地址, 或者httpserver的地址,(摄像头 url = "0") [input] type = "url" url = "${HILENS_APP_ROOT}/data/demo_120fps.mp4" # 任务输出,目前仅支持"webhook", 和本地输出"local"(输出到屏幕,url="0", 输出到rtsp,填写rtsp地址) # (local 还可以输出到本地文件,这个时候注意,文件可以是相对路径,是相对这个mock_task.toml文件本身) [output] type = "local" url = "${HILENS_DATA_DIR}/demo_result.mp4"在技能流程图中开启性能统计配置项:[profile] profile=true trace=true之后双击bin/main.bat或在bash中运行技能:./bin/main.bat运行完成后生成的视频与性能统计文件都在hilens_data_dir文件夹下:可以看到使用ModelBox SDK进行推理可以达到79fps,名不虚传哇,我们可以在Chrome浏览器chrome://tracing/中加载性能统计文件查看:逐项查看后发现耗时最久的是检测后处理功能单元,平均耗时11.69ms,因为ModelBox是静态图并行推理,fps取决于耗时最久的功能单元,理论计算$fps = 1000 / 11.69 \approx 85$,与我们在程序中打点计算结果接近。总结:ModelBox真的很快,nice!
  • [技术干货] ModelBox开发案例 - 体感小游戏
    ModelBox开发案例 - 体感小游戏前段时间,小鱼老师在AI说发布了文章 ModelBox推理真的高效吗,里面介绍了双阶段单人人体关键点检测案例,运行速度超快:使用原生的ONNXRuntime API做开发,可以达到36fps;而ModelBox版本(推理框架同样是ONNXRuntime),更是达到了接近80fps!于是乎,笔者产生了一个大胆的想法:这么快的人体关键点检测应用,不用来跑游戏可惜了呀!经过一段时间的开发调试,结合一个仿制的简易Flappy Bird游戏,ModelBox体感小游戏诞生了:玩家通过上下摆动双臂做出“扇动翅膀”的动作,阻止小鸟下落,躲避画面中的“狼柱”;如果小鸟不小心碰到了“狼”或者触碰到画面边缘,游戏停止,然后会重新开始。画面左上方显示的是玩家存活的时长。本案例使用的是Windows版本的 ModelBox SDK,如果对此还不熟悉,请先阅读 ModelBox端云协同AI开发套件(Windows)开发环境篇,如果对 ModelBox AI应用开发感兴趣,欢迎参加我们的 ModelBox实战营。工程结构本案例是在小鱼老师的 single_human_pose 应用基础上修改而来,案例所需资源(代码、模型、测试数据等)均可从 obs桶 下载。工程目录与原始版本基本一致,下面列出其中不一样的地方:single_human_pose ├─data │ ├─game // 体感游戏资源目录 │ ├─icon // 体感游戏所需的图标资源 │ ├─src // 体感游戏源代码,可独立运行 │ └─dance_120fps.mp4 // 测试视频 ├─etc │ └─flowunit // 功能单元目录 │ ├─draw_pose // 关键点绘制功能单元 │ ├─draw_pose.py // 关键点绘制功能单元入口文件 │ ├─draw_pose.toml // 关键点绘制功能单元配置文件 │ ├─draw_utils.py // 其他功能函数存放文件 │ ├─flappy.py // Flappy Bird核心逻辑 │ ├─smooth.py // 平滑算法,使关键点更稳定 │ └─vector.py // 平面坐标系中点的运算 │ ├─... // 其他功能单元 ├─graph │ ├─single_human_pose.toml // 默认的技能流程图 │ └─single_human_pose_camera.toml // 使用摄像头运行的技能流程图 ├─... └─build_project.shFlappy Bird本案例中游戏相关的资源和代码在 data/game 目录下,我们可以直接执行其中 src 下的 main.py 文件,得到一个使用键盘控制的 Flappy Bird 游戏。main.py 文件中的核心函数内容如下:def run(): # 初始化游戏 snake = Snake() # 贪吃蛇 flappy = Flappy() # Flappy Bird pacman = Pacman() # 吃豆人 # 初始化摄像头,参数0表示设备的第一个摄像头 cap = cv2.VideoCapture(0) # 判断摄像头初始化是否成功 if not cap.isOpened(): print('failed to open camera 0') exit() # 设置分辨率为 960 x 540 cap.set(3, 960) cap.set(4, 540) while True: # 读取一帧视频图像,ret表示读取是否成功 ret, frame = cap.read() # 对原始图像做高斯模糊,避免干扰到游戏画面 frame = cv2.GaussianBlur(frame, (0, 0), 5) # 阻塞等待键盘响应1ms,获取按下的按键值 pressed_key = cv2.waitKey(1) & 0xFF if pressed_key == ord('q'): # 如果按下q键则退出游戏 break else: # 根据按键类型更新游戏画面 # frame = snake.update_snake_keyboard(frame, pressed_key) frame = flappy.update_flappy_keyboard(frame, pressed_key) # frame = pacman.update_pacman_keyboard(frame, pressed_key) # 打开一个名为game的窗口,显示图像 cv2.imshow('game', frame) # 释放摄像头资源 cap.release() # 关闭所有窗口 cv2.destroyAllWindows()可以看到,其中包含了3个小游戏:贪吃蛇、吃豆人和Flappy Bird。游戏界面使用OpenCV绘制,程序将打开0号摄像头,将游戏画面叠加在摄像头画面上;在每帧的绘制中,程序阻塞1ms等待键盘响应,根据按键不同控制游戏的运行:按下空格键将控制小鸟往上飞行一段距离(具体内容查看 src/flappy.py )。开发者可以解开另外两个游戏的注释代码,试试它们的游戏效果。关键点绘制功能单元Flappy Bird游戏与人体关键点检测应用的结合,完全容纳在 draw_pose 功能单元中。在原始的 single_human_pose 应用里,这个功能单元只是将检测到的关键点数据绘制到画面中;本应用中,在得到人体关键点数据后,又计算了双臂与身体的夹角,如果检测到“扇动翅膀”的动作,则控制小鸟往上飞行一段距离。游戏画面与高斯模糊后的人体关键点画面叠加在一起显示,既能看到AI应用的效果,也不至于干扰到游戏画面的显示。 def open(self, config): ... # 使用图标资源初始化Flappy Bird游戏控制示例 icon_path = config.get_string("icon_path", ".") self.flappy = Flappy(icon_path) return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): ... for image, hand_pose in zip(in_image, in_feat): ... # 获取上一功能单元输出的人体关键点数据 pose_data = np.array(hand_pose.as_object(), copy=False) pose_data = pose_data.reshape((self.kps, 3)) # 计算双臂与身体的夹角 arm_angles = get_arm_angles(bbox, pose_data, self.keypoints_smooth) ... # 在摄像头画面中画出主要的人体关节,并作高斯模糊 draw_pose(out_img, bbox, pose_data, self.score_thre) out_img = cv2.GaussianBlur(out_img, (0, 0), 5) # 根据双臂动作控制游戏画面更新,叠加到摄像头画面中做展示 out_img, alive = self.flappy.update_flappy_pose(out_img, arm_angles, fps) ... return modelbox.Status.StatusCode.STATUS_SUCCESScamera流程图游戏的运行需要实时的摄像头画面,因此本案例将原始的流程图 single_human_pose.toml改成了使用PC自带或者外接的USB摄像头作为输入源,其中的流程图描述 graphconf 内容如下:graphconf = """digraph single_human_pose { node [shape=Mrecord] queue_size = 1 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=960, cam_height=540] det_pre[type=flowunit, flowunit=det_pre, device=cpu, deviceid=0] color_transpose[type=flowunit flowunit=packed_planar_transpose device=cpu deviceid=0] normalize[type=flowunit flowunit=normalize device=cpu deviceid=0 standard_deviation_inverse="0.003921568627451, 0.003921568627451, 0.003921568627451"] det_human[type=flowunit, flowunit=det_human, device=cpu, deviceid=0, batch_size=1] det_post[type=flowunit, flowunit=det_post, device=cpu, deviceid=0] object_tracker[type=flowunit, flowunit=object_tracker, device=cpu, deviceid=0] expand_box[type=flowunit, flowunit=expand_box, device=cpu, deviceid=0] image_resize[type=flowunit flowunit=resize device=cpu deviceid="0" image_width=192, image_height=256] color_transpose2[type=flowunit flowunit=packed_planar_transpose device=cpu deviceid=0] mean[type=flowunit flowunit=mean device=cpu deviceid="0" mean="116.28,103.53,123.68"] normalize2[type=flowunit flowunit=normalize device=cpu deviceid="0" standard_deviation_inverse="0.0175070,0.01742919,0.01712475"] det_pose[type=flowunit, flowunit=det_pose, device=cpu, deviceid=0, batch_size=1] pose_post[type=flowunit, flowunit=pose_post, device=cpu, deviceid=0] draw_pose[type=flowunit, flowunit=draw_pose, device=cpu, deviceid=0] video_out[type=flowunit, flowunit=video_out, device=cpu, deviceid=0] input1 -> data_source_parser:in_data data_source_parser:out_video_url -> local_camera:in_camera_packet local_camera:out_camera_frame -> det_pre:in_image det_pre:resized_image -> color_transpose:in_image color_transpose:out_image -> normalize:in_data normalize:out_data -> det_human:input det_human:output1 -> det_post:in_feat1 det_human:output2 -> det_post:in_feat2 det_human:output3 -> det_post:in_feat3 det_pre:out_image -> det_post:in_image det_post:has_human -> object_tracker:in_image object_tracker:out_image -> expand_box:in_image expand_box:out_image -> image_resize:in_image image_resize:out_image -> color_transpose2:in_image color_transpose2:out_image -> mean:in_data mean:out_data -> normalize2:in_data normalize2:out_data -> det_pose:image det_pose:heatmap -> pose_post:in_feat pose_post:out_data -> draw_pose:in_feat object_tracker:out_image -> draw_pose:in_image draw_pose:out_image -> video_out:in_video_frame det_post:no_human -> video_out:in_video_frame }"""与 single_human_pose.toml 相比,这个流程图使用 local_camera 替换了 video_demuxer 和 video_decoder 功能单元,其他部分是一致的。打开工程目录下bin/mock_task.toml文件,修改其中的任务输入和任务输出配置为如下内容:[input] type = "url" url = "0" # 表示0号摄像头,即PC自带摄像头,若PC无摄像头需外接USB摄像头 [output] type = "local" url = "0:pose_game" # 表示名为```pose_game```的本地窗口即使用编号为0的摄像头(默认为PC自带的摄像头),输出画面显示到名为pose_game的本地屏幕窗口中。执行.\bin\main.bat运行应用,就可以开始游戏了: 改造自己的体感小游戏本案例展示了 ModelBox AI应用与游戏的结合,开发者可以调整其中的游戏逻辑控制游戏的难易程度,如小鸟降落/飞升的速度、狼柱的出现频率与位置等;还可以改成使用其他动作或者手势控制小鸟飞行,如僵尸跳、开合跳等;另外,案例中提供了贪吃蛇、吃豆人这两款游戏源码,开发者也可以将它们改造成体感小游戏。行动起来,去享受AI与游戏的乐趣吧~~
  • ModelBox开发案例 - 使用OpenPose做多人人体关键点检测
    ModelBox开发案例 - 使用OpenPose做多人人体关键点检测本案例将使用OpenPose模型,实现一个多人人体关键点检测应用,最终效果如下所示: 本案例所需资源(代码、模型、测试数据等)已做成模板放到华为云上,查看和下载模板可以使用如下命令:Windows PC版本请使用solution.bat工具:PS ███\modelbox>: .\solution.bat -l ... Solutions name: ... multi_person_pose_openpose ...结果中的multi_person_pose_openpose即为多人关键点检测模板,可使用如下命令下载模板:PS ███\modelbox>: .\solution.bat -s multi_person_pose_openpose ...Linux开发板版本请使用solution.py脚本:rock@rock-3a:~/███/modelbox$ ./solution.py -l ... Solutions name: ... multi_person_pose_openpose ...结果中的multi_person_pose_openpose即为多人关键点检测模板,可使用如下命令下载模板:rock@rock-3a:~/███/modelbox$ ./solution.py -s multi_person_pose_openpose ...solution.bat/solution.py工具的参数中,-l 代表list,即列出当前已有的模板名称;-s 代表solution-name,即下载对应名称的模板。下载下来的模板资源,将存放在ModelBox核心库的solution目录下。如果对ModelBox AI应用开发还不熟悉,请先阅读ModelBox 端云协同AI开发套件(RK3568)上手指南,或者ModelBox 端云协同AI开发套件(博时特EC02)上手指南。模型准备本案例使用的是OpenPose模型,在下载的资源包中,已经包含了转换好的rknn模型或者onnx模型,可以在对应的平台上直接使用。 原始模型是通过Keras-TensorFlow框架训练得到的,训练工程可参考此项目,感兴趣的开发者可使用该项目自行训练模型。 得到TensorFlow Frozen Graph格式的模型后,如果想体验rknn模型的转换过程,rknpu版本可参考RK1808模型转换验证案例,rknpu2版本可参考RK3568模型转换验证案例。应用开发打开VS Code,连接到ModelBox sdk所在目录或者远程开发板,开始进行多人人体关键点检测应用开发。下面以RK3568版本为例进行说明,其他版本与之类似。1)创建工程使用create.py创建multi_person_pose_openpose工程(Windows PC版本请使用create.bat):rock@rock-3a:~/███/modelbox$ ./create.py -t server -n multi_person_pose -s multi_person_pose_openpose sdk version is modelbox-rk-aarch64 success: create multi_person_pose in /home/rock/███/modelbox/workspace将会用多人关键点检测模板创建出一个multi_person_pose工程。2)查看推理功能单元AI应用的核心是模型推理部分,推理功能单元在工程目录下的model/pose_infer文件夹中,目录结构如下(以RK3568开发板版本为例): 其中openpose_288x512_3stages_rknpu2.rknn是转换好的rknn模型,pose_infer.toml是该模型的ModelBox功能单元配置文件,其内容如下:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. [base] name = "pose_infer" # 功能单元名称 device = "rknpu" # 功能单元运行的设备类型 version = "1.0.0" # 功能单元版本号 type = "inference" # 功能单元类型,推理功能单元此处为固定值inference virtual_type = "rknpu2" # 推理引擎类型,RK3399pro/RK1808设备为rknpu,RK3568设备为rknpu2 group_type = "Inference" # 功能单元分组信息, 推理功能单元默认为Inference is_input_contiguous = "false" # rk芯片推理时的固定属性 description = "openpose model for human joints detection" # 功能单元的描述信息 entry = "./openpose_288x512_3stages_rknpu2.rknn" # 模型文件路径,默认在当前路径下 # 模型输入节点描述:uint8格式的图片数据 [input] [input.input1] name = "input" type = "uint8" device = "rknpu" # 模型输出节点描述 [output] [output.output1] # 输出数据1:关节信息对应的特征向量 name = "out_pafs" type = "float" [output.output2] # 输出数据2:关键点信息对应的特征向量 name = "out_heatmaps" type = "float"可以看到该模型有两个输出节点,即关键点信息和关节信息对应的feature map,需要从中解码出所有的关键点并组合成每个人的关节。3)后处理功能单元后处理功能单元负责从模型推理结果中解码出关键点并画在原图上,它在工程目录的etc/flowunit/openpose_post文件夹中: 解码过程的核心逻辑在openpose_utils.py文件中,可以查阅OpenPose模型细节阅读代码。4)查看流程图模型推理和配套的功能单元准备好后,我们就可以串联出流程图进行测试了,工程的默认流程图为multi_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 = false # 是否记录profile信息 trace = false # 是否记录trace信息 dir = "${HILENS_DATA_DIR}/mb_profile" # profile/trace信息的保存位置 [flow] desc = "multi-person pose estimation example using openpose for local video or rtsp video stream" # 应用的简单描述 [graph] format = "graphviz" # 流程图的格式,当前仅支持graphviz graphconf = """digraph multi_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] pose_detection[type=flowunit, flowunit=pose_infer, device=rknpu, deviceid=0] openpose_post[type=flowunit, flowunit=openpose_post, 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 -> pose_detection:input pose_detection:out_heatmaps -> openpose_post:in_heatmaps pose_detection:out_pafs -> openpose_post:in_pafs video_decoder:out_video_frame -> openpose_post:in_image openpose_post:out_image -> video_out:in_video_frame }"""该流程图对于某个视频流,经过视频解码、图像缩放、openpose推理、关键点后处理与画图等一系列操作后,将结果保存下来。工程的任务配置文件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 = "${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 = "${HILENS_APP_ROOT}/hilens_data_dir/multi_person_pose_result.mp4"我们将common资源包中的data/multi_person_pose.mp4测试视频拷贝到本工程的data目录下,该流程图使用这一视频进行人体关键点检测,检测结果绘制后保存为hilens_data_dir/multi_person_pose_result.mp4文件。5)运行应用在multi_person_pose_openpose工程路径下执行build_project.sh进行工程构建(以RK3568开发板版本为例):rock@rock-3a:~/███/modelbox/workspace/multi_person_pose_openpose$ ./build_project.sh build success: you can run main.sh in ./bin folder rock@rock-3a:~/███/modelbox/workspace/multi_person_pose_openpose$执行bin/main.sh(Windows PC版本请使用bin\main.bat)运行应用,运行结束后在hilens_data_dir目录下生成了multi_person_pose_result.mp4文件,可以下载到PC端查看。
  • ModelBox开发案例 - 使用Lightweight OpenPose做多人人体关键点检测
    ModelBox开发案例 - 使用Lightweight OpenPose做多人人体关键点检测本案例将使用Lightweight OpenPose模型,实现一个多人人体关键点检测应用,最终效果如下所示: 本案例所需资源(代码、模型、测试数据等)已做成模板放到华为云上,查看和下载模板可以使用如下命令:Windows PC版本请使用solution.bat工具:PS ███\modelbox>: .\solution.bat -l ... Solutions name: ... multi_person_pose_lightweight_openpose ...结果中的multi_person_pose_lightweight_openpose即为多人关键点检测模板,可使用如下命令下载模板:PS ███\modelbox>: .\solution.bat -s multi_person_pose_lightweight_openpose ...Linux开发板版本请使用solution.py脚本:rock@rock-3a:~/███/modelbox$ ./solution.py -l ... Solutions name: ... multi_person_pose_lightweight_openpose ...结果中的multi_person_pose_lightweight_openpose即为多人关键点检测模板,可使用如下命令下载模板:rock@rock-3a:~/███/modelbox$ ./solution.py -s multi_person_pose_lightweight_openpose ...solution.bat/solution.py工具的参数中,-l 代表list,即列出当前已有的模板名称;-s 代表solution-name,即下载对应名称的模板。下载下来的模板资源,将存放在ModelBox核心库的solution目录下。如果对ModelBox AI应用开发还不熟悉,请先阅读ModelBox 端云协同AI开发套件(RK3568)上手指南,或者ModelBox 端云协同AI开发套件(博时特EC02)上手指南。模型准备本案例使用的是Lightweight OpenPose模型,在下载的资源包中,已经包含了转换好的rknn模型或者onnx模型,可以在对应的平台上直接使用。 原始模型是通过PyTorch框架训练得到的,训练工程可参考此项目,相比原始的OpenPose模型,该模型更轻量推理速度更快,感兴趣的开发者可使用该项目自行训练模型。 得到onnx格式的模型后,如果想体验rknn模型的转换过程,rknpu版本可参考RK1808模型转换验证案例,rknpu2版本可参考RK3568模型转换验证案例。应用开发打开VS Code,连接到ModelBox sdk所在目录或者远程开发板,开始进行多人人体关键点检测应用开发。下面以RK3568版本为例进行说明,其他版本与之类似。1)创建工程使用create.py创建multi_person_pose_lightweight工程(Windows PC版本请使用create.bat):rock@rock-3a:~/███/modelbox$ ./create.py -t server -n multi_person_pose_lightweight -s multi_person_pose_lightweight_openpose sdk version is modelbox-rk-aarch64 success: create multi_person_pose_lightweight in /home/rock/███/modelbox/workspace将会用多人关键点检测模板创建出一个multi_person_pose_lightweight工程。2)查看推理功能单元AI应用的核心是模型推理部分,推理功能单元在工程目录下的model/pose_infer文件夹中,目录结构如下(以RK3568开发板版本为例): 其中lightweight_openpose_288x512_rknpu2.rknn是转换好的rknn模型,pose_infer.toml是该模型的ModelBox功能单元配置文件,其内容如下:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. [base] name = "pose_infer" # 功能单元名称 device = "rknpu" # 功能单元运行的设备类型 version = "1.0.0" # 功能单元版本号 type = "inference" # 功能单元类型,推理功能单元此处为固定值inference virtual_type = "rknpu2" # 推理引擎类型,RK3399pro/RK1808设备为rknpu,RK3568设备为rknpu2 group_type = "Inference" # 功能单元分组信息, 推理功能单元默认为Inference is_input_contiguous = "false" # rk芯片推理时的固定属性 description = "lightweight-openpose model for human joints detection" # 功能单元的描述信息 entry = "./lightweight_openpose_288x512_rknpu2.rknn" # 模型文件路径,默认在当前路径下 # 模型输入节点描述:uint8格式的图片数据 [input] [input.input1] name = "input" type = "uint8" device = "rknpu" # 输入端口数据位于哪种设备 # 模型输出节点描述 [output] [output.output1] # 输出数据1:关键点信息对应的特征向量 name = "out_heatmaps" type = "float" [output.output2] # 输出数据2:关节信息对应的特征向量 name = "out_pafs" type = "float"可以看到该模型有两个输出节点,即关键点信息和关节信息对应的feature map,需要从中解码出所有的关键点并组合成每个人的关节。3)后处理功能单元后处理功能单元负责从模型推理结果中解码出关键点和关节,它在工程目录的etc/flowunit/pose_post_light文件夹中: 解码过程的核心逻辑在pose_utils_light.py文件中,可以查阅OpenPose模型细节阅读代码。4)画图功能单元得到关键点后可以画在原图上进行输出展示,它在工程目录的etc/flowunit/draw_pose_light文件夹中: 5)查看流程图模型推理和配套的功能单元准备好后,我们就可以串联出流程图进行测试了,工程的默认流程图为multi_person_pose_lightweight.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 = false # 是否记录profile信息 trace = false # 是否记录trace信息 dir = "${HILENS_DATA_DIR}/mb_profile" # profile/trace信息的保存位置 [flow] desc = "multi-person pose estimation example using lightweight-openpose for local video or rtsp video stream" # 应用的简单描述 [graph] format = "graphviz" # 流程图的格式,当前仅支持graphviz graphconf = """digraph multi_person_pose_lightweight { 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] pose_detection[type=flowunit, flowunit=pose_infer, device=rknpu, deviceid=0] pose_post_light[type=flowunit, flowunit=pose_post_light, device=cpu, deviceid=0] draw_pose_light[type=flowunit, flowunit=draw_pose_light, 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 -> pose_detection:input pose_detection:out_heatmaps -> pose_post_light:in_heatmaps pose_detection:out_pafs -> pose_post_light:in_pafs video_decoder:out_video_frame -> draw_pose_light:in_image pose_post_light:out_pose -> draw_pose_light:in_pose draw_pose_light:out_image -> video_out:in_video_frame }"""该流程图对于某个视频流,经过视频解码、图像缩放、lightweight openpose推理、关键点后处理与画图等一系列操作后,将结果保存下来。工程的任务配置文件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 = "${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 = "${HILENS_APP_ROOT}/hilens_data_dir/multi_person_pose_result.mp4"测试视频为data/multi_person_pose.mp4,流程图使用这一视频进行人体关键点检测,检测结果绘制后保存为hilens_data_dir/multi_person_pose_result.mp4文件。6)运行应用在工程路径下执行build_project.sh进行工程构建(以RK3568开发板版本为例):rock@rock-3a:~/███/modelbox/workspace/multi_person_pose_lightweight$ ./build_project.sh build success: you can run main.sh in ./bin folder rock@rock-3a:~/███/modelbox/workspace/multi_person_pose_lightweight$执行bin/main.sh(Windows PC版本请使用bin\main.bat)运行应用,运行结束后在hilens_data_dir目录下生成了multi_person_pose_result.mp4文件,可以下载到PC端查看。
  • 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端查看。
  • ModelBox开发指南 - 使用条件功能单元实现单人人体关键点检测
    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_image、single_pose_post、draw_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端查看。
  • ModelBox开发指南 - 使用流数据功能单元实现行人检测与跟踪
    ModelBox开发指南 - 流数据功能单元本文将使用一个行人检测与跟踪的案例,介绍ModelBox中流数据功能单元的特性,案例效果如下所示: 本案例所需资源(代码、模型、测试数据等)已做成模板放到华为云上,查看和下载模板可以使用如下命令:Windows PC版本请使用solution.bat工具:PS ███\modelbox>: .\solution.bat -l ... Solutions name: ... pedestrian_tracking_yolox ...结果中的pedestrian_tracking_yolox即为行人检测与跟踪模板,可使用如下命令下载模板:PS ███\modelbox>: .\solution.bat -s pedestrian_tracking_yolox ...Linux开发板版本请使用solution.py脚本:rock@rock-3a:~/███/modelbox$ ./solution.py -l ... Solutions name: ... pedestrian_tracking_yolox ...结果中的pedestrian_tracking_yolox即为行人检测与跟踪模板,可使用如下命令下载模板:rock@rock-3a:~/███/modelbox$ ./solution.py -s pedestrian_tracking_yolox ...solution.bat/solution.py工具的参数中,-l 代表list,即列出当前已有的模板名称;-s 代表solution-name,即下载对应名称的模板。下载下来的模板资源,将存放在ModelBox核心库的solution目录下。如果对ModelBox AI应用开发还不熟悉,请先阅读ModelBox 端云协同AI开发套件(RK3568)上手指南,或者ModelBox 端云协同AI开发套件(博时特EC02)上手指南。模型准备本案例使用的是YOLOX模型进行人体检测,与ModelBox sdk中自带的car_det模板使用的模型相同。在下载的资源包中,已经包含了转换好的rknn模型或者onnx模型,可以在对应的平台上直接使用。当然,开发者也可以直接使用car_det模板中的YOLOX模型。 原始模型是通过PyTorch框架训练得到的,训练工程可参考此项目,感兴趣的开发者可使用该项目自行训练模型。 如果想体验rknn模型的转换过程,rknpu版本可参考RK1808模型转换验证案例,rknpu2版本可参考RK3568模型转换验证案例。应用开发打开VS Code,连接到ModelBox sdk所在目录或者远程开发板,开始进行行人检测与跟踪应用开发。下面以RK3568(rknpu2)版本为例进行说明,其他版本与之类似。1)创建工程使用create.py创建pedestrian_tracking工程(Windows PC版本请使用create.bat):rock@rock-3a:~/███/modelbox$ ./create.py -t server -n pedestrian_tracking -s pedestrian_tracking_yolox sdk version is modelbox-rk-aarch64 success: create pedestrian_tracking in /home/rock/███/modelbox/workspace将会用行人检测与跟踪模板创建出一个pedestrian_tracking工程。2)查看跟踪功能单元跟踪模块的功能单元,它根据当前帧的检测结果和保存下来的历史跟踪目标,进行新一轮的跟踪匹配,最后更新到跟踪目标中。它在工程目录的etc/flowunit/object_tracker文件夹下: 打开object_tracker.toml配置文件,可以看到功能单元属性部分:# 工作模式,以下配置项默认全为false,表示通用功能单元;且配置项之前互斥,即最多只能设置其中一个为true stream = true # 是否是Stream类型功能单元,此处设置为true,即表示object_tracker是一个stream功能单元 condition = false # 是否是条件功能单元 collapse = false # 是否是合并功能单元 expand = false # 是否是展开功能单元这些属性全部为false时表示通用功能单元,通用功能单元在处理数据时,只针对当前数据,并不关心数据之间的关联,也不记录任何状态。对于同一个数据流内的数据,它在一次process调用中并不保证这些数据处理的先后(当然数据处理完毕后会由框架重新收集排序),甚至一次process调用中可能包含来自多个流的数据。例如,图像缩放功能单元(resize)在处理当前图像时,处理过程和输出结果只跟当前输入图像相关,并不需要关心其他图像的状态,因此它是一个通用功能单元。本案例中的模型后处理功能单元yolox_post也是一个通用功能单元,因为YOLOX模型的后处理结果只跟当前帧的推理结果相关。object_tracker功能单元属性部分的stream属性设置成了true,即object_tracker被设置成一个流数据功能单元。流数据功能单元在处理数据时,一次process调用中处理的都是来自同一个数据流的数据,且这些数据会顺序的进入process,开发者无需关心数据之前是否是有序的,在process此处,已经由框架保证顺序。例如,object_tracker功能单元在处理当前帧时,需要依赖来自同一数据流的历史跟踪目标,且当前帧一定要在上一帧处理完之后才能处理,因此,object_tracker需要被设置成一个流数据功能单元。 除了通用功能单元的open/close/process等函数外,流数据功能单元还需要实现data_pre/data_post函数,用于一个数据流中状态数据的初始化和释放。因此,object_tracker功能单元中,跟踪器的初始化就放在data_pre函数中: def data_pre(self, data_context): # 视频流开始前的初始化动作,跟踪器的初始化放在此处 # self.tracker = Sort(self.init_hits, self.max_age, self.min_iou) # 可以选用sort跟踪算法 self.tracker = EasyTracker(self.init_hits, self.max_age, self.min_iou) return modelbox.Status()object_tracker中使用的是简单的IoU匹配做新的检测目标与历史跟踪目标的关联(详见easy_tracker.py),开发者也可以尝试更复杂的跟踪策略。 关于流数据功能单元的更细致介绍,详见ModelBox介绍。3)查看流程图工程的默认流程图为graph/pedestrian_tracking.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 = "pedestrian detection and tracking example using yolox for local video or rtsp video stream" # 应用的简单描述 [graph] format = "graphviz" # 流程图的格式,当前仅支持graphviz graphconf = """digraph pedestrian_tracking { 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, batch_size = 1] yolox_post[type=flowunit, flowunit=yolox_post, device=cpu, deviceid=0] object_tracker[type=flowunit, flowunit=object_tracker, 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 video_decoder:out_video_frame -> object_tracker:in_image yolox_post:out_data -> object_tracker:in_bbox object_tracker: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/ped_test.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:pedestrian_tracking" url = "${HILENS_APP_ROOT}/hilens_data_dir/tracking_result.mp4"测试视频为data/ped_test.mp4,流程图使用这一视频进行人检测与跟踪,跟踪结果绘制后保存为hilens_data_dir/tracking_result.mp4文件。4)运行应用在工程路径下执行build_project.sh进行工程构建(以RK3568开发板版本为例):rock@rock-3a:~/███/modelbox/workspace/pedestrian_tracking$ ./build_project.sh build success: you can run main.sh in ./bin folder rock@rock-3a:~/███/modelbox/workspace/pedestrian_tracking$执行bin/main.sh(Windows PC版本请使用bin\main.bat)运行应用,运行结束后在hilens_data_dir目录下生成了tracking_result.mp4文件,可以下载到PC端查看。
  • [问题求助] 是否可以将HiLens Kit摄像头拍摄的画面保存为视频
    HiLens Kit摄像头拍摄到的画面可以通过什么方式把他保存下来吗
  • [问题求助] 模型推理出现错误码17
    如题,在模型推理过程中,出现错误码17,这个报错是啥问题呀
  • [问题求助] 如何给新增的IAM用户设置Hilens使用权限
    如题,新增了一个IAM用户,如何给该账户添加Hilens权限。
  • [问题求助] HiLens搭载的摄像头参数配置
    社区的各位大家好,我想问一下目前HiLens自带的摄像头有更多的说明文档吗?我想尝试修改摄像头的ISO和曝光时间这两个参数
  • ModelBox开发案例 - 使用YOLO v3做口罩检测
    ModelBox开发案例 - 使用YOLO v3做口罩检测本案例将使用YOLO v3模型,实现一个简单的口罩检测应用,最终效果如下所示: 本案例所需资源(代码、模型、测试数据等)已做成模板放到华为云上,查看和下载模板可以使用如下命令:Windows PC版本请使用solution.bat工具:PS ███\modelbox>: .\solution.bat -l ... Solutions name: mask_det_yolo3 ...结果中的mask_det_yolo3即为口罩检测模板,可使用如下命令下载模板:PS ███\modelbox>: .\solution.bat -s mask_det_yolo3 ...Linux开发板版本请使用solution.py脚本:rock@rock-3a:~/███/modelbox$ ./solution.py -l ... Solutions name: mask_det_yolo3 ...结果中的mask_det_yolo3即为口罩检测模板,可使用如下命令下载模板:rock@rock-3a:~/███/modelbox$ ./solution.py -s mask_det_yolo3 ...solution.bat/solution.py工具的参数中,-l 代表list,即列出当前已有的模板名称;-s 代表solution-name,即下载对应名称的模板。下载下来的模板资源,将存放在ModelBox核心库的solution目录下。如果对ModelBox AI应用开发还不熟悉,请先阅读ModelBox 端云协同AI开发套件(RK3568)上手指南,或者ModelBox 端云协同AI开发套件(博时特EC02)上手指南。模型准备本案例使用的是YOLOv3_ResNet18模型,在下载的资源包中,已经包含了转换好的rknn模型或者onnx模型,可以在对应的平台上直接使用。 原始模型是通过TensorFlow框架训练得到的,我们可以用AI Gallery中的物体检测YOLOv3_ResNet18算法和口罩检测小数据集,自行训练出该模型。 得到TensorFlow Frozen Graph格式的模型后,如果想体验rknn模型的转换过程,rknpu版本可参考RK1808模型转换验证案例,rknpu2版本可参考RK3568模型转换验证案例。应用开发打开VS Code,连接到ModelBox sdk所在目录或者远程开发板,开始进行口罩检测应用开发。下面以RK3568版本为例进行说明,其他版本与之类似。1)创建工程使用create.py创建mask_det工程(Windows PC版本请使用create.bat):rock@rock-3a:~/███/modelbox$ ./create.py -t server -n mask_det -s mask_det_yolo3 sdk version is modelbox-rk-aarch64 success: create mask_det in /home/rock/███/modelbox/workspace将会用口罩检测模板创建出一个mask_det工程。2)查看推理功能单元AI应用的核心是模型推理部分,mask_det工程的推理功能单元在工程目录下的model/mask_infer文件夹中,目录结构如下(以RK3568开发板版本为例): 其中yolo3_resnet18_mask_det_288x512-rknpu2.rknn是转换好的rknn模型,mask_infer.toml是该模型的ModelBox功能单元配置文件,其内容如下:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. [base] name = "mask_infer" # 功能单元名称 device = "rknpu" # 功能单元运行的设备类型 version = "1.0.0" # 功能单元版本号 type = "inference" # 功能单元类型,推理功能单元此处为固定值inference virtual_type = "rknpu" # 推理引擎类型,RK3399pro/RK1808设备为rknpu,RK3568设备为rknpu2 group_type = "Inference" # 功能单元分组信息, 推理功能单元默认为Inference is_input_contiguous = "false" # rk芯片推理时的固定属性 description = "yolo3 model for mask detection" # 功能单元的描述信息 entry = "./yolo3_resnet18_mask_det_288x512_rknpu.rknn" # 模型文件路径,默认在当前路径下 # 模型输入节点描述:yolo3模型有1个输入,uint8格式的图片数据 [input] [input.input1] name = "data" type = "uint8" device = "rknpu" # 输入数据位于哪种设备 # 模型输出节点描述:yolo3模型有3个输出,不同大小的float类型特征向量 [output] [output.output1] name = "yolo/output1" type = "float" [output.output2] name = "yolo/output2" type = "float" [output.output3] name = "yolo/output3" type = "float"可以看到该模型有3个输出节点,即YOLO v3模型输出的3个feature map,需要从中解码出检测框。3)其他逻辑功能单元后处理功能单元负责从模型推理结果中解码出检测框,在工程目录下的etc/flowunit/yolo3_post文件夹中: 解码过程的核心逻辑在yolo3_utils.py文件中,可以查阅YOLO v3模型细节阅读代码。画图功能单元在工程目录下的etc/flowunit/draw_mask_bbox文件夹中: 画图的核心逻辑在draw_mask_bbox.py文件的draw_mask_info函数中: def draw_mask_info(self, image, bboxes): '''在图中画出口罩佩戴信息''' thickness = 2 font_scale = 1 text_font = cv2.FONT_HERSHEY_SIMPLEX for bbox in bboxes: label_index = int(bbox[5]) # 以头肩部为处理对象 if self.labels[label_index] != 'head': continue x_min, y_min, x_max, y_max = bbox[0], bbox[1], bbox[2], bbox[3] # 根据头肩部找到匹配的人脸框 face_bbox = self.find_max_cover_bbox( bbox, bboxes, 'face', self.face_cover_ratio) if not face_bbox: # 没找到对应的人脸,输出'unknown' yellow = (255, 255, 0) cv2.rectangle(image, (x_min, y_min), (x_max, y_max), yellow, thickness) cv2.putText(image, 'unknown', (x_min, y_min-20), text_font, font_scale, yellow, thickness) continue # 根据人脸框找到匹配的口罩框 mask_bbox = self.find_max_cover_bbox( face_bbox, bboxes, 'mask', self.mask_cover_ratio) if not mask_bbox: # 没找到对应的口罩框,输出'no mask' red = (0, 0, 255) cv2.putText(image, 'no mask', (x_min, y_min-20), text_font, font_scale, red, thickness) cv2.rectangle(image, (x_min, y_min), (x_max, y_max), red, thickness) else: # 找到对应的口罩框,输出'has mask' green = (0, 255, 0) cv2.putText(image, 'has mask', (x_min, y_min-20), text_font, font_scale, green, thickness) cv2.rectangle(image, (x_min, y_min), (x_max, y_max), green, thickness) cv2.rectangle(image, (mask_bbox[0], mask_bbox[1]), (mask_bbox[2], mask_bbox[3]), green, thickness) return image可以看到,针对每个人,该模型会尝试检测出head(头肩部)、face和mask三个检测框。如果face检测框与mask检测框的重合度大于某个阈值,就判为佩戴口罩;否则,就判为没有佩戴口罩;如果没有检测到face检测框,就会显示Unknown,表示未知。4)查看流程图模型推理和配套的功能单元准备好后,我们就可以串联出流程图进行测试了,口罩检测工程的默认流程图为graph/mask_det.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 = "mask detection example using yolo3 for local video or rtsp video stream" # 应用的简单描述 [graph] format = "graphviz" # 流程图的格式,当前仅支持graphviz graphconf = """digraph mask_det { 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] mask_detection[type=flowunit, flowunit=mask_infer, device=rknpu, deviceid=0] yolo3_post[type=flowunit, flowunit=yolo3_post, device=cpu, deviceid=0] draw_mask_bbox[type=flowunit, flowunit=draw_mask_bbox, 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 -> mask_detection:data mask_detection:"yolo/output1" -> yolo3_post:in_feat3 mask_detection:"yolo/output2" -> yolo3_post:in_feat2 mask_detection:"yolo/output3" -> yolo3_post:in_feat1 video_decoder:out_video_frame -> draw_mask_bbox:in_image yolo3_post:out_data -> draw_mask_bbox:in_bbox draw_mask_bbox: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/mask_test.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:mask_det" # "rtsp://192.168.2.2:8554/outstream" url = "${HILENS_APP_ROOT}/hilens_data_dir/mask_test_result.mp4"测试视频为data/mask_test.mp4,该流程图使用这一视频进行口罩检测,检测结果绘制后保存为hilens_data_dir/mask_test_result.mp4文件。5)运行应用在mask_det工程路径下执行build_project.sh进行工程构建(以RK3568开发板版本为例):rock@rock-3a:~/███/modelbox/workspace/mask_det$ ./build_project.sh build success: you can run main.sh in ./bin folder rock@rock-3a:~/███/modelbox/workspace/mask_det$然后执行bin/main.sh(Windows PC版本请使用bin\main.bat)运行应用,运行结束后在hilens_data_dir目录下生成了mask_test_result.mp4文件,可以下载到PC端查看。