Automatic thresholding (histogram-based)

Prerequisites

Before starting this lesson, you should be familiar with:

Learning Objectives

After completing this lesson, learners should be able to:
  • Understand how an image histogram can be used to derive a threshold

  • Apply automatic threshold to distinguish foreground and background pixels

Motivation

The manual determination of a threshold value is tedious and subjective. This is problematic as it reduces the reproducibility of the results and may preclude determining threshold values for many different images as the dataset becomes large. It is therefore important to know about reproducible mathematical approaches to automatically determine threshold values for image segmentation.

Concept map

graph TD I("Image") --> H("Histogram") H -- algorithm --> T("Threshold value")

Figure


Input images, histograms (Huang threshold - blue, Otsu threshold - orange), binary images (Huang), binary images (Otsu).



Key points

  • Most auto thresholding methods do two class clustering
  • If the histogram is bimodal, most automated methods will perform well
  • If the histogram has more than two peaks, automated methods could produce noisy results

Activities


Show activity for:

ImageJ GUI

  • Manual vs. auto thresholding
    • Open xy_8bit__nuclei_without_offset.tif
    • [ Image > Adjust > Threshold… ]
      • [X] Dark Background
      • Selecting Lower threshold level = 20 is a good example. Note down this value
      • Press Reset
      • Don’t press Set or Apply
    • Open xy_8bit__nuclei_with_offset.tif
    • [ Image > Adjust > Threshold… ]
      • [X] Dark Background
      • Selecting Lower threshold level = 20 now does not work (i.e. all pixels are foreground)
      • Here, selecting Lower threshold level = 40 works
      • Press Reset
    • Select window xy_8bit__nuclei_without_offset.tif
    • [ Image > Adjust > Auto Threshold ]
      • Method = Try all
      • [X] White objects on black background
      • Keep all other options unchecked
      • Press OK
    • Select window xy_8bit__nuclei_with_offset.tif
    • [ Image > Adjust > Auto Threshold ]
      • Method = Try all
      • [X] White objects on black background
      • Keep all other options unchecked
      • Press OK
    • In auto thresholding several methods produce acceptable results and one can get rid of selecting manual threshold values for each different image

skimage napari

import numpy as np
from skimage.io import imread
import napari

# Read the images
image1 = imread('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__nuclei_without_offset.tif')
image2 = imread('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__nuclei_with_offset.tif')

# Inspect image data type and values
print(image1.dtype, image1.shape, np.min(image1), np.max(image1))
print(image2.dtype, image2.shape, np.min(image2), np.max(image2))

# Instantiate the napari viewer
viewer = napari.Viewer()

# Visualize images using matplotlib
viewer.add_image(image1, name='image1')
viewer.add_image(image2, name='image2')

# Explore the histograms
info_type = np.iinfo(image1.dtype)
print('\n', info_type)
min_val = info_type.min
max_val = info_type.max

import matplotlib.pyplot as plt
plt.hist(image1.flatten(), bins=np.arange(max_val+1), log=True);

plt.hist(image2.flatten(), bins=np.arange(max_val+1), log=True);

# Try manual thresholding
thr1 = 25
thr2 = 75

manual1 = image1>thr1
manual2 = image2>thr2

viewer.add_labels(manual1, name='manual_threshold1')
viewer.add_labels(manual2, name='manual_threshold2')
# Identify possible problems with this solution

# Explore auto-thresholding options on:
# https://scikit-image.org/docs/stable/api/skimage.filters.html

# Obtain the thresholding values
from skimage.filters import threshold_mean

thr1 = threshold_mean(image1)
thresholded1 = image1>thr1
print(thr1)

thr2 = threshold_mean(image2)
thresholded2 = image2>thr2
print(thr2)

# Visualize auto-thresholded images
viewer.add_labels(thresholded1, name='threshold_mean1')
viewer.add_labels(thresholded2, name='threshold_mean2')

# Explore other thresholding options
# Note that there exists a threshold_multiotsu function to handle cases with multi-peaks histograms

Exercises

Show exercise/solution for:

ImageJ GUI

  • Auto thresholding on stack
    • Open xyz_8bit__nuclei_autothresh.tif
    • [ Image > Duplicate… ]
      • Title = nohist
      • [X] Duplicate stack
    • Select window nohist
    • [ Image > Adjust > Auto Threshold ]
      • Method = Try all
      • [X] White objects on black background
      • [X] Stack
      • Press OK
    • It can be seen that for many methods, background is also segmented. This is due to the fact that in this case auto threshold algorithms is treating each slice separately (segmentation is done based on slice histogram)
    • Select a method e.g. Otsu and repeat the above process using
      • Method = Otsu
      • [X] Stack
      • [X] Show threshold values in log window
      • Press OK
    • It can be seen that Otsu’s method is calculating threshold for each individual slice
    • Select window xyz_8bit__nuclei_autothresh.tif
    • [ Image > Duplicate… ]
      • Title = hist
      • [X] Duplicate stack
    • Select window hist and repeat the procedure above using
      • Method = Otsu
      • [X] Stack
      • [X] Use stack histogram
      • [X] Show threshold values in log window
      • Press OK
    • It can be observed that now one threshold value (i.e. 91, see log window) is used for binarization and background is not segmented

Assessment

True or False

Solution

  • True
  • False

Follow-up material

Recommended follow-up modules:

Learn more: