【问题标题】:Rescaling Complex data after FFT Convolution在 FFT 卷积之后重新缩放复杂数据
【发布时间】:2018-07-24 18:55:56
【问题描述】:

我已经通过在 FFT 卷积输出上应用了两个重新缩放函数来测试它们。

第一个收集自this link

    public static void RescaleComplex(Complex[,] convolve)
    {
        int imageWidth = convolve.GetLength(0);
        int imageHeight = convolve.GetLength(1);

        double maxAmp = 0.0;
        for (int i = 0; i < imageWidth; i++)
        {
            for (int j = 0; j < imageHeight; j++)
            {
                maxAmp = Math.Max(maxAmp, convolve[i, j].Magnitude);
            }
        }
        double scale = 1.0 / maxAmp;
        for (int i = 0; i < imageWidth; i++)
        {
            for (int j = 0; j < imageHeight; j++)
            {
                convolve[i, j] = new Complex(convolve[i, j].Real * scale,
                    convolve[i, j].Imaginary * scale);
            }
        }
    }

这里的问题是对比度不正确。

第二个收集自this link

    public static void RescaleComplex(Complex[,] convolve)
    {            
        int imageWidth = convolve.GetLength(0);
        int imageHeight = convolve.GetLength(1);

        double scale = imageWidth * imageHeight;

        for (int j = 0; j < imageHeight; j++)
        {
            for (int i = 0; i < imageWidth; i++)
            {
                double re = Math.Max(0.0, Math.Min(convolve[i, j].Real * scale, 1.0));
                double im = Math.Max(0.0, Math.Min(convolve[i, j].Imaginary * scale, 1.0));
                convolve[i, j] = new Complex(re, im);
            }
        }
    }

这里的输出是全白的。

因此,您可以看到其中两个版本给出了一个正确和另一个错误的输出。

我该如何解决这个难题?

注意。 矩阵是以下内核:

 0  -1   0 
-1   5  -1 
 0  -1   0

源代码。 这是我的 FFT 卷积函数。

    private static Complex[,] ConvolutionFft(Complex[,] image, Complex[,] kernel)
    {
        Complex[,] imageCopy = (Complex[,])image.Clone();
        Complex[,] kernelCopy = (Complex[,])kernel.Clone();
        Complex[,] convolve = null;

        int imageWidth = imageCopy.GetLength(0);
        int imageHeight = imageCopy.GetLength(1);

        int kernelWidth = kernelCopy.GetLength(0);
        int kernelHeight = kernelCopy.GetLength(1);

        if (imageWidth == kernelWidth && imageHeight == kernelHeight)
        {
            Complex[,] fftConvolved = new Complex[imageWidth, imageHeight];

            Complex[,] fftImage = FourierTransform.ForwardFFT(imageCopy);
            Complex[,] fftKernel = FourierTransform.ForwardFFT(kernelCopy);                

            for (int j = 0; j < imageHeight; j++)
            {
                for (int i = 0; i < imageWidth; i++)
                {
                    fftConvolved[i, j] = fftImage[i, j] * fftKernel[i, j];
                }
            }

            convolve = FourierTransform.InverseFFT(fftConvolved);

            RescaleComplex(convolve);

            convolve = FourierShifter.ShiftFft(convolve);
        }
        else
        {
            throw new Exception("Padded image and kernel dimensions must be same.");
        }

        return convolve;
    }

【问题讨论】:

    标签: c# image-processing convolution


    【解决方案1】:

    这并不是一个真正的困境。这只是显示范围有限的问题,以及您的期望,这两种情况是不同的。

    1. (顶部):这是一个标准化内核(其元素总和为 1)。它不会改变图像的对比度。但是因为里面有负值,所以会产生超出原始范围的值。

    2. (底部):这不是规范化内核。它会改变输出的对比度。

    例如,玩弄内核

     0, -1,  0
    -1,  6, -1
     0, -1,  0
    

    (注意中间的 6)。总和为2。图像对比度将增加一倍。即在输入全为0的区域,输出也为0,但输入全为1的区域,输出为2。

    通常,如果卷积过滤器不是为了改变图像对比度,它会被归一化。如果应用这样的过滤器,则无需重新缩放输出以进行显示(尽管如果出现超出范围的值,您可能需要对其进行剪裁)。但是,超出范围的值可能是相关的,在这种情况下,您需要重新缩放输出以匹配显示范围。

    在您的情况 2(图像内核)中,您可以规范化内核以避免重新缩放输出。但这不是一般的解决方案。一些过滤器加起来等于 0(例如 Sobel 核或 Laplace 核,它们都基于去除 DC 分量的导数)。这些无法标准化,您将始终必须重新缩放输出图像以进行显示(尽管您不会重新缩放它们的输出以进行分析,因为它们的输出值具有在重新缩放时被破坏的物理意义)。

    也就是说,卷积有时旨在产生与输入图像具有相同对比度(在大致相同范围内)的输出图像,有时则不然。您需要知道要应用什么过滤器才能使输出有意义,并且能够在预期图像在特定范围内的屏幕上显示输出。


    编辑:解释你的数字中发生了什么。

    第一个数字:在这里您正在重新缩放,以便整个图像强度范围可见。从逻辑上讲,您不会得到任何饱和像素。但是因为矩阵核增强了高频,所以输出图像的值超出了原始范围。重新缩放以适应显示器范围内的整个范围会降低图像的对比度。

    第二张图:您正在将频域卷积结果重新缩放N = imageWidth * imageHeight。这会产生正确的输出。您需要应用此缩放表示您的正向 FFT 缩放 1/N,而您的反向 FFT 不缩放。

    对于IFFT(FFT(img))==img,FFT 或 IFFT 必须按1/N 缩放。通常是 IFFT 被缩放。原因是卷积按预期进行,没有任何进一步的缩放。为了看到这一点,想象一个所有像素都具有相同值的图像。 FFT(img) 除 0 频率分量(直流分量)为 sum(img) 外,处处为零。归一化内核总和为 1,因此其 DC 分量为sum(kernel)==1。将这两者相乘,我们再次获得与输入类似的频谱,其直流分量为sum(img)。它的逆变换将等于img。这正是我们对这个卷积的期望。

    现在,使用另一种形式的规范化(即您有权访问的 FFT 使用的规范化形式)。 FFT(img) 的直流分量将是 sum(img)/N。内核的 D​​C 组件将是 1/N。将这两者相乘,得到sum(img)/(N*N) 的直流分量。它的逆变换将等于img/N。因此,您需要乘以N 才能获得预期结果。这正是您在“矩阵内核”的频域卷积中看到的,它是标准化的。

    正如我上面提到的,“图像内核”没有标准化。 FFT(kernel)的直流分量是sum(img)/N,乘以FFT(img)有一个直流分量sum(img)*sum(img)/(N*N),所以逆变换有一个对比度乘以sum(img)/N,乘以N仍然是你系数sum(img) 太大。如果要对内核进行规范化,则将其除以sum(img),这将使您的输出达到预期范围。

    【讨论】:

    • 虽然,我接受了答案:空间输出正确地使用相同的锐化内核。那么,在 FFT 卷积的情况下它不会工作吗?
    • @anonymous:我不确定你的意思。原则上,空间和频域卷积应该产生相同的结果(除了填充相关的问题)。如果这些结果对比不同,则其中一种方法存在归一化问题。例如,前向 FFT 不应该进行归一化,但反向 FFT 应该通过 N 进行归一化。在这种情况下,卷积产生正确的对比度,并且不需要进一步重新缩放以将其与空间域的输出进行比较卷积。
    • I'm not sure what you mean. --- 我的意思是:我已经使用相同的图像和内核在空间域和 FFT 域中测试了卷积。空间域中的输出具有正确的对比度。但是,FFT 域中的输出具有不正确的对比度。我怀疑,我的Rescale() 有问题。可能是我错了。
    • @anonymous:我已经添加了关于您的缩放发生了什么的解释。我不确定它是否足够清楚,这里已经很晚了,我很难表达自己。我觉得我需要包含关于傅立叶变换的完整讲座才能正确解释这一点。请让我知道您对此了解多少。 :)
    猜你喜欢
    • 2012-12-10
    • 2016-12-31
    • 1970-01-01
    • 1970-01-01
    • 2012-03-06
    • 1970-01-01
    • 2020-07-12
    • 2013-04-11
    • 2013-04-16
    相关资源
    最近更新 更多