CV Contours and Shapes

A contour is a curve that follows the boundary of an object. Picture tracing the coastline of an island with a pen. The pen path is the contour, and the island is the object. Contours let software count objects, measure sizes, and name shapes such as circles and rectangles.

The Contour Pipeline

Contour tools work best on clean black-and-white images, so a short preparation chain comes first.

Photo -> Grayscale -> Blur -> Threshold -> Find contours -> Measure and label

Blurring removes noise, thresholding separates objects from the background, and contour finding traces each white region.

Finding Contours

import cv2

img = cv2.imread("shapes.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
_, binary = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY_INV)

contours, hierarchy = cv2.findContours(
    binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

print("Objects found:", len(contours))
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

OpenCV expects white objects on a black background. The inverse threshold flag above flips dark shapes on paper into white shapes on black.

Measuring a Contour

Area and Perimeter

Area counts the pixels inside the outline. Perimeter measures the outline length.

area = cv2.contourArea(c)
perimeter = cv2.arcLength(c, True)

Bounding Shapes

Bounding shapes wrap an object in the simplest box or circle that fits.

Straight box        Rotated box        Enclosing circle
+---------+           /\                  .-"""-.
|  /\     |          /  \                /  /\   \
| /  \    |         /    \               | /  \   |
|/____\   |         \    /               \/____\ /
+---------+          \  /                 '-...-'
                      \/
x, y, w, h = cv2.boundingRect(c)
rect = cv2.minAreaRect(c)
(cx, cy), radius = cv2.minEnclosingCircle(c)

Center Point

Image moments reveal the centroid, which is the balance point of the shape.

M = cv2.moments(c)
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])

Naming Shapes With Corners

The function approxPolyDP simplifies a wiggly outline into a few straight edges. Counting the corners of the simplified outline names the shape.

Corners   Likely Shape
-------   ----------------------------------
3         Triangle
4         Rectangle (square if width = height)
5         Pentagon
8 to 12+  Circle or oval
epsilon = 0.02 * cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, epsilon, True)
corners = len(approx)

A smaller epsilon keeps more detail, and a larger epsilon simplifies more aggressively. A value near two percent of the perimeter suits most clean shapes.

Circularity Check

Many corners can describe a circle or a star. Circularity separates them with a single number.

circularity = 4 x pi x area / perimeter^2

Perfect circle  -> 1.0
Square          -> about 0.78
Thin rod        -> close to 0

Convex Hull

The convex hull is the tightest rubber band stretched around a shape. Comparing the contour area with the hull area measures how many dents a shape has. Hand gesture tools use this idea to count raised fingers.

hull = cv2.convexHull(c)
solidity = cv2.contourArea(c) / cv2.contourArea(hull)

Nested Shapes and Hierarchy

A donut has an outer outline and an inner hole outline. Retrieval modes control which outlines OpenCV returns.

  • RETR_EXTERNAL returns only outer outlines
  • RETR_LIST returns every outline without family links
  • RETR_TREE returns every outline plus parent and child links

Practical Tips

  • Skip tiny contours by testing that area exceeds a minimum value
  • Clean masks with morphological opening before tracing
  • Draw results on a copy so the original stays untouched
  • Sort contours by area to find the largest object quickly

Real-World Walkthrough: Counting Screws

A factory camera photographs a tray of screws on a white background. The pipeline blurs the image, applies an inverse threshold, and finds external contours. A filter drops any contour with an area below 200 pixels, which removes dust and shadows. The remaining contour count equals the number of screws. Bounding boxes and labels show operators exactly what the system found.

Tray photo -> Blur -> Inverse threshold -> Contours -> Area filter -> Count: 24 screws

Common Mistakes

  • Passing a color image to findContours, which expects a single-channel binary image
  • Tracing white paper rather than dark shapes because the threshold direction is reversed
  • Ignoring noise, which creates hundreds of tiny contours
  • Reading the return values in the wrong order, since older OpenCV versions return three values

Practice Exercise

Draw a triangle, a square, and a circle on white paper and photograph it. Run the pipeline and print the corner count for each contour. Label every shape with putText. Try tilting the paper and watch how the rotated box adapts while the straight box does not.