Segmentation is the process of dividing an image into meaningful regions to separate objects from the background for further analysis. This is often achieved by applying morphological operations which is a shape-based techniques primarily performed on binary images.
Among these, dilation and erosion are two fundamental operators used to refine object boundaries and remove noise within segmented regions.
Step 1: Import the required libraries
Import Numpy, OpenCV and Matplotlib.
import numpy as np
import cv2
from matplotlib import pyplot as plt
from google.colab import files
Step 2: Upload and Decode the Image
Upload an image file and convert the raw file data into an image array for processing.
uploaded = files.upload()
for file in uploaded.keys():
img = cv2.imdecode(np.frombuffer(
uploaded[file], np.uint8), cv2.IMREAD_COLOR)
Step 3: Convert to Greyscale and Blur
- Convert the image to grayscale for easier processing.
- Use Gaussian blur to reduce noise and help with more accurate segmentation.
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
Step 4: Apply OTSU's Thresholding
OTSU's thresholding is an automatic method that determines the optimal threshold value to separate the foreground and background in a grayscale image by maximizing the difference between the two classes.
- Automatically binarize the image to separate foreground (objects) from background.
- Display the binary image using matplotlib.
ret, thresh = cv2.threshold(
gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
plt.imshow(thresh, cmap='gray')
plt.axis('off')
plt.show()
Output:

Step 5: Morphological Closing and Background Dilation
- Perform closing to fill small holes and unify nearby regions.
- Dilate the closed image to further expand object areas and clarify boundaries.
kernel = np.ones((9, 9), np.uint8)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)
bg = cv2.dilate(closing, kernel, iterations=2)
Step 6: Contour Filtering and Object Mask Creation
- Find external contours (outlines of main objects).
- Keep only contours with area greater than 1,000 pixels, filtering out small noise.
- Fill these contours into a new mask to isolate major object regions.
contours, _ = cv2.findContours(
closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
result = np.zeros(gray.shape, dtype=np.uint8)
for contour in contours:
if cv2.contourArea(contour) > 1000:
cv2.fillPoly(result, [contour], 255)
Step 7: Morphological Opening and Erosion
- Apply opening to remove small foreground artefacts.
- Finish by eroding to refine and clean up the boundaries of the segmented objects.
kernel_open = np.ones((6, 6), np.uint8)
opened_result = cv2.morphologyEx(
result, cv2.MORPH_OPEN, kernel_open, iterations=2)
kernel_erode = np.ones((9, 9), np.uint8)
final_result = cv2.erode(opened_result, kernel_erode, iterations=2)
Step 8: Result
final_result = cv2.erode(opened_result, kernel_erode, iterations=2)
plt.imshow(final_result, cmap='gray')
plt.axis('off')
plt.show()
Output:

By combining thresholding with targeted morphological operations, we can efficiently segment and extract meaningful objects from an image, removing noise and clarifying boundaries.
You can download source code from here.