我想在频域中自己卷入Lena。 Here is an excerpt from a book.表明卷积的输出应该如何:
我编写了以下应用程序来实现频域中两个图像的卷积。我遵循的步骤如下:
- 将Lena转换为复数矩阵。
- 应用FFT以获得复杂矩阵。
- 逐个元素地乘以两个复杂矩阵(如果这是卷积的定义)。
- 将IFFT应用于乘法结果。
醇>
输出似乎未达到预期效果:
这里可以看到两个问题:
。
注意。 FFT和I-FFT与相同的库完美配合。
注意-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;
}
答案 0 :(得分:4)
在工作的FFT-&gt; IFFT应用程序和损坏的卷积应用程序之间调用InverseFFT
的方式有所不同。在后一种情况下,您没有明确传递Width
和Height
参数(您应该从输入图像中获取):
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);
}
结果Width
和Height
都为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
中移除ForwardFFT
,或在RemoveFFTShift
中添加InverseFFT
),那么我们最终会得到: