专栏硬件技术在J5上使用DSP进行RoiResize

在J5上使用DSP进行RoiResize

芯链情报局2023-06-15
183
2

前言

DSP是J5芯片上专用于视觉/图像处理的数字信号处理器。在OE包的ddk/samples/vdsp_rpc_sample路径下,提供了DSP使用示例,主要包括nn示例和cv示例。nn示例涵盖了深度学习模型的相关算子,包括量化、反量化和Softmax。cv示例展示了如何调用地平线基于DSP封装的图像处理算子,目前已支持20多个,并且仍在持续扩充当中。
从J5芯片算法工具链的1.1.49b版本开始,DSP的cv示例新增了RoiResize算子,本文会基于此版本的OE交付包,对该算子的功能和使用做详细介绍。
在正式阅读前,希望您已经对DSP的软硬件特点、编程思路和板端运行方法有基本的了解,关于这方面的内容可以查看社区文章《DSP开发快速上手》。

RoiResize功能介绍

ROI(Region of Interest)在这里指检测模型在模型输入原图上检测出的Bounding Box,后续一般会从原图中抠出再送给下游任务做分类等计算处理。但一般来说这个BoundingBox的大小不会直接符合下游模型的输入尺寸要求,所以往往需要进行crop/resize/padding等操作,而RoiResize便是为了满足这一需求而产生的。
在J5上,可以使用DSP进行RoiResize操作,该算子的原理如下:

首先判断ROI是否超出了原图的边界,如果超出,调整ROI边界至原图有效范围内,再从原图中进行crop,等比例resize至小于等于模型输入尺寸,最后对短边进行对称padding,完成整个RoiResize操作。若ROI不超出原图边界,则直接做crop、等比例resize和对称padding。
以下图情况举例:

原图的边长为1000x800,ROI的边长为600x600,模型的输入尺寸为200x200。可以看到,ROI超出了原图边界,因此首先需要修改ROI边界,将600x600调整为420x600,crop之后进行等比例resize,将600的长边变为200,420的短边变为140,最后进行对称padding,在140的短边两侧各填充30列数,得到符合模型输入要求的200x200的边长。

padding值配置说明

地平线工具链支持在模型前端插入一个预处理节点,内部依次完成如下操作:

  1. 颜色空间转换:如 nv12 --> rgb

  2. 数据归一化:(data - mean_value) * scale_value

假设 rgb 训练的原始模型需要 padding 0 值,mean_value 在 3 个通道上的均值分别为 R、G、B,那么应通过如下方程组换算回 nv12 runtime 输入数据上的 padding 值:

  • Y + 1.14V - R = 0

  • Y - 0.39U - 0.58V - G = 0

  • Y + 2.03U - B = 0

求解后得:

  • Y = 0.299R + 0.587G + 0.114B

  • U = -0.147R - 0.289G + 0.436B

  • V = 0.615R - 0.515G - 0.100B

与BPU SDK API对比

DSP侧的RoiReisze接口名为hbDSPRoiResize,BPU侧有一个推理接口hbDNNRoiInfer,两者功能不同,请开发者不要混淆。
hbDSPRoiResize调用了一个封装在DSP侧的cv算子,用于将原图中的单个ROI调整成合适的大小,常用于推理前的数据处理。hbDNNRoiInfer是BPU SDK API,处理多个ROI后调用模型执行推理,详细介绍可以查看社区文章《resizer模型使用与部署》。
这两个接口对ROI处理的主要区别在于,前者会对ROI做padding并等比例缩放到模型输入大小,物体的宽高比不发生变化,后者则是将ROI直接缩放到模型输入大小,物体的宽高比有可能改变。

ARM接口介绍

DSP侧的RoiResize功能,地平线以库的形式进行了封装,开发者只需重点关注在ARM侧的调用方式。对此,地平线提供了hbDSPRoiResize接口,以下是针对该接口的具体说明。

ARM侧的RoiResize接口。
输入值:
  • task 任务句柄。
  • dstImg 输出图像。
  • srcImg 输入图像,数据类型只支持Y和NV12(YUV420)。
  • roi ROI区域,有效范围取ROI区域与srcImg的交集。
  • roiResizeParam 算子参数,包括插值类型和padding的值。
  • ctrlParam 任务控制参数。

返回值:

  • 返回 0 则表示API成功执行,否则执行失败。

hbDSPRoiResize的ROI区域配置。ROI区域的width等于right-left+1,height=bottom-top + 1。
成员名称及描述:
  • left ROI区域的左边下标。
  • top ROI区域的上边下标。
  • right ROI区域的右边下标。
  • bottom ROI区域的下边下标。
hbDSPRoiResize的算子参数配置。
成员名称及描述:
  • interpolation 插值类型,0表示最邻近插值,1表示双线性插值。
  • paddingValue[4] 具体的padding值,每个通道对应一个值,Y数据类型使用一个通道,NV12数据类型使用三个通道,未使用的通道可不赋值,或填写任意值,不起实际作用。
有关hbDSPRoiResize接口的更多详细信息可以查看芯片算法工具链手册的6.4章节《DSP运行时API手册》。

示例代码解读

地平线在OE包中提供了示例代码,指导开发者如何调用hbDSPRoiResize接口执行任务。可以进入OE包的ddk/samples/vdsp_rpc_sample/arm/cv/src/目录,打开test_roi_resize.cc文件查看详细代码。以下从示例代码中拆解出重要部分进行讲解。

此段代码用于读取本地的NV12图片文件,并将y分量数据存放在src_img_y地址,uv分量数据存放在src_img_uv地址。src_stride指的是图像每行占用的字节数,等于宽度乘以每个像素的字节大小,在数值上等于src_width。因为4个y分量对应1组uv分量,1组uv分量的高和宽为y分量的一半,所以src_uv_width和src_uv_height的数值分别是基于src_width和src_height向右移位一次得到的位运算结果。而src_uv_stride是uv在width方向上占用的字节数,数值上等于src_uv_width的两倍。

src_y_mem和src_uv_mem存储了图像各个分量的信息,在arm侧申请分配,同时也在dsp侧访问处理,两端在操作时需要做内存同步处理,因为y分量的字节数就等于图片的像素个数,所以src_y_mem分配的内存空间为src_width * src_height字节,而u和v在height方向是y的一半,width方向上总量和y相同,所以src_uv_mem分配的内存空间为src_uv_stride * src_uv_height。之后用hbDSPImage结构体定义src,存储DSP输入数据的相关信息。

先使用hbDSPRoi结构体定义ROI左上右下四条边的位置,之后代码编写思路同上一步,初始化相关参数并为DSP的输出内存分配空间。

这里首先初始化RoiResize的padding参数,参数有两个,第一个是padding的插值方式,最近邻插值对应HB_CV_INTER_NEAREST和数字0,双线性插值对应HB_CV_INTER_LINEAR和数字1。对于paddingValue,如果图像输入是Y数据类型,那么只有paddingValue[0]是有效的,如果图像输入是NV12数据类型,那么只有paddingValue[0/1/2]这三个是有效的,无效部分填的值不起作用,示例代码中填的无效值仅用于占位。
关于paddingValue的数值设定,如果训练时使用的数据类型就是Y或NV12,那么直接使用训练时设置的padding值即可,如果训练时使用的是BGR/RGB数据,那么paddingValue的数值需要基于训练时设置的padding值做rgb转yuv的公式得到。
之后初始化DSP任务参数ctrl_param并定义任务句柄,就可以调用hbDSPRoiResize接口让DSP执行RoiResize的操作了。

DSP任务执行结束后,使用dst_img_y和dst_img_uv存储输出的结果并保存,最后再释放掉申请的内存空间。

板端运行及可视化展示

OE包的ddk/samples/vdsp_rpc_sample目录下,有个script文件夹,里面已经包含了编译好的DSP镜像和ARM可执行文件及相关依赖,我们可以将script文件夹整个复制进J5开发板进行功能验证。

首先将script文件夹复制到J5开发板的/userdata目录下,然后我们编写一个脚本用于配置DSP镜像:

之后cd进入/userdata/script/cv目录,依次运行以下命令:

  1. chmod 777 ../lib/dsp_relay_server 为DSP的relay模式所需的依赖文件赋予运行权限;

  2. chmod 777 ./bin/test_cv 为cv算子示例的可执行文件赋予运行权限;

  3. sh run_cv_test.sh roiResize 运行cv算子的RoiResize示例。

成功运行后,终端会打印如下信息,表明DSP的RoiResize算子成功执行:

此时,在/userdata/script/cv/目录下会生成一个output文件夹,保存了图像处理结果,RoiResize算子的输出文件名为roi_resize_output.480x380.yuv,原始输入图像为/userdata/script/cv/data/500x480.lena.yuv。

我们可以使用YUV可视化工具YUView查看图片的处理效果,该工具的下载地址为:
https://github.com/IENT/YUView/releases
原始数据和处理结果的可视化效果图如下所示:

可以看到处理结果符合预期,DSP成功使用RoiResize算子执行了crop、等比例resize和对称padding操作。

硬件技术
评论1
0/1000
  • 芯链情报局
    Lv.4

    关于padding值的详细配置说明

    1、假设浮点模型基于rgb色彩空间训练,且训练时RoiResize的padding值设置为0(可称为padding_0),以黑边的形式填充。那么DSP的RoiResize接口设定的padding值,最终在模型内部也需要变成0。
    2、地平线的工具链支持在模型前面插入一个预处理节点,执行色彩空间转换(如nv12 -> rgb)和数据归一化(如[0,255] -> [-1,1])等操作,数据归一化需要设置yaml文件的参数mean_value和scale_value,其中mean_value用于在rgb的三个通道做减法。因为数据归一化也会影响RoiResize的padding值,所以在rgb输入时padding值不设置为0,而是设置为mean_value(可称为padding_rgb),这样padding值在经过归一化后就会变为0。
    3、又考虑到模型在实际部署时,接收的往往是nv12输入数据,因此在使用DSP做RoiResize时,padding值需要从基于rgb的mean_value转变为基于nv12的数值(可称为padding_nv12),这样在经过预处理节点的色彩空间转换和数据归一化操作后,padding值才会变成0。
    以下是RoiResize的padding值经过两次转换,输入到模型内部的过程:
    padding_nv12 => padding_rgb => padding_0

    padding_0的三通道值都为0,padding_rgb的三通道值为padding_0加上yaml的mean_value,padding_nv12的三通道值需要通过公式计算得出。

    rgb转nv12的计算公式如下:
    Y = 0.299R + 0.587G + 0.114B
    U = -0.147R - 0.289G + 0.436B
    V = 0.615R - 0.515G - 0.100B

    nv12转rgb的计算公式如下:
    R = Y + 1.14V
    G = Y - 0.39U - 0.58V
    B = Y + 2.03U

    以上两个公式都可以从padding_rgb计算出padding_nv12。

    2023-06-16
    0
    1
    • Liqp回复芯链情报局:

      现在这个接口是不是只支持单张图,多batch不支持的

      2024-03-05
      0