Morphological Operations
Morphological Operations¶
Overview¶
Morphological Operations are operations based on the shape of binary or grayscale images. They are mainly used for noise removal, object separation, hole filling, and more. This document covers everything from the concept of structuring elements to various applications of morphological operations.
Difficulty: ββ (Beginner-Intermediate)
Learning Objectives: - Understanding Structuring Elements - Erosion and Dilation operations - Opening and Closing operations - Gradient, Top-hat, and Black-hat operations - Noise removal and object separation applications
Table of Contents¶
- Morphological Operations Overview
- Structuring Element - getStructuringElement()
- Erosion - erode()
- Dilation - dilate()
- Opening and Closing - morphologyEx()
- Gradient, Top-hat, Black-hat
- Practical Applications
- Exercises
- Next Steps
- References
1. Morphological Operations Overview¶
What is Morphology?¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Morphological Operations Overview β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Morphology = Study of shape β
β Operations based on the shape of images β
β β
β Main Uses: β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 1. Noise removal - Remove small noise dots β β
β β 2. Hole filling - Fill holes inside objects β β
β β 3. Object separation - Separate connected objects β β
β β 4. Object connection - Connect disconnected parts β β
β β 5. Edge detection - Morphological gradient β β
β β 6. Skeletonization - Extract object skeleton β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Basic Operations: β
β - Erosion: Shrink objects β
β - Dilation: Expand objects β
β β
β Combined Operations: β
β - Opening = Erosion β Dilation β
β - Closing = Dilation β Erosion β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Operating Principle¶
Morphological operations determine pixel values by moving a small mask called a Structuring Element across the image.
2. Structuring Element - getStructuringElement()¶
What is a Structuring Element?¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Structuring Element β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Structuring Element = A small binary matrix used in operationsβ
β β
β Main Shapes: β
β β
β MORPH_RECT (Rectangle) MORPH_CROSS (Cross) MORPH_ELLIPSE β
β βββββ¬ββββ¬ββββ βββββ¬ββββ¬ββββ βββββ¬ββββ¬ββββ β
β β 1 β 1 β 1 β β 0 β 1 β 0 β β 0 β 1 β 0 β β
β βββββΌββββΌββββ€ βββββΌββββΌββββ€ βββββΌββββΌββββ€ β
β β 1 β 1 β 1 β β 1 β 1 β 1 β β 1 β 1 β 1 β β
β βββββΌββββΌββββ€ βββββΌββββΌββββ€ βββββΌββββΌββββ€ β
β β 1 β 1 β 1 β β 0 β 1 β 0 β β 0 β 1 β 0 β β
β βββββ΄ββββ΄ββββ βββββ΄ββββ΄ββββ βββββ΄ββββ΄ββββ β
β All directions Vertical/Horizontal Elliptical β
β β
β Effect by Size: β
β - Small size (3x3): Fine processing β
β - Large size (7x7, 9x9): Strong effect β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Creating Structuring Elements¶
import cv2
import numpy as np
# getStructuringElement(shape, ksize, anchor=(-1,-1))
# shape: Structuring element shape
# ksize: (width, height) size
# anchor: Reference point (default: center)
# Rectangle
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
print("RECT (5x5):\n", rect_kernel)
# Cross
cross_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
print("\nCROSS (5x5):\n", cross_kernel)
# Ellipse
ellipse_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
print("\nELLIPSE (5x5):\n", ellipse_kernel)
# Custom structuring element
custom_kernel = np.array([
[0, 1, 0],
[1, 1, 1],
[0, 1, 0]
], dtype=np.uint8)
Visualizing Structuring Elements¶
import cv2
import numpy as np
import matplotlib.pyplot as plt
shapes = [
('RECT', cv2.MORPH_RECT),
('CROSS', cv2.MORPH_CROSS),
('ELLIPSE', cv2.MORPH_ELLIPSE)
]
sizes = [(5, 5), (7, 7), (11, 11)]
fig, axes = plt.subplots(len(shapes), len(sizes), figsize=(12, 10))
for i, (name, shape) in enumerate(shapes):
for j, size in enumerate(sizes):
kernel = cv2.getStructuringElement(shape, size)
axes[i, j].imshow(kernel, cmap='gray')
axes[i, j].set_title(f'{name} {size}')
axes[i, j].axis('off')
plt.tight_layout()
plt.show()
3. Erosion - erode()¶
Erosion Operation Principle¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Erosion β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Principle: β
β - Move the structuring element across the image β
β - Set center pixel to 1 only if all pixels under the β
β structuring element are 1 β
β - If any pixel is 0, center pixel becomes 0 β
β β
β Effect: β
β - Shrinks foreground (white) area β
β - Removes small noise β
β - Separates connected objects β
β - Smooths boundaries β
β β
β Example: β
β Original: After Erosion (3x3): β
β βββββββββββββββ βββββββββββββββ β
β β βββββββββββββ β ββββββββ β β
β β βββββββββββββ βββΆ β ββββββββ β β
β β βββββββββββββ β ββββββββ β β
β β βββββββββββββ β β β
β βββββββββββββββ βββββββββββββββ β
β Borders shrink by 1 pixel β
β β
β Noise Removal: β
β βββββββββββββββ βββββββββββββββ β
β β ββ β ββββ β β ββ βββ β β
β β ββββ ββββ β βββΆ β ββ ββ β Small dots (β ) removed β
β β β ββββ β β βββ β β
β βββββββββββββββ βββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Using Erosion¶
import cv2
import numpy as np
# Prepare binary image
img = cv2.imread('binary_image.png', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# Create structuring element
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# erode(src, kernel, iterations=1)
# iterations: Number of iterations (default 1)
eroded_1 = cv2.erode(binary, kernel, iterations=1)
eroded_2 = cv2.erode(binary, kernel, iterations=2)
eroded_3 = cv2.erode(binary, kernel, iterations=3)
cv2.imshow('Original', binary)
cv2.imshow('Eroded 1x', eroded_1)
cv2.imshow('Eroded 2x', eroded_2)
cv2.imshow('Eroded 3x', eroded_3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Creating Erosion Test Images¶
import cv2
import numpy as np
# Create test image
img = np.zeros((300, 400), dtype=np.uint8)
# Large rectangle
cv2.rectangle(img, (50, 50), (150, 150), 255, -1)
# Small noise dots
for _ in range(50):
x, y = np.random.randint(200, 350), np.random.randint(50, 250)
cv2.circle(img, (x, y), 2, 255, -1)
# Connected circles
cv2.circle(img, (280, 150), 40, 255, -1)
cv2.circle(img, (320, 150), 40, 255, -1)
# Apply erosion
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
eroded = cv2.erode(img, kernel, iterations=1)
cv2.imshow('Original', img)
cv2.imshow('Eroded', eroded)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. Dilation - dilate()¶
Dilation Operation Principle¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Dilation β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Principle: β
β - Move the structuring element across the image β
β - Set center pixel to 1 if any pixel under the β
β structuring element is 1 β
β - Opposite of erosion β
β β
β Effect: β
β - Expands foreground (white) area β
β - Fills holes β
β - Connects broken parts β
β - Emphasizes objects β
β β
β Example: β
β Original: After Dilation (3x3): β
β βββββββββββββββ βββββββββββββββ β
β β ββββββ β β βββββββββββββ β
β β ββββββ β βββΆ β βββββββββββββ β
β β ββββββ β β βββββββββββββ β
β βββββββββββββββ βββββββββββββββ β
β Borders expand by 1 pixel β
β β
β Connect Broken Parts: β
β βββββββββββββββ βββββββββββββββ β
β β ββ ββ β β ββββ βββββ β
β β ββ .. ββ β βββΆ β ββββββββββ β Dotted line connected β
β β ββ ββ β β ββββ βββββ β
β βββββββββββββββ βββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Using Dilation¶
import cv2
import numpy as np
# Prepare binary image
img = cv2.imread('binary_image.png', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# dilate(src, kernel, iterations=1)
dilated_1 = cv2.dilate(binary, kernel, iterations=1)
dilated_2 = cv2.dilate(binary, kernel, iterations=2)
dilated_3 = cv2.dilate(binary, kernel, iterations=3)
cv2.imshow('Original', binary)
cv2.imshow('Dilated 1x', dilated_1)
cv2.imshow('Dilated 2x', dilated_2)
cv2.imshow('Dilated 3x', dilated_3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Comparing Erosion and Dilation¶
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Test image
img = np.zeros((200, 200), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (150, 150), 255, -1)
cv2.circle(img, (100, 100), 20, 0, -1) # Inner hole
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
eroded = cv2.erode(img, kernel, iterations=1)
dilated = cv2.dilate(img, kernel, iterations=1)
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
axes[0].imshow(img, cmap='gray')
axes[0].set_title('Original')
axes[1].imshow(eroded, cmap='gray')
axes[1].set_title('Eroded (Shrink)')
axes[2].imshow(dilated, cmap='gray')
axes[2].set_title('Dilated (Expand)')
for ax in axes:
ax.axis('off')
plt.tight_layout()
plt.show()
5. Opening and Closing - morphologyEx()¶
Opening¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Opening β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Opening = Erosion β Dilation β
β β
β Effect: β
β - Removes small noise (dots) β
β - Maintains overall object size approximately β
β - Breaks thin connections β
β β
β Original Erosion Dilation (Opening result) β
β ββββββββ ββββββββ ββββββββ β
β βββ β ββ ββ β βββ ββ β
β ββββββββ ββΆ β ββββ β ββΆ ββββββββ β
β β β βββ β β β β βββ β
β ββββββββ ββββββββ ββββββββ β
β Small dots (β ) removed β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Closing¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Closing β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Closing = Dilation β Erosion β
β β
β Effect: β
β - Fills small holes β
β - Maintains overall object size approximately β
β - Connects broken parts β
β β
β Original Dilation Erosion (Closing result) β
β ββββββββ ββββββββ ββββββββ β
β ββββββββ ββββββββ ββββββββ β
β ββββ βββ ββΆ ββββββββ ββΆ ββββββββ β
β ββββββββ ββββββββ ββββββββ β
β ββββββββ ββββββββ ββββββββ β
β Inner hole (β) filled β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Using morphologyEx()¶
import cv2
import numpy as np
img = cv2.imread('binary_image.png', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# morphologyEx(src, op, kernel, iterations=1)
# op: Operation type
# Opening: Noise removal
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# Closing: Hole filling
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
# Opening then Closing (Noise removal + Hole filling)
clean = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
clean = cv2.morphologyEx(clean, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Original', binary)
cv2.imshow('Opening', opening)
cv2.imshow('Closing', closing)
cv2.imshow('Open + Close', clean)
cv2.waitKey(0)
cv2.destroyAllWindows()
Comparing Opening and Closing Test¶
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Test image: Rectangle with noise + holes
img = np.zeros((200, 200), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (150, 150), 255, -1)
# Add noise (small dots)
noise = img.copy()
for _ in range(30):
x, y = np.random.randint(10, 45), np.random.randint(10, 190)
cv2.circle(noise, (x, y), 2, 255, -1)
for _ in range(30):
x, y = np.random.randint(155, 190), np.random.randint(10, 190)
cv2.circle(noise, (x, y), 2, 255, -1)
# Add holes (inside object)
holes = noise.copy()
for _ in range(10):
x, y = np.random.randint(60, 140), np.random.randint(60, 140)
cv2.circle(holes, (x, y), 3, 0, -1)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
opening = cv2.morphologyEx(holes, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(holes, cv2.MORPH_CLOSE, kernel)
both = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel)
fig, axes = plt.subplots(2, 2, figsize=(10, 10))
axes[0, 0].imshow(holes, cmap='gray')
axes[0, 0].set_title('Original (Noise + Holes)')
axes[0, 1].imshow(opening, cmap='gray')
axes[0, 1].set_title('Opening (Noise Removed)')
axes[1, 0].imshow(closing, cmap='gray')
axes[1, 0].set_title('Closing (Holes Filled)')
axes[1, 1].imshow(both, cmap='gray')
axes[1, 1].set_title('Open + Close')
for ax in axes.flatten():
ax.axis('off')
plt.tight_layout()
plt.show()
6. Gradient, Top-hat, Black-hat¶
Morphological Gradient¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Morphological Gradient β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Gradient = Dilation - Erosion β
β β
β Effect: Extract object outline (boundary) β
β β
β Original Dilation Erosion β
β ββββββββ ββββββββ ββββββββ β
β β ββββ β ββββββββ β ββ β β
β β ββββ β - ββββββββ = β ββ β β
β β ββββ β ββββββββ β ββ β β
β ββββββββ ββββββββ ββββββββ β
β β
β Gradient Result: β
β ββββββββ β
β β ββββ β β Only outline remains β
β β β β β β
β β ββββ β β
β ββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Top-hat and Black-hat¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Top-hat / Black-hat β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Top-hat = Original - Opening β
β - Extract small bright parts from bright areas β
β - Detect small objects brighter than background β
β β
β Black-hat = Closing - Original β
β - Extract small dark parts from dark areas β
β - Detect small holes/objects darker than background β
β β
β Applications: β
β - Correct images with uneven illumination β
β - Remove shadows from document images β
β - Detect small defects β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Implementation and Usage¶
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))
# Morphological gradient
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
# Top-hat
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
# Black-hat
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
# Manual calculation (for verification)
dilated = cv2.dilate(img, kernel)
eroded = cv2.erode(img, kernel)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
gradient_manual = dilated - eroded
tophat_manual = img - opening
blackhat_manual = closing - img
# Visualization
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes[0, 0].imshow(img, cmap='gray')
axes[0, 0].set_title('Original')
axes[0, 1].imshow(gradient, cmap='gray')
axes[0, 1].set_title('Gradient (Edge)')
axes[0, 2].imshow(tophat, cmap='gray')
axes[0, 2].set_title('Top Hat (Bright spots)')
axes[1, 0].imshow(blackhat, cmap='gray')
axes[1, 0].set_title('Black Hat (Dark spots)')
# Enhance contrast using top-hat + black-hat
enhanced = cv2.add(img, tophat)
enhanced = cv2.subtract(enhanced, blackhat)
axes[1, 1].imshow(enhanced, cmap='gray')
axes[1, 1].set_title('Enhanced (Top+Black Hat)')
for ax in axes.flatten():
ax.axis('off')
axes[1, 2].axis('off')
plt.tight_layout()
plt.show()
Summary of All Morphological Operations¶
import cv2
# List of operations available in morphologyEx()
operations = {
cv2.MORPH_ERODE: "Erode",
cv2.MORPH_DILATE: "Dilate",
cv2.MORPH_OPEN: "Open (Erode + Dilate)",
cv2.MORPH_CLOSE: "Close (Dilate + Erode)",
cv2.MORPH_GRADIENT: "Gradient (Dilate - Erode)",
cv2.MORPH_TOPHAT: "Top Hat (Src - Open)",
cv2.MORPH_BLACKHAT: "Black Hat (Close - Src)",
cv2.MORPH_HITMISS: "Hit-Miss (Pattern Matching)"
}
for op, name in operations.items():
print(f"{op}: {name}")
7. Practical Applications¶
Noise Removal Pipeline¶
import cv2
import numpy as np
def remove_noise_morphology(binary_img, noise_size=3):
"""
Remove noise using morphological operations
Parameters:
- binary_img: Binary image
- noise_size: Maximum size of noise to remove
"""
# Kernel size = noise size * 2 + 1
kernel_size = noise_size * 2 + 1
kernel = cv2.getStructuringElement(
cv2.MORPH_ELLIPSE, (kernel_size, kernel_size)
)
# Opening to remove small noise dots
cleaned = cv2.morphologyEx(binary_img, cv2.MORPH_OPEN, kernel)
# Closing to fill small holes
cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_CLOSE, kernel)
return cleaned
# Usage example
img = cv2.imread('noisy_document.png', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
cleaned = remove_noise_morphology(binary, noise_size=2)
Object Separation¶
import cv2
import numpy as np
def separate_objects(binary_img, erosion_iterations=3):
"""
Separate connected objects
"""
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# Erosion to shrink objects (break connections)
eroded = cv2.erode(binary_img, kernel, iterations=erosion_iterations)
# Distance transform to find center points (optional)
dist_transform = cv2.distanceTransform(eroded, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(
dist_transform, 0.5 * dist_transform.max(), 255, 0
)
sure_fg = np.uint8(sure_fg)
return eroded, sure_fg
# Usage example
img = cv2.imread('connected_circles.png', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
separated, centers = separate_objects(binary)
Document Image Preprocessing¶
import cv2
import numpy as np
def preprocess_document(img):
"""
Document image preprocessing (shadow removal + binarization)
"""
# Grayscale conversion
if len(img.shape) == 3:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
else:
gray = img
# Top-hat to extract bright background
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15))
# Black-hat to correct shadows/dark areas
blackhat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, kernel)
# Subtract black-hat from original (shadow removal effect)
no_shadow = cv2.add(gray, blackhat)
# Adaptive binarization
binary = cv2.adaptiveThreshold(
no_shadow, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 21, 15
)
# Noise removal
kernel_small = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel_small)
binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel_small)
return binary
# Usage example
img = cv2.imread('document_with_shadow.jpg')
result = preprocess_document(img)
Skeletonization¶
import cv2
import numpy as np
def skeletonize(img):
"""
Extract skeleton using morphological operations
"""
skeleton = np.zeros_like(img)
temp = img.copy()
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
while True:
# Opening operation
opened = cv2.morphologyEx(temp, cv2.MORPH_OPEN, kernel)
# Calculate difference
diff = cv2.subtract(temp, opened)
# Erosion
temp = cv2.erode(temp, kernel)
# Add to skeleton
skeleton = cv2.bitwise_or(skeleton, diff)
# Stop if no more white pixels
if cv2.countNonZero(temp) == 0:
break
return skeleton
# Usage example
img = cv2.imread('character.png', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
skeleton = skeletonize(binary)
8. Exercises¶
Exercise 1: Compare Structuring Element Effects¶
Apply erosion and dilation to the same binary image using three different structuring elements (RECT, CROSS, ELLIPSE) and analyze the differences in results.
Exercise 2: Adjust Character Thickness¶
Write a function to adjust the thickness of characters in handwriting images: - Positive value: Thicken with dilation - Negative value: Thin with erosion
def adjust_stroke_width(img, amount):
"""
amount > 0: Thicken
amount < 0: Thin
"""
pass
Exercise 3: Compare Boundary Extraction¶
Extract object boundaries using three different methods and compare: 1. Morphological gradient 2. Canny edge detection 3. findContours
Exercise 4: Braille Recognition Preprocessing¶
Design a preprocessing pipeline to detect individual dots in braille images. (Hint: Use erosion to separate dots)
Exercise 5: Cell Separation (Watershed Preprocessing)¶
Implement preprocessing to separate connected cells in microscope cell images: 1. Binarization 2. Noise removal (opening/closing) 3. Find sure background area (dilation) 4. Find sure foreground area (distance transform + threshold)
9. Next Steps¶
In 07_Thresholding.md, you'll learn various binarization methods and thresholding techniques!
Next Topics:
- Global thresholding (cv2.threshold)
- OTSU automatic threshold
- Adaptive thresholding
- HSV-based thresholding
10. References¶
Official Documentation¶
- erode() documentation
- dilate() documentation
- morphologyEx() documentation
- getStructuringElement() documentation
Related Materials¶
| File | Related Content |
|---|---|
| 05_Image_Filtering.md | Filtering basics |
| 09_Contours.md | Contour detection after preprocessing |