hadim / read-roi

Read ROI files .zip or .roi generated with ImageJ.
BSD 3-Clause "New" or "Revised" License
51 stars 22 forks source link

converting shapes to areas #2

Closed swharden closed 7 years ago

swharden commented 7 years ago

Currently all ROIs (rectangle, polygon, oval, etc.) just report x/y coordinates of their edges. Do you have any plans to add a function to convert an ROI to area, which would output x,y pixel coordinates of all pixels inside the ROI?

This:

rois = read_roi_zip('RoiSet.zip')
for key in [x for x in rois.keys() if rois[x]['type'] is 'polygon']:
     print(key, rois[key]['type'], rois[key]['x'],rois[key]['y'])

Currently outputs:

0194-0028 polygon [19, 43, 39, 21, 13, 20] [179, 184, 202, 210, 194, 179]
0177-0225 polygon [220, 210, 208, 229, 243, 242] [165, 175, 186, 190, 187, 172]

But it would be great if:

print(rois[key]['area'])

...displayed a large list of all X/Y positions encircled by each shape. Each ROI dictionary would be pre-loaded with the area key when the data is read. Do you have code on hand that does this?

Resources:

swharden commented 7 years ago

demo

I came up with this hack (supporting rectangles only) to get me started. It might help. It produces the output above when given an ROI (zip). Currently only rectangles are supported. The roi_areas function is what I think may eventually belong in your project.

"""this example is extremely simplistic and not very efficient, but it's easy to read."""
import inspect_t
from read_roi import read_roi_zip
import os
import numpy as np
import matplotlib.pyplot as plt

def roi_areas(roiFile):
    """
    Given an ROI file (created with ImageJ's ROI Manager), analyze each
    ROI and return a dictionary with its keys including 'area' which is
    a list of X/Y pixel pairs encircled by the ROI.

    Also adds a 'bounds' key [X1,X2,Y1,Y2] of the bounds of the ROI
    based on its area.
    """
    assert os.path.exists(roiFile)
    rois = read_roi_zip(roiFile)
    for key in rois:
        roi=rois[key]

        # populate keys for rectangles
        if roi['type'] is 'rectangle':
            X1,Y1=roi['left'],roi['top']
            X2,Y2=roi['width']+X1,roi['height']+Y1
            area=[]
            for x in range(X1,X2):
                for y in range(Y1,Y2):
                    area.append((x,y))
            rois[key]['area']=area

        else: #TODO: add eclipse and polygon support
            print("WARNING: 'area' key unavailable for",roi['type'])

        # calculate bounds of a shape based on its area
        if 'area' in rois[key].keys():
            Xs,Ys=[],[]
            for X,Y in rois[key]['area']:
                Xs.append(X)
                Ys.append(Y)
            rois[key]['bounds']=np.min(Xs),np.max(Xs),np.min(Ys),np.max(Ys)

    return rois

def label_rois(rois,color='k',drawBounds=False):
    """
    given the rois (dict), label each ROI on an existing subplot.
    Assume matplot's imshow() has already been called. Optionally
    add ROI labels, set their color, and/or draw bounding box rectangles.
    """
    assert type(rois) is dict
    for i,key in enumerate(rois.keys()):
        X1,X2,Y1,Y2=rois[key]['bounds']
        plt.text(X1,Y1+-1,i,verticalalignment='bottom',color=color)
        if drawBounds:
            plt.plot([X1,X2,X2,X1,X1],[Y1,Y1,Y2,Y2,Y1],'-',color=color)
        plt.margins(0,0)
    return

if __name__=="__main__":
    # load the 3D image data (basically a list of 2D images) from disk
    fldr=r'C:\Users\swharden\Documents\temp\TSeries-01112017-1536-1177'
    G,R,fnamesCH1,fnamesCH2,conf=inspect_t.inspect_folder(fldr)

    pic=R[1].copy() # a 2D array representing an image
    picROI=R[1].copy() # a 2D array we will use to depict what's masked
    picROI[:,:]=np.nan

    roiFile=r'C:\Users\swharden\Documents\temp\TSeries-01112017-1536-1177\RoiSet.zip'
    rois=roi_areas(roiFile)

    for i,key in enumerate(rois):
        mask=np.zeros(R[0].shape,dtype=np.bool)
        for Y,X in rois[key]['area']:
            mask[Y,X]=True
            picROI[X,Y]=pic[X,Y]
        roiValues=pic[mask]

    # plot the ROIs
    plt.figure(figsize=(15,10))

    plt.subplot(131)
    plt.imshow(pic)

    plt.subplot(132)
    plt.imshow(picROI)
    label_rois(rois)

    plt.subplot(133)
    plt.imshow(pic)
    label_rois(rois,color='w',drawBounds=True)

    plt.tight_layout()
    plt.show()

    print("DONE")
hadim commented 7 years ago

Hello,

read-roi only focus on reading IJ ROIs files so if the information like the area is not the ROI file read-roi won't provide it.

I think this kind of analysis should be done in your code. Or you could also create a new library that use read-roi for I/O and has its main part doing some ROIs computation such as calculating the area.

swharden commented 7 years ago

I agree with your logic! I'll make a standalone area_from_roi() function that does this then link back to it when it's complete.

hadim commented 7 years ago

Thank you for your understanding !