频域图像卷积

问题描述 投票:1回答:1

我想在频域中将Lena自己​​卷入其中。 Here is an excerpt from a book.,它建议卷积的输出应该如何:

enter image description here

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

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

输出似乎未达到预期:

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;
    }
c# image-processing fft convolution ifft
1个回答
4
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.