OpenCV: Morphological Dilation and Erosion

Sasani Perera
6 min readAug 15, 2023

--

In the previous articles, we have discussed the basics of image processing. Let’s move on to a section that is very important in image pre-processing.

In this article, we will dive into and discuss the basics of morphological operations. Morphological operations are a set of mathematical techniques used in image processing and computer vision. They are based on the principles of set theory and involve the manipulation of binary images, where pixels are either considered foreground (1) or background (0).

Imagine you have a drawing or a photograph. You can notice some dots or gaps in this image.

Image 01 with unnecessary dots and Image 02 with unnecessary gaps

Let us take the above two images as an example. The first image has some kind of dots or drawings all over the image. The next image has cuts or gaps through the letters. This effect reduces the overall quality of the picture.

How can you treat this issue and improve the quality of the picture?

Morphological Operations

This is where morphological operations come into play. Morphological operations are like these special tools that we can use to change the shapes and structures in these pictures.

Pictures made up of tiny pixels

As we know now, these images are made up of tiny pixels. What we do with morphological operations is manipulate the pixels to either stick together or remove the unwanted pixels to alter the way things in the picture look.

Dilation and Erosion are the main morphological operations we are going to discuss and use in this article.

Morphological Dilation

Take this operation of dilation as a function. Dilation takes mainly two inputs.
1. The image that we need to apply the dilation in Black and White.
2. Kernel or the structural element.

Process of dilation

Let us have an image, converted to a black-and-white image (pixels have values either ‘1’ or ‘0’). We send a kernel, in most cases n-by-n matrix of ones over this image.

Example 1.1: Let our kernel be a 3x3 matrix of ones.

3x3 matrix

While this kernel is running over the image, if any element of the matrix come across with value ‘1’ of the image, the pixel which is overlapped with the kernel[1][1] element is turned to ‘1’.

Red square denoting the moving kernel across the image

Example 1.2: Let’s see how this is applied to a bigger image.

Image subjected to dilation with 3x3 kernel
Mathematical-Morphology Dialation

Dilation with OpenCV

First, let’s read the image, and convert it to black and white image.

#import the necessary libraries
import cv2
import numpy as np
import matplotlib.pyplot as plt

#read image
im=cv2.imread("open_cv.png")

#read grey scale image
gray_im=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)

#read BW image
thresh,BW_im = cv2.threshold(gray_im,150,255,cv2.THRESH_BINARY)


#visualize them
cv2.namedWindow("Original Image", cv2.WINDOW_NORMAL);
cv2.imshow("Original Image",im)

cv2.namedWindow("Gray scale image", cv2.WINDOW_NORMAL);
cv2.imshow("Gray scale image",gray_im)

cv2.namedWindow("BW Im", cv2.WINDOW_NORMAL);
cv2.imshow("BW Im",BW_im)

cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()
Visualized original, grey-scale, and black-and-white images

As we discussed earlier, in dilation, the kernel turns the necessary pixels from ‘0’ to ‘1’. Then, it is understandable that the pixels that need to be dilated (region of interest) should have the value ‘0’, meaning the gaps in this image should be ‘black’ coloured or should carry the value ‘0’. Hence we must use THRESH_BINARY_INV.

#read BW image
thresh,BW_im = cv2.threshold(gray_im,150,255,cv2.THRESH_BINARY_INV)

#visualizing
cv2.namedWindow("BW Im", cv2.WINDOW_NORMAL);
cv2.imshow("BW Im",BW_im)
cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

Now let’s introduce our kernel, considering how much dilation we needed to do. We can always trial and error to find out the optimal kernel.

#defining kernel of 3-by-3 matrix of ones
kernel = np.ones((3,3),np.uint8)

#defining kernel of 5-by-5 matrix of ones
kernel2 = np.ones((5,5),np.uint8)

Now we use this kernel and apply the dilation to our black-and-white image using cv2.dilate.

cv2.dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

  • src - input image/array
  • kernel - defined kernel
  • anchor - matrix position to compare the value with the image. (In the above example we too the anchor as [1][1].
  • iterations - How many times the procedure should repeat
#morphological dialation

#defining kernel
kernel = np.ones((3,3),np.uint8)

#getting dialated image
dilated_im=cv2.dilate(BW_im,kernel,anchor=(0,0),iterations=2)

#visualize
cv2.namedWindow("Dilated Image", cv2.WINDOW_NORMAL);
cv2.imshow("Dilated Image",dilated_im)
cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

Now we can play with these values until we find the best values for kernel, anchor and iterations. And get the best possible answer to our desire.

Variations of the kernel, iterations used and their outputs

Complete Code

import cv2
import numpy as np
import matplotlib.pyplot as plt

#read image
im=cv2.imread("open_cv.png") #your image name
#read grey scale image
gray_im=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
#read BW image
thresh,BW_im = cv2.threshold(gray_im,150,255,cv2.THRESH_BINARY_INV)

#morphological dialation
#defining kernel
kernel = np.ones((3,3),np.uint8)


#getting dialated image
dilated_im=cv2.dilate(BW_im,kernel,anchor=(0, 0),iterations=5)


#visualize them
cv2.namedWindow("Dilated Image", cv2.WINDOW_NORMAL);
cv2.imshow("Dilated Image",dilated_im)

cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

Morphological Erosion

Erosion is basically the inverse of dilation. It deletes the values of the pixels of the image we provide.

Same as above, Erosion takes mainly two inputs.
1. The image that we need to apply the erosion in Black and White.
2. Kernel / Structural element.

Same to the process of dilation, we must define a kernel. Let’s take the same example from above.

Example 2.1: 3x3 unit matrix. Let our anchor be (1,1).

3x3 unit matrix

Process of erosion

This kernel runs over every pixel of the image. If all the pixels that are overlapping with the kernel happen to be ‘1’s, no change occurs. But, if any of the overlapping pixels happen to be ‘0’ the pixel that is overlapping with the (1,1) element of the kernel is set to ‘0’.

Erosion process

Example 2.2: Applying erosion to a bigger image.

Mathematical-Morphology Erosion

Erosion with OpenCV

First, read the images and visualize them. Notice that, here we want to convert the ‘1’ values to ‘0’ values. Hence our region of interest should be ‘white’ or ‘1’. There’s no need to use THRESH_BINARY_INV as previously. Just THRESH_BINARY is good.

import cv2
import numpy as np
import matplotlib.pyplot as plt

#read image
im=cv2.imread("generative_ai.png")
#read grey scale image
gray_im=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
#read BW image
thresh,BW_im = cv2.threshold(gray_im,150,255,cv2.THRESH_BINARY)

#visualize them
cv2.namedWindow("Original Image", cv2.WINDOW_NORMAL);
cv2.imshow("Original Image",im)
cv2.namedWindow("Gray scale image", cv2.WINDOW_NORMAL);
cv2.imshow("Gray scale image",gray_im)
cv2.namedWindow("BW Im", cv2.WINDOW_NORMAL);
cv2.imshow("BW Im",BW_im)
cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()
Reading the original, grey-scale and black-and-white images

Then we define the kernel and apply cv2.erode.

cv2.erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

#morphological erosion
#defining kernel
kernel = np.ones((3,3),np.uint8)

#getting eroded image
erod_im=cv2.dilate(BW_im,kernel,anchor=(0, 0),iterations=2)


#visualize resulted image
cv2.namedWindow("Eroded Image", cv2.WINDOW_NORMAL);
cv2.imshow("Eroded Image",erod_im)

cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

We can see already the unnecessary pixel values have been removed. Let us change the kernel and iteration values and see better outputs.

Variations of the kernel, iterations used and their outputs

Complete Code

import cv2
import numpy as np
import matplotlib.pyplot as plt

#read image
im=cv2.imread("generative_ai.png") #your image name
#read grey scale image
gray_im=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
#read BW image
thresh,BW_im = cv2.threshold(gray_im,150,255,cv2.THRESH_BINARY)

#morphological erosion
#defining kernel
kernel = np.ones((3,3),np.uint8)

#getting eroded image
erod_im=cv2.dilate(BW_im,kernel,anchor=(0, 0),iterations=3)


#visualize resulted image
cv2.namedWindow("Eroded Image", cv2.WINDOW_NORMAL);
cv2.imshow("Eroded Image",erod_im)

cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

Thanks For Reading, Follow For More.
Happy learning!

Connect with me on Linkedin

--

--