Shrink, grow, clean and outline shapes by sliding a small shape over the image.
Morphological operations process images based on shapes. They work on binary (black/white) or grayscale images.
A small kernel that defines the neighbourhood shape: rectangle, ellipse, cross or custom.
min(neighbourhood)Shrinks bright regions, removes small white noise, disconnects objects.
max(neighbourhood)Expands bright regions, fills small holes, connects nearby objects.
Each pixel takes the minimum of its neighbourhood: white regions shrink and small white noise disappears.
cv2.erode, 5×5
import cv2
import numpy as np
# binary image
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# create structuring element
kernel = np.ones((5, 5), np.uint8) # ans:
# ans: can also use cv2.getStructuringElement()
# erosion
eroded = cv2.erode(binary, kernel, iterations=1) # ans:
# ans: iterations: number of times to apply
# effect: shrinks white regions, removes small noise
# ans: larger kernel or more iterations = more erosion
Each pixel takes the maximum of its neighbourhood: white regions grow and small black holes fill in.
cv2.dilate, 5×5
# dilation
dilated = cv2.dilate(binary, kernel, iterations=1) # ans:
# effect: expands white regions, fills small holes
# ans: larger kernel or more iterations = more dilation
# multiple iterations
dilated = cv2.dilate(binary, kernel, iterations=3)
# ans: equivalent to applying it 3 times
Opening = erosion followed by dilation.
kernel = np.ones((5, 5), np.uint8)
# opening = erosion + dilation
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # ans:
# equivalent to:
eroded = cv2.erode(binary, kernel) # ans:
opening = cv2.dilate(eroded, kernel) # ans:
# removes small white noise, preserves large shapes
Closing = dilation followed by erosion.
# closing = dilation + erosion
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) # ans:
# equivalent to:
dilated = cv2.dilate(binary, kernel) # ans:
closing = cv2.erode(dilated, kernel) # ans:
# fills small black holes in white regions
Gradient = dilation − erosion. It outlines the edges of objects.
# morphological gradient
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel) # ans:
# equivalent to:
dilated = cv2.dilate(binary, kernel) # ans:
eroded = cv2.erode(binary, kernel) # ans:
gradient = dilated - eroded # ans:
# highlights object boundaries
Top hat = image − opening(image). It extracts small bright features.
# top hat
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, kernel) # ans:
# extracts bright regions smaller than the kernel
# ans: useful for bright objects on a dark background
Black hat = closing(image) − image. It extracts small dark features.
# black hat
blackhat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, kernel) # ans:
# extracts dark regions smaller than the kernel
# ans: useful for dark objects on a bright background
# rectangular kernel
rect = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # ans:
# elliptical kernel
ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # ans:
# cross-shaped kernel
cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # ans:
# custom kernel
custom = np.array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]], np.uint8)
# ans: cross-shaped, like a 3x3 MORPH_CROSS
Answer first, then press → to check.
What is erosion?
It shrinks bright regions: each pixel becomes its neighbourhood minimum.
What is dilation?
It expands bright regions: each pixel becomes its neighbourhood maximum.
What is opening?
Erosion then dilation; it removes noise.
What is closing?
Dilation then erosion; it fills holes.
What is the morphological gradient?
Dilation minus erosion; it outlines edges.
When to use opening?
To remove small white noise while preserving object shape.
When to use closing?
To fill small holes and connect broken parts.
What is top hat?
img − opening: it extracts small bright features.
What is black hat?
closing − img: it extracts small dark features.
The effect of a larger kernel?
A more aggressive operation that affects larger structures.
# erosion with 3x3 kernel
kernel = np.ones((3, 3), np.uint8)
eroded = cv2.erode(binary, kernel, iterations=1) # ans:
# dilation with 5x5 kernel
kernel = np.ones((5, 5), np.uint8)
dilated = cv2.dilate(binary, kernel, iterations=1) # ans:
# opening
kernel = np.ones((5, 5), np.uint8)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # ans:
# closing
kernel = np.ones((5, 5), np.uint8)
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) # ans:
# morphological gradient
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel) # ans:
# elliptical kernel
ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) # ans:
result = cv2.morphologyEx(binary, cv2.MORPH_OPEN, ellipse) # ans:
# top hat with 9x9 kernel
kernel = np.ones((9, 9), np.uint8)
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, kernel) # ans:
# black hat
blackhat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, kernel) # ans:
# multiple erosions
kernel = np.ones((3, 3), np.uint8)
eroded = cv2.erode(binary, kernel, iterations=3) # ans:
# cross-shaped structuring element
cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # ans:
result = cv2.morphologyEx(binary, cv2.MORPH_OPEN, cross) # ans: