Esempio n. 1
0
def mask_to_geojson(img_mask, label=None, simplify_tol=1.5):
    """
    Args:
      img_mask (numpy array): numpy data, with each object being assigned with a unique uint number
      label (str): like 'cell', 'nuclei'
      simplify_tol (float): give a higher number if you want less coordinates.
    """
    # for img_mask, for cells on border, should make sure on border pixels are # set to 0
    shape_x, shape_y = img_mask.shape
    shape_x, shape_y = shape_x - 1, shape_y - 1
    img_mask[0, :] = img_mask[:, 0] = img_mask[shape_x, :] = img_mask[:, shape_y] = 0
    features = []
    label = label or "cell"
    # Get all object ids, remove 0 since this is background
    ind_objs = np.unique(img_mask)
    ind_objs = np.delete(ind_objs, np.where(ind_objs == 0))
    for obj_int in np.nditer(ind_objs):
        # Create binary mask for current object and find contour
        img_mask_loop = np.zeros((img_mask.shape[0], img_mask.shape[1]))
        img_mask_loop[img_mask == obj_int] = 1
        contours_find = measure.find_contours(img_mask_loop, 0.5)
        if len(contours_find) == 1:
            index = 0
        else:
            pixels = []
            for _, item in enumerate(contours_find):
                pixels.append(len(item))
            index = np.argmax(pixels)
        contour = contours_find[index]

        contour_as_numpy = contour[:, np.argsort([1, 0])]
        contour_as_numpy[:, 1] = np.array([img_mask.shape[0] - h[0] for h in contour])
        contour_asList = contour_as_numpy.tolist()

        if simplify_tol is not None:
            poly_shapely = shapely_polygon(contour_asList)
            poly_shapely_simple = poly_shapely.simplify(
                simplify_tol, preserve_topology=False
            )
            contour_asList = list(poly_shapely_simple.exterior.coords)
            contour_as_Numpy = np.asarray(contour_asList)

        # Create and append feature for geojson
        pol_loop = geojson_polygon([contour_asList])
        
        full_label = label + "_idx"
        index_number = int(obj_int - 1)
        features.append(
            Feature(
                geometry=pol_loop, properties={full_label: index_number, "label": label}
            )
        )

    feature_collection = FeatureCollection(
        features, bbox=[0, 0, img_mask.shape[1] - 1, img_mask.shape[0] - 1]
    )
    return feature_collection
 def _as_shapely_polygon(self) -> shapely_polygon:
     points = [
         (self.x1, self.y1),
         (self.x2, self.y1),
         (self.x2, self.y2),
         (self.x1, self.y2),
         (self.x1, self.y1),
     ]
     return shapely_polygon(points)
def _convert_mask(img_mask, label=None, simplify_tol=1.5):
    # for img_mask, for cells on border, should make sure on border pixels are # set to 0
    shape_x, shape_y = img_mask.shape
    shape_x, shape_y = shape_x - 1, shape_y - 1
    img_mask[0, :] = img_mask[:,
                              0] = img_mask[shape_x, :] = img_mask[:,
                                                                   shape_y] = 0
    features = []
    label = label or "cell"
    # Get all object ids, remove 0 since this is background
    ind_objs = np.unique(img_mask)
    ind_objs = np.delete(ind_objs, np.where(ind_objs == 0))
    for obj_int in np.nditer(ind_objs, flags=["zerosize_ok"]):
        # Create binary mask for current object and find contour
        img_mask_loop = np.zeros((img_mask.shape[0], img_mask.shape[1]))
        img_mask_loop[img_mask == obj_int] = 1
        contours_find = measure.find_contours(img_mask_loop, 0.5)
        if len(contours_find) == 1:
            index = 0
        else:
            pixels = []
            for _, item in enumerate(contours_find):
                pixels.append(len(item))
            index = np.argmax(pixels)
        contour = contours_find[index]

        contour_as_numpy = contour[:, np.argsort([1, 0])]
        contour_as_numpy[:, 1] = np.array(
            [img_mask.shape[0] - h[0] for h in contour])
        contour_asList = contour_as_numpy.tolist()

        if simplify_tol is not None:
            poly_shapely = shapely_polygon(contour_asList)
            poly_shapely_simple = poly_shapely.simplify(
                simplify_tol, preserve_topology=False)
            contour_asList = list(poly_shapely_simple.exterior.coords)
            contour_as_Numpy = np.asarray(contour_asList)

        # Create and append feature for geojson
        pol_loop = geojson_polygon([contour_asList])

        full_label = label + "_idx"
        index_number = int(obj_int - 1)
        features.append(
            Feature(geometry=pol_loop,
                    properties={
                        full_label: index_number,
                        "label": label
                    }))
    return features
Esempio n. 4
0
def simplify_contour(contour_asList, simplify_tol=0.2):
    poly_shapely = shapely_polygon(contour_asList)
    poly_shapely_simple = poly_shapely.simplify(simplify_tol,
                                                preserve_topology=True)
    contour_asList = list(poly_shapely_simple.exterior.coords)
    return contour_asList
Esempio n. 5
0
def masks_to_polygon(img_mask,
                     label=None,
                     simplify_tol=0,
                     plot_simplify=False,
                     save_name=None):
    ''' 
    Find contours with skimage, simplify them (optional), store as geojson:
        
    1. Loops over each detected object, creates a mask and finds it contour
    2. Contour can be simplified (reduce the number of points) 
        - uses shapely: https://shapely.readthedocs.io/en/stable/manual.html#object.simplify
         - will be performed if tolernace simplify_tol is != 0
   3. Polygons will be saved in geojson format, which can be read by ImJoys' 
      AnnotationTool. Annotations for one image are stored as one feature collection
      each annotation is one feature:   
         "type": "Feature",
          "geometry": {"type": "Polygon","coordinates": [[]]}
          "properties": null  
        
    Args:
        img_mask (2D numpy array): image wiht segmentation masks. Background is 0, 
                                each object has a unique pixel value.
        simplify_tol (float): tolerance for simplification (All points in the simplified object 
                              will be within the tolerance distance of the original geometry)
                              No simplification will be performed when set to 0.
        plot_simplify (Boolean): plot results of simplifcation. Plot will be shown for EACH mask. 
                                 Use better for debuggin only.
        save_name (string): full file-name to save GeoJson file. Not file will be
                            saved when None.                       
        
    Returns:
        contours (List): contains polygon of each object stored as a numpy array.
        feature_collection : GeoJson feature collection
    '''

    # Prepare list to store polygon coordinates and geojson features
    features = []
    contours = []

    Ncells = img_mask.max()
    # Loop over all masks (except with index 0 which is background)
    for i, obj_int in enumerate(range(1, Ncells)):

        # Create binary mask for current object and find contour
        img_mask_loop = np.zeros((img_mask.shape[0], img_mask.shape[1]))
        img_mask_loop[img_mask == obj_int] = 1
        contour = measure.find_contours(img_mask_loop, 0.5)

        # Proceeed only if one contour was found
        if len(contour) == 1:

            contour_asNumpy = contour[0]
            contour_asList = contour_asNumpy.tolist()

            # Simplify polygon if tolerance is set to any value except 0
            if simplify_tol != 0:
                poly_shapely = shapely_polygon(contour_asList)
                poly_shapely_simple = poly_shapely.simplify(
                    simplify_tol, preserve_topology=False)
                contour_asList = list(poly_shapely_simple.exterior.coords)
                contour_asNumpy = np.asarray(contour_asList)

                if plot_simplify:
                    plot_polygons(poly_shapely, poly_shapely_simple, obj_int)

            # Append to polygon list
            contours.append(contour_asNumpy)

            # Create and append feature for geojson
            pol_loop = geojson_polygon(contour_asList)
            features.append(
                Feature(geometry=pol_loop, properties={"label": label}))

        #elif len(contour) == 0:
        #    print(f'No contour found for object {obj_int}')
        #else:
        #    print(f'More than one contour found for object {obj_int}')

    # Save to json file
    if save_name:
        feature_collection = FeatureCollection(features)
        with open(save_name, 'w') as f:
            dump(feature_collection, f)
            f.close()

    return features, contours
 def _as_shapely_polygon(self) -> shapely_polygon:
     coordinates = self.get_evenly_distributed_ellipse_coordinates()
     return shapely_polygon(coordinates)
 def _as_shapely_polygon(self) -> shapely_polygon:
     """
     Returns the Polygon object as a shapely polygon which is used for calculating intersection between shapes.
     """
     return shapely_polygon([(point.x, point.y) for point in self.points])