Need help with regionprops

Dear Sckit users,
I thresholded muscle fibers to obtain a binary mask (preMask) as such:
binaryMask
and I would like to filter the fibers based on size and circularity, here is the different steps I followed:

  1. label_image = sc.measure.label(preMask)
  2. fiberList = sc.measure.regionprops(label_image)

The number of objects found is 299 using len(fiberList), I then filter the list using:

correct_labels = [fiber.label for fiber in fiberList if
                  (
                      (fiber.area*(pixelSize**2) >= 150) and
                      (fiber.area*(pixelSize**2)<=10000) and
                      (4*math.pi*(fiber.area/fiber.perimeter**2) > 0.4)
                  )
                 ]

To obtain 293 fibers (len(correct_labels)) that pass the cut.
I then recreate an image with only the correct fibers for subsequent processing:

finalMask = np.zeros_like(preMask) # create the final mask image, empty at the moment

for label in correct_labels: # loop through the list of filtered labels
    obj = fiberList[label-1] # assign the coming fiber to 'obj' (need to be label-1 because it start at 0)
    for r,c in obj.coords:
        finalMask[r,c] = True # Assign label value

To obtain this final mask:

Now is the core of my question: how can I recreate this final mask with a more intelligent solution? One way would be to put the pixels belonging to rejected fibers to False in the preMask images but it still requires to loop through a certain number of pixels…
Is there a way to pop regions out of the fiberList and recreate an Image using this “cleaned” List ?
I couldn’t find a way to go back from a regionprops to an image without drawing each pixels independently.
Thank you for your help.

Ah, yes! You should look at the source code for skimage.morphology.remove_small_objects for inspiration. The core idea is that you can index a NumPy array with another:

In [1]: labels = np.array([0, 1, 2, 0, 4])                                             
In [2]: image = np.array([[0, 0, 1, 1, 1], 
   ...:                   [2, 2, 0, 0, 0], 
   ...:                   [0, 0, 3, 0, 4]])                                            
In [3]: labels[image]                                                                  
Out[3]: 
array([[0, 0, 1, 1, 1],
       [2, 2, 0, 0, 0],
       [0, 0, 0, 0, 4]])

Note how the 3 has disappeared.

So, first make an indexing array:

labels = np.arange(np.max(label_image) + 1)

Then, in your for loop, set to 0 the labels you don’t want:

for fiber in fiberList:
    fiber_area = float(fiber.area * pixelSize**2)
    circularity = 4*math.pi*(fiber.area/fiber.perimeter**2) > 0.4
    if not (
        150 <= fiber_area <= 10000
        and circularity > 0.4
    ):
    labels[fiber.label] = 0

finalMask = labels[mask]
3 Likes

Perfect ! Thank you very much, it works better than I hoped !
Just to know, it is not possible to go from regionprops to labels in a single command ?

No. You might be interested in our new function, regionprops_table, in version 0.16 of scikit-image coming out in the next few days, but there is no performance advantage to it — it’s doing something very similar to that for-loop.

Just to offer an alternative method to the one suggested by @jni.
You can use numpy.isin.
So you woud build a list with labels that you want to remove, e.g. labels_to_remove = [4,5,6] (you would of course use regionprops for this and then do something like

labels[np.isin(labels, labels_to_remove)] = 0

This method also only goes through the pixels once. The method @jni suggested is more powerful as you can also use it to renumber your labels if you do it correctly. However, I find the np.isin method easier to get my head around and remember (for the other method I always need to go back and look at an example).