1 前言
本文主要介绍在地平线开发板上针对resizer模型的使用与部署,如果您是一个初学者,对于该内容可能会有以下几点疑问:
2.为什么要用resizer模型?
3.如何得到resizer模型?
4.resizer模型如何部署?
答1:resizer是指芯片上用于处理图像缩放的一个功能模块,可以把图像(nv12) 里的一个指定 ROI,缩放成指定的大小,缩放的方法是双线性插值,通过BPU的functioncall驱动。在一些人工智能业务场景中需要使用到该模块的算法模型,称之为resizer模型。
答2:为了方便大家理解,以车辆检测与识别场景为例,如下图所示。先通过一个上游网络检测得到图片中不同车辆的位置信息,把检测到的车辆图片信息送入小网络进行识别前,需要先进行尺寸缩放,为了适配类似业务场景,加快图片尺寸缩放速度,地平线芯片上提供针对resizer模型的专门加速硬件模块。

答4:在编译得到resizer模型后,具体使用和部署细节将在第3节详细介绍。
2 resizer模型约束
resizer模型推理时,输入从DDR读取,会先使用roi从原始图像中抠图后resize到模型输入大小,输出存储在一块特殊的片上内存中,只有BPU可以读取,且读取的过程会改变数据的排布,所以resizer的输出只能送入BPU用作模型推理,上述过程中,一些约束信息如下表所示,



H和W方向的缩放倍数均有限制,以下src_len表示roi.size.h或roi.size.w。dst_len表示输出的h或w。
step = ((src_len - 1) * 65536 + (dst_len - 1) / 2) / (dst_len - 1)
step需要在范围[0, 262143]内,即缩放倍数在[1/4, 65536)之内。如果相关接口中有keep ratio选项,并且设置该参数为true。则限制需要以keep ratio之后的输出尺寸来考虑step限制。
h_ratio = roi.size.h * 65536 / dest_h
w_ratio = roi.size.w * 65536 / dest_w
如果h_ratio>w_ratio,则需要在上述公式中dest_w调整为
roi.size.w * dest_h / roi.size.h
如果w_ratio>h_ratio,则需要在上述公式中dest_h调整为
roi.size.h * dest_w / roi.size.w
resizer模型目前支持多输入的nv12数据,resizer常用的输出尺寸(HxW)如下:
128x128
128x64
64x128
160x96
3 resizer模型使用流程
3.1 所需文件结构说明
resizer模型示例是在horizon_runtime_sample目录下,该目录下需要用到的目录结构如下所示:
├── code # 开发机中编译示例源码
│ ├── 01_api_tutorial # dnn API使用示例代码
│ │ └──roi_infer # resizer模型推理示例
│ ├── build_xj3.sh # xj3 ARM端编译脚本
│ ├── CMakeLists.txt
│ ├── deps
│ │ └──aarch64 # xj3 ARM端编译依赖库
├── xj3 # 板端运行目录结构
│ ├── data # 预置图片数据文件
│ ├── model # resizer模型文件
│ │ └──model_name.bin # mobilenetv1_128x128_resizer_nv12.bin
│ ├── script # ARM端示例运行脚本
│ ├── 01_api_tutorial # dnn API使用示例代码
│ └──roi_infer.sh # resizer板端运行脚本
└── README.md
3.2 环境准备
3.3 板端部署的主要流程

参数介绍:
[out] taskHandle:任务句柄指针。
[in/out] output:推理任务的输出。
[in] input:推理任务的输入。
[in] rois:Roi框信息。
[in] roiCount:Roi框数量。
[in] dnnHandle:dnn句柄指针。
[in] inferCtrlParam:控制推理任务的参数。
返回值:
返回 0 则表示API成功执行,否则执行失败。
其中需要注意的有四个参数,分别是output, input, rois, roiCount,为了方便大家理解,下面举两个例子进行介绍。

此时,推理任务的输入input可以依次表示为:
推理任务的输出Output可以表示为:
其中Output0中内容包括:roi0_output0, roi1_output0,可以理解为data_batch=2,model_batch=1的模型推理。

此时,推理任务的输入input可以依次表示为:
推理任务的输出Output可以依次表示为:
其中Output0中内容包括:data_batch0_output0, data_batch1_output0,Output1中内容包括:data_batch0_output1, data_batch1_output1,因此,在准备输出内存时需要结合data_batch数量和对应输出分支的shape来进行分配。
此时模型推理这 3 批数据需要准备独立地址的 input_tensor 数量为 3个输入分支 x 3批数据 = 9。
另假设模型输入/输出的静态信息如下:
模型输入(model_info):
tensor_0_resizer: [2, 3, 128, 128]
tensor_1_resizer: [2, 3, 256, 256]
tensor_2_ddr: [2, 80, 1, 100]
模型输出(model_info):
tensor_out:[2, 100, 1, 56]
那么模型在推理时的动态信息则为:
模型输入(input_tensors):
[1x3x128x128, 1x3x256x256, 1x80x1x100, 1x3x128x128, 1x3x256x256, 1x80x1x100, 1x3x128x128, 1x3x256x256, 1x80x1x100]
模型输出(output_tensors):
[4x100x1x56]
3.4 hbDNNRoiInfer调用源码简析
3.5 工程编译
1. 执行horizon_runtime_sample/code目录下的build_xj3.sh脚本即可一键编译生成xj3开发板端的可执行程序和对应依赖库,分别存放在xj3/script/aarch64目录下的bin和lib子目录;
aarch64
├── bin
│ └── roi_infer # 编译产生的可执行文件
└── lib # 编译执行依赖相关库
├── libdnn.so
├── libhbrt_bernoulli_aarch64.so
└── libopencv_world.so.3.4
├── xj3 # 板端运行目录结构
│ ├── data # 预置图片数据文件
│ ├── model # resizer模型文件
│ │ └──model_name.bin # 真实路径为model/runtime/mobilenetv1/xxx.bin
│ ├── script # ARM端示例运行脚本
│ ├── aarch64 # 板端可执行程序和对应依赖库,执行build_xj3.sh后自动生成
│ ├── 01_api_tutorial # dnn API使用示例代码
│ └──roi_infer.sh # resizer板端运行脚本
推荐您使用scp命令进行文件传输,执行命令可参考:
3.6 上板推理
运行结果如下:
至此,完成resizer模型推理和结果输出的全过程。
4 板端使用工具评测resizer模型
4.1 hrt_model_exec工具简介
当模型包含resizer输入源时,infer 和perf 功能都需要设置 roi_infer 为true,并且配置与输入源一一对应的 input_file 和 roi 参数。例如:某模型有三个输入,输入源顺序分别为[ddr, resizer, resizer],则评测两组输入数据的命令如下:
此时,输出的第一维是有效数据,第二维是无效数据
推理data_batch=2数据的命令如下:
4.2 板端实测
- infer模型推理
运行命令:
输出结果:
- perf评测性能latency
运行命令:
输出结果:
- perf评测性能FPS
运行命令:
输出结果:

