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

*1D Gaussian function*

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):

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