def locate_tails_roughly(self, tails=None):
        """ locate tail objects using thresholding """
        # find features, using annotations in the first _frame
        use_annotations = (self.frame_id == self.frame_start)
        labels, _ = self.get_features(tails, use_annotations=use_annotations)

        # find the contours of these features
        contours = cv2.findContours(labels, cv2.RETR_EXTERNAL,
                                    cv2.CHAIN_APPROX_SIMPLE)[1]

        # locate the tails using these contours
        tails = []
        for contour in contours:
            if cv2.contourArea(contour) > self.params['detection/area_min']:
                points = contour[:, 0, :]
                threshold = 0.002 * curves.curve_length(points)
                points = curves.simplify_curve(points, threshold)
                tails.append(Tail(points))

#         debug.show_shape(*[t.contour_ring for t in tails], background=self._frame,
#                          wait_for_key=False)

        logging.debug('Found %d tail(s) in _frame %d', len(tails),
                      self.frame_id)
        return tails
    def get_statistics(self):
        """ returns statistics for all the polygons """
        result = {'name': self.name}
            
        # save results about ground line
        points = self.ground_line.points
        ground_width_px = abs(points[0, 0] - points[-1, 0])
        ground_cm_per_pixel = self.params['cage/width_norm'] / ground_width_px
        result['ground'] = {'ground_length': self.ground_line.length,
                            'ground_width_pixel': ground_width_px,
                            'cm_per_pixel': ground_cm_per_pixel}  
        
        # check the scale bar
        if self.scale_bar:
            logging.info('Found %d pixel long scale bar' % self.scale_bar.size)
            cm_per_pixel = self.params['scale_bar/length_cm']/self.scale_bar.size
            units = pint.UnitRegistry()
            scale_factor = cm_per_pixel * units.cm

            # check the ground line
            points = self.ground_line.points
            len_x_cm = abs(points[0, 0] - points[-1, 0]) * scale_factor
            w_min = self.params['cage/width_min'] * units.cm
            w_max = self.params['cage/width_max'] * units.cm
            if not w_min < len_x_cm < w_max:
                raise RuntimeError('The length (%s cm) of the ground line is '
                                   'off.' % len_x_cm)
                
            result['scale_bar'] = {'length_pixel': self.scale_bar.size,
                                   'cm_per_pixel': cm_per_pixel}
        else:
            scale_factor = 1
            result['scale_bar'] = None
        
        # collect result of all burrows
        result['burrows'] = []
        for burrow in self.burrows:
            # calculate some additional statistics
            perimeter_exit = self._get_burrow_exit_length(burrow)
            exit_count = sum(1 for ep in burrow.endpoints if ep.is_exit)
            branch_length = sum(curves.curve_length(points)
                                for points in burrow.branches)
            
            #graph = burrow.morphological_graph 
            data = {'pos_x': burrow.centroid[0] * scale_factor,
                    'pos_y': burrow.centroid[1] * scale_factor,
                    'area': burrow.area * scale_factor**2,
                    'length': burrow.length * scale_factor,
                    'length_upwards': burrow.length_upwards * scale_factor,
                    'fraction_upwards': burrow.length_upwards / burrow.length,
                    'exit_count': exit_count,
                    'branch_length': branch_length * scale_factor,
                    'branch_count': len(burrow.branches),
                    'total_length': (branch_length + burrow.length)*scale_factor,
                    'perimeter': burrow.perimeter * scale_factor,
                    'perimeter_exit': perimeter_exit * scale_factor,
                    'openness': perimeter_exit / burrow.perimeter}
            result['burrows'].append(data)
            
        return result
Exemple #3
0
 def centerline(self, points):
     """ set the new centerline """
     if points is None:
         self._centerline = None
     else:
         self._centerline = np.array(points, np.double)
         self.length = curves.curve_length(self._centerline)
     self._cache = {}
Exemple #4
0
    def get_mouse_distance(self):
        """ returns the total distance traveled by the mouse """
        trajectory = self.get_mouse_track_data('trajectory_smoothed')

        # calculate distance
        valid = np.isfinite(trajectory[:, 0])
        distance = curves.curve_length(trajectory[valid, :])
        return distance
Exemple #5
0
 def centerline(self, points):
     """ set the new centerline """
     if points is None:
         self._centerline = None
     else:
         self._centerline = np.array(points, np.double)
         self.length = curves.curve_length(self._centerline)
     self._cache = {}
Exemple #6
0
    def get_mouse_distance(self):
        """ returns the total distance traveled by the mouse """
        trajectory = self.get_mouse_track_data('trajectory_smoothed')

        # calculate distance
        valid = np.isfinite(trajectory[:, 0])
        distance = curves.curve_length(trajectory[valid, :])
        return distance
Exemple #7
0
    def determine_centerline(self):
        """ determines the centerline """
        spacing = self.parameters['centerline_segment_length']
        skip_length = self.parameters['centerline_skip_length']

        endpoints = [p.coords for p in self.endpoints]
        centerline = self.get_centerline_smoothed(spacing=spacing,
                                                  skip_length=skip_length,
                                                  endpoints=endpoints)
        self._centerline = np.asarray(centerline)
        self.length = curves.curve_length(self._centerline)
Exemple #8
0
    def determine_centerline(self):
        """ determines the centerline """
        spacing = self.parameters['centerline_segment_length']
        skip_length = self.parameters['centerline_skip_length']

        endpoints = [p.coords for p in self.endpoints]
        centerline = self.get_centerline_smoothed(spacing=spacing,
                                                  skip_length=skip_length,
                                                  endpoints=endpoints)
        self._centerline = np.asarray(centerline)
        self.length = curves.curve_length(self._centerline)
Exemple #9
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
Exemple #10
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
Exemple #11
0
    def find_sure_mouse_tracks(self, tracks):
        """ identifies tracks which surely describe mice. These tracks can then
        be used to split up the connection problem into distinct parts """
        smoothing_window = self.params['tracking/position_smoothing_window']
        dist_threshold = self.params['tracking/mouse_distance_threshold']
        min_mean_speed = self.params['tracking/mouse_min_mean_speed']
        max_speed = self.params['mouse/speed_max']
        min_length = int(dist_threshold/(0.25 * max_speed))
         
        sure_tracks = []
        for track_id, track in enumerate(tracks):
            if len(track) < min_length:
                continue
            
            # get smoothed trajectory
            trajectory = track.get_trajectory(smoothing_window)
            dist = curves.curve_length(trajectory)
            
            if dist > dist_threshold and dist/len(track) > min_mean_speed:
                sure_tracks.append(track_id)

        # remove overlapping tracks that were marked as true tracks
        if sure_tracks:
            k = 1
            t1 = tracks[sure_tracks[0]]
            while k < len(sure_tracks):
                t2 = tracks[sure_tracks[k]]
                if t1.end > t2.start:
                    # overlap => delete shorter track
                    if t1.duration > t2.duration:
                        del sure_tracks[k]
                    else:
                        del sure_tracks[k-1]
                        t1 = t2
                else:
                    # check next track
                    k += 1
                    t1 = t2

        return sure_tracks
    def _get_burrow_exit_length(self, burrow):        
        """ calculates the length of all exists of the given burrow """
        # identify all points that are close to the ground line
        dist_max = burrow.parameters['ground_point_distance']
        g_line = self.ground_line.linestring
        points = burrow.contour
        exitpoints = [g_line.distance(geometry.Point(point)) < dist_max
                      for point in points]
        
        # find the indices of contiguous true regions
        indices = math.contiguous_true_regions(exitpoints)

        # find the total length of all exits        
        exit_length = 0
        for a, b in indices:
            exit_length += curves.curve_length(points[a : b+1])
        
        # handle the first and last point if they both belong to an exit 
        if exitpoints[0] and exitpoints[-1]:
            exit_length += curves.point_distance(points[0], points[-1])
            
        return exit_length
    def _get_burrow_exit_length(self, burrow):
        """ calculates the length of all exists of the given burrow """
        # identify all points that are close to the ground line
        dist_max = burrow.parameters['ground_point_distance']
        g_line = self.ground_line.linestring
        points = burrow.contour
        exitpoints = [
            g_line.distance(geometry.Point(point)) < dist_max
            for point in points
        ]

        # find the indices of contiguous true regions
        indices = math.contiguous_true_regions(exitpoints)

        # find the total length of all exits
        exit_length = 0
        for a, b in indices:
            exit_length += curves.curve_length(points[a:b + 1])

        # handle the first and last point if they both belong to an exit
        if exitpoints[0] and exitpoints[-1]:
            exit_length += curves.point_distance(points[0], points[-1])

        return exit_length
    def locate_tails_roughly(self, tails=None):
        """ locate tail objects using thresholding """
        # find features, using annotations in the first _frame        
        use_annotations = (self.frame_id == self.frame_start)
        labels, _ = self.get_features(tails, use_annotations=use_annotations)

        # find the contours of these features
        contours = cv2.findContours(labels, cv2.RETR_EXTERNAL,
                                    cv2.CHAIN_APPROX_SIMPLE)[1]

        # locate the tails using these contours
        tails = []
        for contour in contours:
            if cv2.contourArea(contour) > self.params['detection/area_min']:
                points = contour[:, 0, :]
                threshold = 0.002 * curves.curve_length(points)
                points = curves.simplify_curve(points, threshold)
                tails.append(Tail(points))
        
#         debug.show_shape(*[t.contour_ring for t in tails], background=self._frame,
#                          wait_for_key=False)
    
        logging.debug('Found %d tail(s) in _frame %d', len(tails), self.frame_id)
        return tails
    def get_statistics(self):
        """ returns statistics for all the polygons """
        result = {'name': self.name}

        # save results about ground line
        points = self.ground_line.points
        ground_width_px = abs(points[0, 0] - points[-1, 0])
        ground_cm_per_pixel = self.params['cage/width_norm'] / ground_width_px
        result['ground'] = {'ground_length': self.ground_line.length,
                            'ground_width_pixel': ground_width_px,
                            'cm_per_pixel': ground_cm_per_pixel}

        # check the scale bar
        if self.scale_bar:
            logging.info('Found %d pixel long scale bar' % self.scale_bar.size)
            cm_per_pixel = self.params['scale_bar/length_cm'] / self.scale_bar.size
            units = pint.UnitRegistry()
            scale_factor = cm_per_pixel * units.cm

            # check the ground line
            points = self.ground_line.points
            len_x_cm = abs(points[0, 0] - points[-1, 0]) * scale_factor
            w_min = self.params['cage/width_min'] * units.cm
            w_max = self.params['cage/width_max'] * units.cm
            if not w_min < len_x_cm < w_max:
                raise RuntimeError('The length (%s) of the ground line is '
                                   'not in [%s, %s] for image `%s`.'
                                   % (len_x_cm, w_min, w_max, self.name))

            result['scale_bar'] = {'length_pixel': self.scale_bar.size,
                                   'cm_per_pixel': cm_per_pixel}

        else:
            # there is no scale bar
            scale_factor = 1
            result['scale_bar'] = None
            logging.warn('Could not find a scale bar in the image `%s`',
                         self.name)

        # determine some parameters
        try:
            angle_dist_px = (self.params['burrow/angle_measurement_distance_cm']
                             / cm_per_pixel)
        except UnboundLocalError:
            angle_dist_px = self.params['burrow/angle_measurement_distance_cm']

        # collect result of all burrows
        result['burrows'] = []
        for burrow in self.burrows:
            # calculate some additional statistics
            perimeter_exit = self._get_burrow_exit_length(burrow)
            exit_count = sum(1 for ep in burrow.endpoints if ep.is_exit)
            branch_length = sum(curves.curve_length(points)
                                for points in burrow.branches)

            # determine burrow angles and distance between end points
            angle1 = np.rad2deg(burrow.get_entry_angle(angle_dist_px))
            angle2 = np.rad2deg(burrow.get_exit_angle(angle_dist_px))
            level_difference = (burrow.centerline[0, 1]
                                - burrow.centerline[-1, 1])

            # make sure the values are reported correctly
            if self._centerline_is_oriented(burrow):
                angle_entrance, angle_exit = angle1, angle2
            else:
                # switch the measurements as if the centerline was reoriented
                angle_entrance, angle_exit = angle2, angle1
                level_difference *= -1

            # get the branch angles, measured at the point on the centerline
            branch_angles = []
            for branch in burrow.branches:
                p0, p1 = branch[-1], branch[0]
                # calculate the angle, note that y-axis points down
                angle = np.arctan2(p0[1] - p1[1], p1[0] - p0[0])
                branch_angles.append(np.rad2deg(angle))

            while len(branch_angles) < 2:
                branch_angles.append(np.nan)

            #graph = burrow.morphological_graph
            data = {'pos_x': burrow.centroid[0] * scale_factor,
                    'pos_y': burrow.centroid[1] * scale_factor,
                    'area': burrow.area * scale_factor**2,
                    'length': burrow.length * scale_factor,
                    'length_upwards': burrow.length_upwards * scale_factor,
                    'fraction_upwards': burrow.length_upwards / burrow.length,
                    'exit_count': exit_count,
                    'entrance_angle [degree]': angle_entrance,
                    'exit_angle [degree]': angle_exit,
                    'entrance_exit_difference': level_difference * scale_factor,
#                     'centerline_oriented': self._centerline_is_oriented(burrow),
                    'branch_length': branch_length * scale_factor,
                    'branch_count': len(burrow.branches),
                    'branch_angle_1 [degree]': branch_angles[0],
                    'branch_angle_2 [degree]': branch_angles[1],
                    'total_length': (branch_length + burrow.length)*scale_factor,
                    'perimeter': burrow.perimeter * scale_factor,
                    'perimeter_exit': perimeter_exit * scale_factor,
                    'openness': perimeter_exit / burrow.perimeter}
            result['burrows'].append(data)

        return result
 def is_moving(self):
     """ return if the object has moved in the last frames """
     dist = curves.curve_length(self.get_track(-self.moving_window_frames, None))
     return dist > self.moving_threshold_pixel
Exemple #17
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.')
Exemple #18
0
 def length(self):
     """ returns the length of the profile """
     return curves.curve_length(self.points)
 def length(self):
     """ returns the length of the profile """
     return curves.curve_length(self.points)
 def is_moving(self):
     """ return if the object has moved in the last frames """
     dist = curves.curve_length(
         self.get_track(-self.moving_window_frames, None))
     return dist > self.moving_threshold_pixel
Exemple #21
0
    def get_statistics_periods(self, keys=None, slice_length=None):
        """ calculate statistics given in `keys` for consecutive time slices.
        If `keys` is None, all statistics are calculated.
        If `slice_length` is None, the statistics are calculated for the entire
            video. """
        if keys is None:
            keys = OmniContainer()

        # determine the frame slices
        frame_range = self.get_frame_range()
        if slice_length:
            frame_range = range(frame_range[0], frame_range[1],
                                int(slice_length))
        frame_ivals = [
            (a, b + 1) for a, b in itertools.izip(frame_range, frame_range[1:])
        ]
        frame_slices = [slice(a, b + 1) for a, b in frame_ivals]

        # save the time slices used for analysis
        result = {
            'frame_interval': frame_ivals,
            'period_start': [a * self.time_scale for a, _ in frame_ivals],
            'period_end': [b * self.time_scale for _, b in frame_ivals],
            'period_duration':
            [(b - a) * self.time_scale for a, b in frame_ivals]
        }

        # get the area changes of the ground line
        if 'ground_removed' in keys or 'ground_accrued' in keys:
            area_removed, area_accrued = [], []
            for f in frame_slices:
                poly_rem, poly_acc = self.get_ground_changes((f.start, f.stop))
                area_removed.append(poly_rem.area)
                area_accrued.append(poly_acc.area)

            if 'ground_removed' in keys:
                result['ground_removed'] = area_removed * self.length_scale**2
            if 'ground_accrued' in keys:
                result['ground_accrued'] = area_accrued * self.length_scale**2

        # get durations of the mouse being in different states
        for key, pattern in (('time_spent_moving', '...M'),
                             ('time_at_burrow_end', '.(B|D)E.')):
            # special case in which the calculation has to be done
            c = (key == 'time_at_burrow_end' and 'mouse_digging_rate' in keys)
            # alternatively, the computation might be requested directly
            if c or key in keys:
                states = self.get_mouse_state_vector([pattern])
                duration = [
                    np.count_nonzero(states[t_slice] == 0)
                    for t_slice in frame_slices
                ]
                result[key] = duration * self.time_scale

        # get velocity statistics
        speed_statistics = {
            'mouse_speed_mean': lambda x: np.nan_to_num(np.array(x)).mean(),
            'mouse_speed_mean_valid': lambda x: np.nanmean(x),
            'mouse_speed_max': lambda x: np.nanmax(x)
        }
        if any(key in keys for key in speed_statistics.keys()):
            velocities = self.get_mouse_velocities()
            speed = np.hypot(velocities[:, 0], velocities[:, 1])
            for key, stat_func in speed_statistics.iteritems():
                res = [stat_func(speed[t_slice]) for t_slice in frame_slices]
                result[key] = np.array(res) * self.speed_scale

        # get distance statistics
        if 'mouse_distance_covered' in keys:
            trajectory = self.get_mouse_trajectory()
            dist = []
            for t_slice in frame_slices:
                trajectory_part = trajectory[t_slice]
                valid = np.isfinite(trajectory_part[:, 0])
                dist.append(curves.curve_length(trajectory_part[valid]))
            result['mouse_distance_covered'] = dist * self.length_scale

        if 'mouse_trail_longest' in keys:
            ground_dist = self.get_mouse_track_data('ground_dist')
            dist = [
                -np.nanmin(ground_dist[t_slice]) for t_slice in frame_slices
            ]
            result['mouse_trail_longest'] = dist * self.length_scale

        if 'mouse_deepest_diagonal' in keys or 'mouse_deepest_vertical' in keys:
            dist_diag, dist_vert = self.get_mouse_ground_distance_max(
                frame_ivals)
            if 'mouse_deepest_diagonal' in keys:
                result[
                    'mouse_deepest_diagonal'] = dist_diag * self.length_scale
            if 'mouse_deepest_vertical' in keys:
                result[
                    'mouse_deepest_vertical'] = dist_vert * self.length_scale

        # get statistics about the burrow evolution
        if any(key in keys
               for key in ('burrow_area_excavated', 'mouse_digging_rate',
                           'time_burrow_grew')):
            stats = [
                self.get_burrow_growth_statistics((f.start, f.stop))
                for f in frame_slices
            ]
            stats = np.array(stats)

            if 'burrow_area_excavated' in keys or 'mouse_digging_rate' in keys:
                result[
                    'burrow_area_excavated'] = stats[:,
                                                     0] * self.length_scale**2
            if 'time_burrow_grew' in keys:
                result['time_burrow_grew'] = stats[:, 1] * self.time_scale

        # calculate the digging rate by considering burrows and the mouse
        if 'mouse_digging_rate' in keys:
            time_min = self.params['mouse/digging_rate_time_min']
            if self.use_units:
                time_min *= self.time_scale
                unit_rate = self.length_scale**2 / self.time_scale
                area_min = 0 * self.length_scale**2
            else:
                unit_rate = 1
                area_min = 0
            # calculate the digging rate
            digging_rate = []
            for area, time in itertools.izip(result['burrow_area_excavated'],
                                             result['time_at_burrow_end']):
                if area > area_min and time > time_min:
                    digging_rate.append(area / time)
                else:
                    digging_rate.append(np.nan * unit_rate)
            result['mouse_digging_rate'] = digging_rate

        # determine the remaining keys
        if not isinstance(keys, OmniContainer):
            keys = set(keys) - set(result.keys())

        if not isinstance(keys, OmniContainer):
            keys = set(keys) - set(result.keys())
            if keys:
                # report statistics that could not be calculated
                self.logger.warn(
                    'The following statistics are not defined in '
                    'the algorithm and could therefore not be '
                    'calculated: %s', ', '.join(keys))

        return result
Exemple #22
0
    def get_statistics_periods(self, keys=None, slice_length=None):
        """ calculate statistics given in `keys` for consecutive time slices.
        If `keys` is None, all statistics are calculated.
        If `slice_length` is None, the statistics are calculated for the entire
            video. """
        if keys is None:
            keys = OmniContainer()
            
        # determine the frame slices
        frame_range = self.get_frame_range()
        if slice_length:
            frame_range = range(frame_range[0], frame_range[1],
                                int(slice_length))
        frame_ivals = [(a, b + 1) for a, b in itertools.izip(frame_range,
                                                             frame_range[1:])]
        frame_slices = [slice(a, b + 1) for a, b in frame_ivals]

        # length of the periods
        period_durations = (np.array([(b - a + 1) for a, b in frame_ivals]) 
                            * self.time_scale) 

        # save the time slices used for analysis
        result = {'frame_interval': frame_ivals,
                  'period_start': [a*self.time_scale for a, _ in frame_ivals],
                  'period_end': [b*self.time_scale for _, b in frame_ivals],
                  'period_duration': [(b - a)*self.time_scale 
                                      for a, b in frame_ivals]}

        # get the area changes of the ground line
        if 'ground_removed' in keys or 'ground_accrued' in keys:
            area_removed, area_accrued = [], []
            for f in frame_slices:
                poly_rem, poly_acc = self.get_ground_changes((f.start, f.stop))
                area_removed.append(poly_rem.area)
                area_accrued.append(poly_acc.area)

            if 'ground_removed' in keys:
                result['ground_removed'] = area_removed * self.length_scale**2
            if 'ground_accrued' in keys:
                result['ground_accrued'] = area_accrued * self.length_scale**2

        # get durations of the mouse being in different states        
        for key, pattern in (('time_spent_moving', '...M'), 
                             ('time_at_burrow_end', '.(B|D)E.')):
            # special case in which the calculation has to be done
            c = (key == 'time_at_burrow_end' and 'mouse_digging_rate' in keys)
            # alternatively, the computation might be requested directly
            if c or key in keys:
                states = self.get_mouse_state_vector([pattern])
                duration = [np.count_nonzero(states[t_slice] == 0)
                            for t_slice in frame_slices]
                result[key] = duration * self.time_scale
                key_fraction = key.replace('time_', 'fraction_')
                result[key_fraction] = np.array(result[key]) / period_durations

        # get velocity statistics
        speed_statistics = {
            'mouse_speed_mean': lambda x: np.nan_to_num(np.array(x)).mean(),
            'mouse_speed_mean_valid': lambda x: np.nanmean(x),
            'mouse_speed_max': lambda x: np.nanmax(x)
        }
        if any(key in keys for key in speed_statistics.keys()):
            velocities = self.get_mouse_velocities()
            speed = np.hypot(velocities[:, 0], velocities[:, 1])
            for key, stat_func in speed_statistics.iteritems():
                res = [stat_func(speed[t_slice]) for t_slice in frame_slices]
                result[key] = np.array(res) * self.speed_scale
        
        # get distance statistics
        if 'mouse_distance_covered' in keys:
            trajectory = self.get_mouse_trajectory()
            dist = []
            for t_slice in frame_slices:
                trajectory_part = trajectory[t_slice]
                valid = np.isfinite(trajectory_part[:, 0])
                dist.append(curves.curve_length(trajectory_part[valid]))
            result['mouse_distance_covered'] = dist * self.length_scale

        if 'mouse_trail_longest' in keys:
            ground_dist = self.get_mouse_track_data('ground_dist')
            dist = [-np.nanmin(ground_dist[t_slice])
                    for t_slice in frame_slices]
            result['mouse_trail_longest'] = dist * self.length_scale
            
        if 'mouse_deepest_diagonal' in keys or 'mouse_deepest_vertical' in keys:
            dist_diag, dist_vert = self.get_mouse_ground_distance_max(frame_ivals)
            if 'mouse_deepest_diagonal' in keys:
                result['mouse_deepest_diagonal'] = dist_diag * self.length_scale
            if 'mouse_deepest_vertical' in keys:
                result['mouse_deepest_vertical'] = dist_vert * self.length_scale

        # get statistics about the burrow evolution
        if any(key in keys for key in ('burrow_area_excavated',
                                       'mouse_digging_rate',
                                       'time_burrow_grew')):
            
            stats = [self.get_burrow_growth_statistics((f.start, f.stop))
                     for f in frame_slices]
            stats = np.array(stats)
            
            if 'burrow_area_excavated' in keys or 'mouse_digging_rate' in keys:
                try:
                    area_excavated = stats[:, 0] * self.length_scale**2
                except IndexError:
                    area_excavated = []
                result['burrow_area_excavated'] = area_excavated
                 
            if 'time_burrow_grew' in keys:
                try:
                    burrow_time = stats[:, 1] * self.time_scale
                except IndexError:
                    burrow_time = []
                result['time_burrow_grew'] = burrow_time 
                result['fraction_burrow_grew'] = burrow_time / period_durations 
                                        
        # calculate the digging rate by considering burrows and the mouse
        if 'mouse_digging_rate' in keys:
            time_min = self.params['mouse/digging_rate_time_min']
            if self.use_units:
                time_min *= self.time_scale
                unit_rate = self.length_scale**2 / self.time_scale
                area_min = 0 * self.length_scale**2
            else:
                unit_rate = 1
                area_min = 0
            # calculate the digging rate
            digging_rate = []
            for area, time in itertools.izip(result['burrow_area_excavated'],
                                             result['time_at_burrow_end']):
                if area > area_min and time > time_min:
                    digging_rate.append(area / time)
                else:
                    digging_rate.append(np.nan * unit_rate)
            result['mouse_digging_rate'] = digging_rate

        # determine the remaining keys
        if not isinstance(keys, OmniContainer):
            keys = set(keys) - set(result.keys())  

        if not isinstance(keys, OmniContainer):
            keys = set(keys) - set(result.keys())
            if keys:
                # report statistics that could not be calculated
                self.logger.warn('The following statistics are not defined in '
                                 'the algorithm and could therefore not be '
                                 'calculated: %s', ', '.join(keys))
        
        return result