Distance transform

Prerequisites

Before starting this lesson, you should be familiar with:

Learning Objectives

After completing this lesson, learners should be able to:
  • Understand how to use distance transform to quantify morphology of objects

  • Understand how to use distance transform to quantify distance between objects

  • Understand approximate nature of distance transform

Motivation

We use distance transform to quantify how a structure of interest is away from object boundaries or other structures. The distance transform is also use to characterize the morphology of an object in 2D and 3D, find its center, dimensions, etc.. Finally distance transform can be used as a pre-processing step to improve the segmentation results and split touching objects.

Concept map

graph TD B[Binary image] --> DT(Distance transform) DT --> D["Distance map image"] D -- contains --- DN("Distances to nearest background pixel") D -- is --- A("Approximation of euclidian distance") DT -- has --- VI("Various implementations")



Figure


Upper panel - Binary image and the corresponding distance map. The distance map has three local maxima, which are very useful for object splitting and defining object centers. Lower panel - Inverted binary image and corresponding distance map, which is useful to compute distances to closest objects.



Activities

Distance transform basics

Distance measurements

Region selection by distance gating


Show activity for:  

Basics, ImageJ GUI MorpholibJ

Load and presettings

Distance map using default ImageJ distance transform

  • [Image > Duplicate…]
    • Title: "Distance Map"
  • [Process › Binary › Distance Map], note that the resulting image is 8-bit

  • Select the binary window
  • [Image > Duplicate…]
    • Title: "inverted"
  • [Edit > Invert]
  • [Image > Duplicate…]
    • Title: "Distance Map inverted"
  • [Process › Binary › Distance Map], discuss the results and how far away pixels have always a value 255!

Distance map using MorphoLibJ

  • Select window inverted
  • [Plugins › MorphoLibJ › Binary Images › Chamfer Distance Map]
    • Discuss the menu and the fact that there are different options for the metric.
    • Preview the different distances and how you get unexpected values for certain distances
    • Run the distance transform with following options
    • Distances: Borgefors (3,4)
    • Output Type: 16 bits
    • [x] Normalize weights
  • [Image > Rename]
    • “Borgefors”
  • [Plugins › MorphoLibJ › Binary Images › Chamfer Distance Map]
    • Discuss the menu and the fact that there are different options for the metric.
    • Preview the different distances and how you get unexpected values for certain distances
    • Run the distance transform with following options
    • Distances: Chessknight (5,7,11)
    • Output Type: 16 bits
    • [x] Normalize weights
  • [Image > Rename]
    • “Chessknight”
  • Discuss the obtained values

Basics, ImageJ Macro MorpholibJ

/*
 * Requirements:
 * - MorpholibJ, Update site: IJPB-Plugins
 *
 **/

run("Close All");
open("https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit_labels__dist_trafo_a/xy_8bit_binary__dist_trafo_a.tif");
// [Image > Rename...]
rename("binary");


// ImageJ distance map
selectWindow("binary");
// [Process > Binary > Options...] [x] black
run("Options...", "black"); // Otherwise results not in line with standard conventions
//[Image > Duplicate...]
run("Duplicate...", "Distance Map");
//[Process › Binary › Distance Map]
run("Distance Map"); // Within objects


selectWindow("binary");
//[Image > Duplicate...]
run("Duplicate...", " inverted");
// [Edit > Invert]
run("Invert");

// ImageJ distance map on inverted image
// [Image > Duplicate...]
run("Duplicate...", "Distance Map invert");
// [Process › Binary › Distance Map]
run("Distance Map"); // Data type limits maximal distance

// MorpholibJ distance map
selectWindow("inverted");
// [Plugins › MorphoLibJ › Binary Images › Chamfer Distance Map]
run("Chamfer Distance Map", "distances=[Borgefors (3,4)] output=[16 bits] normalize");
rename("Borgefors");
print(getTitle() + ": " + getPixel(0, 761));
selectWindow("invert");
// [Plugins › MorphoLibJ › Binary Images › Chamfer Distance Map]
run("Chamfer Distance Map", "distances=[Chessknight (5,7,11)] output=[16 bits] normalize");
rename("Chessknight");
print(getTitle() + ": " + getPixel(0, 761));

Measure, ImageJ Macro MorpholibJ

/*
 * Requirements:
 * - MorpholibJ, Update site: IJPB-Plugins
 *
 **/


// Distance measurements
run("Close All");
open("https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit_labels__dist_trafo_b.tif");
rename("labels");
// Make invert mask of first label
run("Duplicate...", "title=binary");
run("Manual Threshold...", "min=1 max=1");
run("Convert to Mask");
run("Invert");
// Compute distance transform 
run("Chamfer Distance Map", "distances=[Chessknight (5,7,11)] output=[32 bits] normalize");
rename("dist");
run("Intensity Measurements 2D/3D", "input=dist labels=labels mean max min");

Region selection, ImageJ Macro MorpholibJ

/*
 * Requirements:
 * - MorpholibJ, Update site: IJPB-Plugins
 *
 **/
// Region selection by Distance gating
open("https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit_binary__single_object.tif");
run("Invert");
run("Chamfer Distance Map", "distances=[Chessknight (5,7,11)] output=[16 bits] normalize");
run("Manual Threshold...", "min=100 max=120");
run("Convert to Mask");

Python Napari

# Distance transform with skimage using napari as a viewer

import imageio
import napari
from scipy import ndimage

if 'viewer' not in globals():
    viewer = napari.Viewer()

image = imageio.imread('https://github.com/NEUBIAS/training-resources'
                       '/raw/master/image_data/xy_8bit_labels__four_objects.tif')
print(image.dtype)

distance_map = ndimage.distance_transform_edt(image)
print(distance_map.dtype)

viewer.add_image(image)
viewer.add_image(distance_map)

Exercises

Measure distance to center of cell

We would like to measure the distance within a binary mask to a specific point in the cell. This is called Geodesic distance.

Measure thickness of glial branches

The goal is to combine skeletonization and distance map computation to measure skeleton branch length and thickness. For this exercise you need the binary image xy_8bit_binary__glialcell.tif and the skeletonized and normalized version xy_8bit_binary__glialcell_skeleton.tif

Show exercise/solution for:

Distance to center, ImageJ GUI

  • [File > Open…] xy_8bit_binary__glialcell.tif
  • You need to define a reference marker using the point ROI (in the Fiji menu 7th element from the left). Place it close to the center of the cell.
  • [Plugins › MorphoLibJ › Binary Images › Interactive Geodesic Distance Map]
    • Distances: Chessknight (5,7,11)
    • Output Type: 32 bits
    • [x] Normalize weights
  • The image values correspond to the distance to the reference point within the binary mask.

Glial thickness, ImageJ GUI

  • [File > Open…] xy_8bit_binary__glialcell.tif
  • Compute the distance map using the default plugin (we need a 8 bit image for the analyze skeleton step)
  • [Process > Binary > Distance Map]
  • [ Image › Rename…]
    • “Distance Map”
  • [File > Open…] xy_8bit_binary__glialcell_skeleton.tif
  • [ Image › Rename…]
    • “Skeleton”
  • Use the image calculator function [ Process › Image Calculator…] to multiply the skeleton image by the distance map:
    • Image1: Skeleton
    • Operation: Multiply
    • Image2: Distance map
    • ‘create new window’
    • ‘32-bit float result’
  • [ Image › Lookup Tables › Fire]
  • Obtain branch information by analyzing the skeleton: [Analyze › Skeleton › Analyze Skeleton (2D/3D)]
    • ‘Prune ends’
    • ‘Calculate largest shortest path’
    • ‘Show detailed info’
    • ‘Display labeled skeletons’.
  • In the ‘Branch information’ table, you can find information on branch length, as well as average intensity. Since the distance map tells you the distance a pixel is away from the boundary, you can estimate the average branch thickness by multiplying this value by 2.



Assessment

Discuss with your neighbor

  1. Knowing the image calibration, how could we convert a 2D distance map to physical values?
  2. Knowing the image calibration, how could we convert a 3D distance map to physical values? Is it important if the image is isotropic sampled or not?

Explanations

Chamfer distance (modified from MorpholibJ Manual)

Several methods (metrics) exist for computing distance maps. The MorphoLibJ library implements distance transforms based on chamfer distances. Chamfer distances approximate Euclidean distances with integer weights, and are simpler to compute than exact Euclidean distance (Borgefors, 1984, 1986). As chamfer weights are only an approximation of the real Euclidean distance, some differences are expected compared to the actual Euclidean distance map.

Several choices for chamfer weights are illustrated in above Figure (showing the not normalized distance).

  • The “Chessboard” distance, also named Chebyshev distance, gives the number of moves that an imaginary Chess-king needs to reach a specific pixel. A king can move one pixel in each direction.
  • The “City-block” distance, also named Manhattan metric, weights diagonal moves differently.
  • The “Borgefors” weights were claimed to be best approximation when considering the 3-by-3 neighborhood.
  • The “Chess knight” distance also takes into account the pixels located at a distance from (±1, ±2) in any direction. It is usually the best choice, as it considers a larger neighborhood.

To remove the scaling effect due to weights > 1, it is necessary to perform a normalization step. In MorphoLibJ this is performed by dividing the resulting image by the first weight (option Normalize weights).




Follow-up material

Recommended follow-up modules:

Learn more: