FFT卷积 - 如何应用核函数

3

我对图像处理还比较陌生,但发现使用FFT卷积可以大大提高大核卷积的速度。

我的问题是,在使用kissFFT时,如何在频率空间中将卷积核应用于图像?

我已经完成了以下步骤:

//I have an image with RGB pixels and given width/height

const int dim[2] = {height, width}; // dimensions of fft
const int dimcount = 2; // number of dimensions. here 2
kiss_fftnd_cfg stf = kiss_fftnd_alloc(dim, dimcount, 0, 0, 0); // forward 2d
kiss_fftnd_cfg sti = kiss_fftnd_alloc(dim, dimcount, 1, 0, 0); // inverse 2d

kiss_fft_cpx *a = new kiss_fft_cpx[width * height];
kiss_fft_cpx *r = new kiss_fft_cpx[width * height];
kiss_fft_cpx *g = new kiss_fft_cpx[width * height];
kiss_fft_cpx *b = new kiss_fft_cpx[width * height];
kiss_fft_cpx *mask = new kiss_fft_cpx[width * height];

kiss_fft_cpx *outa = new kiss_fft_cpx[width * height];
kiss_fft_cpx *outr = new kiss_fft_cpx[width * height];
kiss_fft_cpx *outg = new kiss_fft_cpx[width * height];
kiss_fft_cpx *outb = new kiss_fft_cpx[width * height];
kiss_fft_cpx *outmask = new kiss_fft_cpx[width * height];

for(unsigned int i=0; i<height; i++) {
    for(unsigned int l=0; l<width; l++) {
        float red = intToFloat((int)Input(i,l)->Red);
        float green = intToFloat((int)Input(i,l)->Green);
        float blue = intToFloat((int)Input(i,l)->Blue);

        int index = i * height + l;

        a[index].r = 1.0;
        r[index].r = red;
        g[index].r = green;
        b[index].r = blue;
    }
}

kiss_fftnd(stf, a, outa);
kiss_fftnd(stf, r, outr);
kiss_fftnd(stf, g, outg);
kiss_fftnd(stf, b, outb);
kiss_fftnd(stf, mask, outmask);


kiss_fftnd(sti, outa, a);
kiss_fftnd(sti, outr, r);
kiss_fftnd(sti, outg, g);

当我再次设置图像的rgb值时,我确实可以得到原始图像。所以转换是有效的。 如果我想应用一个核,例如9x9的盒状模糊(1/9, 1/9, ... 1/9),我现在该怎么做?
我读了一些有关快速卷积的东西,但它们都不同,取决于FFT的实现方式。是否有一种“清单”列出在应用滤镜之前需要注意的事项?
我认为的方法是:
图像大小必须是2的幂; 我必须创建一个与图像相同大小的核。将9个中间值设置为1/9,其余值设置为0,然后将此核转换为频域,将源图像乘以它,然后将源图像转换回来。但这并不真正起作用:DD

听起来你的想法是正确的。当你在最后一句话中说“那并不真正起作用”时,你具体指的是什么? - Paul R
好的,在反向变换之后,图像看起来像垃圾。我仍然能够识别出图像,但其中有许多颜色、一个灰度反转的图像和其他一些东西,所以并不是很有用^^ - Marco
我现在已经阅读了更多关于FFT卷积的内容,有些人说我也需要填充源图像,为什么需要这样做,以及需要填充到哪个大小? - Marco
1
这个PDF文件中有一些有用的图表,可以帮助解释:http://developer.download.nvidia.com/compute/cuda/2_2/sdk/website/projects/convolutionFFT2D/doc/convolutionFFT2D.pdf - 实际实现是针对CUDA的,但介绍的细节是相当通用的。 - Paul R
1个回答

4

在频域中执行的卷积实际上是循环卷积。因此,当内核的非零元素到达图片边缘时,它会绕过并包含来自图片另一侧的像素,这可能不是您想要的。为了解决这个问题,您需要使用与内核中非零元素数量相同的元素对输入进行零填充(实际上少一点就可以)。对于一个3x3的内核,您需要在每个维度上添加 3-1=2 个零像素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接