How to measure standard deviation of intensities with scikit-image regionprops

Hi all,

I’m trying to measure the standard deviation of labelled regions using scikit-image. The documentation unfortunately doesn’t contain the terms “standard deviation” and “variance” and google is also not very helpful. Thus, I’m asking here.

Assume this is our image:

1 2 3
4 5 6
7 8 9

Depending on if you use the equation for a population or a sample of pixels, you can determine a standard deviation of 2.58 or 2.74, respectively. numpy.std returns the first. So far, so good. But how to do this for multiple labels using skimage.measure.regionprops ? The variance is also known as the second moment and thus, I would expect to find the variance of my image in its moments.

Here is some python code for printing out moments. Again my question: How can I retrieve the standard deviation from these?

import numpy as np

image = np.asarray([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])

labels = np.asarray([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1]
])

print("numpy standard deviation", np.std(image))
print("numpy variance", np.var(image))

from skimage.measure import regionprops
stats = regionprops(labels, intensity_image=image)

print(stats[0].moments)
print(stats[0].moments_central)
print(stats[0].moments_normalized)

Output:

numpy standard deviation 2.581988897471611
numpy variance 6.666666666666667
[[ 9.  9. 15. 27.]
 [ 9.  9. 15. 27.]
 [15. 15. 25. 45.]
 [27. 27. 45. 81.]]
[[9. 0. 6. 0.]
 [0. 0. 0. 0.]
 [6. 0. 4. 0.]
 [0. 0. 0. 0.]]
[[       nan        nan 0.07407407 0.        ]
 [       nan 0.         0.         0.        ]
 [0.07407407 0.         0.00548697 0.        ]
 [0.         0.         0.         0.        ]]

Thanks for your help and Merry Christmas btw.!

Cheers,
Robert

1 Like

Hello Robert!

The moments are not of the pixel intensities but rather the image moments, which are defined in terms of image coordinates. (They can also be a “convolution” of image coordinates and image intensities, and those are available under the weighted_moments* properties.)

So we don’t actually have an image variance, but thanks to @VolkerH, as of 0.18 we now have the extra_properties= parameter to regionprops, so you can measure whatever you can dream of!

import numpy as np
from skimage import measure


image = np.arange(1, 10).reshape((3, 3))
labels = np.ones_like(image, dtype=int)

# arguments must be in the specified order, matching regionprops
def image_stdev(region, intensities):
    # note the ddof arg to get the sample var if you so desire!
    return np.std(intensities[region], ddof=1)

props = measure.regionprops(
    labels, image, extra_properties=[image_stdev]
)

print(props[0].image_stdev)

Output:

2.7386127875258306

Merry Christmas! :blush: :christmas_tree:

3 Likes

Fantastic! Thanks @jni for the instant support. I hope you were wearing a red hat while typing the solution :santa:

1 Like