Region Growing and Watershed - iffatAGheyas/computer-vision-handbook GitHub Wiki

✂️ Region Growing & Watershed Segmentation

Segmentation divides an image into meaningful regions. Two classic methods are Region Growing, which expands from seed points, and the Watershed algorithm, which treats an image as a topographic map and “floods” basins to find boundaries.


1. Region Growing

What Is It?
A seed‐based method that starts from one or more pixels and adds neighbouring pixels whose intensity or colour is similar enough.

Like dripping ink on tissue paper: it spreads until it hits a boundary.

How It Works

  1. Choose seed point(s)
  2. Compare neighbours: if a neighbour’s value is within a threshold, include it
  3. Repeat for each newly added pixel

Limitations

  • Requires manual or automatic seed selection
  • Not a single built-in OpenCV function (usually custom code or connected components)

2. Watershed Algorithm — Simulated Water Flow

What Is It?
Interprets the grayscale image as a topographic surface:

  • Bright regions → “hills”
  • Dark regions → “valleys”

Rain “floods” each valley; where floods meet, dams form—these dams become segment boundaries.

Steps in Watershed Segmentation

  1. Convert to grayscale
  2. Threshold (e.g. Otsu or edge detection)
  3. Remove noise with morphological opening
  4. Find sure background by dilation
  5. Find sure foreground via distance transform + threshold
  6. Mark unknown region (subtract foreground from background)
  7. Label markers with cv2.connectedComponents()
  8. Apply cv2.watershed() → boundaries marked as –1

Python Code: Watershed Example

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

# 1. Load and pre-process
img = cv2.imread("bird.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255,
                          cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 2. Noise removal
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN,
                           kernel, iterations=2)

# 3. Sure background
sure_bg = cv2.dilate(opening, kernel, iterations=3)

# 4. Sure foreground
dist = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist,
                           0.7 * dist.max(),
                           255, 0)
sure_fg = np.uint8(sure_fg)

# 5. Unknown region
unknown = cv2.subtract(sure_bg, sure_fg)

# 6. Marker labelling
_, markers = cv2.connectedComponents(sure_fg)
markers = markers + 1
markers[unknown == 255] = 0

# 7. Apply watershed
img_ws = img.copy()
markers = cv2.watershed(img_ws, markers)
img_ws[markers == -1] = [255, 0, 0]  # red boundary

# 8. Display
img_rgb = cv2.cvtColor(img_ws, cv2.COLOR_BGR2RGB)
plt.figure(figsize=(6, 6))
plt.imshow(img_rgb)
plt.title("Watershed Segmentation")
plt.axis("off")
plt.show()

image

Summary: Region Growing vs Watershed

Feature Region Growing Watershed
Type Seed-based expansion Topographic flooding
Requires Seed ✅ Yes (manual or auto) ✅ Yes (foreground/background)
Boundary Sharpness ❌ Not always sharp ✅ Sharp contour boundaries
OpenCV Support ❌ Custom implementation cv2.watershed()