Python OpenCV - Morphological Operations
Python OpenCV Morphological operations are one of the Image processing techniques that processes image based on shape. This processing strategy is usually performed on binary images. Morphological operations based on OpenCV are as follows:
- Erosion
- Dilation
- Opening
- Closing
- Morphological Gradient
- Top hat
- Black hat
For all the above techniques the two important requirements are the binary image and a kernel structuring element that is used to slide across the image.
Images used for demonstration:


Erosion
Erosion is a morphological operation that shrinks the white foreground regions in a binary image (values 0 and 255). It removes noise and thins object boundaries. The extent of erosion depends on the size and shape of the kernel, typically defined using np.ones(), though custom kernels can also be used based on specific needs.
import cv2, numpy as np, matplotlib.pyplot as plt
img = cv2.imread(r"Downloads\test (2).png", 0) # Load grayscale image
bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1] # Binarize
k = np.ones((5, 5), np.uint8) # Define 5x5 kernel
inv = cv2.bitwise_not(bin)
out = cv2.erode(inv, k, 1)
plt.imshow(out, cmap='gray'), plt.axis('off'), plt.show()
Output:
The output should be a thinner image than the original one.

Explanation:
- Binarize with cv2.threshold(..., cv2.THRESH_BINARY + cv2.THRESH_OTSU) separates foreground and background.
- Create kernel using np.ones((5, 5), np.uint8) defines the erosion structure.
- Invert image with cv2.bitwise_not() makes the foreground white for erosion to work correctly.
- Apply erosion using cv2.erode() shrinks white areas to clean noise or disconnect objects.
- Display result with plt.imshow() and plt.axis('off') shows image without axes.
Dilation
Dilation is a morphological operation that expands the white foreground regions in a binary image (values 0 and 255). It thickens object boundaries and fills small holes or gaps. Like erosion, the effect depends on the kernel’s size and shape, usually defined using np.ones(). Dilation is the opposite of erosion it grows white areas instead of shrinking them.
import cv2, numpy as np, matplotlib.pyplot as plt
img = cv2.imread(r"Downloads\test (2).png", 0) # Load grayscale image
bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1] # Binarize
k = np.ones((3, 3), np.uint8) # Define 3x3 kernel
inv = cv2.bitwise_not(bin)
out = cv2.dilate(inv, k, 1)
plt.imshow(out, cmap='gray'), plt.axis('off'), plt.show()
Output
The output should be a thicker image than the original one.

Explanation:
- Binarize with cv2.threshold (..., cv2.THRESH_BINARY + cv2.THRESH_OTSU) separates foreground and background.
- Create kernel using np.ones((3, 3), np.uint8) defines the dilation structure.
- Invert image with cv2.bitwise_not() makes the foreground white for dilation to work.
- Apply dilation using cv2.dilate() expands white areas, fills gaps or connects objects.
- Display result with plt.imshow() and plt.axis('off') shows the dilated image cleanly.
Opening
Opening involves erosion followed by dilation in the outer surface (the foreground) of the image. All the above-said constraints for erosion and dilation applies here. It is a blend of the two prime methods. It is generally used to remove the noise in the image.
import cv2, numpy as np, matplotlib.pyplot as plt
img = cv2.imread(r"Downloads\test (2).png", 0) # Load grayscale
bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1] # Binarize
k = np.ones((3, 3), np.uint8) # 3x3 kernel
opened = cv2.morphologyEx(bin, cv2.MORPH_OPEN, k)
plt.imshow(opened, cmap='gray'), plt.axis('off'), plt.show()
Output

Explanation:
- cv2.imread() loads grayscale image.
- cv2.threshold() binarizes using Otsu’s method.
- np.ones() creates kernel for morphological op.
- cv2.morphologyEx(..., MORPH_OPEN, ...) erodes, then dilates.
- plt.imshow() displays cleaned result (noise removed).
Closing
Closing involves dilation followed by erosion in the outer surface (the foreground) of the image. All the above-said constraints for erosion and dilation applies here. It is a blend of the two prime methods. It is generally used to remove the noise in the image.
import cv2, numpy as np, matplotlib.pyplot as plt
img = cv2.imread(r"Downloads\test (2).png", 0)
bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
k = np.ones((3, 3), np.uint8)
closed = cv2.morphologyEx(bin, cv2.MORPH_CLOSE, k) # Apply closing
plt.imshow(closed, cmap='gray'), plt.axis('off'), plt.show()
Output

Explanation:
- cv2.imread() loads grayscale image.
- cv2.threshold() binarizes using Otsu’s method.
- np.ones() creates kernel for morphological op.
- cv2.morphologyEx(..., MORPH_OPEN, ...) erodes, then dilates.
- plt.imshow() displays cleaned result (noise removed).
Morphological Gradient
Morphological gradient is slightly different than the other operations, because, the morphological gradient first applies erosion and dilation individually on the image and then computes the difference between the eroded and dilated image. The output will be an outline of the given image.
import cv2, numpy as np, matplotlib.pyplot as plt
img = cv2.imread(r"path to your image", 0) # Load grayscale image
bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1] # Binarize
k = np.ones((3, 3), np.uint8) # Define 3x3 kernel
inv = cv2.bitwise_not(bin)
out = cv2.morphologyEx(inv, cv2.MORPH_GRADIENT, k)
plt.imshow(out, cmap='gray'), plt.axis('off'), plt.show()
Output

Explanation:
- Binarize image using cv2.threshold with Otsu’s method.
- Define 3×3 kernel using np.ones(...).
- Invert binary image to make foreground white.
- Apply cv2.morphologyEx with cv2.MORPH_GRADIENT to extract edges.
- Display result using plt.imshow and remove axes with plt.axis('off').
Top Hat
Top Hat is yet another morphological operation where Opening is performed on the binary image and the output of this operation is a difference between the input image and the opened image.
import cv2, numpy as np, matplotlib.pyplot as plt
img = cv2.imread("path", 0)
bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
k = np.ones((13, 13), np.uint8)
top = cv2.morphologyEx(bin, cv2.MORPH_TOPHAT, k)
plt.imshow(top, cmap='gray'), plt.axis('off'), plt.show()
Output

Explanation:
- Binarize with cv2.threshold(..., cv2.THRESH_BINARY + cv2.THRESH_OTSU) to segment image.
- Define kernel with np.ones((13, 13), np.uint8) for opening operation.
- Apply Top Hat using cv2.morphologyEx(..., cv2.MORPH_TOPHAT, ...) to subtract opened image.
- Display with plt.imshow(...), plt.axis('off') to show clean output.
Black Hat
The black-hat operation is used to do the opposite, enhancing dark objects of interest on a bright background. The output of this operation is the difference between the closing of the input image and the input image.
import cv2, numpy as np, matplotlib.pyplot as plt
img = cv2.imread("path", 0)
bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
inv = cv2.bitwise_not(bin)
k = np.ones((5, 5), np.uint8)
bh = cv2.morphologyEx(inv, cv2.MORPH_BLACKHAT, k)
plt.imshow(bh, cmap='gray'), plt.axis('off'), plt.show()
Output

Explanation:
- Binarize with cv2.threshold(..., cv2.THRESH_BINARY + cv2.THRESH_OTSU) to extract objects.
- Invert image using cv2.bitwise_not() to focus on dark features.
- Define kernel with np.ones((5, 5), np.uint8) for closing.
- Apply Black Hat using cv2.morphologyEx(..., cv2.MORPH_BLACKHAT, ...) to get dark outlines.
- Display using plt.imshow(...), plt.axis('off') for clean view.