Skip to main content
GameDev.net gamedev.net
🔒 Locked

gaussian blur algorithm

Started by docyoung83 Jul 17, 2004 at 1:33 PM 35 replies 14.8k views
Original Post
docyoung83
docyoung83
I've searched on Google several times for a gaussian blur algorithm and I've found two sites that sort of have what I'm looking for. The best candidate so far has been http://incubator.quasimondo.com/processing/fastblur.pde. It's written in a "langauge" called Processing, but it looks more to me like a bunch of Java wrappers. I would think it would be an easy conversion to C++, but I'm having quite a bit of trouble getting it to work correctly. Considering I have little time to get this working, does any body out there have a gaussian blur algorithm already in C++ that works fairly well?
soconne
soconne
You can just take an average of the surrounding pixels and use that as the new value. Here's code to do it.

// your image you want to blur
unsigned char *image;

// blurred image
unsigned char *blurimage = new unsigned char[size*size*3];

for(int y=0; y < size; y++) {
for(int x=0; x < size; x++) {

// hold summed up values of pixels
int sum=0;
int cnt=0;

for(int rgb=0; rgb<3; rgb++) {

sum+=image[(y*size+x)*3+rgb];
cnt++;

if(x>1) {
sum+=image[(y*size+x-1)*3+rgb];
cnt++;
}
if(x sum+=image[(y*size+x+1]*3+rgb];
cnt++;
}
if(y>1) {
sum+=image[((y-1)*size+x)*3+rgb];
cnt++;
}
if(y sum+=image[((y+1)*size+x]*3+rgb];
cnt++;
}
sum/=cnt;
blurimage[(y*size+x)*3+rgb] = sum;
}
}
}

// now copy blurred data to original image
for(int i=0; i < size*size*3; i++)
image = blurimage;




And that's it. A simple blur function. Hope this helps.
Author Freeworld3Dhttp://www.freeworld3d.org
ngill
ngill
just curious... was this a homework question in your "intro to computer graphics" class?
docyoung83
docyoung83
Hehe, no. I'm a premed undergraduate and I'm spending my summer working at a medical school doing intern-type work. It just so happens that the doctor I'm working with needs a program of his to be able to do this sort of blur effect (it's a long story...but I can assure it has a medical purpose and it's not just because he likes sitting in front of his computer doing artwork all day). So, that's the story.
Punty50
Punty50
What is described to you is NOT a Gaussian convolution, but just a straight averaging of surrounding pixels. What you need to use is a weighting in the kernel of the convolution that conforms to an approximation of the Gaussian distribution in 2 dimensions.

For more information about kernels, convolutions, and Gaussian convolutions, I reference you to this:

http://www.intel.com/software/products/ipp/docs/ippiman.htm

This is the library that I use for writing computer vision systems.
Brendan"Mathematics is the Queen of the Sciences, and Arithmetic the Queen of Mathematics" -Gauss
soconne
soconne
Quote:
Original post by oconnellseanm
You can just take an average of the surrounding pixels and use that as the new value.


That's why I said its a simple 'average' of the pixels, not an actual gaussian blur. This is by far the easiest way.
Author Freeworld3Dhttp://www.freeworld3d.org
Steadtler
Steadtler
Here we go:

This is to create the gaussian filter:
You can replace the Image with a simple 2D buffer if you want.

ErrorCode CreateGaussianFilter(double piSigma, double piAlpha, Image &poFilter){long lSize = ((int)(piAlpha * piSigma) / 2)*2 + 1; //force odd-size filters		poFilter.ResetImage(lSize, lSize);		for (int x = 0; x < lSize ; x++)	{		long FakeX = x - long(floor(lSize / 2.0));		for (int y = 0 ; y < lSize ; y++)		{			long FakeY = y - long(floor(lSize / 2.0));			double k = 1.0 / (2.0 * GetPI * piSigma * piSigma);			poFilter.SetPixel(x, y, k * exp( (-1 * ((FakeX * FakeX) + (FakeY *FakeY))) / (2 * piSigma * piSigma)));		}		}	//normalise the filter	double lTotal=0;	for (x = 0;x < lSize; x++)	{		for (long y = 0;y < lSize; y++)		{			lTotal += poFilter.GetPixel(x, y);		}	}		for (x = 0;x < lSize; x++)	{		for (long y = 0;y < lSize; y++)		{			poFilter->SetPixel(x, y, poFilter.GetPixel(x, y) / lTotal);		}	}	return OK;}


Now that we have the filter, we only need to convolute it with our image.

ErrorCode Convolute(Image &piImage, Image &piFilter, Image & poResult){	long lSizeX = piImage.GetSizeX();	long lSizeY = piImage.GetSizeY();	//for each pixel    for (long x = 0; x < lSizeX; x++)	{		for (long y = 0; y < lSizeY; y++)		{			double Sum = 0;			//For each point of the filter.			for (long i = 0; i < piFilter.GetSizeX(); i++)			{				//This is to make our origin in the center of the filter								long FakeI = i - long(floor(piFilter.GetSizeX() / 2.0));				for (long j = 0; j < piFilter.GetSizeY(); j++)				{					long FakeJ = j - long(floor(piFilter.GetSizeY() / 2.0));					double Factor = 0;					if (x+FakeJ < lSizeX && y+FakeI < lSizeY && x+FakeJ >= 0 && y+FakeI >= 0)					{						Factor = piImage.GetPixel((x+FakeJ), (y+FakeI));					}					Sum += Factor * piFilter.GetPixel(j, i);				}							}			poResult.SetPixel(x,y,Sum);		}	}	return OK;}


So:

CreateGaussianFilter(...);
Convolute(...);

Sigma is your gaussian variance. From 0.92 to up, the bigger Sigma, the more the blur. Alpha is a constant that determine the percentage of your gaussian that will be represented in your discreet filter. Try between 2 and 10. (6 is considered best).

Of course this code aint optimised. If you still need help, dont hesitate to PM me, or reply here. Good Luck!

*Edit*: If you want to blur color images, run the convolution on the 3 color band using the same filter.

[Edited by - Steadtler on July 20, 2004 7:50:47 PM]
janos
janos
if you need to convolve large images with large kernels, use a FFT; the complexity is much lower:
image w*h pixels
kernel kx*y pixels
convolution complexity (exactly): O(w*h*kx*ky)
fft, term by term multiply, ifft complexity : 0(w*h*ln(w*h)+kx*ky*ln(kx*ky))

Act of War - EugenSystems/Atari
raydog
raydog
In Photoshop and Paint Shop Pro, you can adjust a radius in pixels when applying a Gaussian
blur to an image.

How does this 'pixel radius' relate to the above source code?
raydog
raydog
Ok, wait a minute. I think I see an answer to my question. Is the image size of the GaussianFilter
related to the 'pixel radius' in those programs?
raydog
raydog
Hmmm... but how can that be when you can enter a floating point value as a 'pixel radius', but filter size is discreetly an integer? Confusing.
Steadtler
Steadtler
The 2d gaussian is a function that is symetric around all directions, and is the density of probability of the normal distribution.

The sigma parameter of my above code represent the standard deviation of the distribution. Since the function continues to infinite, you have to cut it somewhere. Thats where alpha comes in. It determine how big your filter needs to be to get x% of the gaussian surface.

If I had photoshop it would be easy to answer your question, but my guess is that either the "radius" IS sigma, the standard deviation, or that sigma is calculated from this radius so that the radius represents a fixed percentage of the gaussian.

Is this radius the only parameter you can enter?
raydog
raydog
Yes, both Photoshop and PSPro gaussian blur functions are identical and there is only one option.


Here's the text docs from PSPro:
Use the Gaussian Blur dialog box when you want more control over the Blur effect. The Gaussian Blur blends a specific number of pixels incrementally, following a bell-shaped curve. The blurring is dense in the center and feathers at the edges.


Here's the text docs from Photshop:
The Gaussian Blur filter quickly blurs a selection by an adjustable amount. Gaussian refers to the bell-shaped curve that is generated when Adobe Photoshop applies a weighted average to the pixels. This filter adds low-frequency detail and can produce a hazy effect.
To use the Gaussian Blur filter:

1 Choose Blur > Gaussian Blur from the Filter menu. The Gaussian Blur dialog box appears.
2 Enter a value in the Radius box from 0.1 to 250.0 to determine the degree of blurring. (The higher the value, the stronger the blurring effect.)

Steadtler
Steadtler
Ok, so what probably happens is that they calculate sigma from the floating-point value of the radius given a hard-coded precision, then calculate the actual filter size needed.
raydog
raydog
Oh, sorry, I didn't see that your CreateGaussianFilter function is already calculating
the filter size from both the alpha and sigma values. I was thinking of something else.
Problem solved! :)
Steadtler
Steadtler
Quote:
Original post by raydog
Oh, sorry, I didn't see that your CreateGaussianFilter function is already calculating
the filter size from both the alpha and sigma values. I was thinking of something else.
Problem solved! :)


Glad I could help :)
Dont hesitate if you have other imaging problems. Its good practice for my master degree final examination!
raydog
raydog

Actually, I do. If StonieJ doesn't mind, but I think his question was alreaddy answered.

I don't suppose you are familiar with the problem of seams appearing on a 3D model when you blur
a texture? I'm looking for a blur algorithm that doesn't create seams. The problem is that when
I blur a seamless texture, the pixels on the border no longer match up, so seams appear.

Question is, do you know if it possible to apply a blur to a seamless texture, and still retain the
texture's seamlessness?
Steadtler
Steadtler
Sorry for my bad english, you mean textures where the border pixels matches so that it can be "wrapped"? If so...

Try this:

ErrorCode ConvoluteWrap(Image &piImage, Image &piFilter, Image & poResult){	long lSizeX = piImage.GetSizeX();	long lSizeY = piImage.GetSizeY();	//for each pixel    for (long x = 0; x < lSizeX; x++)	{		for (long y = 0; y < lSizeY; y++)		{			double Sum = 0;			//For each point of the filter.			for (long i = 0; i < piFilter.GetSizeX(); i++)			{				//This is to make our origin in the center of the filter								long FakeI = i - long(floor(piFilter.GetSizeX() / 2.0));				for (long j = 0; j < piFilter.GetSizeY(); j++)				{					long FakeJ = j - long(floor(piFilter.GetSizeY() / 2.0));					double Factor = 0;                                              long PosX = x+FakeJ;                                              long PosY =  y+FakeI;                                              if (PosX < 0)                                                 PosX = lSizeX - PosX;                                              if (PosY < 0)                                                 PosY = lSizeY - PosY;                                              if (PosX >= lSizeX)                                                 PosX = PosX - lSizeX;                                              if (PosY >= lSizeY)                                                 PosY = PosY - lSizeY;											Factor = piImage.GetPixel(PosX,PosY);									Sum += Factor * piFilter.GetPixel(j, i);				}							}			poResult.SetPixel(x,y,Sum);		}	}	return OK;}


As you can see, I modified the convolution process so that points that lies "outside" the image are considered like comming from the other side.

Now Im not at my lab so I cannot test this, but from intuition it should preserve your image "seamlessness" (even increase it). You can use any blurring filter (gaussian, mean, median, cut-off.)

If you cant change the way the convolution is done, you are kinda out of luck...
raydog
raydog
Yes, that's right. Thanks for the help. I think I got it working.


I forgot to ask, is your pixel wrapping safe? Typically, I guess you can say the image size
will always be larger than the filter size, but what if this is not the case (filter is larger than image)?

Say I have a 37x37 filter and a 8x8 image. Center of the filter would be 18,18.

long FakeJ = 0 - 18;

PosX = 0 + -18;

if (PosX < 0)
PosX = 8 - -18 = 26;

if (PosX >= lSizeX)
PosX = 26 - 8 = 18;


So, PosX would be an invalid image index, if no error checking is done.

[Edited by - raydog on July 24, 2004 8:33:43 PM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.