• Advertisement
Sign in to follow this  

Trying To Figure Out Bilateral Blur

This topic is 647 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,
Working my way through my d3d11 book, I've got an assignment to redo my working gaussian blur for a bilateral blur.
So I've digged and digged, googled and googled some more and the monkey explanation for me is:

- gaussian blur: predefined static weights for the surrounding pixels
- bilateral blur: weights depend on the difference in pixel colors and the distance the pixels are from the source pixel
(eventually leading to a 1.0 total weights for all neighbouring pixels)

So I started figuring out my existing gaussian blur, and that's clear (pseudo code):

	/*	float blurWeights[11] = 
		{	
			0.05f, 0.05f, 0.1f, 0.1f, 0.1f, 0.2f, 0.1f, 0.1f, 0.1f, 0.05f, 0.05f,
		};
	
		gCache = org. texture pixels
		blurRadius = 5
		newPixelCol = float4(0, 0, 0, 0);

		for i= -blurRadius to blurRadius
		{
			k = orgPixel.x + 5 + i

			newPixelCol += blurWeights[i+blurRadius] * gCache[k];
		}
		output = newPixelCol;
	*/

And drawn out:
gaussian_blur.JPG

So, now how to get from there to the bilateral blur?

I've written things out in Excel to get a good understanding.
I came up with an idea to calculate the weight based in difference in pixel colors of the neighboring pixels, but my conclusion a few minutes ago, is that averaging the R, G and B differences will not work, because the average will always be 0.0f for all neighboring pictures.

bilateral_blur_eq.jpg

And this is were I stopped / came so far...

My question;
- do you have any idea how I can somehow determine the difference in pixels so I can order them to share the weight of 1.0 over my 11 pixels, based on the sorted difference in pixel colors?

What I can think of, is taking the highest difference of one of the 3 components (R,G,B), that way I can sort all 10/11 pixels and lookup the weights in some sort of lookup table (predefined).

The aimed result is keeping the edges better, besides a nice blur (pixels that are 'a like' will be better visible that way)

Edited by cozzie

Share this post


Link to post
Share on other sites
Advertisement

A hint that should help - it's ok if your weights don't add up to 1.0, as long as you can normalize for this at the end.

If you know your weights sum to 1.0, you can use:

for( i=... )
  result += sample[i] * weight[i];

Otherwise, you can use:

for( i=... )
{
  result += sample[i] * weight[i];
  totalWeight += weight[i];
}
result /= totalWeight;

Or:

for( i=... )
  totalWeight += weight[i];
for( i=... )
  weight[i] /= totalWeight;
//now the weights do sum to 1.0
for( i=... )
  result += sample[i] * weight[i];

So for your blur, you could start out using the Gaussian weights as usual, and then modify them with something like:
weight *= 1-dot(abs(sample - sample

), 1/3.0)

Edited by Hodgman

Share this post


Link to post
Share on other sites

As always, thanks for thinking with me/ helping out hodgman.

 

I'm trying to understand what you're saying, regarding the normalization so the weights sum up to 1.0 I understand what you mean.

As a first attempt I'm trying to modify the weight based on your example (last line in the reply above).

 

When I only do this, the result is that everything's black :)

Probably because only doing this is not enough, if I just change the weights 'per neighbour pixel' (11 times), the end result won't sum up to 1.

Does this mean I have to to some sort of 'pre-pass' to determine the weights, or am I overlooking something?

 

Here's the full code of the horizonal blur pass, to give a better view:

cbuffer cbSettings
{
	float gWeights[11] = 
	{
		0.05f, 0.05f, 0.1f, 0.1f, 0.1f, 0.2f, 0.1f, 0.1f, 0.1f, 0.05f, 0.05f,
	};
};

cbuffer cbFixed
{
	static const int gBlurRadius = 5;
};


#define N 256
#define CacheSize (N + 2*gBlurRadius)
groupshared float4 gCache[CacheSize];

[numthreads(N, 1, 1)]
void HorzBlurCS(int3 groupThreadID : SV_GroupThreadID,
				int3 dispatchThreadID : SV_DispatchThreadID)
{
	// fill local thread storage to reduce bandwidth. Blur N N pixels, needs N + 2 * BlurRadius for Blur radius
	
	// thread group runs N threads. To get the extra 2*BlurRadius pixels, have 2*BlurRadius threads sample an extra pixel.
	if(groupThreadID.x < gBlurRadius)
	{
		// clamp out of bound samples that occur at image borders
		int x = max(dispatchThreadID.x - gBlurRadius, 0);
		gCache[groupThreadID.x] = gInput[int2(x, dispatchThreadID.y)];
	}
	if(groupThreadID.x >= N-gBlurRadius)
	{
		// clamp out of bound samples that occur at image borders
		int x = min(dispatchThreadID.x + gBlurRadius, gInput.Length.x-1);
		gCache[groupThreadID.x+2*gBlurRadius] = gInput[int2(x, dispatchThreadID.y)];
	}

	// clamp out of bound samples that occur at image borders
	gCache[groupThreadID.x+gBlurRadius] = gInput[min(dispatchThreadID.xy, gInput.Length.xy-1)];

	// wait for all threads to finish.
	GroupMemoryBarrierWithGroupSync();
	
	// Now blur each pixel.
	float4 blurColor = float4(0, 0, 0, 0);
	
	[unroll]
	for(int i = -gBlurRadius; i <= gBlurRadius; ++i)
	{
		int k = groupThreadID.x + gBlurRadius + i;
		float tWeight = gWeights[i+gBlurRadius];
		
		//tWeight *= 1 - dot(abs(gCache[k] - groupThreadID.x), 1/3.0f);			// TEST after reply Hodgman
	
		blurColor += tWeight * gCache[k];
	}
	
	gOutput[dispatchThreadID.xy] = blurColor;
}

Edited by cozzie

Share this post


Link to post
Share on other sites

With some help, I've managed to find a few options/ solutions.

The three options:

	/*	OPTION 1
	
		tWeight *= 1 - dot(abs(gCache[k] - gCache[groupThreadID.x]), 1/3.0f);			// TEST after reply Hodgman
	*/

	/*	OPTION 2 
	
		tWeight *= pow(abs(1 - dot(abs(gCache[k] - gCache[groupThreadID.x]), 1/3.0f)), 10);

	*/

	/*	OPTION 3
	
		tWeight *= pow(abs(1 - dot(abs(gCache[k] - gCache[groupThreadID.x]), 1/3.0f)), 2);

	*/

Where I personally find that option 1 gives the best result, then 3 (not 2 for sure).

I've also tried out something completely different, but this results in most pixels being 'pitch black' and the edges being visible-> white/ light greyish:

tWeight *= saturate(dot(abs(gCache[k] - gCache[groupThreadID.x]), 1));

Here are the outputs of the 3 options (in logical order):

 

blur_bilateral_test1.jpg

 

blur_bilateral_test2.jpg

 

blur_bilateral_test3.jpg

 

And the normal gaussian blur version:

 

blur_gaussian.jpg

 

And the non-blurred scene:

 

blur_noblur.jpg

 

What do you think?

 

Thanks everybody for the help.

Edited by cozzie

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement