FaCoffee
FaCoffee

Reputation: 7929

Scikit image: proper way of counting cells in the objects of an image

Say you have an image in the form of a numpy.array:

vals=numpy.array([[3,24,25,6,2],[8,7,6,3,2],[1,4,23,23,1],[45,4,6,7,8],[17,11,2,86,84]])

And you want to compute how many cells are inside each object, given a threshold value of 17 (example):

from scipy import ndimage
from skimage.measure import regionprops

blobs = numpy.where(vals>17, 1, 0)
labels, no_objects = ndimage.label(blobs)
props = regionprops(blobs)

If you check, this gives an image with 4 distinct objects over the threshold:

In[1]: blobs
Out[1]: 
array([[0, 1, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 1, 1, 0],
       [1, 0, 0, 0, 0],
       [0, 0, 0, 1, 1]])

In fact:

In[2]: no_objects
Out[2]: 4

I want to compute the number of cells (or area) of each object. The intended outcome is a dictionary with the object ID: number of cells format:

size={0:2,1:2,2:1,3:2}

My attempt:

size={}
for label in props:
    size[label]=props[label].area

Returns an error:

Traceback (most recent call last):

  File "<ipython-input-76-e7744547aa17>", line 3, in <module>
    size[label]=props[label].area

TypeError: list indices must be integers, not _RegionProperties

I understand I am using label incorrectly, but the intent is to iterate over the objects. How to do this?

Upvotes: 3

Views: 1981

Answers (3)

Stefan van der Walt
Stefan van der Walt

Reputation: 7253

To answer the original question:

  • You have to apply regionprops to the labeled image: props = regionprops(labels)

  • You can then construct the dictionary using:

    size = {r.label: r.area for r in props}

    which yields

    {1: 2, 2: 2, 3: 1, 4: 2}

Upvotes: 2

Divakar
Divakar

Reputation: 221664

That regionprops will generate a lot more information than just the area of each blob. So, if you are just looking to get the count of pixels for the blobs, as an alternative and with focus on performance, we can use np.bincount on labels obtained with ndimage.label, like so -

np.bincount(labels.ravel())[1:]

Thus, for the given sample -

In [53]: labeled_areas = np.bincount(labels.ravel())[1:]

In [54]: labeled_areas
Out[54]: array([2, 2, 1, 2])

To have these results in a dictionary, one additional step would be -

In [55]: dict(zip(range(no_objects), labeled_areas))
Out[55]: {0: 2, 1: 2, 2: 1, 3: 2}

Upvotes: 1

FaCoffee
FaCoffee

Reputation: 7929

A bit of testing and research sometimes goes a long way.

The problem is both with blobs, because it is not carrying the different labels but only 0,1 values, and label, which needs to be replaced by an iterator looping over range(0,no_objects).

This solution seems to be working:

import skimage.measure as measure
import numpy
from scipy import ndimage
from skimage.measure import regionprops

vals=numpy.array([[3,24,25,6,2],[8,7,6,3,2],[1,4,23,23,1],[45,4,6,7,8],[17,11,2,86,84]])

blobs = numpy.where(vals>17, 1, 0) 
labels, no_objects = ndimage.label(blobs)

#blobs is not in an amicable type to be processed right now, so:
labelled=ndimage.label(blobs)
resh_labelled=labelled[0].reshape((vals.shape[0],vals.shape[1])) #labelled is a tuple: only the first element matters

#here come the props
props=measure.regionprops(resh_labelled) 

#here come the sought-after areas
size={i:props[i].area for i in range (0, no_objects)}

Result:

In[1]: size
Out[1]: {0: 2, 1: 2, 2: 1, 3: 2}

And if anyone wants to check for the labels:

In[2]: labels
Out[2]: 
array([[0, 1, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 2, 2, 0],
       [3, 0, 0, 0, 0],
       [0, 0, 0, 4, 4]])

And if anyone wants to plot the 4 objects found:

import matplotlib.pyplot as plt
plt.set_cmap('OrRd')
plt.imshow(labels,origin='upper')

enter image description here

Upvotes: 2

Related Questions