PDA

View Full Version : dinamic gaussian kernel



cignox1
05-25-2006, 12:51 AM
Hi, I'm trying to implement a shader that simulates a translucent window: everything behind it is blurried. I got some nice results, but I would like to implement a gaussian blur which size depends on the distance of the object.
Up to now I use if() statements to decide whether or not to add more blur, but I don't like this way.

Have you a better algorithm (yet fast, if possible) to parametrize the gaussian blur intensity?
Thank you!

songho
05-25-2006, 08:59 AM
cignox1,
I have a code to create a 1-D Gaussian kernel with given sigma value (standard deviation).

///////////////////////////////////////////////////////////////////////////////
// generate 1D Gaussian kernel
// kernel size should be odd number (3, 5, 7, 9, ...)
///////////////////////////////////////////////////////////////////////////////
void makeGaussianKernel(float sigma, float *kernel, int kernelSize)
{
//const double PI = 3.14159265; // PI

int i, center;
float sum = 0; // used for normalization
double result = 0; // result of gaussian func

// compute kernel elements normal distribution equation(Gaussian)
// do only half(positive area) and mirror to negative side
// because Gaussian is even function, symmetric to Y-axis.
center = kernelSize / 2; // center value of n-array(0 ~ n-1)

if(sigma == 0)
{
for(i = 0; i <= center; ++i)
kernel[center+i] = kernel[center-i] = 0;

kernel[center] = 1.0;
}
else
{
for(i = 0; i <= center; ++i)
{
//result = exp(-(i*i)/(double)(2*sigma*sigma)) / (sqrt(2*PI)*sigma);
// NOTE: dividing (sqrt(2*PI)*sigma) is not needed because normalizing result later
result = exp(-(i*i)/(double)(2*sigma*sigma));
kernel[center+i] = kernel[center-i] = (float)result;
sum += (float)result;
if(i != 0) sum += (float)result;
}

// normalize kernel
// make sum of all elements in kernel to 1
for(i = 0; i <= center; ++i)
kernel[center+i] = kernel[center-i] /= sum;
}

// DEBUG //
#if 0
cout << "1-D Gaussian Kernel\n";
cout << "===================\n";
sum = 0;
for(i = 0; i < kernelSize; ++i)
{
cout << i << ": " << kernel[i] << endl;
sum += kernel[i];
}
cout << "Kernel Sum: " << sum << endl;
#endif
}If sigma is increases, the image gets more blurry. And the kernel size would also grow. To determine the kernel size, I use the following formula:

// determine size of kernel (odd #)
// 0.0 <= sigma < 0.5 : 3
// 0.5 <= sigma < 1.0 : 5
// 1.0 <= sigma < 1.5 : 7
// 1.5 <= sigma < 2.0 : 9
// 2.0 <= sigma < 2.5 : 11
// 2.5 <= sigma < 3.0 : 13 ...
kernelSize = 2 * int(2*sigma) + 3;Hope it helps.
==song==

simongreen
05-25-2006, 01:12 PM
Doing variable-width blurs is difficult on current graphics hardware.

This example does it by storing an atlas of different sized kernels in a texture. This works, but you always pay the cost of the largest kernel:
http://developer.nvidia.com/object/convolution_filters.html

This paper describes a much cleverer method, but be warned it's not simple!
http://graphics.pixar.com/DepthOfField/paper.pdf

cignox1
05-26-2006, 06:07 AM
Thank you both! Yes, currently I use up to 4x4 filters, and it can be really slow, but it would be used only for small windows, so it shouln't be a problem :-)

songho
05-27-2006, 10:44 AM
cignox1,
Should the kernel size be odd number, for example 5x5?

And, you may increase performace by using seperable convolution, because Gaussian kernel is separable. Multiplications can be down from M*N to M+N.

Please check the link for separable convolution. There is an example to compare 2D and seperable convolution performance with Gaussian filter, too:
www.songho.ca/dsp/convoluton.html (http://www.songho.ca/dsp/convolution/convolution.html#separable_convolution)

cignox1
05-28-2006, 05:17 AM
Actually, I don't require a true gaussian blur... just something that blurs the pixel using a parameter that specifies the amount of blur desidered...