How to Detect Polygons in Images Using OpenCV and Python

πŸ’‘ Problem Formulation: You have an image containing various shapes, and you need to accurately detect and classify polygons within the image. For instance, you might have an aerial photo in which you want to identify the outlines of fields or properties, and you need to program a way to return those outlines to analyze or process further.

Method 1: Standard Contour Detection with Thresholding

Thresholding is a process that converts an image into a binary form. OpenCV provides cv2.threshold and cv2.findContours functions which help in detecting the edges and contours respectively. The contours can be approximated to polygons using cv2.approxPolyDP.

Here’s an example:

import cv2
import numpy as np

# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('path_to_image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# Find contours and approximate each polygon
contours, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
    epsilon = 0.01*cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, epsilon, True)
    cv2.drawContours(image, [approx], 0, (255,0,0), 5)

cv2.imshow('Polygons Detected', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output is an image with the polygons outlined by red lines.

This code reads an image from a file, converts it to grayscale and smoothes it using a Gaussian filter. It then thresholds the image to create a binary image where the shapes are separated from the background. Contours are found and then approximated to polygons which are then drawn onto the image.

Method 2: Canny Edge Detection

Canny Edge Detection is an algorithm used to detect a wide range of edges in images. OpenCV provides the cv2.Canny() function which is used in combination with cv2.findContours() to detect the edges and contours of the polygons in an image.

Here’s an example:

import cv2
import numpy as np

image = cv2.imread('path_to_image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)

contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)

cv2.imshow('Canny Edge Detection', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output is an image with the edges of the polygons highlighted in green.

This snippet uses Canny Edge Detection to find edges on the image. Detected edges are then processed to find contours. Finally, the contours are drawn onto the original image to display the detected polygons.

Method 3: Watershed Algorithm

The Watershed Algorithm is a technique used for image segmentation. It treats the grayscale image as a topographic surface and ‘floods’ the basins from the lowest intensity to the highest. OpenCV’s implementation of this algorithm can be used to detect and distinguish overlapping objects and polygons.

Here’s an example:

import cv2
import numpy as np

# Load the image
image = cv2.imread('path_to_image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

# Noise removal using Morphological Closing operation
kernel = np.ones((3,3), np.uint8)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)

# Background area using Dilation
bg = cv2.dilate(closing, kernel, iterations=3)

# Finding foreground area
dist_transform = cv2.distanceTransform(closing, cv2.DIST_L2, 5)
_, fg = cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0)

# Finding unknown region
fg = np.uint8(fg)
unknown = cv2.subtract(bg, fg)

# Marker labelling
_, markers = cv2.connectedComponents(fg)

# Add one to all labels so that sure background is not 0, but 1
markers = markers+1

# Now, mark the region of unknown with zero
markers[unknown==255] = 0

# Apply Watershed
markers = cv2.watershed(image, markers)

# Mark the boundaries
image[markers == -1] = [0, 255, 0]

cv2.imshow('Watershed Algorithm', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output is an image with boundaries delineated by the watershed algorithm, marked in green.

The Watershed Algorithm snippet involves pre-processing the image, marking the background and foreground, distinguishing the unknown region, and applying the watershed to find the markers. The final step involves marking the boundaries of the detected regions on the original image.

Method 4: Hough Transform

Hough Transform is a feature extraction technique used in image analysis, computer vision, and digital image processing. It can be used to isolate features of a particular shape within an image. For polygon detection, the probabilistic Hough Line Transform can help identify the lines of polygons.

Here’s an example:

import cv2
import numpy as np

image = cv2.imread('path_to_image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=100, minLineLength=10, maxLineGap=10)

for line in lines:
    x1, y1, x2, y2 = line[0]
    cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2)

cv2.imshow('Hough Transform', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output is an image with lines of detected polygons drawn in green.

This Hough Transform example detects edges using the Canny algorithm, then applies the probabilistic Hough Line Transform to find line segments within the image. These segments help infer the polygons which are marked by drawing each line.

Bonus One-Liner Method 5: Simplified Contour Detection

Sometimes, a simple contour detection is enough to detect basic polygon shapes within an image. This can be performed with a one-liner using OpenCV.

Here’s an example:

cv2.drawContours(image, [cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt, True), True) for cnt in cv2.findContours(cv2.Canny(cv2.cvtColor(cv2.imread('path_to_image.jpg'), cv2.COLOR_BGR2GRAY), 50, 150)[0], cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]], -1, (0,255,0), 3)

The output is similar to Method 1 but achieved with a single line of code.

The one-liner example reads an image, converts it to grayscale, detects edges using Canny edge detection, finds contours, approximates the polygon shapes, and finally draws the contours on the original imageβ€”all in one line.

Summary/Discussion

  • Method 1: Thresholding and Contour Detection. This method is robust for distinct objects and offers precise detection. However, it may not work well with overlapping objects or varying lighting conditions.
  • Method 2: Canny Edge Detection. It is best for clear edge definition and less complex images. Noise and broken edges can be a limitation.
  • Method 3: Watershed Algorithm. Excellent for separating overlapping objects and detailed segmentation. However, it requires careful pre-processing and parameter tuning.
  • Method 4: Hough Transform. Good for polygon detection when the polygon edges are straight lines. May not work as well with curves or noisy data.
  • Bonus Method 5: Simplified Contour Detection. Quick and straightforward but less customizable and potentially less accurate for complex images.