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
Example #3
0
    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