Gaussian Blur – Noise Reduction Filter


This tutorial is about Gaussian blur as a non-linear noise reduction low-pass filter. This filter is widely used in image processing tasks. It is designed in a way to reduce image noise and details. The visual effect of Gaussian filter is a smooth blurry image.  It is a common preprocessing step in image processing and objects recognition algorithms.  Some of its most popular features are:

  • It is a separable filter
  • Preserve edges better than some uniform blurring filters
  • Useful as a preprocessing step when reduce image size

How does Gaussian Blur works?

The Gaussian filter is working by convolving the input image with a Gaussian kernel. Each pixel value is calculated as a weighted average of its neighborhoods in a way that the pixel original value has the highest weight while the distant ones receive lower weight. This results in a blur which preserve edges better than other uniform blur algorithms.

 

Gaussian function 1D

gaussian-blur-1d-shape

1D Gaussian function

gaussian blur 1d math

Gaussian blur 1D math equation

The math behind 1D Gaussian function such as one above is the following (1):

 

Gaussian function 2D

The calculation of 2D Gaussian function is represented by the following equation (2):

gaussian blur 2d math

Gaussian blur 2D math equation

In practice it is better to take advantage of the Gaussian blur separable properties. This means that the filter can be executed in two separate steps – first apply a one dimensional Gaussian kernel to blur only horizontal or vertical direction. At the second stage apply the same kernel over the other remaining direction. The Gaussian kernel is calculated using the equations mentioned above, so if a separable Gaussian blur is used then then the kernel is calculated using (1). Note that when converting continuous values to discrete ones, the sum of the value will be different than one. This leads to brightening or darkening of the image, so in practice it is good to normalize the kernel by dividing each of its elements by the sum of all of them.


Gaussian Filter source code

Gausian kernel calculation

As a first step it is required to populate Gaussian kernel values based on sigma input argument. The kernel size is calculated using the following code snippet as described into Gaussian function 1D:

 


const GAUSSKERN = 2.0;
var dim = parseInt(Math.max(3.0, GAUSSKERN * sigma + 1.0));

Next the kernel values are calculated using the following code snippet:


function makeGaussKernel(sigma){
	const GAUSSKERN = 2.0;
	var dim = parseInt(Math.max(3.0, GAUSSKERN * sigma + 1.0));
	var sqrtSigmaPi2 = Math.sqrt(Math.PI*2.0)*sigma;
	var sum = 0.0;

	if (dim % 2 == 0)
		dim++;

	var s2 = 2.0*sigma * sigma;
	var i, j, c = parseInt(dim / 2);
	var kernel = new Float32Array(dim);
	for (j = 0, i = -c; i <= c; i++, j++) 
	{
		kernel[j] = Math.exp(-(i*i)/(s2)) / sqrtSigmaPi2;
		sum += kernel[j];
	}

	// Normalize the gaussian kernel to prevent image drakening/brightening
	for (i = 0; i < dim; i++) 
	{
		kernel[i] /= sum;
	}
	return kernel;
}

Note that the kernel values are normalized in order to prevent image drakening/brightening at convolution stage.

Gausian blur convolution

At this stage the image is processed by the already calculated blurring kernel. To improve performance the Gaussian convolution is performed separately – e.g. first columns and second the rows:


var kernel = makeGaussKernel(sigma);
var d = pixels.data;
var w = pixels.width;
var h = pixels.height;
var buff = new Float32Array(w*h); 
var i, j, k, ch = 0; // Color channel (0 - Red, 1 - Green ...)
var kl = kernel.length;
var c = parseInt(kernel.length / 2);

// First step process columns
for (j = 0; j < h; j++) 
{
	var hw = j*w;
	for (i = 0; i < w; i++)
	{
		var p = 0;
		for (k = 0; k < kl; k++)
		{
			var col = i + (k - c);
			col = (col < 0) ? 0 : ((col >= w) ? w - 1 : col);
			p += d[(hw + col)*4 + ch]*kernel[k];
		}
		buff[hw + i] = p;
	}
}

The rows are then processed in a similar manner as shown in the next code segment:


// Second step process rows
for (j = 0; j < h; j++) 
{
	for (i = 0; i < w; i++)
	{
		var p = 0;
		for (k = 0; k < kl; k++)
		{
			var row = j + (k - c);
			row = (row < 0) ? 0 : ((row >= h) ? h - 1 : row);
			
			p += buff[(row*w + i)]*kernel[k];
		}
		var off = (j*w + i)*4;
		(colors == 3) ? d[off + ch] = p : d[off] = d[off + 1] = d[off + 2] = p;
	}
}

Gaussian Blur by OpenGL

The OpenGL GLSL (fragment shader) source code below represents a single direction Gaussian blur algorithm. The uniform parameter u_direction selects trace direction – rows or columns. The variable u_kernel holds normalized kernel values for convolution with Gaussian function.


precision mediump float;

// our texture
uniform sampler2D u_image;

#define KERNEL_SIZE 15
uniform vec2 u_textureSize;
uniform int u_direction;
uniform float u_kernel[KERNEL_SIZE];
void main() {
	vec2 textCoord = gl_FragCoord.xy / u_textureSize;
	vec2 onePixel = ((u_direction == 0) ? vec2(1.0, 0.0) : vec2(0.0, 1.0)) / u_textureSize;
	vec4 meanColor = vec4(0);
	int ms = KERNEL_SIZE / 2;
	for (int i = 0; i < KERNEL_SIZE; i++)
	{
		meanColor += texture2D(u_image, textCoord  + onePixel*vec2(i - ms))*u_kernel[i];
	}
	gl_FragColor = meanColor;
}

Visit FivekoGFX on GitHub for more image processing examples


Gaussian Filter Demo

Sigma: 3

Related Articles

Hough transform