Gaussian Blur – Noise Reduction Filter

The goal of this tutorial is to reveal major details about Gaussian blur and how to use it for mage processing and machine vision taks. FivekoGFX implements a WebGL version of Gaussian filter and uses GPU power by OpenGL shaders (GLSL). This makes it suitable for direct use into HTML5 web apps and to achieve good performance.

What is Gaussian blur?

Gaussian blur is a non-linear noise reduction low-pass filter (LP filter) widely applicable for image processing and computer vision tasks. The visual effect of this filter is a smooth blurry image, meaning it reduce intensity variations between adjacent pixels. Gaussian filter performs better than other uniform low pass filters like the Mean filter and this makes it favorite pre-processing step in image processing and objects recognition algorithms. Gaussian blur with sigma = 3.0

How does Gaussian Blur works?

The Gaussian filter works by convolving the input image with a Gaussian kernel. The convolution with such a kernel performs a weighted average of the current pixel’s neighborhoods in a way that distant pixels receive lower weight than these at the center. The result of this is a blurry image with better edges than other uniform smoothing algorithms. The kernel calculation uses the following math equations for corresponding weights:

Gaussian function 1D 1D Gaussian function Gaussian blur 1D kernel equation (1)

Gaussian function 2D

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

In practice it is better to take advantage of the Gaussian function separable properties. This property allows blur execution in two separate steps and apply a one dimensional kernel instead of the 2D Gaussian filter. As a result, we can achieve a quick blur effect by dividing its execution horizontally and vertically. For Gaussian filter calculation we use equations mentioned above and if processing uses a separable Gaussian blur then for the kernel math use the first equation. Note that when converting continuous values to discrete ones, the total Gaussian kernel sum will be different than one. This leads to brightening or darkening of the image, so in practice we normalize the kernel by dividing each of its elements by the sum of all of them.

Features

We can summarize some of the Gaussian’s filter features:

• It is a separable filter – can use it as a separable 1D kernel and process rows and columns separably
• The use of ‘weighted average’ masks makes it better for edge detection than some uniform blurring filters
• Multiple iterations with same gaussian filter to an image has the same effect as applying a single, larger gaussian blur
• Useful as a pre-processing step for image size reduction

Gaussian Discrete Approximations

In many cases it is enough to use an approximation of Gaussian function. Below are listed several famous masks which are frequently used in computer vision. The pictures reveal the kernel values together with the corresponding scale factor as well as the corresponding 1-dimensional separable vectors.

Separable Filter with Size 3×3 Discrete approximation of Gaussian filter with kernel size 3×3

Separable Filter with Size 5×5 Discrete approximation of Gaussian filter with kernel size 5×5

How to apply fast Gaussian function by FavkoGFX?

Now after we defined some of the Gaussian filter kernels it’s time to pick up one and use it to make a blurry image. Тhe example below shows  how to do a fast image smoothing by a simple separable convolution with one of the masks above. The code uses our web based graphics library FivekoGFX, so you can combine it with your own HTML5 scripts and perform processing directly into your web browser.

var fivekogfx = new FivekoGFX();
// Load source image from Canvas object
// Perform separable convolution with 1D Gaussian function
// In this case we use 5x5 Gaussian kernel size
fivekogfx.conv1d([1,4, 6, 4, 1]);
// Display the resulting image
fivekogfx.draw(canvas);

Gaussian Filter source code

Gaussian kernel calculation

As a first step, we need to fill in the Gaussian kernel values using the sigma input parameter. 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 we calculate the kernel values by the following JavaScript 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 to prevent image drakening/brightening at convolution stage.

Gausian blur convolution

At this stage the image is processed by the already calculated blurring kernel. For better performance we take advantage of Gaussian’s separable property therefore we perform the Gaussian convolution in two steps – once for rows and once column wise.

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;
}
}

We process the rows in a similar manner as 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;
}

Elapsed: