Example #1
0
    def calculate_burrow_centerline(self, burrow, point_start=None):
        """ determine the centerline of a burrow with one exit """
        if point_start is None:
            point_start = burrow.centerline[0]

        # get a binary image of the burrow
        mask, shift = burrow.get_mask(margin=2,
                                      dtype=np.int32,
                                      ret_offset=True)

        # move starting point onto ground line
        ground_line = self.ground.linestring
        point_start = curves.get_projection_point(ground_line, point_start)
        point_start = (int(point_start[0]) - shift[0],
                       int(point_start[1]) - shift[1])
        mask[point_start[1], point_start[0]] = 1

        # calculate the distance from the start point
        regions.make_distance_map(mask, [point_start])

        # find the second point by locating the farthest point
        _, _, _, p_end = cv2.minMaxLoc(mask)

        # find an estimate for the centerline from the shortest distance from
        # the end point to the burrow exit
        points = regions.shortest_path_in_distance_map(mask, p_end)

        # translate the points back to global coordinates
        centerline = curves.translate_points(points, shift[0], shift[1])
        # save centerline such that burrow exit is first point
        centerline = centerline[::-1]

        # add points that might be outside of the burrow contour
        ground_start = curves.get_projection_point(ground_line, centerline[0])
        centerline.insert(0, ground_start)

        # simplify the curve
        centerline = cv2.approxPolyDP(np.array(centerline, np.int),
                                      epsilon=1,
                                      closed=False)

        # save the centerline in the burrow structure
        burrow.centerline = centerline[:, 0, :]
Example #2
0
    def calculate_burrow_centerline(self, burrow, point_start=None):
        """ determine the centerline of a burrow with one exit """
        if point_start is None:
            point_start = burrow.centerline[0]
        
        # get a binary image of the burrow
        mask, shift = burrow.get_mask(margin=2, dtype=np.int32, ret_offset=True)
        
        # move starting point onto ground line
        ground_line = self.ground.linestring
        point_start = curves.get_projection_point(ground_line, point_start)
        point_start = (int(point_start[0]) - shift[0],
                       int(point_start[1]) - shift[1])
        mask[point_start[1], point_start[0]] = 1

        # calculate the distance from the start point 
        regions.make_distance_map(mask, [point_start])
        
        # find the second point by locating the farthest point
        _, _, _, p_end = cv2.minMaxLoc(mask)
        
        # find an estimate for the centerline from the shortest distance from
        # the end point to the burrow exit
        points = regions.shortest_path_in_distance_map(mask, p_end)

        # translate the points back to global coordinates 
        centerline = curves.translate_points(points, shift[0], shift[1])
        # save centerline such that burrow exit is first point
        centerline = centerline[::-1]
        
        # add points that might be outside of the burrow contour
        ground_start = curves.get_projection_point(ground_line, centerline[0])
        if isinstance(centerline, np.ndarray):
            centerline = np.insert(centerline, 0, ground_start).reshape(-1, 2)
        else:
            centerline.insert(0, ground_start)
            
        # simplify the curve        
        centerline = cv2.approxPolyDP(np.array(centerline, np.int),
                                      epsilon=1, closed=False)
            
        # save the centerline in the burrow structure
        burrow.centerline = centerline[:, 0, :]
Example #3
0
    def _get_burrow_centerline(self, burrow, points_start, points_end=None):
        """ determine the centerline of a burrow with one exit """
            
        ground_line = self.ground.linestring
            
        # get a binary image of the burrow
        mask, shift = burrow.get_mask(margin=2, dtype=np.int32, ret_offset=True)
        
        # mark the start points according to their distance to the ground line
#         dists_g = [ground_line.distance(geometry.Point(p))
#                    for p in points_start]
        points_start = curves.translate_points(points_start, -shift[0], -shift[1])
        for p in points_start:
            mask[p[1], p[0]] = 1

        if points_end is None:
            # end point is not given and will thus be determined automatically

            # calculate the distance from the start point 
            regions.make_distance_map(mask.T, points_start)
            
            
            # find the second point by locating the farthest point
            _, _, _, p_end = cv2.minMaxLoc(mask)
        
        else:
            # prepare the end point if present

            # translate that point to the mask _frame
            points_end = curves.translate_points(points_end, -shift[0], -shift[1])
            for p in points_end:
                mask[p[1], p[0]] = 1

            # calculate the distance from the start point 
            regions.make_distance_map(mask.T, points_start, points_end)
            
            # get the distance between the start and the end point
            dists = [mask[p[1], p[0]] for p in points_end]
            best_endpoint = np.argmin(dists)
            p_end = points_end[best_endpoint]
            
        # find an estimate for the centerline from the shortest distance from
        # the end point to the burrow exit
        points = regions.shortest_path_in_distance_map(mask, p_end)

#         debug.show_shape(geometry.MultiPoint(points_start),
#                          geometry.Point(p_end),
#                          background=mask)
#         exit()

        # translate the points back to global coordinates 
        centerline = curves.translate_points(points, shift[0], shift[1])
        # save centerline such that burrow exit is first point
        centerline = centerline[::-1]
        
        # add points that might be outside of the burrow contour
        ground_start = curves.get_projection_point(ground_line, centerline[0]) 
        centerline.insert(0, ground_start)
        if points_end is not None:
            ground_end = curves.get_projection_point(ground_line, centerline[-1]) 
            centerline.append(ground_end)
            
        # simplify the curve        
        centerline = cv2.approxPolyDP(np.array(centerline, np.int),
                                      epsilon=1, closed=False)
            
        # save the centerline in the burrow structure
        burrow.centerline = centerline[:, 0, :]
    def calculate_burrow_properties(self, burrow, ground_line=None):
        """ calculates additional properties of the burrow """
        
        # load some parameters
        burrow_width = self.params['burrow/width_typical']
        min_length = self.params['burrow/branch_length_min']
        
        # determine the burrow end points
        endpoints = burrow.get_endpoints(ground_line)
        
        # get distance map from centerline
        distance_map, offset = burrow.get_mask(margin=2, dtype=np.uint16,
                                               ret_offset=True)
        cline = burrow.centerline
        start_points = curves.translate_points(cline, -offset[0], -offset[1])
        regions.make_distance_map(distance_map, start_points)
        
        # determine endpoints, which are not already part of the centerline
        end_coords = np.array([ep.coords for ep in endpoints])
        dists = spatial.distance.cdist(end_coords, cline)
        extra_ends = end_coords[dists.min(axis=1) > burrow_width, :].tolist()
        extra_ends = curves.translate_points(extra_ends, -offset[0], -offset[1])
        
        # get additional points that are far away from the centerline
        map_max = ndimage.filters.maximum_filter(distance_map, burrow_width)
        map_maxima =  (distance_map == map_max) & (distance_map > min_length)
        maxima = np.array(np.nonzero(map_maxima)).T
        
        # determine the object from which we measure the distance to the sky
        if ground_line is not None:
            outside = ground_line.linestring
        else:
            outside = geometry.MultiPoint(end_coords)

        # define a helper function for checking the connection to the ground
        burrow_poly = burrow.polygon.buffer(2)
        def _direct_conn_to_ground(point, has_offset=False):
            """ helper function checking the connection to the ground """
            if has_offset:
                point = (point[0] + offset[0], point[1] + offset[1])
            p_ground = curves.get_projection_point(outside, point)
            conn_line = geometry.LineString([point, p_ground])
            return conn_line.length < 1 or conn_line.within(burrow_poly)
        
        branch_points = []
        branch_point_separation = self.params['burrow/branch_point_separation']
        if maxima.size > 0:
            if len(maxima) == 1:
                clusters = [0]
            else:
                # cluster maxima to reduce them to single end points 
                # this is important when a burrow has multiple exits to the ground
                clusters = cluster.hierarchy.fclusterdata(
                    maxima, branch_point_separation,
                    method='single', criterion='distance'
                )
            
            cluster_ids = np.unique(clusters)
            logging.debug('Found %d possible branch point(s)' % len(cluster_ids))
            
            # find the additional point from the clusters
            for cluster_id in cluster_ids:
                candidates = maxima[clusters == cluster_id, :]
                
                # get point with maximal distance from center line
                dists = [distance_map[x, y] for x, y in candidates]
                y, x = candidates[np.argmax(dists)] 
                
                # check whether this point is close to an endpoint
                point = geometry.Point(x + offset[0], y + offset[1])
                branch_depth = point.distance(outside)
                if (branch_depth > min_length or 
                        not _direct_conn_to_ground((x, y), has_offset=True)):
                    branch_points.append((x, y))

            # save some output for debugging
            self._debug['possible_branches'] = \
                    curves.translate_points(branch_points, offset[0], offset[1])

        # find the burrow branches
        burrow.branches = []
        
        if extra_ends or branch_points:
            num_branches = len(extra_ends) + len(branch_points)
            logging.info('Found %d possible branch(es)' % num_branches)

            # create generator for iterating over all additional points
            gen = ((p_class, p_coords)
                   for p_class, points in enumerate([extra_ends, branch_points])
                   for p_coords in points)

            # connect all additional points to the centerline -> branches
            for ep_id, ep in gen:
                line = regions.shortest_path_in_distance_map(distance_map, ep)
                line = curves.translate_points(line, offset[0], offset[1])
                
                # estimate the depth of the branch
                depth = max(geometry.Point(p).distance(outside)
                            for p in (line[0], line[-1]))
                
                # check whether the line corresponds to an open branch
                # open branches are branches where all connection lines between
                # the branch and ground line are fully contained in the burrow
                # polygon
                if ep_id == 1 and depth < min_length:
                    num_direct = sum(1 for p_branch in line
                                     if _direct_conn_to_ground(p_branch))
                    ratio_direct = num_direct / len(line)
                    if ratio_direct > 0.75:                        
                        # the ground is directly reachable from most points
                        line = None
                
                if line:
                    burrow.branches.append(line)
        
        if ground_line:
            self._add_burrow_angle_statistics(burrow, ground_line)
        
        return
        
        # filter branches to make sure that they are not all connected to ground
        
        # determine the morphological graph
        graph = burrow.get_morphological_graph()
        
        # combine multiple branches at exits and remove short branches that are
        # not exits
        exit_nodes = []
        for points_exit in burrow.get_exit_regions(ground_line):
            exit_line = geometry.LineString(points_exit)

            # find the graph nodes that are close to the exit            
            graph_nodes = []
            for node, data in graph.nodes(data=True):
                node_point = geometry.Point(data['coords'])
                if exit_line.distance(node_point) < 3:
                    graph_nodes.append((node, node_point))

            # find the graph node that is closest to the exit point
            exit_point = exit_line.interpolate(0.5, normalized=True)
            dists = [exit_point.distance(node_point)
                     for _, node_point in graph_nodes]
            exit_id = np.argmin(dists)
            
            for node_id, (node, _) in enumerate(graph_nodes):
                if node_id == exit_id:
                    # save the exit node for later use
                    exit_nodes.append(node)
                else:
                    # remove additional graph nodes
                    graph.remove_node(node)                           
                
        # remove short branches that are not exit nodes
        length_min = self.params['burrow/branch_length_min']
        graph.remove_short_edges(length_min=length_min,
                                 exclude_nodes=exit_nodes)
                
        # remove nodes of degree two  
        graph.simplify()

        burrow.morphological_graph = graph