0
votes

I have been trying to implement a simple Gaussian blur algorithm, for my image editing program. However, I have been having some trouble making this work, and I think the problem lies in the below snippet:

        for( int j = 0; j < pow( kernel_size, 2 ); j++ )
        {
            int idx = ( i + kx + ( ky * img.width ));

            //Try and overload this whenever possible
            valueR += ( img.p_pixelArray[ idx ].r * kernel[ j ] );
            valueG += ( img.p_pixelArray[ idx ].g * kernel[ j ] );
            valueB += ( img.p_pixelArray[ idx ].b * kernel[ j ] );

            if( kx == kernel_limit )
            {
                kx = -kernel_limit;
                ky++;
            }

            else    
            {
                kx++;
            }
       }

       kx = -kernel_limit;
       ky = -kernel_limit;

A brief explanation of the code above: kernel size is the size of the kernel (or matrix) generated by the Gaussian blur formula. kx and ky are variables to be used for iterating over the kernel. i is the parent loop, that nests this one, and goes over every pixel in the image. Each value variable simply holds a float R, G, or B value, and is used afterwards to obtain the final result. The if-else is used to increase kx and ky. idx is used to find the correct pixel. kernel limit is a variable set to

(*kernel size* - 1) / 2

So I can have kx going from -1 ( with a 3x3 kernel ) to +1, and the same thing with ky. I think the problem lies with the line

int idx = ( i + kx + ( ky * img.width ));

But I am not sure. The image I get is:

Lenna

As can be seen, the color is blurred in a diagonal direction, and looks more like some kind of motion blur than Gaussian blur. If someone could help out, I would be very grateful.

EDIT: The way I fill the kernel is as follows:

for( int i = 0; i < pow( kernel_size, 2 ); i++ )
{
    // This. Is. Lisp.
    kernel[i] = (( 1 / ( 2 * pi * pow( sigma, 2 ))) * pow (e, ( -((( pow( kx, 2 ) + pow( ky, 2 )) / 2 * pow( sigma, 2 ))))));

    if(( kx + 1 ) == kernel_size )
    {
        kx = 0;
        ky++;
    }

    else
    {
        kx++;
    }
}
1
The last two statements should be outside of your loop I think? Otherwise you overwrite kx and ky at every iteration and you'd always get the same value for idx. Also, can you show how you fill kernel, maybe that's what's wrong.sgvd
Oh yeah, they were outside the loop anyway, I just put them in the wrong place now...MKII
@sgvd Edited the question to include the kernel calculation.MKII

1 Answers

2
votes

Few problems:

Your Gaussian misses brackets (even though you already have plenty..) around 2 * pow( sigma, 2 ). Now you multiply by variance instead of divide.

But what your problem is, is that your gaussian is centered at kx = ky = 0, as you let it run from 0 to kernel_size, instead of from -kernel_limit to kernel_limit. This results in the diagonal blurring. Something like the following should work better

kx = -kernel_limit;
ky = -kernel_limit;

int kernel_size_sq = kernel_size * kernel_size;

for( int i = 0; i < kernel_size_sq; i++ )
{
  double sigma_sq = sigma * sigma;
  double kx_sq = kx * kx;
  double ky_sq = ky * ky;
  kernel[i] =  1.0 / ( 2 * pi * sigma_sq) * exp(-(kx_sq + ky_sq) / (2 * sigma_sq));

  if(kx == kernel_limit )
  {
    kx = -kernel_limit;
    ky++;
  }
  else
  {
    kx++;
  }
}

Also note how I got rid of your lisp-ness and some improvements: use some intermediate variables for clarity (compiler will optimize them away if anyway you ask it to); simple multiplication is faster than pow(x, 2); pow(e, x) == exp(x).