频域图像卷积
Image convolution in frequency domain
我想在频域中将 Lena 与其自身进行卷积。 Here is an excerpt from a book. 这表明卷积的输出应该如何:
我写了下面的应用来实现两幅图像在频域的卷积。我遵循的步骤如下:
- Convert Lena into a matrix of complex numbers.
- Apply FFT to obtain a complex matrix.
- Multiply two complex matrices element by element (if that is the definition of Convolution).
- Apply IFFT to the result of the multiplication.
输出似乎没有按预期出现:
这里有两个问题:
- 输出仅包含一个黑色背景,其中心只有一个点。
- 原始图像在执行卷积后发生变形。
。
注意。 FFT 和 I-FFT 与相同的库完美配合。
Note-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;
}
在工作的 FFT->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);
}
然后您应该注意到一些尖锐的 white/black 边缘。这些是由输出值中的环绕引起的。为避免这种情况,您可能需要在逆变换后重新调整输出以适应可用比例,例如:
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 线性卷积,您需要确保 图像都被填充到尺寸之和 :
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
转变。如果我们调整这些(在 ForwardFFT
中删除 FFTShift
,或者在 InverseFFT
中添加 RemoveFFTShift
),那么我们最终得到:
我想在频域中将 Lena 与其自身进行卷积。 Here is an excerpt from a book. 这表明卷积的输出应该如何:
我写了下面的应用来实现两幅图像在频域的卷积。我遵循的步骤如下:
- Convert Lena into a matrix of complex numbers.
- Apply FFT to obtain a complex matrix.
- Multiply two complex matrices element by element (if that is the definition of Convolution).
- Apply IFFT to the result of the multiplication.
输出似乎没有按预期出现:
这里有两个问题:
- 输出仅包含一个黑色背景,其中心只有一个点。
- 原始图像在执行卷积后发生变形。
。
注意。 FFT 和 I-FFT 与相同的库完美配合。
Note-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;
}
在工作的 FFT->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);
}
然后您应该注意到一些尖锐的 white/black 边缘。这些是由输出值中的环绕引起的。为避免这种情况,您可能需要在逆变换后重新调整输出以适应可用比例,例如:
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 线性卷积,您需要确保 图像都被填充到尺寸之和 :
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
转变。如果我们调整这些(在 ForwardFFT
中删除 FFTShift
,或者在 InverseFFT
中添加 RemoveFFTShift
),那么我们最终得到: