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.
In auto thresholding several methods produce acceptable results and one can get rid of selecting manual threshold values for each different image
skimage napari
importnumpyasnpfromskimage.ioimportimreadimportnapari# 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.minmax_val=info_type.maximportmatplotlib.pyplotaspltplt.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=25thr2=75manual1=image1>thr1manual2=image2>thr2viewer.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
fromskimage.filtersimportthreshold_meanthr1=threshold_mean(image1)thresholded1=image1>thr1print(thr1)thr2=threshold_mean(image2)thresholded2=image2>thr2print(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
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
Using stack histogram yields only one threshold value for binarization when applying auto thresholding
Auto thresholding gives better segmentation results than manual thresholding in the presence of noise