# Gaussian blur algorithm

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

## Recommended Posts

I'm working on my implementation of a gaussian blur algorithm. The code I'm using is based off of one from a gamedev.net article: http://www.gamedev.net/reference/programming/features/imageproc/page2.asp While the code didn't appear to be API specific, my implementation uses Direct3D. I'm not sure whether the problem lies within Direct3D or the algorithm itself. The reason I'm doing this is to create a glow effect as described in these documents: http://www.gamasutra.com/view/feature/2107/realtime_glow.php http://developer.nvidia.com/docs/IO/8230/D3DTutorial_EffectsNV.pdf Below is my code for performing the gaussian blur (just the algorithm itself):
.....

// Perform the gaussian blur
if( SUCCEEDED( pTempSurface->LockRect( &LockedRect, NULL, 0 ) ) )
{
pdwPixels = (DWORD*) LockedRect.pBits;
Pitch = (INT) LockedRect.Pitch;

// Horizontal blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;

for( int k = 0; k < GaussWidth; k++ )
{
INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;
INT y = j;
DWORD dwColour = pdwPixels[y*Pitch+x];

DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;

dwAlpha += a * GaussFact[k];
dwRed   += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue  += b * GaussFact[k];
}

// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha/GaussSum, dwRed/GaussSum, dwGreen/GaussSum, dwBlue/GaussSum );
}
}

// Vertical blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;

for( int k = 0; k < GaussWidth; k++ )
{
INT x = i;
INT y = j - ( ( GaussWidth - 1 ) >> 1 ) + k;
DWORD dwColour = pdwPixels[y*Pitch+x];

DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;

dwAlpha += a * GaussFact[k];
dwRed   += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue  += b * GaussFact[k];
}

dwAlpha /= GaussSum;
dwRed   /= GaussSum;
dwGreen /= GaussSum;
dwBlue  /= GaussSum;

// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha, dwRed, dwGreen, dwBlue );
}
}

// Unlock the texture
pTempSurface->UnlockRect();

.....


And here is the full source to the function I wrote to do this (including Direct3D code).
//--------------------------------------------------------------------------------------
// Name: DoGaussianBlur
// Desc: Performs the gaussian blur algorithm on the entire render target, then saves the
//		 output to a texture of the same size and format.  This code assumes the texture
//		 is 32-bit (D3DFMT_A8R8G8B8).
//--------------------------------------------------------------------------------------
bool DoGaussianBlur( INT Width, INT Height, IDirect3DSurface9* pRTSurface, IDirect3DTexture9** ppTexture )
{
IDirect3DSurface9* pTempSurface = NULL;
DWORD	dwRed = 0, dwGreen = 0, dwBlue = 0, dwAlpha = 0;
const INT GaussWidth = 7;
INT		GaussFact[GaussWidth] = { 1, 6, 15, 20, 15, 6, 1 };
INT		GaussSum = 64;
D3DLOCKED_RECT LockedRect;
DWORD*	pdwPixels = NULL;
INT		Pitch = 0;

// Verify this texture
if( !pRTSurface )
return false;

// Create a temporary surface.
HRESULT hr = lpD3DDevice->CreateOffscreenPlainSurface( Width, Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pTempSurface, NULL );
if( FAILED( hr ) )
{
OutputDebugStringA( "Error creating temporary surface!\n" );
return false;
}

// Copy the contents of the render target to the newly created surface
hr = lpD3DDevice->GetRenderTargetData( pRTSurface, pTempSurface );
if( FAILED( hr ) )
{
OutputDebugStringA( "Error getting render target data!\n" );
SAFE_RELEASE( pTempSurface );
return false;
}

// Perform the gaussian blur
if( SUCCEEDED( pTempSurface->LockRect( &LockedRect, NULL, 0 ) ) )
{
pdwPixels = (DWORD*) LockedRect.pBits;
Pitch = (INT) LockedRect.Pitch;

// Horizontal blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;

for( int k = 0; k < GaussWidth; k++ )
{
INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;
INT y = j;
DWORD dwColour = pdwPixels[y*Pitch+x];

DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;

dwAlpha += a * GaussFact[k];
dwRed   += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue  += b * GaussFact[k];
}

// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha/GaussSum, dwRed/GaussSum, dwGreen/GaussSum, dwBlue/GaussSum );
}
}

// Vertical blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;

for( int k = 0; k < GaussWidth; k++ )
{
INT x = i;
INT y = j - ( ( GaussWidth - 1 ) >> 1 ) + k;
DWORD dwColour = pdwPixels[y*Pitch+x];

DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;

dwAlpha += a * GaussFact[k];
dwRed   += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue  += b * GaussFact[k];
}

dwAlpha /= GaussSum;
dwRed   /= GaussSum;
dwGreen /= GaussSum;
dwBlue  /= GaussSum;

// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha, dwRed, dwGreen, dwBlue );
}
}

// Unlock the texture
pTempSurface->UnlockRect();
}
else
{
OutputDebugStringA( "Error locking surface!\n\n" );
SAFE_RELEASE( pTempSurface );
return false;
}

// Create a dynamic texture to write the blurred surface to.
if( !ppTexture )
{
hr = lpD3DDevice->CreateTexture( Width, Height, 0, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, ppTexture, NULL );
if( FAILED( hr ) )
{
SAFE_RELEASE( pTempSurface );
OutputDebugStringA( "Error creating copy texture!\n" );
return false;
}
}

DWORD* pdwSurface = NULL;
DWORD* pdwTexture = NULL;
D3DLOCKED_RECT LockedSurface, LockedTexture;
bool bError = false;

// Lock the temporary surface
if( SUCCEEDED( pTempSurface->LockRect( &LockedSurface, NULL, 0 ) ) )
{
// Lock the texture
if( SUCCEEDED( (*ppTexture)->LockRect( 0, &LockedTexture, NULL, 0 ) ) )
{
// Copy the data from the surface to the texture
pdwSurface = (DWORD*) LockedSurface.pBits;
pdwTexture = (DWORD*) LockedTexture.pBits;

::CopyMemory( pdwTexture, pdwSurface, sizeof( pdwSurface ) );

// Unlock
(*ppTexture)->UnlockRect(0);
}
else
{
OutputDebugStringA( "Error locking duplicate texture!\n" );
bError = true;
}

// Unlock
pTempSurface->UnlockRect();
}
else
{
OutputDebugStringA( "Error locking temporary surface!\n" );
bError = true;
}

return !bError;
}


The crash occurs when reading the first few pixels for modification. I followed the algorithm exactly (or at least to my best understanding). Is there a better way to do this? Or should I try another approach to the glow effect entirely? Thanks.

##### Share on other sites
Is there a reason you're not doing it in a shader?

##### Share on other sites
Quote:
 Original post by HaladriaIs there a reason you're not doing it in a shader?

Because I don't know how (unless there's a simpler way). Besides, I need a fixed-pipeline implementation either way (for OpenGL ES) later on.

##### Share on other sites
Quote:
Original post by blueshogun96
Quote:
 Original post by HaladriaIs there a reason you're not doing it in a shader?

Because I don't know how (unless there's a simpler way). Besides, I need a fixed-pipeline implementation either way (for OpenGL ES) later on.

No, there is no simple way I know of. You need to render a fullscreen quad with a guassian blur shader to a texture. (this should be separated in two passes for better performance) This is the shader I usually use:

##### Share on other sites
It looks like the reason your code your code is crashing is that you're reading off the edge of the image at:

INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;

In the case where i is 1 and k is 0 that will give you -2. It looks like that code was designed for a GaussWidth of 3, and won't work for any other value without modification.

##### Share on other sites
Quote:
 Original post by Adam_42(..) won't work for any other value without modification.

And those modifications include any one of:

                int offset = ( GaussWidth - 1 ) >> 1;		for( int i = offset ; i < Width - offset ; i++ )		{			for( int j = offset ; j < Height - offset ; j++ )

* Not reading outside of the buffer:
DWORD dwColour = x >= 0 && x < Width && y >= 0 && y < Height ? pdwPixels[y*Pitch+x] : 0;

* Clamping your iteration values (the best idea, since you can then iterate over the entire image range):
INT x = std::max(0, std::min(Width, i - ( ( GaussWidth - 1 ) >> 1 ) + k));

INT y = std::max(0, std::min(Height, j - ( ( GaussWidth - 1 ) >> 1 ) + k));

##### Share on other sites
Quote:
Quote:
Original post by blueshogun96
Quote:
 Original post by HaladriaIs there a reason you're not doing it in a shader?

Because I don't know how (unless there's a simpler way). Besides, I need a fixed-pipeline implementation either way (for OpenGL ES) later on.

No, there is no simple way I know of. You need to render a fullscreen quad with a guassian blur shader to a texture. (this should be separated in two passes for better performance) This is the shader I usually use:

TBH, your fragment program makes things look much easier. I'll go ahead and try that instead and see if it works. I'm sure I can easily convert it to a HLSL pixel shader. Thanks.

Quote:
Original post by mattd
Quote:
 Original post by Adam_42(..) won't work for any other value without modification.

And those modifications include any one of:

*** Source Snippet Removed ***

* Not reading outside of the buffer:
*** Source Snippet Removed ***

* Clamping your iteration values (the best idea, since you can then iterate over the entire image range):
*** Source Snippet Removed ***

*** Source Snippet Removed ***

I'll go ahead and try this too just in case, but I'll probably stick with the shader since it appears to be easier to use and maintain. Thanks again.

1. 1
2. 2
3. 3
Rutin
15
4. 4
5. 5

• 10
• 9
• 9
• 11
• 11
• ### Forum Statistics

• Total Topics
633682
• Total Posts
3013309
×