向前FFT FFT图像并向后FFT图像以获得相同的结果

时间:2011-10-17 07:59:56

标签: c++ c image-processing fft fftw

我正在尝试使用http://www.fftw.org/中的库来FFT图像,以便我可以在频域中进行卷积。但我无法弄清楚如何让它发挥作用。 为了理解如何做到这一点,我试图将FFT图像转发为像素颜色数组,然后反向FFT,得到相同的像素颜色数组。这是我的所作所为:

fftw_plan planR, planG, planB;
fftw_complex *inR, *inG, *inB, *outR, *outG, *outB, *resultR, *resultG, *resultB;

//Allocate arrays.
inR = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);
inG = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);
inB = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);

outR = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);
outG = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);
outB = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);

resultR = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);
resultG = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);
resultB = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * width);

//Fill in arrays with the pixelcolors.
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        int currentIndex = ((y * width) + (x)) * 3;
        inR[y * width + x][0] = pixelColors[currentIndex];
        inG[y * width + x][0] = pixelColors[currentIndex + 1];
        inB[y * width + x][0] = pixelColors[currentIndex + 2];
    }
}

//Forward plans.
planR = fftw_plan_dft_2d(width, width, inR, outR, FFTW_FORWARD, FFTW_MEASURE);
planG = fftw_plan_dft_2d(width, width, inG, outG, FFTW_FORWARD, FFTW_MEASURE);
planB = fftw_plan_dft_2d(width, width, inB, outB, FFTW_FORWARD, FFTW_MEASURE);

//Forward FFT.
fftw_execute(planR);
fftw_execute(planG);
fftw_execute(planB);

//Backward plans.
planR = fftw_plan_dft_2d(width, width, outR, resultR, FFTW_BACKWARD, FFTW_MEASURE);
planG = fftw_plan_dft_2d(width, width, outG, resultG, FFTW_BACKWARD, FFTW_MEASURE);
planB = fftw_plan_dft_2d(width, width, outB, resultB, FFTW_BACKWARD, FFTW_MEASURE);

//Backward fft
fftw_execute(planR);
fftw_execute(planG);
fftw_execute(planB);

//Overwrite the pixelcolors with the result.
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        int currentIndex = ((y * width) + (x)) * 3;
        pixelColors[currentIndex] = resultR[y * width + x][0];
        pixelColors[currentIndex + 1] = resultG[y * width + x][0];
        pixelColors[currentIndex + 2] = resultB[y * width + x][0];
    }
}

有人可以告诉我一个如何转发FFT图像,然后使用FFTW反向FFT图像得到相同结果的例子吗?我一直在看很多展示如何使用FFTW进行FFT的例子,但我无法弄清楚它是如何应用于我有一个像素颜色数组表示图像的情况。

3 个答案:

答案 0 :(得分:15)

当您转发FFT然后进行逆FFT时,需要注意的一点是,这通常会导致将N的比例因子应用于最终结果,即生成的图像像素值需要按顺序除以N匹配原始像素值。 (N是FFT的大小。)所以你的输出循环应该看起来像这样:

//Overwrite the pixelcolors with the result.
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        int currentIndex = ((y * width) + (x)) * 3;
        pixelColors[currentIndex] = resultR[y * width + x][0] / (width * height);
        pixelColors[currentIndex + 1] = resultG[y * width + x][0] / (width * height);
        pixelColors[currentIndex + 2] = resultB[y * width + x][0] / (width * height);
    }
}

另请注意,您可能希望进行从实际到复杂的FFT,然后是复杂到实际的IFFT(在内存和性能方面更高效)。现在虽然看起来你在两个方向都做复杂到复杂,这很好,但你没有正确填充你的输入数组。如果您要坚持复杂到复杂,那么您可能希望将输入循环更改为以下内容:

//Fill in arrays with the pixelcolors.
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        int currentIndex = ((y * width) + (x)) * 3;
        inR[y * width + x][0] = (double)pixelColors[currentIndex];
        inR[y * width + x][1] = 0.0;
        inG[y * width + x][0] = (double)pixelColors[currentIndex + 1];
        inG[y * width + x][1] = 0.0;
        inB[y * width + x][0] = (double)pixelColors[currentIndex + 2];
        inB[y * width + x][1] = 0.0;
    }
}

即。像素值进入复数输入值的实部,虚部需要归零。

还有一点需要注意:当你最终得到这个工作时,你会发现性能很糟糕 - 创建一个相对于实际FFT所用时间的计划需要很长时间。这个想法是你只创建一次计划,但用它来执行许多FFT。因此,您需要将计划创建与实际的FFT代码分开,并将其放入初始化例程或构造函数或其他任何内容中。

答案 1 :(得分:2)

但是如果你使用realToComplex或ComplexToRealFunction,请注意图像将存储在维度[height x(width / 2 +1)]的矩阵中,如果你想在中进行一些中间计算频域,它们会变得有点难......

答案 2 :(得分:2)

之所以不起作用,是因为fftw_plan_dft_2d()做一些基准测试以找到最佳算法并在此过程中更改输入数据,因此您必须在fftw_plan_dft_2d()之后而不是之前填充输入数据。