Local maxima detection

Local maxima detection#

For detecting local maxima, pixels surrounded by pixels with lower intensity, we can use some functions in scikit-image and clesperanto.

See also

from skimage.feature import peak_local_max
import pyclesperanto_prototype as cle
from skimage.io import imread, imshow
from skimage.filters import gaussian 
import matplotlib.pyplot as plt

We start by loading an image and cropping a region for demonstration purposes. We used image set BBBC007v1 image set version 1 (Jones et al., Proc. ICCV Workshop on Computer Vision for Biomedical Image Applications, 2005), available from the Broad Bioimage Benchmark Collection [Ljosa et al., Nature Methods, 2012].

image = imread("../../data/BBBC007_batch/A9 p7d.tif")[-100:, 0:100]

cle.imshow(image)
../_images/8641a082f73416921fa012245d92e7dcb078bcb651b843865b416a1a714e51e0.png

Preprocessing#

A common preprocessing step before detecting maxima is blurring the image. This makes sense to avoid detecting maxima that are just intensity variations resulting from noise.

preprocessed = gaussian(image, sigma=2, preserve_range=True)

cle.imshow(preprocessed)
../_images/a1fb76dec30384ba824f283d6040b30910372248319637e4af2d3cc6fb41ee32.png

peak_local_max#

The peak_local_max function allows detecting maxima which have higher intensity than surrounding pixels and other maxima according to a defined threshold.

coordinates = peak_local_max(preprocessed, threshold_abs=5)
coordinates
array([[23, 85],
       [11, 29],
       [41, 40],
       [88, 34],
       [72, 83],
       [69, 89],
       [31, 72],
       [75, 16],
       [80, 22],
       [ 6, 56]], dtype=int64)

These coordinates can be visualized using matplotlib’s plot function.

cle.imshow(preprocessed, continue_drawing=True)
plt.plot(coordinates[:, 1], coordinates[:, 0], 'r.')
[<matplotlib.lines.Line2D at 0x2309908fbb0>]
../_images/6eabc8dfa98723116061ddb967031bf8ff8fb0b6e38bfc0a30276647794d298d.png

If there are too many maxima detected, one can modify the results by changing the sigma parameter of the Gaussian blur above or by changing the threshold passed to the peak_local_max function.

detect_maxima_box#

The function peak_local_max tends to take long time, e.g. when processing large 3D image data. Thus, an alternaive shall be introduced: clesperanto’s detect_maxima_box is an image filter that sets pixels to value 1 in case surrounding pixels have lower intensity. It typically performs fast also on large 3D image data.

local_maxima_image = cle.detect_maxima_box(preprocessed)
local_maxima_image
cle._ image
shape(100, 100)
dtypeuint8
size9.8 kB
min0.0
max1.0

Obviously, it results in a binary image. This binary image can be converted to a label image by labeling individual spots with different numbers. From this label image, we can remove maxima detected at image borders, which might be useful in this case.

all_labeled_spots = cle.label_spots(local_maxima_image)

labeled_spots = cle.exclude_labels_on_edges(all_labeled_spots)
labeled_spots
cle._ image
shape(100, 100)
dtypeuint32
size39.1 kB
min0.0
max11.0

To visualize these spots on the original image, it might make sense to increase the size of the spots - just for visualization purposes.

label_visualization = cle.dilate_labels(labeled_spots, radius=3)

cle.imshow(preprocessed, continue_drawing=True)
cle.imshow(label_visualization, labels=True, alpha=0.5)
../_images/8458205f2922ebc84185e00c56daf9cba55b7592be2b3208a0e9502710cf4c43.png

In the lower center of this image we see now a local maximum that has been detected in the background. We can remove those maxima in lower intensity regions by thresholding.

binary_image = cle.threshold_otsu(preprocessed)
binary_image
cle._ image
shape(100, 100)
dtypeuint8
size9.8 kB
min0.0
max1.0

We can now exclude labels from the spots image where the intensity in the binary image is not within range [1..1] (i.e. exactly 1).

final_spots = cle.exclude_labels_with_map_values_out_of_range(
    binary_image,
    labeled_spots,
    minimum_value_range=1,
    maximum_value_range=1
)
final_spots
cle._ image
shape(100, 100)
dtypeuint32
size39.1 kB
min0.0
max10.0

We can then visualize the spots again using the strategy introduced above, but this time on the original image.

label_visualization2 = cle.dilate_labels(final_spots, radius=3)

cle.imshow(image, continue_drawing=True)
cle.imshow(label_visualization2, labels=True, alpha=0.5)
../_images/ae780bf69815d2f1e8a6c74bf226ae08899765e680ce393270c28a47db834cb9.png