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, etc. 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.
Concept map
graph TD
li[Label Image] --> sa("Shape Analysis")
feature_columns -.- |"e.g."| ex["area (volume) perimeter (surface) circularity = 4 Pi A/P^2"]
sa --> table("Results table")
table --> object_rows["Rows are objects"]
table --> feature_columns["Columns are shape features"]
Explore simple shape features (area, volume, perimeter) and some more complex ones, like circularity, elongation.
Discuss (using a white board) some shape features and concepts. For example:
Area
Perimeter
Circularity = ( 4 Pi Area ) / Perimeter^2
Solidity = Convexity = Area / Area-Convex-Hull
Ellipse fit
(Optional) Draw a square (=circle) of different size 2x2 pixels (paper, whiteboard, …)
Measure area, perimeter and circularity
Discuss the results
(Optional) To show effect of small sized objects use
xy_8bit_labels__circles_different_size.tif. Discuss how discrete nature of image may give mathematically unprecise results for small objects
diameter-circle (px)
Area (theory)
Perimeter (theory)
Area (MLJ)
Perimeter (MLJ)
1
0.78
3.141
1
2.68
3
7.06
9.42
5
8.04
5
19.63
15.70
21
15.62
11
95.03
34.55
97
33.94
51
2042.82
160.22
2053
161.19
(Optional) Discuss the England’s coastline paradox Wiki
#%% [markdown]
# # Measure shapes in 2D
#%%
fromOpenIJTIFFimportopen_ij_tiffimportnaparifromskimage.measureimportregionprops#%%
# 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
forregioninshape_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
forregioninshape_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)
Measure object shapes and find the label index of the nucleus with the largest perimeter
Change the pixel size to 0.5 um and repeat the measurements. Why do some parameters change while others don’t?
(Optional) Create an image where each object is coloured according to the measured circularity
Solution
[Plugins > MorphoLibJ > Analyze > Analyze Regions] the upper right nuclei.
Some features are the ratio of dimensional features and so are independent of the spatial calibration.
[Plugins > MorphoLibJ > Label Regions > Assign Measure to Label].
skimage napari
#%% [markdown]
# # Practice measure shape
#%%
fromOpenIJTIFFimportopen_ij_tiffimportmatplotlib.pyplotaspltimportnapariimportnumpyasnpfromskimage.measureimportregionprops,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
forregioninshape_measurements:print(region.label,region.perimeter,region.eccentricity)#%%
# Optional: find the label index of the nucleus with the largest perimeter
perimeters=[region.perimeterforregioninshape_measurements]labels=[region.labelforregioninshape_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)forregioninshape_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
Circularity is independent of image calibration.
Area is independent of image calibration.
Perimeter can strongly depend on spatial sampling.
Volume can strongly depend on spatial sampling.
Drawing test images to check how certain shape parameters behave is a good idea.
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