Statistics using Scikit-image#
We can use scikit-image for extracting features from label images. For convenience reasons we use the napari-skimage-regionprops library.
Before we can do measurements, we need an image and a corresponding label_image. Therefore, we recapitulate filtering, thresholding and labeling:
from skimage.io import imread
from skimage import filters
from skimage import measure
from napari_skimage_regionprops import regionprops_table
from pyclesperanto_prototype import imshow
import pandas as pd
import numpy as np
import pyclesperanto_prototype as cle
# load image
image = imread("../../data/blobs.tif")
# denoising
blurred_image = filters.gaussian(image, sigma=1)
# binarization
threshold = filters.threshold_otsu(blurred_image)
thresholded_image = blurred_image >= threshold
# labeling
label_image = measure.label(thresholded_image)
# visualization
imshow(label_image, labels=True)
Measurements / region properties#
We are now using the very handy function regionprops_table. It provides features based on the scikit-image regionprops list of measurements library. Let us check first what we need to provide for this function:
regionprops_table?
Signature:
regionprops_table(
image: 'napari.types.ImageData',
labels: 'napari.types.LabelsData',
size: bool = True,
intensity: bool = True,
perimeter: bool = False,
shape: bool = False,
position: bool = False,
moments: bool = False,
napari_viewer: 'napari.Viewer' = None,
) -> 'pandas.DataFrame'
Docstring: Adds a table widget to a given napari viewer with quantitative analysis results derived from an image-label pair.
File: c:\users\maral\mambaforge\envs\feature_blogpost\lib\site-packages\napari_skimage_regionprops\_regionprops.py
Type: function
We see that we need to provide which feature categories we want to measure. One way of dividing these categories is shown here:

Feature categories which are set to True are measured by default. In this case, the categories are size and intensity. But perimeter and shape would be also interesting to investigate. So we need to set them to True as well.
df = pd.DataFrame(regionprops_table(image , label_image,
perimeter = True,
shape = True,
position=True,
moments=True))
df
| label | area | bbox_area | equivalent_diameter | convex_area | max_intensity | mean_intensity | min_intensity | perimeter | perimeter_crofton | ... | moments_hu-1 | moments_hu-2 | moments_hu-3 | moments_hu-4 | moments_hu-5 | moments_hu-6 | standard_deviation_intensity | aspect_ratio | roundness | circularity | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 429 | 750 | 23.371345 | 479 | 232.0 | 191.440559 | 128.0 | 89.012193 | 87.070368 | ... | 0.018445 | 0.000847 | 1.133411e-04 | 1.205336e-08 | -2.714451e-06 | 3.298765e-08 | 29.793138 | 2.088249 | 0.451572 | 0.680406 |
| 1 | 2 | 183 | 231 | 15.264430 | 190 | 224.0 | 179.846995 | 128.0 | 53.556349 | 53.456120 | ... | 0.010549 | 0.001177 | 6.734903e-05 | -1.483503e-08 | -5.401836e-06 | -1.180484e-08 | 21.270534 | 1.782168 | 0.530849 | 0.801750 |
| 2 | 3 | 658 | 756 | 28.944630 | 673 | 248.0 | 205.604863 | 120.0 | 95.698485 | 93.409370 | ... | 0.000113 | 0.000104 | 1.146642e-06 | 4.801450e-13 | 9.889775e-09 | -1.248247e-11 | 29.392255 | 1.067734 | 0.918683 | 0.902871 |
| 3 | 4 | 433 | 529 | 23.480049 | 445 | 248.0 | 217.515012 | 120.0 | 77.455844 | 76.114262 | ... | 0.000096 | 0.000348 | 3.349309e-07 | -3.612652e-12 | 2.526209e-09 | -9.198337e-14 | 35.852345 | 1.061942 | 0.917813 | 0.906963 |
| 4 | 5 | 472 | 551 | 24.514670 | 486 | 248.0 | 213.033898 | 128.0 | 83.798990 | 82.127941 | ... | 0.005876 | 0.000072 | 4.356690e-06 | -6.223212e-11 | -3.178823e-07 | 4.522442e-11 | 28.741080 | 1.579415 | 0.621952 | 0.844645 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 57 | 58 | 213 | 285 | 16.468152 | 221 | 224.0 | 184.525822 | 120.0 | 52.284271 | 52.250114 | ... | 0.001745 | 0.000002 | 1.126452e-07 | 5.814575e-14 | 4.625041e-09 | -6.410600e-15 | 28.255467 | 1.296143 | 0.771094 | 0.979146 |
| 58 | 59 | 79 | 108 | 10.029253 | 84 | 248.0 | 184.810127 | 128.0 | 39.313708 | 39.953250 | ... | 0.056792 | 0.001777 | 1.725200e-04 | -5.364882e-08 | -2.906455e-05 | -7.903632e-08 | 33.739912 | 3.173540 | 0.300766 | 0.642316 |
| 59 | 60 | 88 | 110 | 10.585135 | 92 | 216.0 | 182.727273 | 128.0 | 45.692388 | 46.196967 | ... | 0.097966 | 0.002268 | 2.994111e-04 | -1.724577e-08 | -3.731912e-05 | -2.461160e-07 | 24.417173 | 4.021193 | 0.238521 | 0.529669 |
| 60 | 61 | 52 | 75 | 8.136858 | 56 | 248.0 | 189.538462 | 128.0 | 30.692388 | 32.924135 | ... | 0.046813 | 0.003694 | 3.041106e-04 | -2.539652e-07 | -5.698262e-05 | -1.984555e-07 | 37.867411 | 2.839825 | 0.322190 | 0.693668 |
| 61 | 62 | 48 | 68 | 7.817640 | 53 | 224.0 | 173.833333 | 128.0 | 33.071068 | 35.375614 | ... | 0.125246 | 0.004295 | 1.300612e-03 | 2.775191e-06 | 3.027878e-04 | 1.321910e-06 | 27.987596 | 4.417297 | 0.213334 | 0.551512 |
62 rows × 86 columns
As you can see, we have now plenty of features to investigate. We can print out all feature names with the keys function:
print(df.keys())
Index(['label', 'area', 'bbox_area', 'equivalent_diameter', 'convex_area',
'max_intensity', 'mean_intensity', 'min_intensity', 'perimeter',
'perimeter_crofton', 'extent', 'local_centroid-0', 'local_centroid-1',
'solidity', 'feret_diameter_max', 'major_axis_length',
'minor_axis_length', 'orientation', 'eccentricity', 'centroid-0',
'centroid-1', 'bbox-0', 'bbox-1', 'bbox-2', 'bbox-3',
'weighted_centroid-0', 'weighted_centroid-1', 'moments-0-0',
'moments-0-1', 'moments-0-2', 'moments-0-3', 'moments-1-0',
'moments-1-1', 'moments-1-2', 'moments-1-3', 'moments-2-0',
'moments-2-1', 'moments-2-2', 'moments-2-3', 'moments-3-0',
'moments-3-1', 'moments-3-2', 'moments-3-3', 'moments_normalized-0-0',
'moments_normalized-0-1', 'moments_normalized-0-2',
'moments_normalized-0-3', 'moments_normalized-1-0',
'moments_normalized-1-1', 'moments_normalized-1-2',
'moments_normalized-1-3', 'moments_normalized-2-0',
'moments_normalized-2-1', 'moments_normalized-2-2',
'moments_normalized-2-3', 'moments_normalized-3-0',
'moments_normalized-3-1', 'moments_normalized-3-2',
'moments_normalized-3-3', 'moments_central-0-0', 'moments_central-0-1',
'moments_central-0-2', 'moments_central-0-3', 'moments_central-1-0',
'moments_central-1-1', 'moments_central-1-2', 'moments_central-1-3',
'moments_central-2-0', 'moments_central-2-1', 'moments_central-2-2',
'moments_central-2-3', 'moments_central-3-0', 'moments_central-3-1',
'moments_central-3-2', 'moments_central-3-3', 'moments_hu-0',
'moments_hu-1', 'moments_hu-2', 'moments_hu-3', 'moments_hu-4',
'moments_hu-5', 'moments_hu-6', 'standard_deviation_intensity',
'aspect_ratio', 'roundness', 'circularity'],
dtype='object')
And describe gives us basic statistics like max, mean, min and std of each feature:
df.describe()
| label | area | bbox_area | equivalent_diameter | convex_area | max_intensity | mean_intensity | min_intensity | perimeter | perimeter_crofton | ... | moments_hu-1 | moments_hu-2 | moments_hu-3 | moments_hu-4 | moments_hu-5 | moments_hu-6 | standard_deviation_intensity | aspect_ratio | roundness | circularity | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 62.000000 | 62.000000 | 62.000000 | 62.000000 | 62.000000 | 62.000000 | 62.000000 | 62.000000 | 62.000000 | 62.000000 | ... | 62.000000 | 6.200000e+01 | 6.200000e+01 | 6.200000e+01 | 6.200000e+01 | 6.200000e+01 | 62.000000 | 62.000000 | 62.000000 | 62.000000 |
| mean | 31.500000 | 355.370968 | 475.677419 | 20.074583 | 372.790323 | 233.548387 | 190.429888 | 125.161290 | 67.787235 | 67.071263 | ... | 0.012854 | 5.166382e-04 | 7.053581e-05 | 5.855846e-08 | 3.633047e-06 | -8.703715e-09 | 28.689171 | 1.637991 | 0.692418 | 0.894101 |
| std | 18.041619 | 211.367385 | 300.328169 | 7.091876 | 223.801078 | 19.371838 | 15.382559 | 4.602898 | 25.008581 | 23.507575 | ... | 0.027066 | 1.077748e-03 | 2.093454e-04 | 3.687535e-07 | 3.998053e-05 | 2.897240e-07 | 6.127700 | 0.794366 | 0.210973 | 0.183024 |
| min | 1.000000 | 7.000000 | 9.000000 | 2.985411 | 7.000000 | 152.000000 | 146.285714 | 112.000000 | 6.828427 | 9.155272 | ... | 0.000056 | 2.514436e-07 | 6.992073e-09 | -2.539652e-07 | -5.698262e-05 | -1.757113e-06 | 5.598834 | 1.048053 | 0.213334 | 0.529669 |
| 25% | 16.250000 | 194.750000 | 260.000000 | 15.745692 | 204.750000 | 232.000000 | 182.969505 | 120.000000 | 52.602291 | 52.551616 | ... | 0.000627 | 3.032838e-05 | 1.242304e-07 | -1.881559e-13 | -1.039790e-08 | -7.811552e-13 | 26.514258 | 1.168451 | 0.538616 | 0.805774 |
| 50% | 31.500000 | 366.000000 | 448.500000 | 21.585875 | 376.500000 | 240.000000 | 190.749492 | 128.000000 | 69.112698 | 68.204464 | ... | 0.001971 | 6.198102e-05 | 5.957846e-07 | 6.176303e-14 | 5.408146e-10 | -9.288753e-15 | 29.017801 | 1.316003 | 0.757485 | 0.925560 |
| 75% | 46.750000 | 500.750000 | 685.500000 | 25.250050 | 516.500000 | 248.000000 | 199.725305 | 128.000000 | 86.097980 | 84.307520 | ... | 0.010151 | 3.475818e-04 | 3.799021e-05 | 1.135209e-11 | 1.484276e-08 | 1.902152e-12 | 32.534123 | 1.769976 | 0.851463 | 0.966037 |
| max | 62.000000 | 896.000000 | 1350.000000 | 33.776066 | 977.000000 | 248.000000 | 220.026144 | 136.000000 | 129.982756 | 125.912897 | ... | 0.125246 | 5.440067e-03 | 1.300612e-03 | 2.775191e-06 | 3.027878e-04 | 1.321910e-06 | 38.323999 | 4.417297 | 0.974824 | 1.886542 |
8 rows × 86 columns
Exercises#
Make a table with only solidity, circularity and roundness.