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)
Ejemplo n.º 2
0
    def get_measurement_lines(self, tail):
        """
        determines the measurement segments that are used for the line scan
        """
        f_c = self.params['measurement/line_offset']
        f_o = 1 - f_c

        centerline = tail.centerline
        result = []
        for side in tail.sides:
            # find the line between the centerline and the ventral line
            points = []
            for p_c in centerline:
                p_o = curves.get_projection_point(side, p_c) #< outer line
                points.append((f_c*p_c[0] + f_o*p_o[0],
                               f_c*p_c[1] + f_o*p_o[1]))
                
            # do spline fitting to smooth the line
            smoothing = self.params['measurement/spline_smoothing']*len(points)
            tck, _ = interpolate.splprep(np.transpose(points),
                                         k=2, s=smoothing)
            
            points = interpolate.splev(np.linspace(-0.5, .8, 100), tck)
            points = zip(*points) #< transpose list
    
            # restrict centerline to object
            mline = geometry.LineString(points).intersection(tail.polygon)
            
            # pick longest line if there are many due to strange geometries
            if isinstance(mline, geometry.MultiLineString):
                mline = mline[np.argmax([l.length for l in mline])]
                
            result.append(np.array(mline.coords))
            
        return result   
Ejemplo n.º 3
0
    def extend_mouse_trail(self):
        """ extends the mouse trail using the current mouse position """
        ground_line = self.ground.linestring

        # remove points which are in front of the mouse
        if self.mouse_trail:
            spacing = self.params['mouse/model_radius']
            trail = np.array(self.mouse_trail)

            # get distance between the current point and the previous ones
            dist = np.hypot(trail[:, 0] - self.mouse_pos[0],
                            trail[:, 1] - self.mouse_pos[1])
            points_close = (dist < spacing)

            # delete obsolete points
            if np.any(points_close):
                i = np.nonzero(points_close)[0][0]
                del self.mouse_trail[i:]

        # check the two ends of the mouse trail
        if self.mouse_trail:
            # move first point to ground
            ground_point = curves.get_projection_point(ground_line,
                                                       self.mouse_trail[0])
            self.mouse_trail[0] = ground_point

            # check whether a separate point needs to be inserted
            p1, p2 = self.mouse_trail[-1], self.mouse_pos
            if curves.point_distance(p1, p2) > spacing:
                mid_point = (0.5 * (p1[0] + p2[0]), 0.5 * (p1[1] + p2[1]))
                self.mouse_trail.append(mid_point)

            # append the current point
            self.mouse_trail.append(self.mouse_pos)
            ground_dist = curves.curve_length(self.mouse_trail)

        else:
            # create a mouse trail if it is not too far from the ground
            # the latter can happen, when the mouse suddenly appears underground
            ground_point = curves.get_projection_point(ground_line,
                                                       self.mouse_pos)
            ground_dist = curves.point_distance(ground_point, self.mouse_pos)
            if ground_dist < self.params['mouse/speed_max']:
                self.mouse_trail = [ground_point, self.mouse_pos]

        return ground_dist
Ejemplo n.º 4
0
    def extend_mouse_trail(self):
        """ extends the mouse trail using the current mouse position """
        ground_line = self.ground.linestring
        
        # remove points which are in front of the mouse
        if self.mouse_trail:
            spacing = self.params['mouse/model_radius']
            trail = np.array(self.mouse_trail)
            
            # get distance between the current point and the previous ones
            dist = np.hypot(trail[:, 0] - self.mouse_pos[0],
                            trail[:, 1] - self.mouse_pos[1])
            points_close = (dist < spacing)
                
            # delete obsolete points
            if np.any(points_close):
                i = np.nonzero(points_close)[0][0]
                del self.mouse_trail[i:]
           
        # check the two ends of the mouse trail
        if self.mouse_trail:
            # move first point to ground
            ground_point = curves.get_projection_point(ground_line,
                                                       self.mouse_trail[0])
            self.mouse_trail[0] = ground_point

            # check whether a separate point needs to be inserted
            p1, p2 = self.mouse_trail[-1], self.mouse_pos
            if curves.point_distance(p1, p2) > spacing:
                mid_point = (0.5*(p1[0] + p2[0]), 0.5*(p1[1] + p2[1]))
                self.mouse_trail.append(mid_point)

            # append the current point
            self.mouse_trail.append(self.mouse_pos)
            ground_dist = curves.curve_length(self.mouse_trail)
            
        else:
            # create a mouse trail if it is not too far from the ground
            # the latter can happen, when the mouse suddenly appears underground
            ground_point = curves.get_projection_point(ground_line, self.mouse_pos)
            ground_dist = curves.point_distance(ground_point, self.mouse_pos)
            if ground_dist < self.params['mouse/speed_max']:
                self.mouse_trail = [ground_point, self.mouse_pos]

        return ground_dist
Ejemplo n.º 5
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, :]
Ejemplo n.º 6
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, :]
Ejemplo n.º 7
0
    def _connect_burrow_to_structure(self, contour, structure):
        """ extends the burrow contour such that it connects to the ground line 
        or to other burrows """

        outline = geometry.Polygon(contour)

        # determine burrow points close to the structure
        dist = structure.distance(outline)
        conn_points = []
        while len(conn_points) == 0:
            dist += self.params['burrows/width']/2
            conn_points = [point for point in contour
                           if structure.distance(geometry.Point(point)) < dist]
        
        conn_points = np.array(conn_points)

        # cluster the points to detect multiple connections 
        # this is important when a burrow has multiple exits to the ground
        if len(conn_points) >= 2:
            dist_max = self.params['burrows/width']
            data = cluster.hierarchy.fclusterdata(conn_points, dist_max,
                                                  method='single', 
                                                  criterion='distance')
        else:
            data = np.ones(1, np.int)
            
        burrow_width_min = self.params['burrows/width_min']
        for cluster_id in np.unique(data):
            p_exit = conn_points[data == cluster_id].mean(axis=0)
            p_ground = curves.get_projection_point(structure, p_exit)
            
            line = geometry.LineString((p_exit, p_ground))
            tunnel = line.buffer(distance=burrow_width_min/2,
                                 cap_style=geometry.CAP_STYLE.flat)

            # add this to the burrow outline
            outline = outline.union(tunnel.buffer(0.1))
        
        # get the contour points
        outline = regions.get_enclosing_outline(outline)
        outline = regions.regularize_linear_ring(outline)
        contour = np.array(outline.coords)
        
        # fill the burrow mask, such that this extension does not have to be
        # done next time again
        cv2.fillPoly(self.burrow_mask, [np.asarray(contour, np.int32)], 1) 

        return contour
Ejemplo n.º 8
0
    def classify_mouse_track(self):
        """ classifies the mouse at all times """
        self.log_event('Pass 2 - Start classifying the mouse.')
        
        # load the mouse, the ground, and the burrows
        mouse_track = self.data['pass2/mouse_trajectory']
        ground_profile = self.data['pass2/ground_profile']
        burrow_tracks = self.data['pass1/burrows/tracks']
        
        # load some variables
        mouse_radius = self.params['mouse/model_radius']
        trail_spacing = self.params['burrows/centerline_segment_length']
        burrow_next_change = 0
        mouse_trail = None
        for frame_id, mouse_pos in enumerate(mouse_track.pos):
            if not np.all(np.isfinite(mouse_pos)):
                # the mouse position is invalid
                continue
            
            # initialize variables
            state = {}
                    
            # check the mouse position
            ground = ground_profile.get_ground_profile(frame_id)
            if ground is not None:
                if ground.above_ground(mouse_pos):
                    state['underground'] = False
                    if mouse_pos[1] + mouse_radius < ground.get_y(mouse_pos[0]):
                        state['location'] = 'air'
                    elif mouse_pos[1] < ground.midline:
                        state['location'] = 'hill'
                    else:
                        state['location'] = 'valley'

                    mouse_trail = None
                    # get index of the ground line
                    dist = np.linalg.norm(ground.points - mouse_pos[None, :], axis=1)
                    ground_idx = np.argmin(dist)
                    # get distance from ground line
                    ground_dist = ground.linestring.distance(geometry.Point(mouse_pos))

                else:
                    state['underground'] = True
                    # check the burrow structure
                    if frame_id >= burrow_next_change:
                        burrows, burrow_next_change = \
                            burrow_tracks.find_burrows(frame_id, ret_next_change=True)
                    
                    # check whether the mouse is inside a burrow
                    mouse_point = geometry.Point(mouse_pos)
                    for burrow in burrows:
                        if burrow.polygon.contains(mouse_point):
                            state['location'] = 'burrow'
                            break

                    # keep the ground index from last time
                    ground_idx = mouse_track.ground_idx[frame_id - 1]

                    # handle mouse trail
                    if mouse_trail is None:
                        # start a new mouse trail and initialize it with the                         
                        # ground point closest to the mouse       
                        mouse_prev = mouse_track.pos[frame_id - 1]              
                        ground_point = curves.get_projection_point(ground.linestring, mouse_prev)
                        mouse_trail = [ground_point, mouse_pos]

                    else:
                        # work with an existing mouse trail
                        p_trail = mouse_trail[-2]
                        
                        if curves.point_distance(p_trail, mouse_pos) < trail_spacing:
                            # old trail should be modified
                            if len(mouse_trail) > 2:
                                # check whether the trail has to be shortened
                                p_trail = mouse_trail[-3]
                                if curves.point_distance(p_trail, mouse_pos) < trail_spacing:
                                    del mouse_trail[-1] #< shorten trail
                                
                            mouse_trail[-1] = mouse_pos
                        else:
                            # old trail must be extended
                            mouse_trail.append(mouse_pos)
                        
                    # get distance the mouse is under ground
                    ground_dist = -curves.curve_length(mouse_trail)
                    
            # set the mouse state
            mouse_track.set_state(frame_id, state, ground_idx, ground_dist)
        
        self.log_event('Pass 2 - Finished classifying the mouse.')
Ejemplo n.º 9
0
    def store_burrows(self):
        """ associates the current burrows with burrow tracks """
        burrow_tracks = self.result['burrows/tracks']
        ground_polygon = geometry.Polygon(self.get_ground_polygon_points())
        
        # check whether we already know this burrow
        # the burrows in self.burrows will always be larger than the burrows
        # in self.active_burrows. Consequently, it can happen that a current
        # burrow overlaps two older burrows, but the reverse cannot be true
        for burrow in self.burrows:
            # find all tracks to which this burrow may belong
            track_ids = [track_id 
                         for track_id, burrow_last in self.active_burrows()
                         if burrow_last.intersects(burrow)]
            
            if len(track_ids) > 1:
                # merge all burrows to a single track and keep the largest one
                track_longest, length_max = None, 0
                for track_id in track_ids:
                    burrow_last = burrow_tracks[track_id].last
                    # find track with longest burrow
                    if burrow_last.length > length_max:
                        track_longest, length_max = track_id, burrow_last.length
                    # merge the burrows
                    burrow.merge(burrow_last)
                        
            # keep the burrow parts that are below the ground line
            try:
                polygon = burrow.polygon.intersection(ground_polygon)
            except geos.TopologicalError:
                continue
            if polygon.is_empty:
                continue
            
            try:
                burrow.contour = regions.get_enclosing_outline(polygon)
            except TypeError:
                # can occur in corner cases where the enclosing outline cannot
                # be found
                continue
            
            # make sure that the burrow centerline lies within the ground region
            ground_poly = geometry.Polygon(self.get_ground_polygon_points())
            if burrow.linestring.length > 0:
                line = burrow.linestring.intersection(ground_poly)
            else:
                line = None
            
            if isinstance(line, geometry.multilinestring.MultiLineString):
                # pick the longest line if there are multiple
                index_longest = np.argmax(l.length for l in line)
                line = line[index_longest]

            is_line = isinstance(line, geometry.linestring.LineString)
            if not is_line or line.is_empty or line.length <= 1:
                # the centerline disappeared
                # => calculate a new centerline from the burrow contour
                end_point = self.burrow_estimate_exit(burrow)[0]
                self.calculate_burrow_centerline(burrow, point_start=end_point)
            
            else:
                # adjust the burrow centerline to reach to the ground line
                # it could be that the whole line was underground
                # => move the first data point onto the ground line
                line = np.array(line, np.double)
                line[0] = curves.get_projection_point(self.ground.linestring, line[0])
                # set the updated burrow centerline
                burrow.centerline = line
            
            # store the burrow if it is valid    
            if burrow.is_valid:
                if len(track_ids) > 1:
                    # add the burrow to the longest track
                    burrow_tracks[track_longest].append(self.frame_id, burrow)
                elif len(track_ids) == 1:
                    # add the burrow to the matching track
                    burrow_tracks[track_ids[0]].append(self.frame_id, burrow)
                else:
                    # create the burrow track
                    burrow_track = BurrowTrack(self.frame_id, burrow)
                    burrow_tracks.append(burrow_track)
                
        # use the new set of burrows in the next iterations
        self.burrows = [b.copy()
                        for _, b in self.active_burrows(time_interval=0)]
Ejemplo n.º 10
0
    def store_burrows(self):
        """ associates the current burrows with burrow tracks """
        burrow_tracks = self.result['burrows/tracks']
        ground_polygon = geometry.Polygon(self.get_ground_polygon_points())

        # check whether we already know this burrow
        # the burrows in self.burrows will always be larger than the burrows
        # in self.active_burrows. Consequently, it can happen that a current
        # burrow overlaps two older burrows, but the reverse cannot be true
        for burrow in self.burrows:
            # find all tracks to which this burrow may belong
            track_ids = [
                track_id for track_id, burrow_last in self.active_burrows()
                if burrow_last.intersects(burrow)
            ]

            if len(track_ids) > 1:
                # merge all burrows to a single track and keep the largest one
                track_longest, length_max = None, 0
                for track_id in track_ids:
                    burrow_last = burrow_tracks[track_id].last
                    # find track with longest burrow
                    if burrow_last.length > length_max:
                        track_longest, length_max = track_id, burrow_last.length
                    # merge the burrows
                    burrow.merge(burrow_last)

            # keep the burrow parts that are below the ground line
            try:
                polygon = burrow.polygon.intersection(ground_polygon)
            except geos.TopologicalError:
                continue
            if polygon.is_empty:
                continue
            burrow.contour = regions.get_enclosing_outline(polygon)

            # make sure that the burrow centerline lies within the ground region
            ground_poly = geometry.Polygon(self.get_ground_polygon_points())
            line = burrow.linestring.intersection(ground_poly)
            if isinstance(line, geometry.multilinestring.MultiLineString):
                # pick the longest line if there are multiple
                index_longest = np.argmax(l.length for l in line)
                line = line[index_longest]

            is_line = isinstance(line, geometry.linestring.LineString)
            if not is_line or line.is_empty or line.length <= 1:
                # the centerline disappeared
                # => calculate a new centerline from the burrow contour
                end_point = self.burrow_estimate_exit(burrow)[0]
                self.calculate_burrow_centerline(burrow, point_start=end_point)

            else:
                # adjust the burrow centerline to reach to the ground line
                # it could be that the whole line was underground
                # => move the first data point onto the ground line
                line = np.array(line, np.double)
                line[0] = curves.get_projection_point(self.ground.linestring,
                                                      line[0])
                # set the updated burrow centerline
                burrow.centerline = line

            # store the burrow if it is valid
            if burrow.is_valid:
                if len(track_ids) > 1:
                    # add the burrow to the longest track
                    burrow_tracks[track_longest].append(self.frame_id, burrow)
                elif len(track_ids) == 1:
                    # add the burrow to the matching track
                    burrow_tracks[track_ids[0]].append(self.frame_id, burrow)
                else:
                    # create the burrow track
                    burrow_track = BurrowTrack(self.frame_id, burrow)
                    burrow_tracks.append(burrow_track)

        # use the new set of burrows in the next iterations
        self.burrows = [
            b.copy() for _, b in self.active_burrows(time_interval=0)
        ]
Ejemplo n.º 11
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, :]