CLIJ2

Logo

GPU accelerated image processing for everyone

CLIJ2 home

Voronoi Otsu Labeling

Author: Robert Haase, May 2021

Source

This workflow for image segmentation is a rather simple and yet powerful approach, e.g. for detecting and segmenting nuclei in fluorescence micropscopy images. A nuclei marker such as nuclei-GFP, DAPI or histone-RFP in combination with various microscopy techniques can be used to generate images of suitable kind.

To demonstrate the workflow, we used image set BBBC022v1 Gustafsdottir et al., PLOS ONE, 2013, available from the Broad Bioimage Benchmark Collection Ljosa et al., Nature Methods, 2012.

We start by opening an example image and cropping out an interesting sub-region.

run("Close All");
// Init GPU
run("CLIJ2 Macro Extensions", "cl_device=");
Ext.CLIJ2_clear();

open("C:/structure/code/clij2-docs/src/main/resources/BBBC022/IXMtest_A01_s1_w164FBEEF7-F77C-4892-86F5-72D0160D4FB2.tif");
input_image = getTitle();
run("32-bit"); // that's necessary to retrieve a reasonable spot detection result

Ext.CLIJ2_push(input_image);

// crop and visualize
Ext.CLIJ2_crop2D(input_image, crop_image, 400, 00, 256, 256);
Ext.CLIJ2_pull(crop_image);

IXMtest_A01_s1_w164FBEEF7-F77C-4892-86F5-72D0160D4FB2.tif CLIJ2_crop2D_result206

Applying the algorithm

Voronoi-Otsu-labeling is a command, which asks for two sigma parameters. The first sigma controls how close detected cells can be (spot_sigma) and second controls how precise segmented objects are outlined (outline_sigma).

sigma_spot_detection = 5;
sigma_outline = 1;
Ext.CLIJ2_voronoiOtsuLabeling(input_image, label_image, sigma_spot_detection, sigma_outline);
Ext.CLIJ2_voronoiOtsuLabeling(crop_image, crop_label_image, sigma_spot_detection, sigma_outline);

Ext.CLIJ2_pull(label_image);
run("glasbey_on_dark");
Ext.CLIJ2_pull(crop_label_image);
run("glasbey_on_dark");

CLIJ2_voronoiOtsuLabeling_result207 CLIJ2_voronoiOtsuLabeling_result208

How does it work?

The Voronoi-Otsu-Labeling workflow is a combination of Gaussian blur, spot detection, thresholding and binary watershed. The interested reader might want to see the open source code.

Note: If this algorithm is applied to 3D data, it is recommended to make it isotropic first.

As a first step, we blur the image with a given sigma and detect maxima in the resulting image.

Ext.CLIJ2_gaussianBlur2D(crop_image, blurred_image, sigma_spot_detection, sigma_spot_detection);

// detect maxima (spots)
Ext.CLIJ2_detectMaxima2DBox(blurred_image, detected_spots_image, 0, 0);

// count spots
Ext.CLIJ2_getSumOfAllPixels(detected_spots_image, number_of_spots);
print("number of detected spots", number_of_spots);
// show blurred image
Ext.CLIJ2_pull(blurred_image);
// show image with local maxima
Ext.CLIJ2_pull(detected_spots_image);
setMinAndMax(0, 1);
> number of detected spots 22

CLIJ2_gaussianBlur2D_result209 CLIJ2_detectMaxima2DBox_result210

Furthermore, we start again from the cropped image and blur it again, with a different sigma. Afterwards, we threshold the image using Otsu’s thresholding method (Otsu et al 1979).

Ext.CLIJ2_gaussianBlur2D(crop_image, blurred_image, sigma_outline, sigma_outline);
Ext.CLIJ2_thresholdOtsu(blurred_image, binary_image);

// show blurred image
Ext.CLIJ2_pull(blurred_image);
// show binary image
Ext.CLIJ2_pull(binary_image);
setMinAndMax(0, 1);

CLIJ2_gaussianBlur2D_result209 CLIJ2_thresholdOtsu_result211

Afterwards, we take the binary spots image and the binary segmentation image and apply a binary_and operation to exclude spots which were detected in the background area. Those likely corresponded to noise.

Ext.CLIJ2_binaryAnd(detected_spots_image, binary_image, selected_spots_image);
// count selected spots
Ext.CLIJ2_getSumOfAllPixels(selected_spots_image, number_of_spots);
print("number of selected spots", number_of_spots);

// show selected spots image
Ext.CLIJ2_pull(selected_spots_image);
setMinAndMax(0, 1);
> number of selected spots 13

CLIJ2_binaryAnd_result212

Next, we separate the image space between the selected spots using a Voronoi diagram which is limited to the positive pixels in the binary image.

Ext.CLIJ2_maskedVoronoiLabeling(selected_spots_image, binary_image, voronoi_diagram );

// show binary image
Ext.CLIJ2_pull(binary_image);
setMinAndMax(0, 1);

// show selected spots image
Ext.CLIJ2_pull(selected_spots_image);
setMinAndMax(0, 1);

// show result
Ext.CLIJ2_pull(voronoi_diagram );
run("glasbey_on_dark");

CLIJ2_thresholdOtsu_result211 CLIJ2_binaryAnd_result212 CLIJ2_maskedVoronoiLabeling_result213

Finally, we clean up GPU memory.

Ext.CLIJ2_clear();