2016-08-01 9 views
1

Я написал следующее приложение для достижения свертки двух изображений в частотной области.Свертка БПФ между двумя изображениями не работает

Я хочу сжать Лена с собой.

шаги я следовал:

(1) Преобразование Лену в матрицу комплексных чисел.

(2) Применить FFT для получения сложной матрицы.

(3) Затем я бы умножал два элемента сложных матриц на элемент (если это определение свертки).

(4) Затем я применил бы IFFT к результату умножения.

Я написал код C#. Но выход кажется не таким, как ожидалось.

enter image description here

Here is an excerpt from a book. Это говорит о том, как выход свертке должно быть.

enter image description here

Некоторые Актуальной код:

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; 
    } 
} 

Исходный код для GUI,

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; 
    } 

Вот zipped source code как решение VS2013.

Также, there is a thread in SO, который, кажется, обсуждает ту же тему.

P.S. FFT и I-FFT отлично работают с теми же библиотеками.

enter image description here

+0

Я вижу в вашем коде, что вам требуется маска и изображение, чтобы иметь такой же размер, что означает, что он «дополнен» к длине другого. Но когда вы говорите о быстрой свертке с помощью БПФ, «заполнение» означает «нулевое заполнение» входных изображений, так что когда вы выполняете 'ifft (fft (a) * fft (b))' вы получаете линейную свертку, а не круговую свертку, это суть [ответа на ссылку] (http://stackoverflow.com/a/12254741/500207). –

+0

Знает ли C# 'Complex' тип сложного умножения, т. Е.' (A + j * b) * (c + j * d) = (a * c - b * d) + j * (a * d + б * с) '? –

+0

Даже если вы не заполняли нулями два изображения до 2D FFT, ваш результат (черный фон, белая точка) очень неправильный. Если вы свертываете изображение с искаженной версией себя, вы все равно ожидаете (потенциально искаженной) экспоненциально-распадающейся автокорреляции, как предлагает учебник. –

ответ

2

Существует разница в том, как вы называете InverseFFT между рабочей FFT-> приложениями IFFT, а прерывистая приложение свертки. В последнем случае вы не передаете в явном виде 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 и код пропускает большинство обратное двумерное преобразование. Инициализация этих параметров должна дать вам что-то, что по крайней мере не черное.

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 линейной свертки, вам необходимо убедиться, что изображения являются проложенными в сумму размерностей:

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

+0

Спасибо. Но это еще не ожидаемый результат. – anonymous

+1

Результат показан круговой двумерной сверткой, так как вы сохраняете размеры входных данных по сравнению с исходным изображением. Чтобы получить линейную двумерную свертку, вам нужно проложить каждое изображение примерно до 2 * Ширина на 2 * Высота. Результат тогда выглядит как blob, показанный в книге (хотя в два раза больше исходного размера). – SleuthEye

+0

Большое спасибо. Большое спасибо. Это действительно помогло. Не могли бы вы также ответить на этот http://stackoverflow.com/questions/38690315/two-implementations-of-fast-fourier-transform? Вы, кажется, очень хорошо разбираетесь в прикладном аспекте FFT и Concolution. Кроме того, вы очень хорошо разбираетесь в программировании, которого я не нахожу в DSP.SE. – anonymous