1
$\begingroup$

Please see second update

I'm trying to implement gaussian blur in frequency domain using c#.

Those are steps that I'm doing:

  1. Load bitmap.
  2. Split bitmap into separate color channels.
  3. Convert every channel into complex numbers.
  4. Create gaussian kernel:
    1. Create kernel using Gauss Function enter image description here
    2. Transform kernel into complex numbers (Here's my first concern, When I create kernel that contains only values equal to 1 (that's number 1 in every cell), to do so i dont use above function. After transforming that (the kernel with values of 1 only) into complex numbers via external library, i recieve something like this: re=0.0039 im=0.0 for every cell in array, is it valid?).
  5. FFT on every color channel.
  6. FFT on kernel (Second concern - for transformed kernel of all 1, Like i mentioned ealier, i recieve array of re=0.0 im=0.0 complex numbers with 1 and only 1 entery like re=0.0039 im=0.0, is it valid?)
  7. Multiply all channel's complex numbers by filter complex number. (For tests kernel is size of input image).
  8. IFFT on every color channel.
  9. Merge channels into single image.

As a result, for all 1 kernel tests, I recieve totaly black image, so I made some mistakes on the way, but I dont know what's wrong.

I'm fairly new to image processing, since it's school project. Can anyone point the mistakes i made?

UPDATE #1

Uploading my code as @Olli Niemitalo asked. Several values are hardcoded so if something's unclear feel free to ask. The image i'm testing with is lena 512x512 with 3 color channels (RGB).

That's the best result i've received and i'm stuck on that. Below is the most of the code I use.

If you need something more, just ask.

Create gaussian kernel.

private ComplexImage makeGaussKernel(int side, double min, double max, double step, double std)
{   
    Bitmap bitmap = new Bitmap(512, 512, PixelFormat.Format8bppIndexed);

    LockBitmap lbitmap = new LockBitmap(bitmap);

    lbitmap.LockBits();

    for (int i = 0; i < 512; ++i)
    {
        for (int j = 0; j < 512; ++j)
        {
            lbitmap.SetPixel(i, j, Color.FromArgb(255,0,0,0));
        }
    }

    // I'm generating 16x16 white square below.
    for (int i = 0; i < 16; ++i)
    {
        for (int j = 0; j < 16; ++j)
        {
            lbitmap.SetPixel(i, j, Color.FromArgb(255));
        }
    }

    lbitmap.UnlockBits();

    var result = AForge.Imaging.ComplexImage.FromBitmap(bitmap);

    for (int i = 0; i < 512; ++i)
    {
        for (int j = 0; j < 512; ++j)
        {
            // Data after conversion to complex is too low, all i get without below multiplication is black image. With multiplication colors are fine.
            result.Data[i, j].Re *= 1000;
        }
    }

    return result;
}

// The gauss function
private double gauss2d(double x, double y, double std)
{
    return ((1.0 / (2 * Math.PI * std * std)) * Math.Exp(-((x * x + y * y) / (2 * std * std))));
}

The multiplication:

private void applyGauss(ComplexImage complexImage, ComplexImage filter, int side)
{
    int width = complexImage.Data.GetLength(1);
    int height = complexImage.Data.GetLength(0);

    for(int i = 0; i < height; ++i)
    {
        for(int j = 0; j < width; ++j)
        {
            complexImage.Data[i, j] = AForge.Math.Complex.Multiply(complexImage.Data[i, j], filter.Data[i, j]);
        }
    }
}

UPDATE #2

I've made a breakthrough. The weird shift was result of applying the filter in the middle of the image. When I moved the filter to the corner, only few artifacts remains (that's not big deal for now) - see image below.

The updated code from UPDATE #1 is the only problem now prolly (gaussian generation). I need few tips how to generate the kernel properly. As you can see in code above I generate white square in the corner. I belive it's far from real gaussian blur.

Also after conversion from normal bytes to complex numbers I receive very dark (in fact black) image. So I tried to multiply real part of complex numbers (you can see that in the code) and after that I received "good" image. I dont really know what's wrong.

Important thing that was discovered by @Olli Niemitalo is that colors are divied by 255 after conversion to complex (by AForge.NET) so If kernel's bitmap is made of 255s, after conversion I receive kernel made of re=1.0 im=0.0 etc.

I dont have to use AForge.NET to generate complex numbers, so if you've got tips how to generate those without AFN, i will take all of them.

Current result: The artifacts / "good" image

$\endgroup$
7
  • $\begingroup$ The Gaussian kernel created in step 4.1. and converted to complex numbers in step 4.2. should not give the same value in all cells. Re-check how you parameterize the Gaussian function. $\endgroup$ Commented Apr 10, 2015 at 16:57
  • $\begingroup$ I probably made it a bit unclear. I've got 2 test cases: First one is where I use function mentioned in 4.1. That gives various values. Second one where I fill kernel with ones only (every cell contains number 1) and recieve the mentioned values like re=0.0030 im=0.0 after conversion to complex numbers of that kernel. Gonna update it in question. If that's still unclear, please say so. Topic is kinda hard to me so I've got troubles to express what i really mean. $\endgroup$ Commented Apr 10, 2015 at 17:20
  • $\begingroup$ Then the conversion to complex numbers is at fault. You could try doing the conversion yourself: complexnumbers[x, y].re = realnumbers[x, y]; complexnumbers[x, y].im = 0; $\endgroup$ Commented Apr 10, 2015 at 17:28
  • $\begingroup$ Hey I just noticed that 1/255 = 0.0039. Did you typo that as 0.0030? Maybe you should multiply your Gaussian by 255 or 256. $\endgroup$ Commented Apr 10, 2015 at 17:38
  • $\begingroup$ Yes you're right, there should be 0.0039 instead of 0.0030. Updated the question. However after both modifications to code - complexnumbers[x, y].re = realnumbers[x, y]; complexnumbers[x, y].im = 0; and Maybe you should multiply your Gaussian by 255 or 256 (each modification tested separately) all I get now is well.. full redish image (R=181 G=98 B=106). $\endgroup$ Commented Apr 10, 2015 at 18:05

1 Answer 1

2
$\begingroup$

I figured it out.. with some cheating but it works. The main problem was kernel generation and applying FFT to it. Also important thing is that AForge.NET divides image pixels by 255 during conversion to ComplexImage and multiplies by 255 during conversion from ComplexImage to Bitmap (thanks Olli Niemitalo).

How I solved this:

  1. I've found how kernel should look like after FFT (see below).
  2. Looked up colors of that image.
  3. Calculated gauss2d for x = -2; y = -2; std = 1.
  4. Calculated the prescaler to receive color value from value calculated in pt. 3 (see wolfram).
  5. Generated kernel with scaled values with perscaler from pt. 4.

However I cant use FFT on generated filter, because generated filter looks like filter after FFT already. It works - the output image is blurred without artifacts so I think that's not too bad.

The images (I cant post more than 2 links, and images are farily big):

  • Input image: i.imgur.com/MNN2nOf.jpg
  • Generated filter (without FFT!): i.imgur.com/JV9WNis.png
    • Parameters for below function:
    • std = 1.0
    • size = 8.0
    • width = height = 512
  • Result image: i.imgur.com/fZOLdOE.jpg

The final code:

private ComplexImage makeGaussKernel(double size, double std, int imgWidth, int imgHeight)
{
    double scale = 2000.0;
    double hsize = size / 2.0;

    Bitmap bmp = new Bitmap(imgWidth, imgHeight, PixelFormat.Format8bppIndexed);
    LockBitmap lbmp = new LockBitmap(bmp);

    lbmp.LockBits();

    double y = -hsize;
    double yStep = hsize / (lbmp.Height / 2.0);
    double xStep = hsize / (lbmp.Width / 2.0);

    for (int i = 0; i < lbmp.Height; ++i)
    {
        double x = -hsize;

        for (int j = 0; j < lbmp.Width; ++j)
        {
            double g = gauss2d(x, y, std) * scale;

            g = g < 0.0 ? 0.0 : g;
            g = g > 255.0 ? 255.0 : g;

            lbmp.SetPixel(j, i, Color.FromArgb((int)g));

            x += xStep;
        }

        y += yStep;
    }

    lbmp.UnlockBits();

    return ComplexImage.FromBitmap(bmp);
}

private double gauss2d(double x, double y, double std)
{
    return (1.0 / (2 * Math.PI * std * std)) * Math.Exp(-(((x * x) + (y * y)) / (2 * std * std)));
}

private void applyGaussToImage(ComplexImage complexImage, ComplexImage filter)
{
    for (int i = 0; i < complexImage.Height; ++i)
    {
        for (int j = 0; j < complexImage.Width; ++j)
        {
            complexImage.Data[i, j] = AForge.Math.Complex.Multiply(complexImage.Data[i, j], filter.Data[i, j]);
        }
    }
}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.