VPI - 视觉编程接口

3.2 版本

可分离卷积

概述

可分离卷积算法执行 2D 卷积操作,但利用了 2D 核是可分离的这一事实。用户传递一个水平和垂直 1D 核。这通常会带来更好的性能,特别是对于大于 5x5 的核。对于较小的核,最好直接使用带有 2D 核的 卷积 算法。

输入Sobel 核输出

\begin{eqnarray*} k_{col} &=& \frac{1}{64} \begin{bmatrix} 1 \\ 6 \\ 15 \\ 20 \\ 15 \\ 6 \\ 1 \end{bmatrix} \\ k_{row} &=& \begin{bmatrix} -1 & -5 & -6 & 0 & 6 & 5 & 1 \end{bmatrix} \end{eqnarray*}

实现

离散 2D 卷积使用以下离散函数实现

\begin{eqnarray*} I'[x,y] &=& \sum_{m=0}^{k_w} K_{row}[m] \times I[x,y-(m - \lfloor k_w/2 \rfloor)] \\ I''[x,y] &=& \sum_{m=0}^{k_h} K_{col}[m] \times I'[x-(m - \lfloor k_h/2 \rfloor),y] \end{eqnarray*}

其中

  • \(I\) 是输入图像。
  • \(I'\) 是沿行卷积的临时图像。
  • \(I''\) 是最终结果。
  • \(K_{row}\) 是行卷积核。
  • \(K_{col}\) 是列卷积核。
  • \(k_w,k_h\) 分别是核的宽度和高度。
注意
大多数计算机视觉库期望在调用其卷积函数之前反转核。VPI 则不然,我们实现的是实际的卷积,而不是互相关。当然,如果核是对称的,则这无关紧要。

C API 函数

有关实现该算法的限制、约束和后端列表,请查阅以下函数的参考文档

函数描述
vpiSubmitSeparableConvolution 对图像运行通用的 2D 卷积操作,针对可分离核进行了优化。

用法

语言
  1. 导入 VPI 模块
    import vpi
  2. 定义一个 3x3 可分离 Sobel 核。
    sobel_row = [-1, -5, -6, 0, +6, +5, +1];
    sobel_col = [1/64.0, 6/64.0, 15/64.0, 20/64.0, 15/64.0, 6/64.0, 1/64.0]
  3. 使用 CPU 后端和给定的核,在输入图像上运行可分离卷积滤波器。输入和输出都是 VPI 图像。
    with vpi.Backend.CUDA
    output = input.convolution(kernel_x=sobel_row, kernel_y=sobel_col, border=vpi.Border.ZERO)
  1. 初始化阶段
    1. 包含定义所需函数和结构的头文件。
      声明使用卷积核执行图像滤波的函数。
    2. 定义输入图像对象。
      VPIImage input = /*...*/;
      struct VPIImageImpl * VPIImage
      图像的句柄。
      定义: Types.h:256
    3. 创建输出图像。它从输入图像获取其尺寸和格式。
      int32_t w, h;
      vpiImageGetSize(input, &w, &h);
      vpiImageGetFormat(input, &type);
      VPIImage output;
      vpiImageCreate(w, h, type, 0, &output);
      uint64_t VPIImageFormat
      预定义的图像格式。
      VPIStatus vpiImageGetFormat(VPIImage img, VPIImageFormat *format)
      获取图像格式。
      VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
      使用指定的标志创建空的图像实例。
      VPIStatus vpiImageGetSize(VPIImage img, int32_t *width, int32_t *height)
      以像素为单位获取图像尺寸。
    4. 创建将在其中提交算法以供执行的流。
      VPIStream stream;
      vpiStreamCreate(0, &stream);
      struct VPIStreamImpl * VPIStream
      流的句柄。
      定义: Types.h:250
      VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
      创建流实例。
  2. 处理阶段
    1. 定义要使用的核。在本例中,一个简单的 7x7 Sobel 滤波器。
      float sobel_row[7] = {-1, -5, -6, 0, +6, +5, +1};
      float sobel_col[7] = {1/64.f, 6/64.f, 15/64.f, 20/64.f, 15/64.f, 6/64.f, 1/64.f};
    2. 将算法提交到流,传递 1D 核和剩余参数。它将由 CUDA 后端执行。
      vpiSubmitSeparableConvolution(stream, VPI_BACKEND_CUDA, input, output, sobel_row, 7, sobel_col, 7, VPI_BORDER_ZERO);
      VPIStatus vpiSubmitSeparableConvolution(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const float *kernelXData, int32_t kernelXSize, const float *kernelYData, int32_t kernelYSize, VPIBorderExtension border)
      对图像运行通用的 2D 卷积操作,针对可分离核进行了优化。
      @ VPI_BACKEND_CUDA
      CUDA 后端。
      定义: Types.h:93
      @ VPI_BORDER_ZERO
      图像外部的所有像素均被视为零。
      定义: Types.h:278
    3. (可选)等待直到处理完成。
      vpiStreamSync(stream);
      VPIStatus vpiStreamSync(VPIStream stream)
      阻塞调用线程,直到此流队列中的所有提交命令都完成(队列为空)。
  3. 清理阶段
    1. 释放流以及输入和输出图像所持有的资源。
      vpiImageDestroy(output);
      void vpiImageDestroy(VPIImage img)
      销毁图像实例。
      void vpiStreamDestroy(VPIStream stream)
      销毁流实例并释放所有硬件资源。

有关更多信息,请参见 VPI - 视觉编程接口 “C API 参考”部分中的 卷积

性能

有关如何使用以下性能表格的信息,请参见 算法性能表格
在比较测量结果之前,请查阅 比较算法运行时间
有关性能基准测试方式的更多信息,请参见 性能基准

 -