频域图像卷积

我想在频域中将Lena卷入其中。 这是一本书的摘录。 这表明卷积的输出应该如何:

在此处输入图像描述

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

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

输出似乎未达到预期:

在此处输入图像描述

这里有两个问题:

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

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

在此处输入图像描述

笔记2。 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; } 

在工作的FFT-> 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); } 

在此处输入图像描述

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

 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); } } 

这应该给出更合理的输出:

在此处输入图像描述

但是,这仍然没有在您的书中描述。 此时我们有一个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); 

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

在此处输入图像描述

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

在此处输入图像描述