PyrDown

Pyrdowm算子用于获得图像的不同分辨率表达。当前该算子支持的插值方式有两种:高斯插值和双线性插值。 两种插值方式所对应的金字塔分别称为高斯金字塔和双线性金字塔。

  • 高斯金字塔通过高斯平滑、采样等流程将图片进行降采样,生成长宽为原图一半大小的图像。
  • 双线金字塔直接通过双线性插值、降采样等流程生成长宽为当前层的上一层图像一半大小的新图像。
  • 当前金字塔支持5层计算。

算子效果

输入图像参数输出图像
imagelevels = 5
interpolation = HB_VP_INTER_LINEAR
image

原理

根据下采样原理,可以分为高斯下采样和双线性下采样两种方法。

高斯金字塔的下采样的实现流程为先对原图进行高斯平滑,再将偶数行列删除,最终得到目标图像。

在高斯平滑阶段,使用的高斯滤波核如下:

kernel=1256[1464141624164624362464162416414641]\begin{aligned}kernel = \frac{1}{256}\begin{bmatrix} 1 & 4 & 6 & 4 & 1 \\ 4 & 16 & 24 & 16 & 4 \\ 6 & 24 & 36 & 24 & 6 \\ 4 & 16 & 24 & 16 & 4 \\ 1 & 4 & 6 & 4 & 1 \end{bmatrix}\end{aligned}

双线性金字塔使用双线性插值的方式完成图像的下采样计算,双线性插值的原理如下图所示:

image

在插值过程中,假设要输出的插值像素点为P,P临近的四个待插值源点P1,P2,P3,P4之间的距离为1。由于插值方式为线性插值,所以水平方向和竖直方向的插值顺序不影响最终结果。 具体的插值点计算公式如下:

P12=(1a)P1+aP2P_{12} = (1 - a) * P_1 + a * P_2

P34=(1a)P3+aP4P_{34} = (1 - a) * P_3 + a * P_4

P=(1a)(1b)P1+a(1b)P2+(1a)bP3+abP4P = (1 - a) * (1 - b) * P_1 + a * (1 - b) * P_2 + (1 - a) * b * P_3 + a * b * P_4

公式中各个点坐标的系数可以认为是插值源点对于输出像素点的权重值。在该算子中所介绍的pyramid接口使用的双线性插值默认输出点到每个插值源点的距离相等, 对输入的图像数据的长宽尺寸进行二分之一的下采样。在获得一个2*2的图像区域数据后,进行相应的均值化处理,得到一个结果像素。

该算子下采样的层数最大可达到5层,每一层图像尺寸的宽度和高度均为上一层的1/2。具体的计算方式如下:

BLo(i,j)=(SRC(2i,2j)+SRC(2i+1,2j)+SRC(2i,2j+1)+SRC(2i+1,2j+1)+2)>>2BL_{o}(i,j) = \left( \text{SRC}(2*i, 2*j) + \text{SRC}(2*i + 1, 2*j) + \text{SRC}(2*i, 2*j + 1) + \text{SRC}(2*i + 1, 2*j + 1) + 2 \right) >> 2

其中,SRC为输入图像像素,i、j为输出bl层的像素坐标,BL为当前层输出的双线性下采样结果。

API接口

int32_t hbVPPyrDown(hbUCPTaskHandle_t *taskHandle,
                    hbVPImage *dstImgs,
                    hbVPImage const *srcImg
                    hbVPPymParam const *pymCfg);

详细接口信息请查看 hbVPPyrDown

使用方法

// Include the header
#include "hobot/hb_ucp.h"
#include "hobot/vp/hb_vp.h"
#include "hobot/vp/hb_vp_pyr_down.h"

// init Image, allocate memory for image data
hbUCPSysMem src_mem;
hbUCPMallocCached(&src_mem, src_stride * src_height, 0);
hbVPImage src_img{HB_VP_IMAGE_FORMAT_Y,
                  HB_VP_IMAGE_TYPE_U8C1,
                  src_width,
                  src_height,
                  src_stride,
                  src_mem.virAddr,
                  src_mem.phyAddr,
                  nullptr,
                  0,
                  0};

int layers = 5;
std::vector<hbUCPSysMem> dst_mems(layers);
std::vector<hbVPImage> dst_imgs(layers);
volatile int32_t dst_width = src_width;
volatile int32_t dst_height = src_height;
volatile int32_t dst_stride = src_stride;
for (int32_t i{0}; i < layers; i++) {
  dst_width = dst_width / 2;
  dst_height = dst_height / 2;
  dst_stride = dst_stride / 2;
  hbUCPMallocCached(&dst_mems[i], dst_stride * dst_height, 0);
  dst_imgs[i] = {HB_VP_IMAGE_FORMAT_Y,
                 HB_VP_IMAGE_TYPE_U8C1,
                 dst_width,
                 dst_height,
                 dst_stride,
                 dst_mems[i].virAddr,
                 dst_mems[i].phyAddr,
                 nullptr,
                 0,
                 0};
}

// init pyramid config
hbVPPymParam pym_cfg{};
pym_cfg.interpolation = HB_VP_INTER_GAUSSIAN;
pym_cfg.levels = layers;

// init task handle, schedule param and pyramid configuration
hbUCPTaskHandle_t task_handle{nullptr};
hbUCPSchedParam sched_param;
HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param);
sched_param.backend = HB_UCP_DSP_CORE_0;

// create task
hbVPPyrDown(&task_handle, dst_imgs.data(), &src_img, &pym_cfg);

// submit task
hbUCPSubmitTask(task_handle, &sched_param);

// wait for task done 
hbUCPWaitTaskDone(task_handle, 0);

// release task handle
hbUCPReleaseTask(task_handle);

// release memory
hbUCPFree(&src_mem);
for (int32_t i{0}; i < layers; i++) {
  hbUCPFree(&dst_mems[i]);
}