如何使用 iOS Accelerate 框架正确填充 FFT 的二维数组

Posted

技术标签:

【中文标题】如何使用 iOS Accelerate 框架正确填充 FFT 的二维数组【英文标题】:how to correctly pad 2D array for FFT with iOS Accelerate framework 【发布时间】:2015-04-02 15:07:24 【问题描述】:

我正在使用 ios Accelerate 框架来查找二维数组的 FFT。下面的代码适用于 2 张图像的幂。我们必须用zeros 填充输入数组,以获得非 2 个图像的幂。但我无法正确进行填充。目前我填充数组如下

float inputImg2D[3][3] =  1,1,1, 1,1,1, 1,1,1 ; 
float paddedImg2D[4][4] =  1,1,1,0, 1,1,1,0, 1,1,1,0, 0,0,0,0 ;
float expectedOutput[6]6] =  9,0,0,0,0,0
                              0,0,0,0,0,0
                              0,0,0,0,0,0
                              0,0,0,0,0,0
                              0,0,0,0,0,0
                              0,0,0,0,0,0 ;

对于 4*4 数组,我正确地得到输出为 8*8 数组,在 (0,0) 处值为 16。

加速 FFT 代码。

/* 
 * 2D fft sample working only for power of 2 images.
 * expected output for below 3*3 array is a 6*6 array with value 9 at ( 0,0) - all other values will be zero
 * expected output for below 4*4 array is a 8*8 array with value 16 at (0,0) - all other values will be zero
 */

#include <stdio.h>
#include "Accelerate/Accelerate.h"

#define NON_POWER_OF_2_TEST_WILL_FAIL

int main(int argc, const char * argv[]) 

#ifdef NON_POWER_OF_2_TEST_WILL_FAIL
    const int IMG_ROWS = 3;
    const int IMG_COLS = 3;
    float img2D[3][3] =  1,1,1, 1,1,1, 1,1,1 ;
#else
    const int IMG_ROWS = 4;
    const int IMG_COLS = 4;
    float img2D[4][4] =  1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ;
#endif

    /*  build necessary values for fft setup */
    int maxDimension = ( IMG_ROWS > IMG_COLS ) ? IMG_ROWS : IMG_COLS;
    int optimalDftSize = ceil( log2( maxDimension) );

    /* fft setup */
    FFTSetup fftSetup = vDSP_create_fftsetup( optimalDftSize, FFT_RADIX2 );

    /* expand images to power of two size with zero values*/
    COMPLEX_SPLIT in_fft;
    int optimalDftWidth = 1 << optimalDftSize;
    int optimalDftHeight = 1 << optimalDftSize;
    int numElements = optimalDftWidth * optimalDftHeight;
    in_fft.realp = ( float* ) calloc ( numElements, sizeof(float) );
    in_fft.imagp = ( float* ) calloc ( numElements, sizeof(float) );

    /* assign image pixels if only in range */
    for ( int i = 0; i < optimalDftWidth; i++ ) 
        for ( int j = 0; j < optimalDftHeight; j++ ) 
            if (i < IMG_ROWS && j < IMG_COLS) 
                in_fft.realp[i * optimalDftHeight + j] = img2D[i][j];
                //in_fft.imagp[i] = 0.0;
            
        
    

    /* do fft in place */
    int rowStride = 1;
    int columnStride = 0;
    vDSP_fft2d_zip(fftSetup, &in_fft, rowStride, columnStride, optimalDftSize, optimalDftSize, FFT_FORWARD);

    /* print results */
    for(int i=0; i < optimalDftWidth; ++i) 
        for(int j=0; j < optimalDftHeight; ++j) 
            printf (" %.2f, %.2f, ", in_fft.realp[i*optimalDftHeight+j], in_fft.imagp[i*optimalDftHeight+j] );
        
        printf("\n");
    

    /* TODO: free resources */
    return 0;

【问题讨论】:

我认为您误解了零填充的工作原理 - 零不会被忽略,它们有效地在频域中产生插值。所以你的代码(可能)工作正常——你只是误解了结果。 @PaulR 谢谢。但是,那么如何获得expectedOutput(如问题)。对于全为 1 的 3*3 输入数组,我期望结果数组中除 (0,0) 处的值 9 外全为零的结果。我的期望错了吗?我假设函数 vDSP_fft2d_zip 需要填充零。 只有使用实际的 3x3 FFT 才能获得预期的输出。 vDSP_fft2d_zip 不知道您正在给它填充数据 - 它只是将数据盲目地处理为 4x4 FFT,因此您得到一个插值的 4x4 输出。您需要使用支持非 2 次幂大小的 FFT,例如 3x3(例如 FFTW),或者重新考虑您尝试通过此操作实现的目标。 @PaulR 谢谢。我假设,这意味着,我们不能用 Accelerate 框架做一个精确的n*n dft,其中n 不能表示为 2,3 或 5 的幂。关于重新思考的第二部分也有帮助,因为我打算做相关性使用 Accelerate,它并不真正需要精确的 n*m dft。我将遵循convolveDFT 算法,如docs.opencv.org/modules/core/doc/operations_on_arrays.html#dft。此评论有所帮助,并让我知道是否可以将其写为被接受的答案。否则我也可以这样做。 很高兴能提供帮助 - 请随时写下您的结论,作为未来访客的答案,我会支持它。 【参考方案1】:

您可能正确地进行了零填充。任何期望零填充后结果相同的期望都是不正确的。所有 FFT bin 频率都将与更大的数组相关。

【讨论】:

谢谢。是的。我的期望是错误的。在发布之前,我想知道 ios Accelerate 将如何区分零填充的 4*4 矩阵和相同数据的真实 4*4 矩阵。认为可能有一些函数参数(可能的步幅)传入vDSP_fft2d_zip 以表达 3*3 dft 的意图。似乎没有。【参考方案2】:

添加我的 假设 由 Paul R 对问题的评论回复组成(可能是错误的,因为我对 FFT 使用/目的的理解几乎为零)

    使用 iOS Accelerate 框架,您无法精确计算大小不是 2 次方的二维数组/矩阵的 FFT。您可以将数组补零到 2 次方,但这会影响结果。 大多数情况下,您可以在没有 2 的幂次方的情况下进行。例如,我试图计算两个 2D 矩阵之间的卷积,并且没有必要获得精确的 m * n FFT。示例在这里(1)。 与问题无关:如果您尝试用 Accelerate vDSP_fft2d_zripD 替换 OpenCV 的 dft 方法,希望获得更好的性能,我觉得没有或只有更糟。即使接受缓冲区的重用对 Accelerate 的性能至关重要,也令人失望。可能是我的错误代码。

【讨论】:

以上是关于如何使用 iOS Accelerate 框架正确填充 FFT 的二维数组的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 vDSP / Accelerate in swift for iOS 计算向量元素的平方根

使用 Accelerate 框架对向量进行编码

使用 Accelerate 框架的对称带矩阵的特征值

iOS Accelerate Framework vImage - 性能改进?

iOS Accelerate框架中vDSP_ctoz的数据应该是啥格式

使用 Apple Accelerate 框架选择实数和复数 2D FFT