|
4 | 4 | @author: marinag |
5 | 5 | """ |
6 | 6 | from itertools import chain |
7 | | -from typing import Dict, Any |
8 | | -from .process_sync import filter_digital, calculate_delay # NOQA: E402 |
| 7 | +from typing import Dict, Any, Optional, List, Union |
| 8 | +from allensdk.brain_observatory.behavior.sync.process_sync import ( |
| 9 | + filter_digital, calculate_delay) # NOQA: E402 |
9 | 10 | from allensdk.brain_observatory.sync_dataset import Dataset as SyncDataset # NOQA: E402 |
10 | 11 | import numpy as np |
11 | 12 | import scipy.stats as sps |
12 | 13 |
|
13 | | -def get_sync_data(sync_path): |
| 14 | + |
| 15 | +def get_raw_stimulus_frames( |
| 16 | + dataset: SyncDataset, |
| 17 | + permissive: bool = False |
| 18 | +) -> np.ndarray: |
| 19 | + """ Report the raw timestamps of each stimulus frame. This corresponds to |
| 20 | + the time at which the psychopy window's flip method returned, but not |
| 21 | + necessarily to the time at which the stimulus frame was displayed. |
| 22 | +
|
| 23 | + Parameters |
| 24 | + ---------- |
| 25 | + dataset : describes experiment timing |
| 26 | + permissive : If True, None will be returned if timestamps are not found. If |
| 27 | + False, a KeyError will be raised |
| 28 | +
|
| 29 | + Returns |
| 30 | + ------- |
| 31 | + array of timestamps (floating point; seconds; relative to experiment start). |
| 32 | +
|
| 33 | + """ |
| 34 | + try: |
| 35 | + return dataset.get_edges("falling",'stim_vsync', "seconds") |
| 36 | + except KeyError: |
| 37 | + if not permissive: |
| 38 | + raise |
| 39 | + return |
| 40 | + |
| 41 | + |
| 42 | +def get_ophys_frames( |
| 43 | + dataset: SyncDataset, |
| 44 | + permissive: bool = False |
| 45 | +) -> np.ndarray: |
| 46 | + """ Report the timestamps of each optical physiology video frame |
| 47 | +
|
| 48 | + Parameters |
| 49 | + ---------- |
| 50 | + dataset : describes experiment timing |
| 51 | +
|
| 52 | + Returns |
| 53 | + ------- |
| 54 | + array of timestamps (floating point; seconds; relative to experiment start). |
| 55 | + permissive : If True, None will be returned if timestamps are not found. If |
| 56 | + False, a KeyError will be raised |
| 57 | +
|
| 58 | + Notes |
| 59 | + ----- |
| 60 | + use rising edge for Scientifica, falling edge for Nikon |
| 61 | + http://confluence.corp.alleninstitute.org/display/IT/Ophys+Time+Sync |
| 62 | + This function uses rising edges |
| 63 | +
|
| 64 | + """ |
| 65 | + try: |
| 66 | + return dataset.get_edges("rising", '2p_vsync', "seconds") |
| 67 | + except KeyError: |
| 68 | + if not permissive: |
| 69 | + raise |
| 70 | + return |
| 71 | + |
| 72 | + |
| 73 | +def get_lick_times( |
| 74 | + dataset: SyncDataset, |
| 75 | + permissive: bool = False |
| 76 | +) -> Optional[np.ndarray]: |
| 77 | + """ Report the timestamps of each detected lick |
| 78 | +
|
| 79 | + Parameters |
| 80 | + ---------- |
| 81 | + dataset : describes experiment timing |
| 82 | + permissive : If True, None will be returned if timestamps are not found. If |
| 83 | + False, a KeyError will be raised |
| 84 | +
|
| 85 | + Returns |
| 86 | + ------- |
| 87 | + array of timestamps (floating point; seconds; relative to experiment start) |
| 88 | + or None. If None, no lick timestamps were found in this sync |
| 89 | + dataset. |
| 90 | +
|
| 91 | + """ |
| 92 | + return dataset.get_edges( |
| 93 | + "rising", ["lick_times", "lick_sensor"], "seconds", permissive) |
14 | 94 |
|
15 | | - sync_dataset = SyncDataset(sync_path) |
16 | | - meta_data = sync_dataset.meta_data |
17 | | - sample_freq = meta_data['ni_daq']['counter_output_freq'] |
| 95 | + |
| 96 | +def get_stim_photodiode( |
| 97 | + dataset: SyncDataset, |
| 98 | + permissive: bool = False |
| 99 | +) -> Optional[List[float]]: |
| 100 | + """ Report the timestamps of each detected sync square transition (both |
| 101 | + black -> white and white -> black) in this experiment. |
| 102 | +
|
| 103 | + Parameters |
| 104 | + ---------- |
| 105 | + dataset : describes experiment timing |
| 106 | + permissive : If True, None will be returned if timestamps are not found. If |
| 107 | + False, a KeyError will be raised |
| 108 | +
|
| 109 | + Returns |
| 110 | + ------- |
| 111 | + array of timestamps (floating point; seconds; relative to experiment start) |
| 112 | + or None. If None, no photodiode timestamps were found in this sync |
| 113 | + dataset. |
| 114 | +
|
| 115 | + """ |
| 116 | + return dataset.get_edges( |
| 117 | + "all", ["stim_photodiode", "photodiode"], "seconds", permissive) |
| 118 | + |
| 119 | + |
| 120 | +def get_trigger( |
| 121 | + dataset: SyncDataset, |
| 122 | + permissive: bool = False |
| 123 | +) -> Optional[np.ndarray]: |
| 124 | + """ Returns (as a 1-element array) the time at which optical physiology |
| 125 | + acquisition was started. |
| 126 | +
|
| 127 | + Parameters |
| 128 | + ---------- |
| 129 | + dataset : describes experiment timing |
| 130 | + permissive : If True, None will be returned if timestamps are not found. If |
| 131 | + False, a KeyError will be raised |
| 132 | +
|
| 133 | + Returns |
| 134 | + ------- |
| 135 | + timestamps (floating point; seconds; relative to experiment start) |
| 136 | + or None. If None, no timestamps were found in this sync dataset. |
| 137 | +
|
| 138 | + Notes |
| 139 | + ----- |
| 140 | + Ophys frame timestamps can be recorded before acquisition start when |
| 141 | + experimenters are setting up the recording session. These do not |
| 142 | + correspond to acquired ophys frames. |
| 143 | +
|
| 144 | + """ |
| 145 | + return dataset.get_edges( |
| 146 | + "rising", ["2p_trigger", "acq_trigger"], "seconds", permissive) |
| 147 | + |
| 148 | + |
| 149 | +def get_eye_tracking( |
| 150 | + dataset: SyncDataset, |
| 151 | + permissive: bool = False |
| 152 | +) -> Optional[np.ndarray]: |
| 153 | + """ Report the timestamps of each frame of the eye tracking video |
| 154 | +
|
| 155 | + Parameters |
| 156 | + ---------- |
| 157 | + dataset : describes experiment timing |
| 158 | + permissive : If True, None will be returned if timestamps are not found. If |
| 159 | + False, a KeyError will be raised |
| 160 | +
|
| 161 | + Returns |
| 162 | + ------- |
| 163 | + array of timestamps (floating point; seconds; relative to experiment start) |
| 164 | + or None. If None, no eye tracking timestamps were found in this sync |
| 165 | + dataset. |
| 166 | +
|
| 167 | + """ |
| 168 | + return dataset.get_edges( |
| 169 | + "rising", ["cam2_exposure", "eye_tracking"], "seconds", permissive) |
| 170 | + |
| 171 | + |
| 172 | +def get_behavior_monitoring( |
| 173 | + dataset: SyncDataset, |
| 174 | + permissive: bool = False |
| 175 | +) -> Optional[np.ndarray]: |
| 176 | + """ Report the timestamps of each frame of the behavior |
| 177 | + monitoring video |
| 178 | +
|
| 179 | + Parameters |
| 180 | + ---------- |
| 181 | + dataset : describes experiment timing |
| 182 | + permissive : If True, None will be returned if timestamps are not found. If |
| 183 | + False, a KeyError will be raised |
| 184 | +
|
| 185 | + Returns |
| 186 | + ------- |
| 187 | + array of timestamps (floating point; seconds; relative to experiment start) |
| 188 | + or None. If None, no behavior monitoring timestamps were found in this |
| 189 | + sync dataset. |
| 190 | +
|
| 191 | + """ |
| 192 | + return dataset.get_edges( |
| 193 | + "rising", ["cam1_exposure", "behavior_monitoring"], "seconds", |
| 194 | + permissive) |
| 195 | + |
| 196 | + |
| 197 | +def get_sync_data( |
| 198 | + sync_path: str, |
| 199 | + permissive: bool = False |
| 200 | +) -> Dict[str, Union[List, np.ndarray, None]]: |
| 201 | + """ Convenience function for extracting several timestamp arrays from a |
| 202 | + sync file. |
| 203 | +
|
| 204 | + Parameters |
| 205 | + ---------- |
| 206 | + sync_path : The hdf5 file here ought to be a Visual Behavior sync output |
| 207 | + file. See allensdk.brain_observatory.sync_dataset for more details of |
| 208 | + this format. |
| 209 | + permissive : If True, None will be returned if timestamps are not found. If |
| 210 | + False, a KeyError will be raised |
18 | 211 | |
19 | | - # use rising edge for Scientifica, falling edge for Nikon http://confluence.corp.alleninstitute.org/display/IT/Ophys+Time+Sync |
20 | | - # 2P vsyncs |
21 | | - vs2p_r = sync_dataset.get_rising_edges('2p_vsync') |
22 | | - vs2p_f = sync_dataset.get_falling_edges('2p_vsync') # new sync may be able to do units = 'sec', so conversion can be skipped |
23 | | - frames_2p = vs2p_r / sample_freq |
24 | | - vs2p_fsec = vs2p_f / sample_freq |
25 | | - |
26 | | - stimulus_times_no_monitor_delay = sync_dataset.get_falling_edges('stim_vsync') / sample_freq |
27 | | - |
28 | | - if 'lick_times' in meta_data['line_labels']: |
29 | | - lick_times = sync_dataset.get_rising_edges('lick_1') / sample_freq |
30 | | - elif 'lick_sensor' in meta_data['line_labels']: |
31 | | - lick_times = sync_dataset.get_rising_edges('lick_sensor') / sample_freq |
32 | | - else: |
33 | | - lick_times = None |
34 | | - if '2p_trigger' in meta_data['line_labels']: |
35 | | - trigger = sync_dataset.get_rising_edges('2p_trigger') / sample_freq |
36 | | - elif 'acq_trigger' in meta_data['line_labels']: |
37 | | - trigger = sync_dataset.get_rising_edges('acq_trigger') / sample_freq |
38 | | - if 'stim_photodiode' in meta_data['line_labels']: |
39 | | - a = sync_dataset.get_rising_edges('stim_photodiode') / sample_freq |
40 | | - b = sync_dataset.get_falling_edges('stim_photodiode') / sample_freq |
41 | | - stim_photodiode = sorted(list(a)+list(b)) |
42 | | - elif 'photodiode' in meta_data['line_labels']: |
43 | | - a = sync_dataset.get_rising_edges('photodiode') / sample_freq |
44 | | - b = sync_dataset.get_falling_edges('photodiode') / sample_freq |
45 | | - stim_photodiode = sorted(list(a)+list(b)) |
46 | | - if 'cam2_exposure' in meta_data['line_labels']: |
47 | | - eye_tracking = sync_dataset.get_rising_edges('cam2_exposure') / sample_freq |
48 | | - elif 'eye_tracking' in meta_data['line_labels']: |
49 | | - eye_tracking = sync_dataset.get_rising_edges('eye_tracking') / sample_freq |
50 | | - if 'cam1_exposure' in meta_data['line_labels']: |
51 | | - behavior_monitoring = sync_dataset.get_rising_edges('cam1_exposure') / sample_freq |
52 | | - elif 'behavior_monitoring' in meta_data['line_labels']: |
53 | | - behavior_monitoring = sync_dataset.get_rising_edges('behavior_monitoring') / sample_freq |
54 | | - |
55 | | - sync_data = {'ophys_frames': frames_2p, |
56 | | - 'lick_times': lick_times, |
57 | | - 'ophys_trigger': trigger, |
58 | | - 'eye_tracking': eye_tracking, |
59 | | - 'behavior_monitoring': behavior_monitoring, |
60 | | - 'stim_photodiode': stim_photodiode, |
61 | | - 'stimulus_times_no_delay': stimulus_times_no_monitor_delay, |
62 | | - } |
63 | | - |
64 | | - return sync_data |
| 212 | + Returns |
| 213 | + ------- |
| 214 | + A dictionary with the following keys. All timestamps in seconds: |
| 215 | + ophys_frames : timestamps of each optical physiology frame |
| 216 | + lick_times : timestamps of each detected lick |
| 217 | + ophys_trigger : The time at which ophys acquisition was started |
| 218 | + eye_tracking : timestamps of each eye tracking video frame |
| 219 | + behavior_monitoring : timestamps of behavior monitoring video frame |
| 220 | + stim_photodiode : timestamps of each photodiode transition |
| 221 | + stimulus_times_no_delay : raw stimulus frame timestamps |
| 222 | + Some values may be None. This indicates that the corresponding timestamps |
| 223 | + were not located in this sync file. |
| 224 | +
|
| 225 | + """ |
| 226 | + |
| 227 | + sync_dataset = SyncDataset(sync_path) |
| 228 | + return { |
| 229 | + 'ophys_frames': get_ophys_frames(sync_dataset, permissive), |
| 230 | + 'lick_times': get_lick_times(sync_dataset, permissive), |
| 231 | + 'ophys_trigger': get_trigger(sync_dataset, permissive), |
| 232 | + 'eye_tracking': get_eye_tracking(sync_dataset, permissive), |
| 233 | + 'behavior_monitoring': get_behavior_monitoring(sync_dataset, permissive), |
| 234 | + 'stim_photodiode': get_stim_photodiode(sync_dataset, permissive), |
| 235 | + 'stimulus_times_no_delay': get_raw_stimulus_frames(sync_dataset, permissive) |
| 236 | + } |
65 | 237 |
|
66 | 238 |
|
67 | 239 | def frame_time_offset(data: Dict[str, Any]) -> float: |
|
0 commit comments