代码之家  ›  专栏  ›  技术社区  ›  user366312

FFT卷积后的复数据重标

  •  6
  • user366312  · 技术社区  · 6 年前

    我已经测试了两个重标度函数,将它们应用于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);
                }
            }
        }
    

    enter image description here

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

    第二个是从 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);
                }
            }
        }
    

    enter image description here

    这里的输出是完全白色的。

    所以,你可以看到两个版本给出了一个正确的和另一个错误的输出。

    我怎样才能解决这个难题?

    .

    注意。 矩阵是以下核心:

     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;
        }
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Cris Luengo    6 年前

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

    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/号 . 通常是IFFT被缩放。原因是,卷积按预期进行,没有进一步的缩放。要看到这一点,请想象一个所有像素都具有相同值的图像。 FFT(img) 除0频率分量(直流分量)外,其他地方均为零 sum(img) . 标准化的核总计为1,因此其DC分量为 sum(kernel)==1 . 把这两个相乘,我们得到一个和输入一样的频谱,其直流分量为 总和(img) . 它的逆变换等于 img . 这正是我们对这个卷积的期望。

    现在,使用另一种形式的标准化(即您可以访问的FFT所使用的标准化)。的直流分量 快速傅里叶变换(img) sum(img)/N . 内核的DC组件将是 1/号 . 把这两个相乘,得到 sum(img)/(N*N) . 它的逆变换等于 img/N . 因此,你需要乘以 N 以获得预期的结果。这正是你在频域卷积中看到的“矩阵核”,它是标准化的。

    如前所述,“图像内核”不是标准化的。的直流分量 FFT(kernel) 总和(img)/N ,乘以 快速傅里叶变换(img) 有直流分量 sum(img)*sum(img)/(N*N) ,所以反变换的对比度乘以 总和(img)/N ,乘以 N个 仍然给你留下了一个因素 总和(img) 太大了。如果您要规范化内核,您可以将其除以 总和(img) ,这将使您的输出达到预期范围。