Morphological operations

OpenCV · Practical · 10 min

Real thresholded masks are full of specks and holes; morphology fixes them before you count or measure objects.

Morphology treats a binary mask as a set of shapes and grows or shrinks them with a structuring element (kernel), created with cv2.getStructuringElement(cv2.MORPH_RECT | MORPH_ELLIPSE | MORPH_CROSS, (w, h)).

  • cv2.erode: a pixel stays white only if the whole kernel fits inside the white region. Shapes shrink, thin bridges break, small specks vanish.
  • cv2.dilate: a pixel becomes white if the kernel touches any white. Shapes grow, small holes and gaps fill.

Combinations via cv2.morphologyEx(mask, op, kernel):

  • MORPH_OPEN (erode then dilate): removes small white noise while mostly keeping object size
  • MORPH_CLOSE (dilate then erode): fills small black holes and joins nearby fragments
  • MORPH_GRADIENT: dilation minus erosion, an outline of each shape
  • MORPH_TOPHAT and MORPH_BLACKHAT: extract small bright or dark details relative to the background

The kernel size sets the scale of what gets removed: an opening with a 5x5 kernel deletes anything thinner than about 5 pixels. iterations= repeats the operation.

The conventions assume white foreground on black background. If your objects are dark, invert the mask first (or use THRESH_BINARY_INV), otherwise erode and dilate swap meanings.

Example

import cv2
import numpy as np

mask = np.zeros((40, 40), np.uint8)
mask[10:30, 10:30] = 255          # the object
mask[15, 15] = 0                  # a hole inside it
mask[2, 35] = 255                 # a stray speck

k = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
opened = cv2.morphologyEx(mask, cv2.MORPH_OPEN, k)
print(opened[2, 35])              # 0    speck removed
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, k)
print(closed[15, 15])             # 255  hole filled

print((mask == 255).sum())                     # 400
print((cv2.erode(mask, k) == 255).sum())       # fewer: shapes shrank
print((cv2.dilate(mask, k) == 255).sum())      # more: shapes grew
edge = cv2.morphologyEx(closed, cv2.MORPH_GRADIENT, k)
print(edge[20, 10], edge[20, 20])              # 255 0  outline only

Remember

  • Erode shrinks white regions, dilate grows them
  • Opening removes small specks, closing fills small holes
  • Kernel size sets the scale of what gets removed
  • Morphology assumes white objects on black

Watch out for

  • Applying morphology to dark-on-light masks without inverting, which reverses the effect.

Check yourself

  1. After thresholding, a mask has many isolated white dots around the real objects. Which operation removes them?
  2. What does closing (`MORPH_CLOSE`) do first?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.opencv.org/4.x/d9/d61/tutorial_py_morphological_ops.html