Measure distance to a center line#
A common question is how to determine distances of points to the center of a segmented object. For this we can skeletonize the object, produce a distance map and read out intensities from the distance map at the given points to determine their distance to the skeleton / center line.
See also:
https://scikit-image.org/docs/stable/auto_examples/edges/plot_skeleton.html
https://examples.itk.org/src/filtering/distancemap/maurerdistancemapofbinary/documentation
from skimage.io import imread
import napari_segment_blobs_and_things_with_membranes as nsbatwm
import napari_simpleitk_image_processing as nsitk
import numpy as np
import stackview
import pyclesperanto_prototype as cle
Starting point: a binary image#
We start using a binary image that looks like an arm.
binary_arm = imread("../../data/binary_arm.tif")
stackview.insight(binary_arm)
|
Furthermore, we continue with a list of coordinates in X/Y format:
coordinates_xy = np.asarray([
[70, 80],
[70, 70],
[70, 60]]).T
We next produce a label image where the given coordinates are labeled. The first coordinate (index=0 in the list) will be labeled with 1, the second with 2, and so on. Background pixels are 0. We use this label image for visualization and further down, we will also use this image to do the measurement.
# draw the coordinates into an image; for visualization purposes
blank_image = cle.create((binary_arm.shape))
labeled_spots = coordinate_visualization = cle.pointlist_to_labelled_spots(coordinates_xy, blank_image)
# show the labeled pixels on top of the binary image
cle.imshow(binary_arm, continue_drawing=True, max_display_intensity=1)
cle.imshow(labeled_spots, labels=True, alpha=0.6)
Pre-processing#
Before we can skeletonize the image, we need to fill the black holes in the white area.
filled_holes = nsitk.binary_fill_holes(binary_arm)
filled_holes
n-sitk made image
|
Skeletonization#
The skeleton of a binary image is a thin line in the center of white areas.
skeleton = nsbatwm.skeletonize(filled_holes)
skeleton
<__array_function__ internals>:200: RuntimeWarning: Converting input from bool to <class 'numpy.uint8'> for compatibility.
nsbatwm made image
|
Distance map#
We next draw a Signed Maurer Distance Map. Distance maps are images where the intensity represents the distance of a pixel to the next white pixel in the binary image from which the distance map was generated from.
distance_map = nsitk.signed_maurer_distance_map(skeleton)
distance_map
n-sitk made image
|
Using stackview.picker
we can hover with the mouse over the image and read out intensities. This only works in a Jupyter-like environment.
stackview.picker(distance_map, zoom_factor=3)
Measurements#
We now can read out the intesity in the distance map at the given locations of the labeled spots.
values_at_positions = cle.read_intensities_from_positions(coordinates_xy, distance_map)
np.asarray(values_at_positions)
array([[-15.033297 , -5.0990195, -4. ]], dtype=float32)
Exercise#
Use the distance map on the binary_arm
image to determine the distance of the three points from the edge of the arm, instead of the center line.