No mask found error in omero-rois

I’m using Cellpose to segment images from Omero using a variation of this script. It works as intended on nearly all of our images, but occasionally throws an error.

---------------------------------------------------------------------------
NoMaskFound                               Traceback (most recent call last)
<ipython-input-42-5237eedf1c65> in <module>
     37             # create segmentation roi
     38             updateService = conn.getUpdateService()
---> 39             msks = omero_rois.masks_from_label_image(masks)
     40             create_roi(image,msks)
     41     else:

~\Anaconda3\envs\omero\lib\site-packages\omero_rois\library.py in masks_from_label_image(labelim, rgba, z, c, t, text, raise_on_no_mask)
    132     for i in range(1, labelim.max() + 1):
    133         mask = mask_from_binary_image(labelim == i, rgba, z, c, t, text,
--> 134                                       raise_on_no_mask)
    135         masks.append(mask)
    136     return masks

~\Anaconda3\envs\omero\lib\site-packages\omero_rois\library.py in mask_from_binary_image(binim, rgba, z, c, t, text, raise_on_no_mask)
     79     else:
     80         if raise_on_no_mask:
---> 81             raise NoMaskFound()
     82         x0 = 0
     83         w = 0

NoMaskFound: No mask found

I can’t figure out why it’s saying no mask found. The error can be reproduced by calling

badMask = np.load ('4359_mask.npy')
goodMask = np.load ('4360_mask.npy')
for mask in [goodMask,badMask]:
    msks = omero_rois.masks_from_label_image(mask)

I have included the masks here masks.zip (668.8 KB)

@jsakkos thanks for sending a minimal example to reproduce your issue. I think the error you are receiving is the expected behavior of the API. The omerorois.masks_from_label_image method currently returns an ordered list of masks where the index of each mask maps to the value of the associated label. If a value is not present in the original array e.g. 1064 in the case of badMask above, the library will:

  • throw an error if raise_on_no_mask is set to True (default)
  • add an empty mask to the list if raise_on_no_mask is set to False

Using the current API, you might want to use the second option although you might need additional code to remove the empty mask if you don’t want them.

Thanks for the reply, @s.besson. I didn’t realize that Cellpose could output a labelled mask with indices missing. I don’t think we want to create empty masks, but perhaps I can check for missing entries and relabel the whole mask if anything is missing?

On a related note, I’m wondering if there is a way to speed up the viewing of the masks in iViewer. Some of these images have ~4k cells and masks, which get nested under a single ROI. It takes sometimes 10-30s to load all the ROIs.

Just as a follow up with a solution, it’s far faster to blindly relabel the output mask from Cellpose with skimage.measure.label, rather than checking for missing masks. The loop to find missing masks in badMask takes about 40s to run…

for i in range(badMask.max()):
    tmp = np.where(badMask==i)
    for a in tmp:
        if a.size == 0:
            print('Mask is empty')

Instead, relabelling the masks with newMask = label(mask) takes < 1s.

Hi Jonathan,

thanks for the update. It’s very useful to know that there is some skimage API allowing to relabel the mask in a performant manner. Just out of curiosity, is the skimage.measure.label idempotent if there is no missing label value?

Answering your previous question, the performance issues of iviewer with a large number of masks (or ROis in general) is a known problem. large number of ROIs: tool not responsive · Issue #335 · ome/omero-iviewer · GitHub is probably the best reference location that captures the issue and where the discussion is taking place before we can come up with an appropriate resolution.

Sebastien,

No, it does not seem to be idempotent. For our specific case, the ordering of the masked cells is arbitrary, but I could imagine this causing issues if the order was important. It would probably be best to check for a missing mask first, but it doesn’t seem worth the computation time, at least how I’ve coded it.

Thanks for linking the Github issue.

measure.label won’t be idempotent (it is doing connected components), but I believe skimage.segmentation.relabel_sequential would be (it is only remapping unique labels).

1 Like

Thanks @jni! That’s good to know.