频域图像卷积

时间:2016-08-01 23:59:48

标签: c# image-processing fft convolution ifft

我想在频域中自己卷入Lena。 Here is an excerpt from a book.表明卷积的输出应该如何:

enter image description here

我编写了以下应用程序来实现频域中两个图像的卷积。我遵循的步骤如下:

  
      
  1. 将Lena转换为复数矩阵。
  2.   
  3. 应用FFT以获得复杂矩阵。
  4.   
  5. 逐个元素地乘以两个复杂矩阵(如果这是卷积的定义)。
  6.   
  7. 将IFFT应用于乘法结果。
  8.   

输出似乎未达到预期效果:

enter image description here

这里可以看到两个问题:

  • 输出仅包含黑色背景,其中心只有一个点。
  • 执行卷积后原始图像失真。

注意。 FFT和I-FFT与相同的库完美配合。

enter image description here

注意-2。 There is a thread in SO似乎在讨论同一主题。

源代码:

public static class Convolution
{
    public static Complex[,] Convolve(Complex[,]image, Complex[,]mask)
    {
        Complex[,] convolve = null;

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

        int maskWidth = mask.GetLength(0);
        int maskeHeight = mask.GetLength(1);

        if (imageWidth == maskWidth && imageHeight == maskeHeight)
        {
            FourierTransform ftForImage = new FourierTransform(image); ftForImage.ForwardFFT();
            FourierTransform ftForMask = new FourierTransform(mask); ftForMask.ForwardFFT();

            Complex[,] fftImage = ftForImage.FourierTransformedImageComplex;                
            Complex[,] fftKernel = ftForMask.FourierTransformedImageComplex;

            Complex[,] fftConvolved = new Complex[imageWidth, imageHeight];

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

            FourierTransform ftForConv = new FourierTransform();
            ftForConv.InverseFFT(fftConvolved);
            convolve = ftForConv.GrayscaleImageComplex;

            //convolve = fftConvolved;
        }
        else
        {
            throw new Exception("padding needed");
        }

        return convolve;
    }
}

    private void convolveButton_Click(object sender, EventArgs e)
    {
        Bitmap lena = inputImagePictureBox.Image as Bitmap;
        Bitmap paddedMask = paddedMaskPictureBox.Image as Bitmap;

        Complex[,] cLena = ImageDataConverter.ToComplex(lena);
        Complex[,] cPaddedMask = ImageDataConverter.ToComplex(paddedMask);

        Complex[,] cConvolved = Convolution.Convolve(cLena, cPaddedMask);

        Bitmap convolved = ImageDataConverter.ToBitmap(cConvolved);

        convolvedImagePictureBox.Image = convolved;
    }

1 个答案:

答案 0 :(得分:4)

在工作的FFT-&gt; IFFT应用程序和损坏的卷积应用程序之间调用InverseFFT的方式有所不同。在后一种情况下,您没有明确传递WidthHeight参数(您应该从输入图像中获取):

public void InverseFFT(Complex[,] fftImage)
{
    if (FourierTransformedImageComplex == null)
    {
       FourierTransformedImageComplex = fftImage;
    }

    GrayscaleImageComplex = FourierFunction.FFT2D(FourierTransformedImageComplex, Width, Height, -1);

    GrayscaleImageInteger = ImageDataConverter.ToInteger(GrayscaleImageComplex);
    InputImageBitmap = ImageDataConverter.ToBitmap(GrayscaleImageInteger);
}

结果WidthHeight都为0,代码跳过大部分逆2D变换。初始化这些参数应该给你一些至少不是全黑的东西。

    if (FourierTransformedImageComplex == null)
    {
        FourierTransformedImageComplex = fftImage;
        Width = fftImage.GetLength(0);
        Height = fftImage.GetLength(1);
    }

enter image description here

然后你应该注意到一些尖锐的白色/黑色边缘。这些是由输出值中的包装引起的。为了避免这种情况,您可能希望在逆变换之后重新调整输出以适应可用的比例,例如:

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 = 255.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);
        maxAmp = Math.Max(maxAmp, convolve[i, j].Magnitude);
    }
}

然后应该给出更合理的输出:

enter image description here

然而,这仍然没有在你的书中描述。此时我们有一个2D循环卷积。要获得2D线性卷积,您需要确保图像都填充到维度的总和

Bitmap lena = inputImagePictureBox.Image as Bitmap;
Bitmap mask = paddedMaskPictureBox.Image as Bitmap;

Bitmap paddedLena = ImagePadder.Pad(lena, lena.Width+ mask.Width, lena.Height+ mask.Height);
Bitmap paddedMask = ImagePadder.Pad(mask, lena.Width+ mask.Width, lena.Height+ mask.Height);

Complex[,] cLena = ImageDataConverter.ToComplex(paddedLena);
Complex[,] cPaddedMask = ImageDataConverter.ToComplex(paddedMask);

Complex[,] cConvolved = Convolution.Convolve(cLena, cPaddedMask);

当您调整填充时,您可能希望将填充颜色更改为黑色,否则填充本身会在两个图像之间引入较大的相关性:

public class ImagePadder
{
    public static Bitmap Pad(Bitmap maskImage, int newWidth, int newHeight)
    {
        ...
        Grayscale.Fill(resizedImage, Color.Black);

现在你应该得到以下内容:

enter image description here

我们越来越近,但自相关结果的峰值不在中心,这是因为你在正向变换中FourierShifter.FFTShift但在逆变换中没有调用相应的FourierShifter.RemoveFFTShift。如果我们对其进行调整(在FFTShift中移除ForwardFFT,或在RemoveFFTShift中添加InverseFFT),那么我们最终会得到:

enter image description here