def generate_polygon(self, sub_mask_anno): contours = measure.find_contours(sub_mask_anno, 0.5, positive_orientation='low') segmentations = [] polygons = [] for contour in contours: for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) if contour.shape[0] < 3: continue poly = Polygon(contour) if poly.area < 5: continue poly = poly.simplify(1.0, preserve_topology=False) if poly.geom_type == 'MultiPolygon': for poly_ in poly: polygons.append(poly_) segmentation = np.array(poly_.exterior.coords).ravel().tolist() segmentations.append(segmentation) else: polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) return segmentations, polygons
def create_sub_mask_annotation(sub_mask): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) sub_mask = np.array(sub_mask) contours = measure.find_contours(sub_mask, 0.5, positive_orientation='low') segmentations = [] polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds width = max_x - x height = max_y - y bbox = (x, y, width, height) area = multi_poly.area annotation = {'segmentation': segmentations, 'bbox': bbox, 'area': area} return annotation
def segmentate_figure(mask, width, height): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) # Pad the mask just in case the polygon is touching the borders mask = np.pad(mask, 1,'constant') contours = measure.find_contours(mask, 0.5, positive_orientation='low') segmentations = [] polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y), # subtract the padding pixel # and situate the points in their correspondent position for i in range(len(contour)): row, col = contour[i] contour[i] = (col + width - 1, row + height - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) if poly.exterior is not None: segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) multi_poly = MultiPolygon(polygons) return segmentations, multi_poly.area
def create_sub_mask_annotation(self, sub_mask): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) contours = measure.find_contours(sub_mask, 0.5, positive_orientation='low') polygons = [] segmentations = [] j = 0 for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) if (poly.is_empty): # Go to next iteration, dont save empty values in list continue polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) return polygons, segmentations
def process_mask_and_bbox(multi_poly): new_mask = np.zeros((576, 1024), dtype=np.bool) for polygon in multi_poly: polygon2mask_aux(new_mask, polygon.exterior.coords) new_mask = create_mask(new_mask) contours = measure.find_contours(new_mask, 0.5, positive_orientation='low') polygons = [] for contour in contours: poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=True) polygons.append(poly) multi_poly = MultiPolygon(polygons) segmentations = [] for polygon in multi_poly: segmentation = np.array(polygon.exterior.coords) temp = np.copy(segmentation[:,0]) segmentation[:,0] = segmentation[:,1] segmentation[:,1] = temp segmentation = segmentation.ravel().tolist() segmentations.append(segmentation) x, y, max_x, max_y = multi_poly.bounds width = max_x - x height = max_y - y bbox = (y, x, height, width) return segmentations, bbox
def save_as_csv(file_path, npy_list, contours, footprints, npys): aoi = '_'.join(npy_list[0].split('/')[-1].split('.')[0].split('_')[5:]) print('save csv: %s, npoly = %d' % (aoi, footprints.sum())) fw = open(file_path[:-4] + '_' + aoi + '.csv', 'w') for j, contour in enumerate(contours): contour = contour / 3 p = Polygon(contour) p = p.simplify(tolerance=0.2) try: contour = np.array(p.boundary.xy, dtype='float32').T except: contour = np.array(p.boundary[0].xy, dtype='float32').T contour = np.round(contour.reshape(-1, 2) * 10) / 10 polygon_str = re.sub(r"[\[\]]", '', ",".join(map(str, contour))) polygon_str = polygon_str.replace(". ", ' ') polygon_str = polygon_str.replace(".,", ',') polygon_str = re.sub(r" {2,}", ' ', polygon_str) polygon_str = re.sub(r" {0,}, {0,}", ',', polygon_str) for i, npy_file in enumerate(npy_list): filename = npy_file.split('/')[-1].split('.')[0] flag = footprints[j, i] if flag: fw.write("%s,%d,\"POLYGON ((%s))\"\n" % (filename, j, polygon_str)) fw.close()
def create_sub_mask_annotation(sub_mask): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) # contours = measure.find_contours( # np.array(sub_mask), 100, positive_orientation="low") contours, hierarchy = cv2.findContours((sub_mask), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1) polygons = [] segmentations = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel new_contour = [] if len(contour) < 4: continue for i in range(len(contour)): row, col = contour[i][0] #contour[i] = (col - 1, row - 1) if (col, row) in new_contour: # multipolygon error continue new_contour.append((col, row)) # Make a polygon and simplify it poly = Polygon(new_contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) return polygons, segmentations
def filter_planes_and_holes(polygons, points, config): """Extracts the plane and obstacles returned from polylidar Will filter polygons according to: number of vertices and size Will also buffer (dilate) and simplify polygons Arguments: polygons {list[Polygons]} -- A list of polygons returned from polylidar points {ndarray} -- MX3 array config {dict} -- Configuration for filtering Returns: tuple -- A list of plane shapely polygons and a list of obstacle polygons """ # filtering configuration pot_post = config['polygon']['postprocess'] pot_filter = pot_post['filter'] # will hold the plane(s) and obstacles found planes = [] obstacles = [] for poly in polygons: shell_coords = [get_point(pi, points) for pi in poly.shell] outline = Polygon(shell=shell_coords) outline = outline.buffer(distance=CMTOM * pot_post['buffer']) outline = outline.simplify(tolerance=CMTOM * pot_post['simplify']) area = outline.area * M2TOCM2 if area >= pot_filter['plane_area']['min']: # Capture the polygon as well as its z height planes.append((outline, shell_coords[0][2])) for hole_poly in poly.holes: # Filter by number of obstacle vertices, removes noisy holes if len(hole_poly) > pot_filter['hole_vertices']['min']: shell_coords = [get_point(pi, points) for pi in hole_poly] outline = Polygon(shell=shell_coords) area = outline.area * M2TOCM2 # filter by area if area >= pot_filter['hole_area'][ 'min'] and area < pot_filter['hole_area']['max']: outline = outline.buffer(distance=CMTOM * pot_post['buffer']) outline = outline.simplify(tolerance=CMTOM * pot_post['simplify']) obstacles.append((outline, shell_coords[0][2])) return planes, obstacles
def simplify(poly): # fig = pyplot.figure(1, figsize=SIZE, dpi=90) um = 1e1 # print(convert_node_to_2d(poly)) # pp = convert_node_to_2d(poly) # p = Polygon(pp) xmax, xmin = poly.max(), poly.min() p = Polygon(poly) # p = Polygon([(0, 0), (1*um, 1*um), (1*um, 0)]) # 1 # ax = fig.add_subplot(121) q = p.simplify(1.5e5) # patch1a = PolygonPatch(p, facecolor=GRAY, edgecolor=GRAY) # ax.add_patch(patch1a) # patch1b = PolygonPatch(q, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2) # ax.add_patch(patch1b) # ax.set_title('a) tolerance 0.2') # # xrange = [xmin*um, xmax*um] # yrange = [xmin*um, xmax*um] # ax.set_xlim(*xrange) # ax.set_ylim(*yrange) # ax.set_aspect(1) #2 # ax = fig.add_subplot(122) # # r = p.simplify(0.5) # # patch2a = PolygonPatch(p, facecolor=GRAY, edgecolor=GRAY, alpha=0.5, zorder=1) # ax.add_patch(patch2a) # patch2b = PolygonPatch(r, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2) # ax.add_patch(patch2b) # ax.set_title('b) tolerance 0.5') # # # xrange = [-3*um, 3*um] # # yrange = [-3*um, 3*um] # xrange = [0*um, 1*um] # yrange = [0*um, 1*um] # ax.set_xlim(*xrange) # ax.set_ylim(*yrange) # ax.set_aspect(1) # pyplot.show() return list(q.exterior.coords)
def _create_annotations(self): # Creates annotations for each isolated mask # Each image may have multiple annotations, so create an array self.annotations = [] for key, mask in self.isolated_masks.items(): annotation = dict() annotation['segmentation'] = [] annotation['iscrowd'] = 0 annotation['image_id'] = self.image_id if not self.category_ids.get(key): print(f'category color not found: {key}; check for missing category or antialiasing') continue annotation['category_id'] = self.category_ids[key] annotation['id'] = self._next_annotation_id() # Find contours in the isolated mask mask = np.asarray(mask, dtype=np.float32) contours = measure.find_contours(mask, 0.5, positive_orientation='low') polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) if (poly.area > 16): # Ignore tiny polygons if (poly.geom_type == 'MultiPolygon'): # if MultiPolygon, take the smallest convex Polygon containing all the points in the object poly = poly.convex_hull if (poly.geom_type == 'Polygon'): # Ignore if still not a Polygon (could be a line or point) polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() annotation['segmentation'].append(segmentation) if len(polygons) == 0: # This item doesn't have any visible polygons, ignore it # (This can happen if a randomly placed foreground is covered up # by other foregrounds) continue # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds self.width = max_x - x self.height = max_y - y annotation['bbox'] = (x, y, self.width, self.height) annotation['area'] = multi_poly.area # Finally, add this annotation to the list self.annotations.append(annotation)
def make_valid(polygon: Polygon) -> Tuple[Polygon, float]: """Ensures shapely.geometry.Polygon object is valid by repeated simplification""" tolerance = 1 for split in range(1, len(polygon.exterior.coords) - 1): if polygon.is_valid or polygon.simplify(polygon.area).is_valid: break # simplification may not be possible (at all) due to ordering # in that case, try another starting point polygon = Polygon(polygon.exterior.coords[-split:] + polygon.exterior.coords[:-split]) for tolerance in range(1, int(polygon.area)): if polygon.is_valid: break # simplification may require a larger tolerance polygon = polygon.simplify(tolerance) a = polygon.area return polygon, tolerance / a if a > 0 else inf
def simplify(points): polygon = Polygon(map(lambda x: (x["x"], x["y"]), points)) polygon = polygon.simplify(0.05) return { "points": map(lambda x: {"x": x[0], "y": x[1]}, polygon.exterior.coords), "centroid": (polygon.centroid.x, polygon.centroid.y), "bounds": polygon.bounds, "area": polygon.area }
def get_tile_geometry(path, origin_espg, tolerance=500): """ Calculate the data and tile geometry for sentinel-2 tiles """ with rasterio.open(path) as src: # Get tile geometry b = src.bounds tile_shape = Polygon([(b[0], b[1]), (b[2], b[1]), (b[2], b[3]), (b[0], b[3]), (b[0], b[1])]) tile_geojson = mapping(tile_shape) # read first band of the image image = src.read(1) # create a mask of zero values mask = image == 0. # generate shapes of the mask novalue_shape = shapes(image, mask=mask, transform=src.affine) # generate polygons using shapely novalue_shape = [ Polygon(s['coordinates'][0]) for (s, v) in novalue_shape ] if novalue_shape: # Make sure polygons are united # also simplify the resulting polygon union = cascaded_union(novalue_shape) # generates a geojson data_shape = tile_shape.difference(union) # If there are multipolygons, select the largest one if data_shape.geom_type == 'MultiPolygon': areas = {p.area: i for i, p in enumerate(data_shape)} largest = max(areas.keys()) data_shape = data_shape[areas[largest]] # if the polygon has interior rings, remove them if list(data_shape.interiors): data_shape = Polygon(data_shape.exterior.coords) data_shape = data_shape.simplify(tolerance, preserve_topology=False) data_geojson = mapping(data_shape) else: data_geojson = tile_geojson # convert cooridnates to degrees return (to_latlon(tile_geojson, origin_espg), to_latlon(data_geojson, origin_espg))
def reduce_complexity(outline): poly = Polygon(outline[0], outline[1:]) poly = poly.simplify(0.0005) reduced = [[[p[0], p[1]] for p in poly.exterior.coords]] reduced += [[[p[0], p[1]] for p in hole.coords] for hole in poly.interiors] if (len(outline) > 1): interiors = list(poly.interiors) return reduced
def contours2Segmentations(contours_array, tolerance: int = 1.0, preserve_topology: bool = False): segmentations = [] polygons = [] for contour in contours_array: poly = Polygon(contour) poly = poly.simplify(tolerance, preserve_topology=preserve_topology) polygons.append(poly) seg = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(seg) return polygons, segmentations
def simplify(points): polygon = Polygon(map(lambda x: (x["x"], x["y"]), points)) polygon = polygon.simplify(0.05) return { "points": map(lambda x: { "x": x[0], "y": x[1] }, polygon.exterior.coords), "centroid": (polygon.centroid.x, polygon.centroid.y), "bounds": polygon.bounds, "area": polygon.area }
def nuclei_segmentation(image, compute_distance=False, radius=10, simp_px=None): # apply threshold logger.debug('thresholding images') thresh_val = filters.threshold_otsu(image) thresh = image >= thresh_val thresh = morphology.remove_small_holes(thresh) thresh = morphology.remove_small_objects(thresh) # remove artifacts connected to image border cleared = segmentation.clear_border(thresh) if len(cleared[cleared > 0]) == 0: return None, None if compute_distance: distance = distance_transform_edt(cleared) local_maxi = feature.peak_local_max(distance, indices=False, labels=cleared, min_distance=radius / 4, exclude_border=False) markers, num_features = ndi.label(local_maxi) if num_features == 0: logger.info('no nuclei found for current stack') return None, None labels = morphology.watershed(-distance, markers, watershed_line=True, mask=cleared) else: labels = cleared logger.info('storing nuclei features') # store all contours found contours = measure.find_contours(labels, 0.9) tform = tf.SimilarityTransform(rotation=math.pi / 2) _list = list() for k, contr in enumerate(contours): contr = tform(contr) contr[:, 0] *= -1 pol = Polygon(contr) if simp_px is not None: pol = pol.simplify(simp_px, preserve_topology=True) _list.append({'id': k, 'boundary': pol}) return labels, _list
def _create_annotations(self): # Creates annotations for each isolated mask self.annotations = [] for key, mask in self.isolated_masks.items(): annotation = dict() annotation['segmentation'] = [] annotation['iscrowd'] = 0 annotation['image_id'] = self.image_id if not self.category_ids.get(key): print( f'category color not found: {key}; check for missing category or antialiasing' ) continue annotation['category_id'] = self.category_ids[key] annotation['id'] = self._next_annotation_id() # Find contours in the isolated mask contours = measure.find_contours(mask, 0.5, positive_orientation='low') polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) if (poly.area > 16): # Ignore tiny polygons poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) segmentation = np.array( poly.exterior.coords).ravel().tolist() annotation['segmentation'].append(segmentation) if len(polygons) == 0: continue # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds self.width = max_x - x self.height = max_y - y annotation['bbox'] = (x, y, self.width, self.height) annotation['area'] = multi_poly.area # Finally, add this annotation to the list self.annotations.append(annotation)
def make_valid(polygon): for split in range(1, len(polygon.exterior.coords) - 1): if polygon.is_valid or polygon.simplify(polygon.area).is_valid: break # simplification may not be possible (at all) due to ordering # in that case, try another starting point polygon = Polygon(polygon.exterior.coords[-split:] + polygon.exterior.coords[:-split]) for tolerance in range(1, int(polygon.area)): if polygon.is_valid: break # simplification may require a larger tolerance polygon = polygon.simplify(tolerance) return polygon
def make_poly(polygon_points): """Instantiate a Polygon from a list of point pairs, or return an error string""" if len(polygon_points) < 4: return 'has too few points' poly = Polygon(polygon_points) if POLY_TOLERANCE: poly = poly.simplify(POLY_TOLERANCE) if not poly.is_valid: return explain_validity(poly) elif poly.is_empty: return 'is empty' elif poly.bounds[0] < 0 or poly.bounds[1] < 0: return 'is negative' return poly
def get_tile_geometry(path, origin_espg, tolerance=500): """ Calculate the data and tile geometry for sentinel-2 tiles """ with rasterio.open(path) as src: # Get tile geometry b = src.bounds tile_shape = Polygon([(b[0], b[1]), (b[2], b[1]), (b[2], b[3]), (b[0], b[3]), (b[0], b[1])]) tile_geojson = mapping(tile_shape) # read first band of the image image = src.read(1) # create a mask of zero values mask = image == 0. # generate shapes of the mask novalue_shape = shapes(image, mask=mask, transform=src.affine) # generate polygons using shapely novalue_shape = [Polygon(s['coordinates'][0]) for (s, v) in novalue_shape] if novalue_shape: # Make sure polygons are united # also simplify the resulting polygon union = cascaded_union(novalue_shape) # generates a geojson data_shape = tile_shape.difference(union) # If there are multipolygons, select the largest one if data_shape.geom_type == 'MultiPolygon': areas = {p.area: i for i, p in enumerate(data_shape)} largest = max(areas.keys()) data_shape = data_shape[areas[largest]] # if the polygon has interior rings, remove them if list(data_shape.interiors): data_shape = Polygon(data_shape.exterior.coords) data_shape = data_shape.simplify(tolerance, preserve_topology=False) data_geojson = mapping(data_shape) else: data_geojson = tile_geojson # convert cooridnates to degrees return (to_latlon(tile_geojson, origin_espg), to_latlon(data_geojson, origin_espg))
def create_sub_mask_annotation(sub_mask, image_id, category_id, annotation_id, is_crowd): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) imgray = cv2.cvtColor(sub_mask, cv2.COLOR_BGR2GRAY) contours, hierarchy = cv2.findContours(imgray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # For the contours of the instances in the binary images, if its parent is background (hierarchy == 0), # then this contour is the mask contour, abandoning the contours whose parent is not background. valid_contours = [] for k in range(len(hierarchy[0])): if hierarchy[0][k][3] == 0: contour = contours[k].reshape((len(contours[k]), 2)) valid_contours.append(contour) segmentations = [] polygons = [] for contour in valid_contours: # Subtract the padding pixel contour = contour - 1 # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=True) polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds width = max_x - x height = max_y - y bbox = (x, y, width, height) area = multi_poly.area annotation = { 'segmentation': segmentations, 'iscrowd': is_crowd, 'image_id': image_id, 'category_id': category_id, 'id': annotation_id, 'bbox': bbox, 'area': area } return annotation
def make_submask_annotations(sub_mask, image_id, category_id, annotation_id, is_crowd): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) contours = measure.find_contours(sub_mask, 0.5, positive_orientation='low') # Code sourced from: # https://www.immersivelimit.com/create-coco-annotations-from-scratch segmentations = [] polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it # TODO: CHANGE THIS DEPENDING ON ACCURACY TO RESULTS poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds width = max_x - x height = max_y - y bbox = (x, y, width, height) area = multi_poly.area # TODO: ADD PROPER ANNOTATIONS TO ALIGN WITH COCO STANDARDS # TODO: ADD SEGMENTATION SECTION FOR MULTIPLE OBJECTS IN AN IMAGE annotation = { 'segmentation': segmentations, 'iscrowd': is_crowd, 'image_id': image_id, 'category_id': category_id, 'id': annotation_id, 'bbox': bbox, 'area': area } return annotation
def create_sub_mask_annotation(sub_mask, image_id, category_id, annotation_id, is_crowd): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) contours = measure.find_contours(sub_mask, 0.5, positive_orientation='low') min_poly_filter = 10 # filter out noises segmentations = [] polygons = [] num_polys = 0 for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) if (poly.exterior): polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() segmentations.append(segmentation) # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) num_polys = len(multi_poly.geoms) # print(num_polys) if len(multi_poly.bounds) == 4: x, y, max_x, max_y = multi_poly.bounds width = max_x - x height = max_y - y bbox = (x, y, width, height) area = multi_poly.area annotation = { 'segmentation': segmentations, 'iscrowd': is_crowd, 'image_id': image_id, 'category_id': category_id, 'id': annotation_id, 'bbox': bbox, 'area': area } return annotation, num_polys else: return None, None
def segment(points): # Make a polygon and simplify it contour = np.array(points) poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) segmentation = np.array(poly.exterior.coords).ravel().tolist() x, y, max_x, max_y = poly.bounds width = max_x - x height = max_y - y bbox = [x, y, width, height] area = poly.area segmentation = [int(x) for x in segmentation] bbox = [int(x) for x in bbox] segm = {'segmentation': [segmentation], 'bbox': bbox, 'area': int(area)} return segm
def _create_sub_mask_annotation(self, sub_mask, image_id, category_id, annotation_id): """ create COCO.PANOPTIC format annotation(i.e. no contour information here) args: sub_mask: dict, the submask generated from self._create_sub_masks image_id: int, the id of the image category_id: int, the id of the category of this piece of submask annotation_id: int, the id of this submask """ # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) contours = measure.find_contours(sub_mask, 0.5, positive_orientation='low') segmentations = [] polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds width = max_x - x height = max_y - y annotation = { 'iscrowd': 0, 'category_id': category_id, 'id': annotation_id, 'bbox': (int(x), int(y), int(width), int(height)), 'area': int(multi_poly.area) } return annotation
def create_sub_mask_annotation(sub_mask, class_id, label_img): ################### # contours ################### # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) contours = measure.find_contours(np.array(sub_mask), 0.5, positive_orientation='low') polygons = [] x_list, y_list = [], [] for idx, contour in enumerate(contours): # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) # segmentation = np.array(poly.exterior.coords, dtype=np.int).ravel().tolist() # segmentations.append(segmentation coords = np.array(poly.exterior.coords, dtype=np.int) if coords.size != 0: x, y = coords[:, 0], coords[:, 1] x_list.extend(x.tolist()) y_list.extend(y.tolist()) region = {} region['region_attributes'] = {} region['shape_attributes'] = {} region['shape_attributes']["name"] = "polygon" region['shape_attributes']["all_points_x"] = x_list region['shape_attributes']["all_points_y"] = y_list region['shape_attributes']["class_id"] = class_id data[obj_name]['regions'][np.str(class_id)] = region
def generate_polygon(mask_image, min_area=20): """convert the mask to polygon Args: mask_image (np.array): input mask image min_area (int, optional): threshold of area, when area < min_area, filter this object. Defaults to 20. Returns: list: list of polygons """ contours = measure.find_contours(mask_image, 0.5, positive_orientation='low') polygons = [] for contour in contours: for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) if contour.shape[0] < 3: continue poly = Polygon(contour) if poly.area < min_area: continue poly = poly.simplify(1.0, preserve_topology=False) if poly.geom_type == 'MultiPolygon': for poly_ in poly: if poly_.area < min_area: continue valid_flag = aitool.single_valid_polygon(poly_) if not valid_flag: continue polygons.append(poly_) elif poly.geom_type == 'Polygon': valid_flag = aitool.single_valid_polygon(poly) if not valid_flag: continue polygons.append(poly) else: continue return polygons
def create_sub_mask_annotation(sub_mask, image_id, category_id, annotation_id, is_crowd): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) contours = measure.find_contours(sub_mask, 0.5, positive_orientation='low') segmentations = [] polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() if len(segmentation) > 0: segmentations.append(segmentation) # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds width = max_x - x height = max_y - y bbox = (x, y, width, height) area = multi_poly.area annotation = { 'segmentation': segmentations, 'iscrowd': is_crowd, 'image_id': image_id, # both are coming from the k enumerate 'category_id': category_id, 'id': image_id, # both are coming from the k enumerate TODO this fixed an issue for what cocovis_custom.py is expecting and that's native coco tools so the original poster was wrong (was using custom display function now makes sense why author just didn't kow about coco tools) 'bbox': bbox, 'area': area } return annotation
def get_polygons(folder, currency): for apath in tqdm(sorted(glob.glob('{}/{}/images/*.png'.format(folder,currency)))): name = pathlib.Path(apath).stem img = cv2.imread('{}'.format(apath), 0) img2 = cv2.imread('{}'.format(apath), cv2.IMREAD_UNCHANGED) img = cv2.medianBlur(img, 5) # contours, hierarchy = cv2.findContours(img.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # find contours in the thresholded image cnts = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) # cnts = imutils.grab_contours(contours) cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:10] cnt = cnts[0] # print(cnt.shape) points, _, _ = cnt.shape ncnt = np.reshape(cnt, (points, 2)) polygon = Polygon(ncnt) simplepolygon = polygon.simplify(1.0, preserve_topology=False) if simplepolygon.type == 'MultiPolygon': points = [] for polygon in simplepolygon: subpoints = list(polygon.exterior.coords) for subpoint in subpoints: points.append(subpoint) elif simplepolygon.type == 'Polygon': points = list(simplepolygon.exterior.coords) label = { 'points' : points } with open('{}/{}/labels/{}.json'.format(folder,currency,name), 'w') as f: json.dump(label, f)
def shape_to_polygons(shape): def inside(pt): return hypot(*pt) <= R def adjust(pt): x, y = pt a = atan2(y, x) x = cos(a) * R y = sin(a) * R return (x, y) result = [] parts = list(shape.parts) + [len(shape.points)] for i1, i2 in zip(parts, parts[1:]): points = map(tuple, shape.points[i1:i2]) points = map(laea, points) points = filter(None, points) p = Polygon(points) p = p.buffer(-0.01) p = p.buffer(0.01) p = p.simplify() if p.is_empty: continue if isinstance(p, Polygon): ps = [p] else: ps = p.geoms for p in ps: points = list(p.exterior.coords) print points for a, b in zip(points, points[1:]): # if not a[-1] and not b[-1]: # continue a = a[:2] b = b[:2] in1 = inside(a) in2 = inside(b) if not in1 and not in2: continue if in1 and not in2: b = adjust(b) if in2 and not in1: a = adjust(a) result.append(LineString([a, b])) return result
def _create_sub_mask_annotation(self, sub_mask, image_id, category_id): # Find contours (boundary lines) around each sub-mask # Note: there could be multiple contours if the object # is partially occluded. (E.g. an elephant behind a tree) contours = measure.find_contours(sub_mask, 0.5, positive_orientation='low') segmentations = [] polygons = [] for contour in contours: # Flip from (row, col) representation to (x, y) # and subtract the padding pixel for i in range(len(contour)): row, col = contour[i] contour[i] = (col - 1, row - 1) # Make a polygon and simplify it poly = Polygon(contour) poly = poly.simplify(1.0, preserve_topology=False) polygons.append(poly) segmentation = np.array(poly.exterior.coords).ravel().tolist() if len(segmentation) >= 6 and len(segmentation) % 2 == 0: segmentations.append(segmentation) # Combine the polygons to calculate the bounding box and area multi_poly = MultiPolygon(polygons) x, y, max_x, max_y = multi_poly.bounds bbox = [ max(0, math.floor(x)), max(0, math.floor(y)), math.ceil(max_x), math.ceil(max_y) ] annotation = { 'bbox': bbox, 'bbox_mode': BoxMode.XYXY_ABS, 'segmentation': segmentations, 'category_id': category_id, } return annotation
def save_geojson(ordered_list, image_src, filename): rasterio_object = rasterio.open(image_src) features = [] def pixelcoord_to_geocoord(pixel_coordinate): return (rasterio_object.transform * pixel_coordinate) for line in ordered_list: tuple_of_tuples = tuple((point[1], point[0]) for point in line) Lstring = Polygon(list(map(pixelcoord_to_geocoord, tuple_of_tuples))) features.append(Feature(geometry=Lstring.simplify(0.00001))) crs = { "type": "name", "properties": { "name": "{}".format(str(rasterio_object.crs)) } } feature_collection = FeatureCollection(features, crs=crs) with open(filename, 'w') as f: dump(feature_collection, f)