def from_data_file(cls, data_file: EyeTrackingFile, sync_file: SyncFile, z_threshold: float = 3.0, dilation_frames: int = 2) -> "EyeTrackingTable": """ Parameters ---------- data_file sync_file z_threshold : float, optional See EyeTracking.from_lims dilation_frames : int, optional See EyeTracking.from_lims """ cls._logger.info(f"Getting eye_tracking_data with " f"'z_threshold={z_threshold}', " f"'dilation_frames={dilation_frames}'") sync_path = Path(sync_file.filepath) frame_times = sync_utilities.get_synchronized_frame_times( session_sync_file=sync_path, sync_line_label_keys=Dataset.EYE_TRACKING_KEYS, trim_after_spike=False) eye_tracking_data = process_eye_tracking_data(data_file.data, frame_times, z_threshold, dilation_frames) return EyeTrackingTable(eye_tracking=eye_tracking_data)
def test_get_synchronized_frame_times(monkeypatch, mock_dataset_fixture, sync_line_label_keys, expected): monkeypatch.setattr(su, "Dataset", mock_dataset_fixture) obtained = su.get_synchronized_frame_times("dummy_path", sync_line_label_keys) assert np.allclose(obtained, expected)
def load_sync_file_timings(sync_file: Path, pupil_params_rows: int) -> pd.Series: """Load sync file timings from .h5 file. Parameters ---------- sync_file : Path Path to .h5 sync file. pupil_params_rows : int Number of rows in pupil params. Returns ------- pd.Series A series of frame times. (New frame times according to synchronized timings from DAQ) Raises ------ RuntimeError If the number of eye tracking frames (pupil_params_rows) does not match up with number of new frame times from the sync file. """ # Add synchronized frame times frame_times = su.get_synchronized_frame_times( session_sync_file=sync_file, sync_line_label_keys=Dataset.EYE_TRACKING_KEYS) if (pupil_params_rows != len(frame_times)): raise RuntimeError("The number of camera sync pulses in the " f"sync file ({len(frame_times)}) do not match " "with the number of eye tracking frames " f"({pupil_params_rows})!!!") return frame_times
def get_eye_tracking(self, z_threshold: float = 3.0, dilation_frames: int = 2) -> Optional[pd.DataFrame]: """Gets corneal, eye, and pupil ellipse fit data Parameters ---------- z_threshold : float, optional The z-threshold when determining which frames likely contain outliers for eye or pupil areas. Influences which frames are considered 'likely blinks'. By default 3.0 dilation_frames : int, optional Determines the number of additional adjacent frames to mark as 'likely_blink', by default 2. Returns ------- Optional[pd.DataFrame] *_area *_center_x *_center_y *_height *_phi *_width likely_blink where "*" can be "corneal", "pupil" or "eye" Will return None if class attr _skip_eye_tracking is True. """ if self._skip_eye_tracking: return None self.logger.info(f"Getting eye_tracking_data with " f"'z_threshold={z_threshold}', " f"'dilation_frames={dilation_frames}'") filepath = Path(self.extractor.get_eye_tracking_filepath()) sync_path = Path(self.extractor.get_sync_file()) eye_tracking_data = load_eye_tracking_hdf(filepath) frame_times = sync_utilities.get_synchronized_frame_times( session_sync_file=sync_path, sync_line_label_keys=Dataset.EYE_TRACKING_KEYS, trim_after_spike=False) eye_tracking_data = process_eye_tracking_data(eye_tracking_data, frame_times, z_threshold, dilation_frames) return eye_tracking_data
def get_eye_tracking(self, z_threshold: float = 3.0, dilation_frames: int = 2): logger = logging.getLogger("BehaviorOphysLimsApi") logger.info(f"Getting eye_tracking_data with " f"'z_threshold={z_threshold}', " f"'dilation_frames={dilation_frames}'") filepath = Path(self.get_eye_tracking_filepath()) sync_path = Path(self.get_sync_file()) eye_tracking_data = load_eye_tracking_hdf(filepath) frame_times = sync_utilities.get_synchronized_frame_times( session_sync_file=sync_path, sync_line_label_keys=Dataset.EYE_TRACKING_KEYS) eye_tracking_data = process_eye_tracking_data(eye_tracking_data, frame_times, z_threshold, dilation_frames) return eye_tracking_data
def write_ecephys_nwb(output_path, session_id, session_start_time, stimulus_table_path, invalid_epochs, probes, running_speed_path, session_sync_path, eye_tracking_rig_geometry, eye_dlc_ellipses_path, eye_gaze_mapping_path, pool_size, optotagging_table_path=None, session_metadata=None, **kwargs): nwbfile = pynwb.NWBFile( session_description='Data and metadata for an Ecephys session', identifier=f"{session_id}", session_id=f"{session_id}", session_start_time=session_start_time, institution="Allen Institute for Brain Science") if session_metadata is not None: nwbfile = add_metadata_to_nwbfile(nwbfile, session_metadata) stimulus_columns_to_drop = [ "colorSpace", "depth", "interpolate", "pos", "rgbPedestal", "tex", "texRes", "flipHoriz", "flipVert", "rgb", "signalDots" ] stimulus_table = read_stimulus_table( stimulus_table_path, columns_to_drop=stimulus_columns_to_drop) nwbfile = add_stimulus_timestamps( nwbfile, stimulus_table['start_time'].values ) # TODO: patch until full timestamps are output by stim table module nwbfile = add_stimulus_presentations(nwbfile, stimulus_table) nwbfile = add_invalid_times(nwbfile, invalid_epochs) if optotagging_table_path is not None: optotagging_table = pd.read_csv(optotagging_table_path) nwbfile = add_optotagging_table_to_nwbfile(nwbfile, optotagging_table) nwbfile = add_probewise_data_to_nwbfile(nwbfile, probes) running_speed, raw_running_data = read_running_speed(running_speed_path) add_running_speed_to_nwbfile(nwbfile, running_speed) add_raw_running_data_to_nwbfile(nwbfile, raw_running_data) add_eye_tracking_rig_geometry_data_to_nwbfile(nwbfile, eye_tracking_rig_geometry) # Collect eye tracking/gaze mapping data from files eye_tracking_frame_times = su.get_synchronized_frame_times( session_sync_file=session_sync_path, sync_line_label_keys=Dataset.EYE_TRACKING_KEYS) eye_dlc_tracking_data = read_eye_dlc_tracking_ellipses( Path(eye_dlc_ellipses_path)) if eye_gaze_mapping_path: eye_gaze_data = read_eye_gaze_mappings(Path(eye_gaze_mapping_path)) else: eye_gaze_data = None add_eye_tracking_data_to_nwbfile(nwbfile, eye_tracking_frame_times, eye_dlc_tracking_data, eye_gaze_data) Manifest.safe_make_parent_dirs(output_path) with pynwb.NWBHDF5IO(output_path, mode='w') as io: logging.info(f"writing session nwb file to {output_path}") io.write(nwbfile, cache_spec=True) probes_with_lfp = [p for p in probes if p["lfp"] is not None] probe_outputs = write_probewise_lfp_files(probes_with_lfp, session_id, session_metadata, session_start_time, pool_size=pool_size) return {'nwb_path': output_path, "probe_outputs": probe_outputs}
def write_ecephys_nwb(output_path, session_id, session_start_time, stimulus_table_path, invalid_epochs, probes, running_speed_path, session_sync_path, eye_tracking_rig_geometry, eye_dlc_ellipses_path, eye_gaze_mapping_path, pool_size, optotagging_table_path=None, session_metadata=None, **kwargs): nwbfile = pynwb.NWBFile(session_description='EcephysSession', identifier='{}'.format(session_id), session_start_time=session_start_time) if session_metadata is not None: nwbfile = add_metadata_to_nwbfile(nwbfile, session_metadata) stimulus_table = read_stimulus_table(stimulus_table_path) nwbfile = add_stimulus_timestamps( nwbfile, stimulus_table['start_time'].values ) # TODO: patch until full timestamps are output by stim table module nwbfile = add_stimulus_presentations(nwbfile, stimulus_table) nwbfile = add_invalid_times(nwbfile, invalid_epochs) if optotagging_table_path is not None: optotagging_table = pd.read_csv(optotagging_table_path) nwbfile = add_optotagging_table_to_nwbfile(nwbfile, optotagging_table) nwbfile = add_probewise_data_to_nwbfile(nwbfile, probes) running_speed, raw_running_data = read_running_speed(running_speed_path) add_running_speed_to_nwbfile(nwbfile, running_speed) add_raw_running_data_to_nwbfile(nwbfile, raw_running_data) # --- Add eye tracking ellipse fits to nwb file --- eye_tracking_frame_times = su.get_synchronized_frame_times( session_sync_file=session_sync_path, sync_line_label_keys=Dataset.EYE_TRACKING_KEYS) eye_dlc_tracking_data = read_eye_dlc_tracking_ellipses( Path(eye_dlc_ellipses_path)) if eye_tracking_data_is_valid(eye_dlc_tracking_data=eye_dlc_tracking_data, synced_timestamps=eye_tracking_frame_times): add_eye_tracking_ellipse_fit_data_to_nwbfile( nwbfile, eye_dlc_tracking_data=eye_dlc_tracking_data, synced_timestamps=eye_tracking_frame_times) # --- Append eye tracking rig geometry info to nwb file (with eye tracking) --- append_eye_tracking_rig_geometry_data_to_nwbfile( nwbfile, eye_tracking_rig_geometry=eye_tracking_rig_geometry) # --- Add gaze mapped positions to nwb file --- if eye_gaze_mapping_path: eye_gaze_data = read_eye_gaze_mappings(Path(eye_gaze_mapping_path)) add_eye_gaze_mapping_data_to_nwbfile(nwbfile, eye_gaze_data=eye_gaze_data) Manifest.safe_make_parent_dirs(output_path) io = pynwb.NWBHDF5IO(output_path, mode='w') logging.info(f"writing session nwb file to {output_path}") io.write(nwbfile) io.close() probes_with_lfp = [p for p in probes if p["lfp"] is not None] probe_outputs = write_probewise_lfp_files(probes_with_lfp, session_start_time, pool_size=pool_size) return {'nwb_path': output_path, "probe_outputs": probe_outputs}