VPI - 视觉编程接口

3.2 版本

逆FFT

概述

逆FFT 实现了二维图像的逆傅里叶变换,支持实值和复值输出。给定一个二维频谱(频域),它返回空间域上的图像表示。它是 FFT 算法的精确逆运算。

以幅度谱作为输入在空间域中输出

实现

逆快速傅里叶变换与正向 FFT 的 实现 非常相似,除了指数的符号,这里是正号。

\[ I[m,n] = \frac{1}{MN} \sum^{M-1}_{u=0} \sum^{N-1}_{v=0} I'[u,v] e^{+2\pi i (\frac{um}{M}+\frac{vn}{N})} \]

其中

  • \(I'\) 是频域中的输入图像
  • \(I\) 是其空间域表示
  • \(M\times N\) 是输入的维度

默认情况下,应用归一化因子 \(\frac{1}{MN}\) 以使 IFFT 成为 FFT 的精确逆运算。由于这会带来性能损失,并且可能不需要归一化,用户可以传递标志 VPI_DENORMALIZED_OUTPUTvpiSubmitIFFT,以指示输出必须保持非归一化状态。

与直接 FFT 一样,根据 \(N\) 的值,采用不同的技术以获得最佳性能

  • CPU 后端
    • 当 \(N\) 可以分解为 \(2^a \times 3^b \times 5^c\) 时的快速路径
  • CUDA 后端
    • 当 \(N\) 可以分解为 \(2^a \times 3^b \times 5^c \times 7^d\) 时的快速路径

一般来说,素因子越小,性能越好,即,2 的幂次是最快的。

IFFT 支持以下变换类型

  • 复数到复数,C2C
  • 复数到实数,C2R。

数据布局

数据布局严格取决于变换类型。在一般的 C2C 变换情况下,输入和输出数据都应为 VPI_IMAGE_FORMAT_2F32 类型且具有相同的大小。在 C2R 模式下,类型为 VPI_IMAGE_FORMAT_2F32 的每个输入图像行 \((X_1,X_2,\dots,X_{\lfloor\frac{N}{2}\rfloor+1})\) (仅包含非冗余值) 会生成类型为 VPI_IMAGE_FORMAT_F32 的行 \((x_1,x_2,\dots,x_N)\),表示整个图像。在这两种情况下,输入和输出图像的高度相同。

注意
在首次调用 vpiSubmitIFFT 时以及每次输入或输出行步幅相对于上次调用发生更改时,都可能发生内存分配。

C API 函数

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

函数描述
vpiCreateIFFT 为逆快速傅里叶变换算法创建负载 (payload)。
vpiSubmitIFFT 在单个图像上运行逆快速傅里叶变换。
注意
此算法要求系统中安装库 libcufft.so.10。

用法

语言
  1. 导入 VPI 模块
    import vpi
  2. 在 VPI 图像输入上运行 C2R IFFT,频谱数据格式为 vpi.Format._2F32。生成的 VPI 图像格式为 vpi.Format.F32。操作由 CUDA 后端执行。
    with vpi.Backend.CUDA
    output = input.irfft()
  1. 初始化阶段
    1. 包含定义逆 FFT 函数的头文件。
      #include <vpi/algo/FFT.h>
      声明实现快速傅里叶变换算法及其逆运算的函数。
    2. 定义输入频谱图像。由于我们执行的是 C2R IFFT,因此输入宽度必须为 \(\lfloor \frac{w}{2} \rfloor+1\),其中 \(w\) 是输出图像宽度。输入和输出高度必须匹配。图像格式必须为 VPI_IMAGE_FORMAT_2F32
      VPIImage input = /*...*/;
      struct VPIImageImpl * VPIImage
      图像的句柄。
      定义: Types.h:256
    3. 创建输出图像。同样,由于是 C2R IFFT,输出类型必须为 VPI_IMAGE_FORMAT_F32。我们传递 0 作为标志,以使函数返回归一化输出。
      VPIImage output;
      #define VPI_IMAGE_FORMAT_F32
      单平面,带有一个 32 位浮点通道。
      VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
      使用指定的标志创建空的图像实例。
    4. 在 CUDA 后端上创建 IFFT 负载 (payload)。输入为复数,输出为实数。问题大小指的是输出大小。
      VPIPayload ifft;
      #define VPI_IMAGE_FORMAT_2F32
      单平面,带有两个交错的 32 位浮点通道。
      VPIStatus vpiCreateIFFT(uint64_t backends, int32_t outputWidth, int32_t outputHeight, const VPIImageFormat inFormat, const VPIImageFormat outFormat, VPIPayload *payload)
      为逆快速傅里叶变换算法创建负载 (payload)。
      struct VPIPayloadImpl * VPIPayload
      算法负载 (payload) 的句柄。
      定义: Types.h:268
      @ VPI_BACKEND_CUDA
      CUDA 后端。
      定义: Types.h:93
    5. 创建流 (stream),算法将提交到该流中执行。
      VPIStream stream;
      vpiStreamCreate(0, &stream);
      struct VPIStreamImpl * VPIStream
      流 (stream) 的句柄。
      定义: Types.h:250
      VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
      创建流 (stream) 实例。
  2. 处理阶段
    1. 将 IFFT 算法提交到流 (stream),传递输入频谱和输出缓冲区。它将在 CUDA 后端执行,因为负载 (payload) 是在那里创建的。
      vpiSubmitIFFT(stream, VPI_BACKEND_CUDA, ifft, input, output, 0);
      VPIStatus vpiSubmitIFFT(VPIStream stream, uint64_t backend, VPIPayload payload, VPIImage input, VPIImage output, uint64_t flags)
      在单个图像上运行逆快速傅里叶变换。
    2. 等待直到处理完成。
      vpiStreamSync(stream);
      VPIStatus vpiStreamSync(VPIStream stream)
      阻塞调用线程,直到此流队列中的所有提交命令都完成(队列为空)...
    3. 现在使用您喜欢的方法显示 output
  3. 清理阶段
    1. 释放流 (stream)、负载 (payload) 以及输入和输出图像所持有的资源。
      vpiImageDestroy(output);
      void vpiImageDestroy(VPIImage img)
      销毁图像实例。
      void vpiPayloadDestroy(VPIPayload payload)
      释放负载 (payload) 对象和所有相关资源。
      void vpiStreamDestroy(VPIStream stream)
      销毁流 (stream) 实例并释放所有硬件资源。

有关更多信息,请参阅 VPI - 视觉编程接口 的 “C API 参考” 部分中的 快速傅里叶变换

性能

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

 -