5 Best Ways to Find Contours of an Image Using Scikit-learn in Python

Rate this post

πŸ’‘ Problem Formulation: Imagine you’re working on a computer vision project where you need to isolate and analyze specific shapes within an image, such as detecting roads on a map or cells in a microscopic image. In Python, the task of finding these outlines, or contours, can be accomplished using the Scikit-learn library. The input would be an image file, and the desired output is a list of contours detected in the image.

Method 1: Utilizing the Canny Edge Detector and findContours from OpenCV

Scikit-learn does not directly provide contour detection functions but can be complemented very well with OpenCV. The Canny Edge Detector is an algorithm used to detect a wide range of edges in images. OpenCV’s findContours function then utilizes these edges to find contours. An effective combination of Canny Edge detection followed by contour detection gives a precise edge-based contour mapping.

Here’s an example:

import cv2

# Read the image
image = cv2.imread('path_to_image.jpg', 0)

# Use the Canny Edge Detector
edges = cv2.Canny(image, threshold1=100, threshold2=200)

# Find contours
contours, hierarchy = cv2.findContours(
    edges, 
    cv2.RETR_EXTERNAL, 
    cv2.CHAIN_APPROX_SIMPLE
)

# Draw contours on the original image (for visualization)
image_with_contours = cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
cv2.imwrite('contours_image.jpg', image_with_contours)

The output would be the image saved as ‘contours_image.jpg’ with green lines outlining the detected contours.

This method leverages OpenCV’s well-optimized functions to provide efficient and accurate contour detection. The Canny edge detector identifies edges in the image, and findContours captures the contours based on these edges. Using the RETR_EXTERNAL flag gives us only the external contours, and CHAIN_APPROX_SIMPLE removes all redundant points and compresses the contour, thereby saving memory.

Method 2: Using Thresholding with Connected Components

Another way to find contours is by thresholding the image to create a binary image and then find connected components. Scikit-learn can be used for preprocessing and thresholding, while OpenCV can be used to find contours among the connected components. This method is great for images with a high contrast between the objects and the background.

Here’s an example:

import cv2
from sklearn.preprocessing import binarize

# Read the image in grayscale
image = cv2.imread('path_to_image.jpg', 0)

# Apply preprocessing and thresholding using scikit-learn
binary_image = binarize(image, threshold=128, copy=True)

# Use OpenCV to find contours in the binary image
contours, hierarchy = cv2.findContours(
    binary_image.astype('uint8'), 
    cv2.RETR_TREE, 
    cv2.CHAIN_APPROX_NONE
)

# Visualize the contours
image_with_contours = cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
cv2.imwrite('binary_contours_image.jpg', image_with_contours)

The output is an image, ‘binary_contours_image.jpg’, which displays the contours over the image.

The binarize function from Scikit-learn’s preprocessing module transforms the gray-scale image into a binary image, which is then used by the findContours function to detect the contours. Connecting components with thresholding and then finding contours can be particularly useful for segmenting different objects in a binary image.

Method 3: Watershed Algorithm for Image Segmentation

The Watershed algorithm is a classic image segmentation technique used to separate different objects in an image. While Scikit-learn does not directly provide this method, it can again be used in tandem with OpenCV. Preprocessing steps may include filtering and morphological operations, after which the Watershed algorithm can discern and isolate contours based on the gradient information of the image.

Here’s an example:

import cv2
import numpy as np
from scipy.ndimage import label

# Read the image in grayscale
image = cv2.imread('path_to_image.jpg', 0)

# Preprocessing to remove noise and smoothen edges
blurred = cv2.GaussianBlur(image, (5, 5), 0)
ret, thresh = cv2.threshold(blurred, 120, 255, cv2.THRESH_BINARY_INV)

# Apply the watershed algorithm
# First, label the background and foreground
markers = label(thresh)[0]

# Use OpenCV's watershed function
watershed_image = image.copy()
cv2.watershed(watershed_image, markers)

# Visualize the result
for label in np.unique(markers):
    if label == 0:
        continue
    mask = np.zeros(image.shape, dtype="uint8")
    mask[markers == label] = 255
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cv2.drawContours(watershed_image, contours, -1, (0, 255, 0), 2)

cv2.imwrite('watershed_contours_image.jpg', watershed_image)

The output is a ‘watershed_contours_image.jpg’ with contours depicted in green.

In this approach, morphological operations and the Watershed algorithm are used to detect contours. Each isolated segment of an image is labeled, and contours are drawn for each label. The strength of this method is its ability to make distinct separations in an image where the boundaries between objects are not clearly defined.

Method 4: Using Scikit-image for Contour Detection

Though Scikit-learn is not typically used for image processing, Scikit-image, which is dedicated to image processing in SciPy, offers straightforward tools for contour detection. Using functions such as find_contours, contours of objects within an image can be quickly found at a given constant intensity level.

Here’s an example:

import matplotlib.pyplot as plt
from skimage import io, color
from skimage.measure import find_contours

# Read the image
image = io.imread('path_to_image.jpg')

# Convert the image to grayscale
gray_image = color.rgb2gray(image)

# Find contours at a constant value of 0.8
contours = find_contours(gray_image, 0.8)

# Display the image and plot all contours found
fig, ax = plt.subplots()
ax.imshow(gray_image, interpolation='nearest', cmap=plt.cm.gray)

for contour in contours:
    ax.plot(contour[:, 1], contour[:, 0], linewidth=2)

ax.axis('image')
ax.set_xticks([])
ax.set_yticks([])
plt.savefig('scikit_image_contours.jpg')

The output is ‘scikit_image_contours.jpg’ where the contours are superimposed on the grayscale image.

This snippet utilizes Scikit-image, a powerful image processing library that is part of the SciPy ecosystem. The find_contours function finds contours at a constant value of intensity in the image. The strength of this method is its simplicity and the ability to find contours based directly on the intensity value.

Bonus One-Liner Method 5: Using the Marching Squares Algorithm

The Marching Squares algorithm is implemented in Scikit-image and provides a simple one-liner approach to finding contours at a specified intensity level in an image. It is conceptually similar to the marching cubes algorithm used for 3D datasets.

Here’s an example:

from skimage import io, measure
import matplotlib.pyplot as plt

# Read the image and convert it to grayscale
image = io.imread('path_to_image.jpg', as_gray=True)

# Find contours using Marching Squares algorithm
contours = measure.find_contours(image, 0.8)

# Plot the contours
plt.figure()
for contour in contours:
    plt.plot(contour[:, 1], contour[:, 0], linewidth=2)
plt.show()

The code snippet visually outputs the contours on a plot.

This one-liner utilizes Scikit-image’s implementation of the Marching Squares algorithm to detect contours quickly. It is an intuitive and convenient way to find and plot contours without the need for additional image preprocessing steps.

Summary/Discussion

  • Method 1: Canny Edge Detector and findContours. Strengths: Provides precise contour detection. Weaknesses: Requires installation and use of OpenCV alongside Scikit-learn.
  • Method 2: Thresholding with Connected Components. Strengths: Effective in high contrast images. Weaknesses: Might require additional preprocessing for optimal results.
  • Method 3: Watershed Algorithm. Strengths: Good for separating objects with unclear boundaries. Weaknesses: More complex and computationally intensive.
  • Method 4: Using Scikit-image’s find_contours. Strengths: Straightforward and part of the SciPy ecosystem. Weaknesses: Limited to detecting contours at specific intensity.
  • Bonus Method 5: Marching Squares Algorithm. Strengths: Simple and quick contour detection. Weaknesses: Limited by the intensity level set for contouring.