def build(self): """ Interpolate each segment to create a piecewise linear curve where the nmber of samples in each segments is matched to its length. """ tot_length = np.sum([seg.length for seg in self.segments]) samples_per_unit_length = self._n_samples / tot_length samples_per_segment = [ int(seg.length * samples_per_unit_length) for seg in self.segments ] for segment, n_samples in zip(self.segments, samples_per_segment): segment.interpolate(n_samples) # stack all segmnts into a curve trace = np.vstack([segment.line for segment in self.segments]) self.trace_ids = np.concatenate( [segment.ids for segment in self.segments]) # smooth the curve to make it more bendy x = convolve_with_gaussian(trace[:, 0], 200) y = convolve_with_gaussian(trace[:, 1], 200) self.trace = np.vstack([x, y]).T # get the orientation of each segment self.trace_orientation = get_dir_of_mvmt_from_xy( self.trace[:, 0], self.trace[:, 1])
def skeleton2path( skeleton: np.ndarray, points_spacing: int, apply_extra_spacing: bool = False, # if true track is padded to look like mice's paths ) -> Trajectory: """ It finds points on the skeleton and sorts them based on their position. Returns a Trajectory over the skeleton (smoothed) """ y, x = np.where(skeleton) points = np.array([x, y]).T p = points[0] pts = [p] added = [0] for n in range(len(points)): # get the closest point to the current one dists = np.apply_along_axis(np.linalg.norm, 1, points - p) for idx in added: dists[idx] = 10000 p = points[np.argmin(dists)] pts.append(p) added.append(np.argmin(dists)) pts = pts[2:-2] X = np.float64([p[0] + 1 for p in pts])[::-1] Y = np.float64([p[1] + 1 for p in pts])[::-1] - 0.5 # adjust coordinates to make it look more like the animals tracking (e.g. because mice have non-zero width) if apply_extra_spacing: Y[Y < 10] -= 2.5 Y[Y > 50] += 1.5 Y[(Y > 40) & (Y < 50) & (5 < X) & (X < 33)] += 2 X[(X > 8) & (X < 20) & (Y > 40) & (Y < 50)] -= 2 X[(X > 20) & (X < 33) & (Y > 40) & (Y < 50)] += 2 Y[(X > 10) & (X < 15) & (Y > 43) & (Y < 48)] += 2.5 X[(Y < 18) & (X > 8) & (X < 14)] -= 1 X[(Y < 18) & (X > 24) & (X < 32)] -= 1 X[(Y < 18) & (X > 16) & (X < 25)] += 1 X[(Y < 18) & (X > 32)] += 1 Y[(X > 30) & (X < 35) & (Y < 5)] += 1 # smooth X = convolve_with_gaussian(X, kernel_width=31) Y = convolve_with_gaussian(Y, kernel_width=31) if points_spacing >= 1: return Trajectory( X, Y, name="skeleton", fps=60, smoothing_window=1).downsample_euclidean(spacing=points_spacing) else: return Trajectory( X, Y, name="skeleton", fps=60, smoothing_window=1).interpolate(spacing=points_spacing)
def calc_angular_velocity(angles: np.ndarray) -> np.ndarray: # convert to radians and take derivative rad = np.unwrap(np.deg2rad(angles)) rad = medfilt(rad, 11) rad = data_utils.convolve_with_gaussian(rad, 11) diff = derivative(rad) return np.rad2deg(diff)
def process_body_part( bp_data: dict, M: np.ndarray, likelihood_th: float = 0.95, cm_per_px: float = 1, ) -> dict: # register to CMM x, y = register(bp_data["x"], bp_data["y"], M) # scale to go px -> cm x *= cm_per_px y *= cm_per_px # remove low confidence intervals like = bp_data["likelihood"] x[like < likelihood_th] = np.nan y[like < likelihood_th] = np.nan # interpolate nans xy = data_utils.interpolate_nans(x=x, y=y) x, y = np.array(list(xy["x"].values())), np.array(list(xy["y"].values())) # median filter pass x = data_utils.convolve_with_gaussian(x, kernel_width=5) y = data_utils.convolve_with_gaussian(y, kernel_width=5) # compute speed speed = Path(x, y, fps=60).speed # in cm/s # make sure there are no nans results = dict(x=x, y=y, bp_speed=speed) for k, var in results.items(): if np.any(np.isnan(var)): raise ValueError(f"Found NANs in {k}") return results
def get_threshold_crossing( tracking: tuple, frame: int, direction: str, threshold: float ) -> int: """ Gets the last/first time the speed of all paws crossed a threshold """ speeds = ( tracking.right_fl.bp_speed, tracking.left_fl.bp_speed, tracking.right_hl.bp_speed, tracking.left_hl.bp_speed, ) if direction == "up": if frame == 0: return frame start = frame - 60 if frame > 60 else 0 crosses = [] for paw in speeds: paw = data_utils.convolve_with_gaussian(paw, 8) try: crosses.append(np.where(paw[start:frame] < threshold)[0][-1]) except IndexError: crosses.append(np.nan) val = np.nanmin(crosses) + start if val > frame or np.isnan(val): logger.warning("Error in precise threshold crossing computation") return None return int(val) else: n_frames = len(tracking.right_fl.speed) if frame == n_frames: return frame end = frame + 60 if frame < (n_frames - 60) else n_frames crosses = [] for paw in speeds: try: crosses.append(np.where(paw[frame:end] < threshold)[0][0]) except IndexError: crosses.append(np.nan) val = np.nanmax(crosses) + frame if val > end or np.isnan(val): logger.warning("Error in precise threshold crossing computation") return None return int(val)
def get_session_bouts( key: dict, tracking: pd.DataFrame, is_hairpin: bool, speed_th: float, max_pause: float, min_duration: float, min_peak_speed: float, min_gcoord_delta: float, ) -> list: """ Gets all the locomotion bouts for an experimental session """ tracking = TrackingData.from_dataframe(tracking) # get when the mouse is moving is_moving = get_when_moving( data_utils.convolve_with_gaussian(tracking.body.speed, 21), speed_th, max_pause, min_duration, min_peak_speed, ) # plot_speeds(tracking.body.speed, is_moving=is_moving) # get bouts onsets and offsets onsets, offsets = get_onset_offset(is_moving, 0.5) logger.debug(f"Found {len(onsets)} bouts") # fill up all details bouts = [] for bstart, bend in zip(onsets, offsets): if bend < bstart: raise ValueError("Something went wrong...") # get precise bout start and end times based on paw speeds bstart, bend = get_bout_start_end_times(tracking, bstart, bend) if bstart is None or bend is None: continue elif bend == len(tracking.body.x): bend -= 1 # remove bouts too close to start and end of recording if bstart < 60 or bend > (len(tracking.body.speed) - 60): continue if is_hairpin: # check there's enough change in global coordinate okay, gdelta = check_gcoord_delta( tracking, bstart, bend, min_gcoord_delta ) if not okay: continue # get bout direction of movement if hairpin direction = get_bout_direction(tracking, bstart, bend) # get complete and ROIs complete, start_roi, end_roi = get_bout_complete_and_rois( tracking, bstart, bend ) else: direction, complete = "none", "none" start_roi, end_roi = -1, -1 gdelta = -1 if bstart in [b["start_frame"] for b in bouts]: continue # check that there's no movement before/after bout if not is_quiet(tracking, bstart, bend, duration=int(0.5 * 60)): continue # put everything together bout = key.copy() bout["start_frame"] = bstart bout["end_frame"] = bend bout["duration"] = (bend - bstart) / 60 bout["direction"] = direction bout["color"] = colors.bout_direction_colors[direction] bout["complete"] = complete bout["start_roi"] = start_roi bout["end_roi"] = end_roi bout["gcoord_delta"] = gdelta bouts.append(bout) n_complete = len([b for b in bouts if b["complete"] == "true"]) logger.debug( f" kept {len(bouts)}/{len(onsets)} ({len(bouts)/len(onsets)*100:.2f} %) bouts of which {n_complete} are complete" ) return bouts
_ = draw.Hairpin() P = [0, 0.125, 1] colors = make_palette(pink_dark, blue_dark, len(P)) traces = [] for n, p in enumerate(P): trace, _ = tp.fit_best_trace( control_points, center_line, K, angle_cost=p, length_cost=(1 - p) * 5e-3, ) # draw best trace X = convolve_with_gaussian(trace.x, kernel_width=11) Y = convolve_with_gaussian(trace.y, kernel_width=11) traces.append(Path(X, Y)) draw.Tracking(X, Y, color=colors[n], lw=3) # %% # ------------------------------- load tracking ------------------------------ # _bouts = pd.read_hdf(paths.analysis_folder / "behavior" / "saved_data" / f"complete_bouts.h5") _bouts = _bouts.loc[_bouts.duration < 8] print(f"Kept {len(_bouts)} bouts") bouts = [] for i, bout in _bouts.iterrows(): bouts.append(LocomotionBout(bout))
axes[c, rn].axis('off') # %% from data.data_utils import convolve_with_gaussian import seaborn as sns f = plt.figure(figsize=(20, 12), constrained_layout=True) f.suptitle(f'{unit.brain_region} - {unit.unit_id}') axes = f.subplot_mosaic(''' AABBCCFF AADDEEGG ''') unit = units.loc[units.unit_id == 1036].iloc[0] frate = convolve_with_gaussian(unit.firing_rate) draw.ROI(roi, ax=axes['A']) X, Y = [], [] for bout in bouts: draw.Tracking(bout.x, bout.y - 2, ax=axes['A']) axes['B'].plot(bout.velocity.magnitude, color='k', lw=.5) axes['C'].plot(bout.thetadot, color='k', lw=.5) axes['D'].plot(frate[bout.start_frame:bout.end_frame]) X.extend(list(frate[bout.start_frame + 2:bout.end_frame - 2])) Y.extend(list(bout.speed)) sns.regplot(X, Y, ax=axes['E']) starts = [b.start_frame for b in bouts]