SciPy - Thresholding in Image Segmentation



Thresholding in image segmentation in SciPy is a fundamental technique used in image processing to separate different regions in an image based on pixel intensity values.

The basic idea is to apply a threshold to an image so that pixels with values above a certain threshold are classified into one group which often called foreground and those below are classified into another group which often called background.

In SciPy library the thresholding image segmentation is often implemented using the scipy.ndimage module along with the other libraries such as Matplotlib and Numpy for image processing. Here in this chapter we will see, thresholding image segmentation in detail.

Types of Thresholding in Image Segmentation

Thresholding is a key method in image processing used to segment an image by converting grayscale values into binary values. There different types of thresholding techniques that cater to various requirements based on the nature of the image as mentioned below −

Global Thresholding

Global thresholding is a simple and effective image segmentation technique where a single threshold value is used to classify pixels into two categories namely, foreground and background. In SciPy global thresholding can be achieved using basic array operations and tools from the scipy.ndimage module.

Following are the steps used to implement the Global thresholding in SciPy −

  • Load or create an image.
  • Define a global threshold value.
  • Apply the threshold by comparing the image pixel values to the threshold.
  • Finally, generate a binary mask where pixels above the threshold are classified as foreground True and pixels below are classified as background False.

Example

Here is a complete example of Global Thresholding using NumPy and Matplotlib to implement a Global thresholding operation −

import numpy as np
import scipy.ndimage as ndi
import matplotlib.pyplot as plt

# Create a synthetic grayscale image
np.random.seed(42)
image = np.random.random((100, 100))  # Values between 0 and 1

# Define a global threshold value
threshold_value = 0.5

# Apply global thresholding manually
binary_image = image > threshold_value

# Display the original and binary thresholded images
fig, axes = plt.subplots(1, 2, figsize=(10, 5))

# Original image
axes[0].imshow(image, cmap='gray')
axes[0].set_title("Original Image")
axes[0].axis("off")

# Binary thresholded image
axes[1].imshow(binary_image, cmap='gray')
axes[1].set_title(f"Binary Thresholded Image (T = {threshold_value})")
axes[1].axis("off")

plt.tight_layout()
plt.show()

Below is the output of the Global thresholding of the image segmentation implemented with the help Numpy and Matplotlib libraries −

Global Thresholding

Otsu's Thresholding

Otsu's Thresholding is a global thresholding technique that determines the optimal threshold value for an image by maximizing the variance between the foreground and background classes. This method assumes that the image has a bimodal histogram i.e., two distinct peaks representing background and foreground.

The main objective of this method is to find the threshold T that minimizes the intra-class variance or equivalently maximizes the inter-class variance.

Example

Otsus Thresholding is not directly implemented in SciPy but we can achieve it by using a combination of SciPy's histogram functionality and NumPy for calculations. Below is the example which gives step-by-step approach of how to implement Otsu's Thresholding in SciPy −

import numpy as np
import scipy.ndimage as ndi
import matplotlib.pyplot as plt

def otsu_threshold(image):
   """
   Computes Otsu's threshold for a grayscale image.
   
   Parameters:
       image (ndarray): Input grayscale image (2D array).
   
   Returns:
       threshold (float): Optimal threshold value.
   """
   # Compute the histogram of the image
   hist, bin_edges = np.histogram(image.ravel(), bins=256, range=(0, 1))
   bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
   
   # Total number of pixels
   total_pixels = image.size
   
   # Cumulative sums and cumulative means
   cumulative_sum = np.cumsum(hist)
   cumulative_mean = np.cumsum(hist * bin_centers)
   
   # Global mean
   global_mean = cumulative_mean[-1] / total_pixels
   
   # Between-class variance
   numerator = (global_mean * cumulative_sum - cumulative_mean) ** 2
   denominator = cumulative_sum * (total_pixels - cumulative_sum)
   between_class_variance = numerator / (denominator + 1e-10)  # Add epsilon to avoid division by zero
   
   # Find the maximum of between-class variance
   optimal_idx = np.argmax(between_class_variance)
   optimal_threshold = bin_centers[optimal_idx]
   
   return optimal_threshold
   
# Generate a synthetic grayscale image (random noise for demonstration)
np.random.seed(0)
image = np.random.random((100, 100))

# Apply Otsu's thresholding
optimal_threshold = otsu_threshold(image)
thresholded_image = image > optimal_threshold

# Display the original and thresholded images
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
ax[0].imshow(image, cmap='gray')
ax[0].set_title("Original Image")
ax[0].axis('off')

ax[1].imshow(thresholded_image, cmap='gray')
ax[1].set_title(f"Thresholded Image\n(Otsu's Threshold = {optimal_threshold:.2f})")
ax[1].axis('off')

plt.tight_layout()
plt.show()

Below is the output of implementing the Otsu thresholding

Ostu Thresholding

Adaptive Thresholding

Adaptive Thresholding is a thresholding technique where the threshold value varies across the image by depending on local image properties. This method is particularly useful for images with uneven lighting or varying intensity levels where a single global threshold would fail to segment the image properly.

Following are the steps involved in implementing the Adaptive Thresholding −

  • Divide the Image into Local Regions: The image is analyzed in small neighborhoods or regions instead of globally.
  • Calculate Local Statistics: Compute a local threshold for each pixel based on the statistics e.g., mean or Gaussian-weighted mean of the surrounding region.
  • Threshold Each Pixel: Classify pixels as foreground or background based on the locally computed threshold.

Example

Here is an example using SciPy's tools to implement adaptive thresholding using a local mean filter −

import numpy as np
import scipy.ndimage as ndi
import matplotlib.pyplot as plt

# Create a synthetic image with uneven lighting
np.random.seed(0)
x, y = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
image = np.sin(10 * x * y) + np.random.random((100, 100)) * 0.5

# Apply a local mean filter
window_size = 15  # Size of the local region
local_mean = ndi.uniform_filter(image, size=window_size)

# Perform adaptive thresholding
thresholded_image = image > local_mean

# Display the results
fig, ax = plt.subplots(1, 3, figsize=(15, 5))
ax[0].imshow(image, cmap='gray')
ax[0].set_title("Original Image")
ax[0].axis('off')

ax[1].imshow(local_mean, cmap='gray')
ax[1].set_title("Local Mean")
ax[1].axis('off')

ax[2].imshow(thresholded_image, cmap='gray')
ax[2].set_title("Adaptive Thresholded Image")
ax[2].axis('off')

plt.tight_layout()
plt.show()
Adaptive Thresholding

Advanced Adaptive Thresholding Technique

For more advanced adaptive thresholding technique we can use a Gaussian-weighted mean instead of a simple mean. Below is the example of the advanced adaptive thresholding technique −

import numpy as np
import scipy.ndimage as ndi
import matplotlib.pyplot as plt

# Create a synthetic image with uneven lighting
np.random.seed(0)
x, y = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
image = np.sin(10 * x * y) + np.random.random((100, 100)) * 0.5

# Apply a local Gaussian filter
sigma = 5  # Standard deviation for Gaussian kernel
local_gaussian_mean = ndi.gaussian_filter(image, sigma=sigma)

# Perform adaptive thresholding
thresholded_image_gaussian = image > local_gaussian_mean

# Display results
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
ax[0].imshow(local_gaussian_mean, cmap='gray')
ax[0].set_title("Local Gaussian Mean")
ax[0].axis('off')

ax[1].imshow(thresholded_image_gaussian, cmap='gray')
ax[1].set_title("Gaussian Adaptive Thresholded Image")
ax[1].axis('off')

plt.tight_layout()
plt.show()

Below is the output of the advanced adapative filtering −

Advanced Adaptive Thresholding
Advertisements