def get_common_interior_polygons(polygon, list_of_polygons): """Check if polygon resides inside any polygon in the list_of_polygons. Parameters ---------- polygon: matplotlib.Polygon Returns ------- list_of_common_polygons: list A filtered list of ids """ if isinstance(polygon, Polygon): polygon = shapelyPolygon(polygon.get_xy()).buffer(0) list_of_common_polygons = [] for index, outside_polygon in enumerate(list_of_polygons): if isinstance(outside_polygon, Polygon): outside_polygon = shapelyPolygon( outside_polygon.get_xy()).buffer(0) if polygon.is_valid and outside_polygon.is_valid: if polygon.within(outside_polygon): list_of_common_polygons.append(index) return list_of_common_polygons
def do(star, stars, vertices): maxArea = 0 start_poly = onestar_to_poly(star, vertices) adjTupleSet = getStarAdj(star, stars) # Get all adjacent combination with polyno number of polygons for polyno in range(1,len(adjTupleSet)+1): print("combine with" + str(polyno) + "\n\n") combis = itertools.combinations(adjTupleSet,polyno) for combi in combis: print("new combination") poly = shapelyPolygon(start_poly) # vertices of this combination polyvert = star.copy() for p in combi: # pdb.set_trace() addPoly = shapelyPolygon(onestar_to_poly(p, vertices)) poly = cascaded_union([poly, addPoly]) for v in p: polyvert.append(v) # pdb.set_trace() polyvert = list(set(polyvert)) polyvert.sort() print("Vertices", polyvert) polyvert = onestar_to_poly(polyvert, vertices) print("Area", poly.area) print("Star", starise.kernel(list(range(len(polyvert))), polyvert)) print("\n")
def intersects_rectangle(self, rectangle): if self.geometry.is_empty: return False x0,z0,x1,z1 = rectangle points = [(x0, z0), (x1, z0), (x1, z1), (x0, z1)] shPolygon = shapelyPolygon(points) return not self.geometry.disjoint(shPolygon)
def contains_rectangle(self, rectangle): if self.geometry.is_empty: return False x0,z0,x1,z1 = rectangle points = [(x0, z0), (x1, z0), (x1, z1), (x0, z1)] shPolygon = shapelyPolygon(points) return all([point_in_rectangle(point, self.geometry.bounds) for point in points]) and self.geometry.contains(shPolygon)
def mpl_polygon_to_shapely_scaled(polygon, x0=0, y0=0, scale_factor=1): """Convert a given matploltib polygon to shapely Shapely allows more operations that we really ned while matplotlib.polygon is in use for legacy reasons Parameters ---------- polygon: matplotlib.Polygon Input object x0, y0: int Origin for scaling scale_factor: float How much to scale Returns ------- shapely_polygon: Polygon Scaled polygon """ xy = polygon.get_xy() shapely_polygon = shapelyPolygon(xy) return scale(shapely_polygon, xfact=scale_factor, yfact=scale_factor, origin=(x0, y0))
def get_annotation_polygons(json_filepath, polygon_type="mpl"): """Get annotation jsons polygons Assumed to be at level 0 Parameters ---------- json_filepath: string Path to json file polygon_type: string 'matplotlib/shapely' Returns ------- polygons: dict(Polygons) dict of matplotlib.Polygons with keys are normal/tumor """ json_parsed = json.load(open(json_filepath)) tumor_patches = json_parsed["tumor"] normal_patches = json_parsed["normal"] polygons = OrderedDict() polygons["tumor"] = [] polygons["normal"] = [] for tumor_patch in tumor_patches: tumor_patch["vertices"] = np.array(tumor_patch["vertices"]) if polygon_type == "mpl": polygon = Polygon(tumor_patch["vertices"]) else: if tumor_patch["vertices"].shape[0] >= 3: polygon = shapelyPolygon(tumor_patch["vertices"]) else: continue polygons["tumor"].append(polygon) for normal_patch in normal_patches: if polygon_type == "mpl": polygon = Polygon(np.array(tumor_patch["vertices"])) else: polygon = shapelyPolygon(np.array(tumor_patch["vertices"])) polygons["normal"].append(polygon) return polygons
def triangulate_polygon_constrained(shape, additional_opts=""): """ Perform constrained polygon triangulation. Essentially a compatibility layer between shapely and triangle. For more information about the triangulation, see documentation for triangle: https://rufat.be/triangle/API.html :param shape: shapely shape to triangulate :param additional_opts: additional options to pass to triangle.triangulate(). "p" option is always used. :type additional_opts: str :return: vertices, faces; where vertices is a list of coordinates, and faces a list of 1-indexed face definitions. """ if not _TRIANGLE_IMPORTED: raise EnvironmentError("Library 'triangle' required for triangulation.") if not _POLYLABEL_IMPORTED: raise EnvironmentError("Constrained polygon triangulation requires shapely>=1.7.0") # Polygon case if shape.geometryType() == "Polygon": tri = {"vertices": [], "segments": [], "holes": []} # add exterior edge ext_pt_cnt = len(shape.exterior.coords) - 1 tri["vertices"] += shape.exterior.coords[:-1] tri["segments"] += [[i, i+1] for i in range(ext_pt_cnt-1)] + [[ext_pt_cnt-1, 0]] # add interior edges and holes offset = ext_pt_cnt for hole in shape.interiors: hole_pt_cnt = len(hole.coords) - 1 tri["vertices"] += list(hole.coords[:-1]) tri["segments"] += [[i, i+1] for i in range(offset, offset+hole_pt_cnt-1)] + [[offset+hole_pt_cnt-1, offset]] rp = shapelyPolygon(hole.coords).representative_point() tri["holes"].append(list(*rp.coords)) offset += hole_pt_cnt if len(tri["holes"]) == 0: tri.pop("holes") # perform triangulation t = triangle.triangulate(tri, "p"+additional_opts) vertices = t["vertices"] faces = [[i+1 for i in j] for j in t["triangles"]] # switch from 0- to 1-indexing return vertices, faces # MultiPolygon/GeometryCollection case (recursive) elif shape.geometryType() in ["MultiPolygon", "GeometryCollection"]: vertices, faces = [], [] for part in shape: if part.geometryType() == "Polygon": v, f = triangulate_polygon_constrained(part, additional_opts) offset = len(vertices) vertices += [list(i) for i in v] faces += [[i+offset for i in j] for j in f] return vertices, faces # Unsupported geometry case else: raise NotImplementedError("Can't do constrained triangulation on geometry type " + shape.geometryType())
def get_approx_tumor_mask(polygons, thumbnail_nrow, thumbnail_ncol, patch_size=256): """Indicate whether a particular tile overlaps with tumor annotation. The method has an approx in its name, as the entire tile might not be coming from a tumor annotated region as parts of it might actually be normal. We just report here if the patch overlaps with a tumor annotation. It might have an overlap with a normal region as well, but that is filtered in a later method so we don't worry about it here. Parameters ---------- polygons: dict dict with keys ['normal', 'tumor'] as obtained from `get_annotation_polygons` thumbnail_nrow: int Number of rows in the thumbnail image thumbnail_ncol: int Number of columns in the thumbnail Returns ------- mask: array tumor mask """ scaled_tumor_polygons = [] for tpol in polygons["tumor"]: scaled = translate_and_scale_polygon(tpol, 0, 0, 1 / 256) scaled_tumor_polygons.append(scaled) polymasked = poly2mask(scaled_tumor_polygons, (thumbnail_nrow, thumbnail_ncol)) # Is any of the masked out points inside a normal annotated region? poly_x, poly_y = np.where(polymasked > 0) set_to_zero = [] for px, py in zip(poly_x, poly_y): point = shapelyPoint(px, py) for npol in polygons["normal"]: scaled = translate_and_scale_polygon(npol, 0, 0, 1 / 256) pol = shapelyPolygon(scaled.get_xy()) if pol.contains(point): set_to_zero.append((px, py)) if len(set_to_zero): set_to_zero = np.array(set_to_zero) polymasked[set_to_zero] = 0 return polymasked
def scoreMap(polygon, events, square_width): samples = createSamplePoints(polygon, square_width, 6371.0) points = [] num_colors = 400 radian_events = [(math.radians(lat), math.radians(lon), weight) for lat,lon,weight in events] for sample in samples: latitude = sample[1] for a, b, c, d, lon_middle in sample[3::]: points.append((math.radians(latitude),math.radians( lon_middle))) scores = compute.computeScores(radian_events, len(radian_events), points, len(points), num_colors) scores_list = compute.unsignedLongArray_frompointer(scores) patches = [] i = 0 colors = list() x,y = polygon.exterior.xy for sample in samples: lat_top = sample[0] lat_middle = sample[1] lat_bottom = sample[2] for lon_left_top, lon_left_bottom, lon_right_top, lon_right_bottom, lon_middle in sample[3::]: temp_polygon = [(lon_left_top, lat_top), (lon_right_top, lat_top), (lon_right_bottom, lat_bottom), (lon_left_bottom, lat_bottom)] sPoly = shapelyPolygon(temp_polygon) if polygon.contains(sPoly) or polygon.intersects(sPoly): patches.append(patchPolygon(temp_polygon, True)) score = scores_list[i] colors.append(score*(100.0/num_colors)) i+= 1 patch_collection = PatchCollection(patches, cmap=matplotlib.cm.plasma, alpha=1.0) patch_collection.set_array(np.array(colors)) figure, axis = plt.subplots() axis.add_collection(patch_collection) plt.plot(x, y) plt.show()
def _editFunc(self, selectedROIParam: RoiParams): """Callback triggered by right click or key press, required a selected Roi""" # extract handle points from the polygon poly = shapelyPolygon(selectedROIParam.roiFile.getRoi().verts) poly = poly.buffer(0) poly = poly.simplify(poly.length ** .5 / 5, preserve_topology=False) handles = poly.exterior.coords def done(verts, handles): verts = verts[0] newRoi = pwsdt.Roi.fromVerts(np.array(verts), selectedROIParam.roiFile.getRoi().mask.shape) self._polyWidg.set_active(False) self._roiManager.updateRoi(selectedROIParam.roiFile, newRoi) self.roiModified.emit(self.metadata, selectedROIParam.roiFile) self.enableHoverAnnotation(True) def cancelled(): self.enableHoverAnnotation(True) self._polyWidg = PolygonModifier(self.ax, onselect=done, onCancelled=cancelled) self._polyWidg.set_active(True) self.enableHoverAnnotation(False) self._polyWidg.initialize([handles])
def create_tumor_mask_from_tile(tile_x, tile_y, polygons, patch_size=256): """Create a patch_size x patch_size mask from tile_x,y coordinates Parameters ---------- tile_x, tile_y: int polygons: dict ['normal', 'tumor'] with corresponding polygons Returns ------- mask: array patch_size x patch_size binary mask """ # Initiate a zero mask mask = np.zeros((patch_size, patch_size)) # patch_polygon = shapelyRectangle(tile_x, tile_y, patch_size, patch_size) x_min = tile_x y_min = tile_y x_max = x_min + 256 y_max = y_min + 256 patch_polygon = shapelyPolygon( [(x_min, y_min), (x_max, y_min), (x_max, y_max), (x_min, y_max)] ) # Is it overlapping any of the tumor polygons? is_inside_tumor = [ patch_polygon.intersection(polygon.buffer(0)) for polygon in polygons["tumor"] ] # the patch will always be inside just one annotated boundary # which are assumed to be non-overlapping and hence we can just fetch # the first sample tumor_poly_index = None tumor_poly_coords = None for index, sample_intersection in enumerate(is_inside_tumor): if sample_intersection.area > 0: tumor_poly_index = index if sample_intersection.geom_type == "Polygon": tumor_poly_coords = np.array(sample_intersection.exterior.coords) elif sample_intersection.geom_type == "MultiPolygon": tumor_poly_coords = [] for p in sample_intersection: tumor_poly_coords += p.exterior.coords tumor_poly_coords = np.array(tumor_poly_coords) elif sample_intersection.geom_type == "GeometryCollection": tumor_poly_coords = [] for p in sample_intersection: if p.geom_type == "LineString" or p.geom_type == "Point": tumor_poly_coords += p.coords elif p.geom_type == "Polygon": tumor_poly_coords += p.exterior.coords else: print("Found geom_type:{}".format(p.geom_type)) raise ValueError("") else: print("Found geom_type:{}".format(sample_intersection.geom_type)) raise ValueError("") break if tumor_poly_index is None: # No overlap with tumor so must return as is return mask # This path belongs to a tumor patch so set everything to one # Set these coordinates to one # Shift the tumor coordinates to tile_x, tile_y tumor_poly_coords = tumor_poly_coords - np.array([tile_x, tile_y]) overlapping_tumor_poly = shapelyPolygon(tumor_poly_coords) # Create a psuedo mask psuedo_mask = poly2mask([overlapping_tumor_poly], (patch_size, patch_size)) # Add it to the original mask mask = np.logical_or(mask, psuedo_mask) # If its inside tumor does this tumor patch actually contain any normal patches? tumor_poly = polygons["tumor"][tumor_poly_index] normal_patches_inside_tumor = get_common_interior_polygons( tumor_poly, polygons["normal"] ) # For all the normal patches, ensure # we set the mask to zero for index in normal_patches_inside_tumor: normal_poly = polygons["normal"][index] # What is the intersection portion of this normal polygon # with our patch of interest? common_area = normal_poly.intersection(patch_polygon) if not common_area.is_valid: return mask if common_area.geom_type == "Polygon": normal_poly_coords = np.array(common_area.exterior.coords) - np.array( [tile_x, tile_y] ) elif common_area.geom_type == "MultiPolygon": normal_poly_coords = [] for p in common_area: normal_poly_coords += p.exterior.coords normal_poly_coords = np.array(normal_poly_coords) - np.array( [tile_x, tile_y] ) elif common_area.geom_type == "LineString": normal_poly_coords = common_area.coords else: raise ValueError("Founr geom {}".format(common_area.geom_type)) if common_area: # normal_poly_coords = np.array( # common_area.exterior.coords) - np.array([tile_x, tile_y]) overlapping_normal_poly = shapelyPolygon(normal_poly_coords) psuedo_mask = poly2mask([overlapping_normal_poly], (patch_size, patch_size)) # Get coordinates wherever this is non zero non_zero_coords = np.where(psuedo_mask > 0) # Add set these explicitly to zero mask[non_zero_coords] = 0 return mask