Свертка изображения в частотной области

Я хочу Свернуть Лену с собой в Частотной области. Вот цитата из книги. вывод свертки:

введите описание изображения здесь

Я написал следующее приложение для достижения свертки двух изображений в частотной области. Я выполнил следующие шаги:

  1. Преобразуйте Лену в матрицу комплексных чисел.
  2. Примените БПФ, чтобы получить комплексную матрицу.
  3. Умножьте две комплексные матрицы элемент на элемент (если это определение свертки).
  4. Примените IFFT к результату умножения.

Кажется, результат не соответствует ожиданиям:

введите описание изображения здесь

Здесь видны две проблемы:

  • Вывод содержит только черный фон с одной точкой в ​​центре.
  • Исходное изображение искажается после выполнения свертки.

.

Примечание. БПФ и I-БПФ отлично работают с одними и теми же библиотеками.

введите описание изображения здесь

Примечание-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;
    }

person user366312    schedule 01.08.2016    source источник
comment
Я вижу в вашем коде, что вам требуется, чтобы маска и изображение имели одинаковый размер, а это означает, что одно «дополняется» по длине другого. Но когда вы говорите о быстрой свертке с помощью БПФ, «заполнение» означает «заполнение нулями» входных изображений, так что, когда вы делаете ifft( fft(a) * fft(b) ), вы получаете линейную свертку, а не круговую свертку - в этом суть ответ, на который вы ссылаетесь.   -  person Ahmed Fasih    schedule 02.08.2016
comment
Умеет ли тип C # Complex выполнять сложное умножение, т. Е. (a + j*b) * (c + j*d) = (a*c - b*d) + j*(a*d + b*c)?   -  person Ahmed Fasih    schedule 02.08.2016
comment
Даже если вы вообще не заполняли два изображения нулями перед 2D FFT, ваш результат (черный фон, белая точка) будет очень неправильным. Если вы сворачиваете изображение с искаженной версией самого себя, вы все равно ожидаете (потенциально искаженной) экспоненциально затухающей автокорреляции, как предлагает учебник.   -  person Ahmed Fasih    schedule 02.08.2016


Ответы (1)


Есть разница в том, как вы вызываете InverseFFT между работающим приложением FFT-> IFFT и сломанным приложением Convolution. В последнем случае вы не передаете явно параметры 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);
    }
}

Это должно дать более разумный результат:

введите описание изображения здесь

Однако это все еще не так, как описано в вашей книге. На данный момент у нас есть двумерная круговая свертка. Чтобы получить двумерную линейную свертку, необходимо убедиться, что оба изображения дополнены суммой размеров:

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), то наконец получим:

введите описание изображения здесь

person SleuthEye    schedule 02.08.2016
comment
Показанный результат представляет собой круговую двумерную свертку, поскольку вы сохраняете входные размеры такими же, как у исходного изображения. Чтобы получить линейную двумерную свертку, вам нужно дополнить каждое изображение примерно до 2 * ширины на 2 * высоты. Результат будет похож на каплю, показанную в книге (хотя размер изображения вдвое больше). - person SleuthEye; 02.08.2016