A straightforward introduction to Image Blurring/Smoothing using python

Sagar Kumar
Spinor
Published in
7 min readNov 17, 2019

What is Image Blurring?

Blurring is a technique in digital image processing in which we perform a convolution operation between the given image and a predefined low-pass filter kernel. The image looks sharper or more detailed if we are able to perceive all the objects and their shapes correctly in it.
E.g. An image with a face looks clearer when we can identify eyes, ears, nose, lips, forehead, etc. very clear. This shape of the object is due to its edges. So, in blurring, we simply reduce the edge content and makes the transition from one color to the other very smooth. It is useful for removing noise. Previously, I had posted an article in the straightforward series related to Thresholding where I used the blurring technique to remove noise in the image. link to that article.

What is a kernel?

Well, In the image processing, a kernel, convolution matrix or mask is a small matrix that is used for blurring, sharpening, embossing, edge detection and more. This is done by a convolution between an image and a kernel. To convolve a kernel with an image, there is a function in OpenCV, cv2.filter2D().

Types of filters in Blurring:

When smoothing or blurring images, we can use diverse linear(Spatial) filters, because linear filters are easy to achieve, and are kind of fast, the most used ones are Homogeneous filter, Gaussian filter, Median filter. However, there are few non-linear filters like a bilateral filter, an adaptive bilateral filter, etc that can be used where we want to blur the image while preserving its edges.

Note: For all the examples I am using Matplotlib and OpenCV.

Therefore please install all the above-mentioned libraries. For me, as I was working on a Google Colab Notebook, I did not require any installation.

First import all the required libraries.

import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab.patches import cv2_imshow
%matplotlib inline

1. Averaging (Mean or Homogeneous filter)

In averaging, we simply take the average of all the pixels under kernel area and replaces the central element with this average. The Average filter is also known as box filter, homogeneous filter, and mean filter. An Average filter has the following properties.

  1. It must be odd ordered.
  2. The sum of all the elements should be 1.
  3. All the elements should be the same.

The kernel ‘K’ for the box filter:

For a mask of 3x3, that means it has 9 cells. The condition that all the element sum should be equal to 1 can be achieved by dividing each value by 9. As 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 = 9/9 = 1

Note: The kernel size must be a positive and odd number.

Let’s see how the above method works with the following image:

A cute puppy (for our experiments 😈 )
# Import the image
img = cv2.imread('Puppy_Dog.jpg', 1)
# define a function for box filter
def box_kernel(size):
k = np.ones((size,size),np.float32)/(size**2)
return k
# Basically, the smallest the kernel, the less visible is the blur. In our example, we will use a 5 by 5 kernel.
size=5
box_filter_img = cv2.filter2D(img,-1,box_kernel(size))

Output:-

Image after averaging

We can also do the same with a function given by OpenCV:

box_filter_img = cv2.blur(img,(size,size))

2. Gaussian Filtering

Gaussian filtering (or Gaussian Blur) is a technique in which instead of a box filter consisting of equal filter coefficients, a gaussian filter is used i.e. using different weight kernels, in both x and y direction. We also need to provide the standard deviation (sigma)

The equation for a Gaussian filter kernel of size (2k+1)×(2k+1) is given by:

Gaussian filter equation

A 5x5 gaussian filter will look like this:-

A 5x5 gaussian blur

Code:-

# Define a function for box filter
def gaussian_kernel(size, sigma=1):
size = int(size) // 2
x, y = np.mgrid[-size:size+1, -size:size+1]
normal = 1 / (2.0 * np.pi * sigma**2)
g = np.exp(-((x**2 + y**2) / (2.0*sigma**2))) * normal
return g
size=5# Apply the gaussian blur
gaussian_filter_img = cv2.filter2D(img,-1,
gaussian_kernel(size, sigma=1))

Output:-

Image after gaussian blur

We can also do the same with a function given by OpenCV:

gaussian_filter_img = cv2.GaussianBlur(img,(size,size),0)

3. Bilateral Filtering

The above-discussed filters will not only dissolve the noise but also smooth the edges, that make edges less sharp, even disappear. To solve this problem, we can use a filter called the bilateral filter. It is an advanced version of Gaussian filter, it introduces another weight which shows how the two pixels can be close (or similar) to one another in value, and by considering both weights in the image, Bilateral filter can keep edges sharp while blurring the image. But the operation is slower as compared to other filters.

Note:

  • Sigma values: For simplicity, you can set the 2 sigma values to be the same. If they are small (< 10), the filter will not have much effect, whereas if they are large (> 150), they will have a very strong effect, making the image look “cartoonish”.
  • Filter size: Large filters (d > 5) are very slow, so it is recommended to use d=5 for real-time applications, and perhaps d=9 for offline applications that need heavy noise filtering.

A side by side comparison of Bilateral filtering and other filtering methods:

Code:-

# Define a function for plotting multiple figuresdef plot_img(images, titles):
fig, axs = plt.subplots(nrows = 1, ncols = len(images),
figsize = (20, 20))
for i, p in enumerate(images):
axs[i].imshow(cv2.cvtColor(p, cv2.COLOR_BGR2RGB))
axs[i].set_title(titles[i])
#axs[i].axis('off')
plt.show()
# To show a side by side comparison of different filters with different kernel sizes.for i in range(3,30,8):
print("with kernel size: "+str(i))
a_img = cv2.blur(img,(i,i))
g_img = cv2.GaussianBlur(img,(i,i),0)
b_img = cv2.bilateralFilter(img,i,75,75)
images=[img, a_img, g_img, b_img]
titles=['original image',
'box filter image',
'gaussian filter image',
'Bilateral filter image']
plot_img(images, titles)
kernel size: 3
kernel size: 11
kernel size: 19
kernel size: 27

As you can observe that the bilateral filter preserves the edges while others just went too blurry.

4. Median Filtering

Median filtering computes the median of all the pixels under the kernel window and replaces the central pixel with this median value. While other filters might be often useful, this method is highly effective in removing salt-and-pepper noise.

Note: In the Gaussian and box filters, the filtered value for the central element can be a value that is not present in the original image. However, this is not the case in median filtering, as the central element is always replaced by some pixel value in the image. This reduces the noise effectively.

Let see how median filtering performs on this image with salt-and-pepper noise:

source: http://people.math.sc.edu/Burkardt/c_src/image_denoise/balloons_noisy.png
# Read the image
noisy_img = cv2.imread('balloons_noisy.png',1)
# Here I've used 5x5 kernel
size = 5
# Denoise the image using median filtering
denoise_img = cv2.medianBlur(noisy_img,size)

Output:

after median filtering

Magic! 😮 😑

Let me show you a side by side comparison:

Conclusion

Image smoothing is one of the most commonly used technique in many image processing tasks. However, we have to keep in mind that for a perfect result we need to try different filters with different kernel size values.

In the end, I want you to try this by yourself and see what results you’ll get with different images. You can try the following image as well:-

Thank you and please let me know if you encountered any problem while implementing this code.

--

--

Sagar Kumar
Spinor
Editor for

Sagar is a computer vision and robotics expert with a focus on Perception & Localization | Twitter: twitter.com/sagarcadet | Linkedin: linkedin.com/in/sagark30