How to get ROI as np arrays with omero-py

I’d like to convert my previous workflow using raw .CZI files to one based on our OMERO server. One area that I don’t quite understand is how ROIs work in omero-py.

To give some background on the problem, I’m performing cell segmentation with Cytoflow. I’d like to generate representative cells by normalizing and averaging all segmented cells in an image or group of images. What I did before OMERO was to use Sci-kit image to loop through the masked image, align the cells horizontally (they are rod shaped bacteria), and save the intensity of each cell to a dataframe. Something like as follows:

    image = czifile.imread(f)
    gfp = image[gfp_channel,:,:,0]
    labelled_image = skimage.measure.label(mask)
    feature_table = skimage.measure.regionprops(labelled_image)

    Averaged_GFP_2D = np.zeros([200, 500]) #create empty array to fill with pixel data
    CellSize_sort = []
    CellSize_label = []

# throw away poorly segmented cells
    for i in feature_table:
        if (i.area) > 40 and (i.major_axis_length/i.minor_axis_length) > 2:
            CellSize_sort.append(i.major_axis_length) 
            CellSize_label.append(i.label)
    n=0
    for i in np.argsort(CellSize_sort):
        sample = feature_table[CellSize_label[i]-1]
        angle = np.degrees(sample.orientation)
        length = sample.major_axis_length
        width = sample.minor_axis_length
# get pixel data from masked channel of interest
        target_channel = gfp[sample.slice[0].start:sample.slice[0].stop, sample.slice[1].start:sample.slice[1].stop]
        target_channel = target_channel * sample.filled_image
# rotate masked pixel data to be horizontal
        rotated_image = sk.transform.rotate(target_channel, -angle-90, resize = True)
        masked = target_channel[target_channel > 0]
        H, W = rotated_image.shape
        image_center = rotated_image[max([int(H/2-width/2), 0]):int(H/2+width/2)+1,max([0,int(W/2-length/2)]):int(W/2+length/2)]
# collect pixel data in a dataframe for later averaging/processing.
        Averaged_GFP_2D = Averaged_GFP_2D + sk.transform.resize(image_center, (Averaged_GFP_2D.shape[0],Averaged_GFP_2D.shape[1]))
        n+=1

With OMERO, the images are loaded from the OMERO server, segmented, and the resulting mask is then saved back to the image as an ROI. An example notebook of my segmentation workflow with OMERO is here. Is it possible to get the ROIs to work with skimage.measure.regionprops, as I did before, or is there perhaps a better way? I followed the OMERO Python developer documentation and was able to load the ROIs, I just cant figure out the best way to get their pixel intensities out as arrays that can be processed. I have a notebook with my work in progress here.

Apologies for the long-winded question.

Jonathan

Hi @jsakkos ,
I wrote a function converting omero ROIs to numpy arrays here:

Should be adapted to regionprops (maybe modulo a transpose call)

Hope this helps,

Guillaume

Hi Jonathan,

Are you effectively looking for a function that does the opposite of omero_rois.masks_from_label_image(masks)?

There is code that probably does most of what you want at omero-cli-zarr/masks.py at d0f076ed1cb469fb2451af79eb16934789cea04f · ome/omero-cli-zarr · GitHub
I see it even says TODO: Move to https://github.com/ome/omero-rois/!

That code returns a boolean numpy array. To get the pixel values for each mask, you’ll need to
get the plane and slice out the region of the mask (x, y, width, height), then use the boolean array to pick the pixel values that are within the mask.
Apologies, I don’t know the numpy methods off the top of my head, but hopefully you can figure it out from there?

If you don’t have the whole plane in hand, you can use pixels.getTiles(list) see docs to get the pixel data for each mask in turn. But I think you’re already loading the whole plane.

Hope I’ve understood correctly. Let us know if not,

Regards,
Will.

I created an issue to move that code to omero-rois label image from masks · Issue #10 · ome/omero-rois · GitHub

Thanks, @glyg. It seems like maskI objects don’t have some of the functions you used. They also need to be converted from binary.

Thanks for the reference @will-moore. It took me a while, but I was able to figure out how to adapt that function to return an array of the masks.

def mask_to_arr(mask):
    t = unwrap(mask.theT)
    c = unwrap(mask.theC)
    z = unwrap(mask.theZ)

    x = int(mask.x.val)
    y = int(mask.y.val)
    w = int(mask.width.val)
    h = int(mask.height.val)

    mask_packed = mask.getBytes()
    # convert bytearray into something we can use
    intarray = np.frombuffer(mask_packed, dtype=np.uint8)
    binarray = np.unpackbits(intarray).astype(np.uint8)
    # truncate and reshape
    binarray = np.reshape(binarray[: (w * h)], (h, w))

    return binarray, (t, c, z, y, x, h, w)

roi_service = conn.getRoiService()
result = roi_service.findByImage(imageId, None)
for roi in result.rois:
    print("ROI:  ID:", roi.getId().getValue())
    for s in roi.copyShapes():
        shape = {}
        shape['id'] = s.getId().getValue()
        if s.getTextValue():
            shape['textValue'] = s.getTextValue().getValue()
        if type(s) == omero.model.MaskI:
            binarray, (t, c, z, y, x, h, w) = mask_to_arr(s)
            plt.imshow(binarray)
            plt.show()
        # Print shape id:
        print("   Shape:",)
        for key, value in shape.items():
            print("  ", key, value,)
        print("")
2 Likes