def get_burrows_from_image(self, mask, ground_line): """ load burrow polygons from an image """ # turn image into gray scale height, width = mask.shape # get a polygon for cutting away the sky above_ground = ground_line.get_polygon(0, left=0, right=width) # determine contours in the mask contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] # iterate through the contours burrows = [] for contour in contours: points = contour[:, 0, :] if len(points) <= 2: continue # get the burrow area area = cv2.contourArea(contour) if area < self.params['scale_bar/area_max']: # object could be a scale bar rect = shapes.Rectangle(*cv2.boundingRect(contour)) at_left = (rect.left < self.params['scale_bar/dist_left']*width) max_dist_bottom = self.params['scale_bar/dist_bottom'] at_bottom = (rect.bottom > (1 - max_dist_bottom) * height) hull = cv2.convexHull(contour) hull_area = cv2.contourArea(hull) is_simple = (hull_area < 2*area) if at_left and at_bottom and is_simple: # the current polygon is the scale bar _, (w, h), _ = cv2.minAreaRect(contour) if max(w, h) > self.params['scale_bar/length_min']: raise RuntimeError('Found something that looks like a ' 'scale bar') if area > self.params['burrow/area_min']: # build polygon out of the contour points burrow_poly = geometry.Polygon(points) # regularize the points to remove potential problems burrow_poly = regions.regularize_polygon(burrow_poly) # build the burrow polygon by removing the sky burrow_poly = burrow_poly.difference(above_ground) # create a burrow from the outline boundary = regions.get_enclosing_outline(burrow_poly) burrow = Burrow(boundary.coords, parameters=self.params['burrow_parameters']) burrows.append(burrow) logging.info('Found %d polygon(s)' % len(burrows)) return burrows
def get_burrows_from_image(self, mask, ground_line): """ load burrow polygons from an image """ # turn image into gray scale height, width = mask.shape # get a polygon for cutting away the sky above_ground = ground_line.get_polygon(0, left=0, right=width) # determine contours in the mask contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] # iterate through the contours burrows = [] for contour in contours: points = contour[:, 0, :] if len(points) <= 2: continue # get the burrow area area = cv2.contourArea(contour) if area < self.params['scale_bar/area_max']: # object could be a scale bar rect = shapes.Rectangle(*cv2.boundingRect(contour)) at_left = (rect.left < self.params['scale_bar/dist_left'] * width) max_dist_bottom = self.params['scale_bar/dist_bottom'] at_bottom = (rect.bottom > (1 - max_dist_bottom) * height) hull = cv2.convexHull(contour) hull_area = cv2.contourArea(hull) is_simple = (hull_area < 2 * area) if at_left and at_bottom and is_simple: # the current polygon is the scale bar _, (w, h), _ = cv2.minAreaRect(contour) if max(w, h) > self.params['scale_bar/length_min']: raise RuntimeError('Found something that looks like a ' 'scale bar') if area > self.params['burrow/area_min']: # build polygon out of the contour points burrow_poly = geometry.Polygon(points) # regularize the points to remove potential problems burrow_poly = regions.regularize_polygon(burrow_poly) # debug.show_shape(geometry.Polygon(points), above_ground, # background=mask) # build the burrow polygon by removing the sky burrow_poly = burrow_poly.difference(above_ground) # create a burrow from the outline boundary = regions.get_enclosing_outline(burrow_poly) burrow = Burrow(boundary.coords, parameters=self.params['burrow_parameters']) burrows.append(burrow) logging.info('Found %d polygons' % len(burrows)) return burrows
def connect_burrow_chunks(self, burrow_chunks): """ takes a list of burrow chunks and connects them such that in the end all burrow chunks are connected to the ground line. """ if len(burrow_chunks) == 0: return [] dist_max = self.params['burrows/chunk_dist_max'] # build the contour profiles of the burrow chunks linear_rings = [geometry.LinearRing(c) for c in burrow_chunks] # handle all burrows close to the ground connected, disconnected = [], [] for k, ring in enumerate(linear_rings): ground_dist = self.ground.linestring.distance(ring) if ground_dist < dist_max: # burrow is close to ground if 1 < ground_dist: burrow_chunks[k] = \ self._connect_burrow_to_structure(burrow_chunks[k], self.ground.linestring) connected.append(k) else: disconnected.append(k) assert (set(connected) | set(disconnected)) == set(range(len(burrow_chunks))) # calculate distances to other burrows burrow_dist = np.empty([len(burrow_chunks)]*2) np.fill_diagonal(burrow_dist, np.inf) for x, contour1 in enumerate(linear_rings): for y, contour2 in enumerate(linear_rings[x+1:], x+1): dist = contour1.distance(contour2) burrow_dist[x, y] = dist burrow_dist[y, x] = dist # handle all remaining chunks, which need to be connected to other chunks while connected and disconnected: # find chunks which is closest to all the others dist = burrow_dist[disconnected, :][:, connected] k1, k2 = np.unravel_index(dist.argmin(), dist.shape) if dist[k1, k2] > dist_max: # don't connect structures that are too far from each other break c1, c2 = disconnected[k1], connected[k2] # k1 is chunk to connect, k2 is closest chunk to connect it to # connect the current chunk to the other structure structure = geometry.LinearRing(burrow_chunks[c2]) enlarged_chunk = self._connect_burrow_to_structure(burrow_chunks[c1], structure) # merge the two structures poly1 = geometry.Polygon(enlarged_chunk) poly2 = regions.regularize_polygon(geometry.Polygon(structure)) poly = poly1.union(poly2).buffer(0.1) # find and regularize the common contour contour = regions.get_enclosing_outline(poly) contour = regions.regularize_linear_ring(contour) contour = contour.coords # replace the current chunk by the merged one burrow_chunks[c1] = contour # replace all other burrow chunks with the same id id_c2 = id(burrow_chunks[c2]) for k, bc in enumerate(burrow_chunks): if id(bc) == id_c2: burrow_chunks[k] = contour # mark the cluster as connected del disconnected[k1] connected.append(c1) # return the unique burrow structures burrows = [] connected_chunks = (burrow_chunks[k] for k in connected) for contour in unique_based_on_id(connected_chunks): contour = regions.regularize_contour_points(contour) try: burrow = Burrow(contour) except ValueError: continue else: if burrow.area >= self.params['burrows/area_min']: burrows.append(burrow) return burrows