Region Processing

Connected Components

Connected component: Group of connected pixels with same value

Connectivity:

  • 4-connectivity: Up, down, left, right
  • 8-connectivity: 4-connected + diagonals

Use case: Count objects, label regions, extract features

Connected Components Labeling

  1. import cv2
  2. import numpy as np
  3. # binary image
  4. _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
  5. # label connected components
  6. #ans: num_labels, labels = cv2.connectedComponents(binary)
  7. #ans: num_labels includes background (label 0)
  8. #ans: actual objects = num_labels - 1
  9. # labels is same size as binary
  10. #ans: each pixel has label (0=background, 1,2,3...=objects)
  11. # visualize labels
  12. #ans: labels_vis = np.uint8(255 * labels / labels.max())

Connected Components with Stats

  1. # get statistics for each component
  2. #ans: num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8)
  3. # stats: [x, y, width, height, area] for each label
  4. #ans: for i in range(1, num_labels):
  5. #ans: x, y, w, h, area = stats[i]
  6. #ans: cx, cy = centroids[i]
  7. #ans: print(f"Object {i}: area={area}, center=({cx}, {cy})")
  8. # filter by area
  9. #ans: min_area = 100
  10. #ans: large_components = [i for i in range(1, num_labels) if stats[i, cv2.CC_STAT_AREA] > min_area]

Contour Properties

Contour: Boundary of region

Properties:

  • Area
  • Perimeter
  • Bounding rectangle
  • Centroid
  • Convex hull
  • Moments

Contour Area & Perimeter

  1. contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  2. #ans: for cnt in contours:
  3. # area
  4. #ans: area = cv2.contourArea(cnt)
  5. # perimeter
  6. #ans: perimeter = cv2.arcLength(cnt, True)
  7. #ans: True means closed contour
  8. # aspect ratio
  9. #ans: x, y, w, h = cv2.boundingRect(cnt)
  10. #ans: aspect_ratio = float(w) / h

Bounding Rectangle

  1. # straight bounding rectangle
  2. #ans: x, y, w, h = cv2.boundingRect(cnt)
  3. #ans: cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
  4. # minimum area rectangle (can be rotated)
  5. #ans: rect = cv2.minAreaRect(cnt)
  6. #ans: box = cv2.boxPoints(rect)
  7. #ans: box = np.int0(box)
  8. #ans: cv2.drawContours(img, [box], 0, (0, 0, 255), 2)

Centroid

Centroid: Center of mass of region

Using moments:

  1. # compute moments
  2. #ans: M = cv2.moments(cnt)
  3. # centroid coordinates
  4. #ans: if M['m00'] != 0:
  5. #ans: cx = int(M['m10'] / M['m00'])
  6. #ans: cy = int(M['m01'] / M['m00'])
  7. #ans: else:
  8. #ans: cx, cy = 0, 0
  9. # draw centroid
  10. #ans: cv2.circle(img, (cx, cy), 5, (255, 0, 0), -1)

Convex Hull

Convex hull: Smallest convex polygon containing contour

Convexity defects: Gaps between contour and hull

  1. # compute convex hull
  2. #ans: hull = cv2.convexHull(cnt)
  3. #ans: cv2.drawContours(img, [hull], 0, (0, 255, 0), 2)
  4. # check if contour is convex
  5. #ans: is_convex = cv2.isContourConvex(cnt)
  6. # convexity defects
  7. #ans: hull = cv2.convexHull(cnt, returnPoints=False)
  8. #ans: defects = cv2.convexityDefects(cnt, hull)

Shape Matching

matchShapes: Compare two contours

Methods:

  • Hu moments (rotation/scale invariant)
  • Shape distance metrics
  1. # compare two contours
  2. cnt1 = contours[0]
  3. cnt2 = contours[1]
  4. #ans: distance = cv2.matchShapes(cnt1, cnt2, cv2.CONTOURS_MATCH_I1, 0)
  5. #ans: lower distance = more similar shapes
  6. #ans: 0 means identical, larger = more different

Approximating Contours

approxPolyDP: Simplify contour to polygon

Douglas-Peucker algorithm: Reduces number of points

  1. # approximate contour
  2. epsilon = 0.01 * cv2.arcLength(cnt, True)
  3. #ans: approx = cv2.approxPolyDP(cnt, epsilon, True)
  4. #ans: epsilon: approximation accuracy (smaller = more accurate)
  5. # detect shapes by number of vertices
  6. #ans: if len(approx) == 3:
  7. #ans: shape = "Triangle"
  8. #ans: elif len(approx) == 4:
  9. #ans: shape = "Rectangle"
  10. #ans: elif len(approx) > 4:
  11. #ans: shape = "Circle"

Exercises - Part 1 (Concepts)

  1. # what is connected component?
  2. #ans: group of connected pixels with same value
  3. # 4 vs 8 connectivity?
  4. #ans: 4 uses up/down/left/right, 8 adds diagonals
  5. # what is contour?
  6. #ans: boundary of shape/region
  7. # what is centroid?
  8. #ans: center of mass of region
  9. # what is convex hull?
  10. #ans: smallest convex polygon containing contour

Exercises - Part 2 (Concepts)

  1. # what are moments?
  2. #ans: mathematical properties describing shape
  3. # how to compute centroid from moments?
  4. #ans: cx = M10/M00, cy = M01/M00
  5. # what is approxPolyDP?
  6. #ans: simplifies contour to polygon with fewer points
  7. # what is shape matching?
  8. #ans: comparing similarity of two contours

Exercises - Part 3 (Coding)

  1. # label connected components
  2. _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
  3. #ans: num_labels, labels = cv2.connectedComponents(binary)
  4. #ans: print(f"Found {num_labels - 1} objects")
  5. # connected components with stats
  6. #ans: num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary)
  7. #ans: for i in range(1, num_labels):
  8. #ans: area = stats[i, cv2.CC_STAT_AREA]
  9. #ans: cx, cy = centroids[i]

Exercises - Part 4 (Coding)

  1. # find and draw contours
  2. contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  3. #ans: for cnt in contours:
  4. #ans: area = cv2.contourArea(cnt)
  5. #ans: if area > 100:
  6. #ans: x, y, w, h = cv2.boundingRect(cnt)
  7. #ans: cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

Exercises - Part 5 (Mixed)

  1. # compute centroid
  2. M = cv2.moments(cnt)
  3. #ans: cx = int(M['m10'] / M['m00'])
  4. #ans: cy = int(M['m01'] / M['m00'])
  5. #ans: cv2.circle(img, (cx, cy), 5, (255, 0, 0), -1)
  6. # convex hull
  7. #ans: hull = cv2.convexHull(cnt)
  8. #ans: cv2.drawContours(img, [hull], 0, (0, 255, 0), 2)
  9. # approximate contour
  10. epsilon = 0.02 * cv2.arcLength(cnt, True)
  11. #ans: approx = cv2.approxPolyDP(cnt, epsilon, True)
  12. #ans: print(f"Vertices: {len(approx)}")

Google tag (gtag.js)