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.

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 2-D shapes in an image

Example data

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

#%% 
# Measure shapes in 2D

#%%
from OpenIJTIFF import open_ij_tiff
import napari

#%%
# Open a label image with a few objects
labels, axes, scales, units = open_ij_tiff("https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit_labels__four_objects.tif")

#%%
# Show the label image in napari 
viewer = napari.Viewer()
viewer.add_labels(labels)

#%%
# Perform shape measurements and discuss their meanings
# See: https://scikit-image.org/docs/stable/api/skimage.measure.html#skimage.measure.regionprops
from skimage.measure import regionprops_table
import pandas as pd
properties = [ 'label', 'area', 'perimeter', 'eccentricity', 'major_axis_length', 'minor_axis_length', 'solidity']
table = regionprops_table(labels, properties = properties)
print(type(table))
df = pd.DataFrame(table)
print(type(df))
print(df)

#%%
# Perform scaled (calibrated) shape measurement
# - Observe which shape measurements are changing due to the scaling
df = pd.DataFrame(regionprops_table(labels, properties = properties, spacing=scales))
print(df)

#%%
# Find the object with the biggest area
print(df['area'].max())
print(df['area'].idxmax())
print(df['label'][0]) # i.e. df['label'][df['area'].idxmax()]

#%%
# Save the table as a CSV
df.to_csv('shape_measurements.csv', sep='\t', index=False)



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

#%% 
# 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: