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
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
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])