【问题标题】:Image convolution in frequency domain频域图像卷积
【发布时间】:2016-08-01 23:59:47
【问题描述】:

我想在频域中将 Lena 与自身进行卷积。 Here is an excerpt from a book. 建议卷积的输出应该如何:

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

  1. 将 Lena 转换为复数矩阵。
  2. 应用 FFT 获得复矩阵。
  3. 将两个复数矩阵逐个元素相乘(如果这是卷积的定义)。
  4. 将 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;
    }

【问题讨论】:

  • 我在您的代码中看到您需要掩码和图像具有相同的大小,这意味着一个被“填充”到另一个的长度。但是当您谈论通过 FFT 进行快速卷积时,“填充”意味着对输入图像进行“零填充”,这样当您执行 ifft( fft(a) * fft(b) ) 时,您会得到线性卷积而不是循环卷积——这就是 answer you link to 的要点。跨度>
  • C# Complex 类型是否知道如何进行复数乘法,即(a + j*b) * (c + j*d) = (a*c - b*d) + j*(a*d + b*c)
  • 即使您在 2D FFT 之前根本没有对两个图像进行零填充,您的结果(黑色背景,白色圆点)也非常错误。如果您将图像与其自身的扭曲版本进行卷积,您仍然会期望(可能扭曲的)指数衰减自相关,就像教科书所暗示的那样。

标签: c# image-processing fft convolution ifft


【解决方案1】:

在工作的 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 线性卷积,您需要确保图像都被填充到维度总和

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),那么我们最终得到:

【讨论】:

  • 显示的结果是圆形 2D 卷积的结果,因为您将输入大小保持为原始图像的大小。要获得线性 2D 卷积,您需要将每个图像填充到大约 2*Width x 2*Height。结果看起来就像书中显示的 blob(尽管是原始图像大小的两倍)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-17
  • 2019-07-19
  • 1970-01-01
  • 2016-10-13
  • 1970-01-01
相关资源
最近更新 更多