Object shape measurements

Prerequisites

Before starting this lesson, you should be familiar with:

Learning Objectives

After completing this lesson, learners should be able to:
  • Understand shape measurements and their limitations

  • Perform shape measurements on objects

Motivation

Our eyes are extremely good in distinguishing forms and patterns and this has proven to be a powerful tool for characterizing different cell-types, functions, phenotypes, and more. In image processing, we use shape measurements (e.g. area, volume, elongation, …) for an automated and objective characterization of forms. Consequently, one can address scientific questions or filter objects that should be used for further processing. Typically, we apply shape measurements on a labeled image. The labeled image, as obtained after a connected component analysis, defines a set of objects in 2D/3D. However, for a quick manual analysis delineating objects by drawing so-called ROIs (regions of interest) is another possibility.

Concept map

graph TD li[Object regions] --> sa("Shape Analysis") sa --> table["Object table
oid | area | perimeter | circularity
001 | 222 | 56 | 0.9
002 | 500 | 101 | 0.2 "] style table text-align:left



Figure


Left: Fluorescence microscopy of nuclei showing various shapes with three nuclei manually delineated. Right: Label mask image of all nuclei. Bottom: Table with some shape measurements of the manually delineated nuclei.



Activities

Measure object shapes in a digital image


Show activity for:  

ImageJ GUI ROI

  • Open an image with objects of different shapes (see activity preface)
  • Use Analyse > Tools > ROI Manager to open the ROI manager
  • Use the ROI tools, e.g. the Polygon selection in the Fiji menu bar to delineate some objects in the image
  • Use ROI Manager > Add to record each ROI
  • Use ROI Manager > Rename... to give them meaningful names
  • For easier region identification use ROI Manager > More > Options
    • Use ROI names as labels
  • Display all regions using ROI Manager
    • Show All
    • Labels
  • Once all are added use ROI Manager > More > Save... to store the ROIs
    • This is important to document your work; the ROI file can be opened via drag&drop on Fiji
  • Use Analyse > Set Measurements to configure what to measure:
    • Area
    • Centroid
    • Perimeter
    • Fit Ellipse
    • Feret’s Diameter
    • Shape Descriptors
    • Display label (adds a column with the object name)
  • Use ROI Manager > More > Multi Measure to measure all ROIs at once
  • Use Image > Properties to check the image calibration
  • Use Results > File > Rename to indicate the image calibration in the table name
    • Use Image > Properties to change the image to pixel units
  • Measure all ROIs again and change the table name to indicate the calibration
  • Understand all the measurements
    • Compare calibrated and pixel unit measurements
    • Go through the columns and see which object has an extreme value and why
    • See ImageJ measurements documentation
    • Select one object and use Edit > Selection > Convex Hull to see the convex hull

ImageJ GUI MorphoLibJ

  • Open image xy_8bit_labels__four_objects.tif
  • Perform shape measurements and discuss their meanings [Plugins > MorphoLibJ > Analyze > Analyze Regions]
  • Explore results visualisation [Plugins > MorphoLibJ > Label Images > Assign Measure to Label]
  • Add a calibration of 2 micrometer to the image and check which shape measurements are affected.
  • Perform a shape analysis for 3D image xyz_16bit_labels__spindle_spots.tif and [Plugins > MorphoLibJ > Analyze > Analyze Regions 3D]

skimage napari

#%% [markdown]
# # Measure shapes in 2D

#%%
from OpenIJTIFF import open_ij_tiff
import napari
from skimage.measure import regionprops

#%%
# Read and display the label-image from https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit_labels__four_objects.tif
image, axes_image, scales_image, units_image = open_ij_tiff(
    "https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit_labels__four_objects.tif"
)

#%%
viewer = napari.Viewer()
viewer.add_labels(image)

#%% [markdown]
# - Perform shape measurements and discuss their meanings

#%%
shape_measurements = regionprops(image)

#%%
# how many regions are in the image
print(len(shape_measurements))

#%%
# what measurements do you get?
# see also https://scikit-image.org/docs/stable/api/skimage.measure.html#regionprops
list(shape_measurements[0])

#%%
# print some features of the first region
print(shape_measurements[0].area, shape_measurements[0].eccentricity)

#%%
# print the area and eccentricity of each region
for region in shape_measurements:
    print(region.label, region.area, region.eccentricity)

#%% [markdown]
# - Add a calibration of 2 micrometer to the image and check which shape measurements are affected.
# - Note: requires skimage>=0.20.0

#%%
spacing = (2, 2)
shape_measurements = regionprops(image, spacing=spacing)
# print some features of first object
print(shape_measurements[0].area, shape_measurements[0].eccentricity)

#%% [markdown]
# ### Perform a shape analysis for 3D image

#%%
# Read https://github.com/NEUBIAS/training-resources/raw/master/image_data/xyz_16bit_labels__spindle_spots.tif
image, axes_image, scales_image, units_image = open_ij_tiff(
    "https://github.com/NEUBIAS/training-resources/raw/master/image_data/xyz_16bit_labels__spindle_spots.tif"
)

#%%
viewer = napari.Viewer()
viewer.add_labels(image)

#%%
# Perform shape measurements and discuss their meanings
shape_measurements = regionprops(image)

#%%
# how many regions are in the image
print(len(shape_measurements))

#%%
# what measurements do you get?
# see also https://scikit-image.org/docs/stable/api/skimage.measure.html#regionprops
list(shape_measurements[0])

#%%
# print some features of the first region
print(shape_measurements[0].area, shape_measurements[0].num_pixels)

#%%
# print the area of each region
for region in shape_measurements:
    print(region.label, region.area)

#%% [markdown]
# - Use calibration from the scales_image and perform measurements using this information.
# - Check which shape measurements are affected.
# - Note: requires skimage>=0.20.0

#%%
shape_measurements = regionprops(image, spacing=scales_image)
# print some features of first object
print(shape_measurements[0].area, shape_measurements[0].num_pixels)



Practice measuring object shapes in an image

Practice performing shape measurements.


Show activity for:  

ImageJ GUI

Open image xy_16bit_labels__nuclei.tif Using MorpholibJ:

  1. Measure object shapes and find the label index of the nucleus with the largest perimeter
  2. Change the pixel size to 0.5 um and repeat the measurements. Why do some parameters change while others don’t?
  3. (Optional) Create an image where each object is coloured according to the measured circularity

Solution

  1. [Plugins > MorphoLibJ > Analyze > Analyze Regions] the upper right nuclei.
  2. Some features are the ratio of dimensional features and so are independent of the spatial calibration.
  3. [Plugins > MorphoLibJ > Label Regions > Assign Measure to Label].

skimage napari

#%% [markdown]
# # Practice measure shape

#%%
from OpenIJTIFF import open_ij_tiff
import matplotlib.pyplot as plt
import napari
import numpy as np
from skimage.measure import regionprops, regionprops_table

#%%
# Open image [xy_16bit_labels__nuclei.tif](https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_16bit_labels__nuclei.tif)
image, axes_image, scales_image, units_image = open_ij_tiff(
    "https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_16bit_labels__nuclei.tif"
)

#%%
viewer = napari.Viewer()
label_layer = viewer.add_labels(image, name="eccentricity")

#%%
# Measure object shapes
shape_measurements = regionprops(image)
# Print perimeter and eccentricity of each label
for region in shape_measurements:
    print(region.label, region.perimeter, region.eccentricity)

#%%
# Optional: find the label index of the nucleus with the largest perimeter
perimeters = [region.perimeter for region in shape_measurements]
labels = [region.label for region in shape_measurements]

idx_max_perimeter = np.argmax(perimeters)
label_max_perimeter = labels[idx_max_perimeter]

print('Largest perimeter:',np.max(perimeters))
print('Label with largest perimeter:',label_max_perimeter)

#%%
# Change the pixel size to 0.5 um and repeat the measurements. Why do some parameters change while others don't?
shape_measurements_scaled = regionprops(image, spacing=scales_image)
for region in shape_measurements_scaled:
    print(region.label, region.perimeter, region.eccentricity)

#%%
# Optional: Create an image where each object is colored according to the measured circularity
shape_measurements_table = regionprops_table(image, properties=("label", "eccentricity"))

#%%
colors = plt.cm.viridis(shape_measurements_table["eccentricity"])
label_layer.color_mode = "direct"
label_layer.color = dict(zip(shape_measurements_table["label"], colors))






Assessment

True or false? Discuss with your neighbour

Solution

  • Circularity is independent of image calibration True
  • Area is independent of image calibration. False
  • Perimeter can strongly depend on spatial sampling. True
  • Volume can strongly depend on spatial sampling. True
  • Drawing test images to check how certain shape parameters behave is a good idea. True

Explanations




Follow-up material

Recommended follow-up modules:

Learn more: