def test_module(input_json): with patch("sys.argv", ["test_run", input_json]): with patch("logging.info"): run_ophys_time_sync.main() with open(input_json, "r") as f: input_data = json.load(f) output_file = input_data.pop("output_file") assert os.path.exists(output_file) input_data.pop("ophys_experiment_id") sync_file = input_data.pop("sync_file") aligner = ts.OphysTimeAligner(sync_file, **input_data) with h5py.File(output_file) as f: t, d = aligner.corrected_ophys_timestamps assert np.all(t == f['twop_vsync_fall'].value) assert np.all(d == f['ophys_delta'].value) st, sd, stim_delay = aligner.corrected_stim_timestamps align = ts.get_alignment_array(t, st) assert np.allclose(align, f['stimulus_alignment'].value, equal_nan=True) assert np.all(sd == f['stim_delta'].value) et, ed = aligner.corrected_eye_video_timestamps align = ts.get_alignment_array(et, t, int_method=np.ceil) assert np.allclose(align, f['eye_tracking_alignment'].value, equal_nan=True) assert np.all(ed == f['eye_delta'].value) bt, bd = aligner.corrected_behavior_video_timestamps align = ts.get_alignment_array(bt, t, int_method=np.ceil) assert np.allclose(align, f['body_camera_alignment'].value, equal_nan=True)
def test_regression_stim_timestamps(nikon_input, scientifica_input): for input_data in [nikon_input, scientifica_input]: sync_file = input_data.pop("sync_file") aligner = ts.OphysTimeAligner(sync_file, **input_data) freq = aligner.dataset.meta_data['ni_daq']['counter_output_freq'] old_times = calculate_stim_vsync_fall(aligner.dataset, freq) assert np.allclose(aligner.stim_timestamps, old_times)
def test_get_corrected_ophys_times_nikon(ophys_data_length): true_times = np.arange(6000) with patch.object(ts, "get_keys", return_value=mock_keys): with patch.object(ts.Dataset, "load"): aligner = ts.OphysTimeAligner("test", "NIKONA1RMP") aligner.ophys_data_length = ophys_data_length with patch.object(ts.Dataset, "get_falling_edges", return_value=true_times): with patch.object(ts.Dataset, "get_rising_edges", return_value=[0]): with patch("logging.info") as mock_log: if ophys_data_length is not None and \ ophys_data_length > len(true_times): with pytest.raises(ValueError): times, delta = aligner.corrected_ophys_timestamps else: times, delta = aligner.corrected_ophys_timestamps if ophys_data_length is None: assert np.all(times == true_times) mock_log.assert_called_once() assert delta == 0 elif ophys_data_length != len(true_times): assert np.all(times == true_times[:-delta]) mock_log.assert_called_once() else: assert mock_log.call_count == 0 assert np.all(times == true_times) aligner.scanner = "bad" with pytest.raises(ValueError): aligner.corrected_ophys_timestamps
def test_get_corrected_behavior_times(behavior_data_length): true_times = np.arange(6000) with patch.object(ts, "get_keys", return_value=mock_keys): with patch.object(ts.Dataset, "load"): aligner = ts.OphysTimeAligner("test") aligner.behavior_data_length = behavior_data_length with patch.object(ts.Dataset, "get_falling_edges", return_value=true_times) as mock_falling: with patch("logging.info") as mock_log: times, delta = aligner.corrected_behavior_video_timestamps if behavior_data_length != 6000: mock_log.assert_called_once() else: assert mock_log.call_count == 0 mock_falling.assert_called_once() assert np.all(times == true_times) if behavior_data_length is None: assert delta == 0 else: assert delta == (len(true_times) - behavior_data_length)
def main(): parser = argparse.ArgumentParser("Generate brain observatory alignment.") parser.add_argument('input_json') parser.add_argument('--log-level', default=logging.DEBUG) mod = PipelineModule("Generate brain observatory alignment.", parser) input_data = mod.input_data() experiment_id = input_data.pop("ophys_experiment_id") sync_file = input_data.pop("sync_file") output_file = input_data.pop("output_file") aligner = ts.OphysTimeAligner(sync_file, **input_data) ophys_times, ophys_delta = aligner.corrected_ophys_timestamps stim_times, stim_delta = aligner.corrected_stim_timestamps eye_times, eye_delta = aligner.corrected_eye_video_timestamps beh_times, beh_delta = aligner.corrected_behavior_video_timestamps # stim array is index of ophys frame for each stim frame to match to # so len(stim_times) stim_alignment = ts.get_alignment_array(ophys_times, stim_times) # camera arrays are index of camera frame for each ophys frame ... # cam_nwb_creator depends on this so keeping it that way even though # it makes little sense... len(video_times) eye_alignment = ts.get_alignment_array(eye_times, ophys_times, int_method=np.ceil) behavior_alignment = ts.get_alignment_array(beh_times, ophys_times, int_method=np.ceil) write_output(output_file, ophys_times, stim_alignment, eye_alignment, behavior_alignment, ophys_delta, stim_delta, eye_delta, beh_delta)
def test_regression_valid_2p_timestamps(nikon_input, scientifica_input): sync_file = nikon_input.pop("sync_file") aligner = ts.OphysTimeAligner(sync_file, **nikon_input) freq = aligner.dataset.meta_data['ni_daq']['counter_output_freq'] old_times = calculate_valid_twop_vsync_fall(aligner.dataset, freq) new_times = aligner.ophys_timestamps assert np.allclose(new_times[1:], old_times) # old scientifica used falling edges as timestamps incorrectly sync_file = scientifica_input.pop("sync_file") aligner = ts.OphysTimeAligner(sync_file, **scientifica_input) freq = aligner.dataset.meta_data['ni_daq']['counter_output_freq'] old_times = calculate_valid_twop_vsync_fall(aligner.dataset, freq) new_times = aligner.ophys_timestamps assert len(new_times) - len(old_times) == 1 assert np.all(new_times[1:] < old_times) assert np.all(new_times[2:] > old_times[:-1])
def test_get_corrected_stim_times(stim_data_length, start_delay): true_falling = np.arange(0, 60, 0.01) true_rising = true_falling + 0.005 if start_delay: true_falling[0] -= 3 true_rising[0] -= 3 with patch.object(ts, "get_keys", return_value=mock_keys): with patch.object(ts.Dataset, "load"): aligner = ts.OphysTimeAligner("test") aligner.stim_data_length = stim_data_length with patch.object(ts, "calculate_monitor_delay", return_value=ASSUMED_DELAY): with patch.object(ts.Dataset, "get_falling_edges", return_value=true_falling): with patch.object(ts.Dataset, "get_rising_edges", return_value=true_rising) as mock_rising: with patch("logging.info") as mock_log: times, delta, stim_delay = \ aligner.corrected_stim_timestamps if stim_data_length is None: mock_log.assert_called_once() assert mock_rising.call_count == 0 assert delta == 0 elif stim_data_length != len(true_falling) and start_delay: mock_rising.assert_called_once() assert mock_log.call_count == 2 assert len(times) == len(true_falling) - 1 assert delta == len(true_falling) - 1 - stim_data_length assert np.all(times == true_falling[1:] + ASSUMED_DELAY) elif stim_data_length != len(true_falling): mock_rising.assert_called_once() mock_log.assert_called_once() assert delta == len(true_falling) - stim_data_length assert np.all(times == true_falling + ASSUMED_DELAY) else: assert mock_rising.call_count == 0 assert np.all(times == true_falling + ASSUMED_DELAY) assert mock_log.call_count == 0 assert delta == 0
def test_regression_calculate_stimulus_alignment(nikon_input, scientifica_input): for input_data in [nikon_input, scientifica_input]: sync_file = input_data.pop("sync_file") aligner = ts.OphysTimeAligner(sync_file, **input_data) old_align = calculate_stimulus_alignment(aligner.stim_timestamps, aligner.ophys_timestamps) new_align = ts.get_alignment_array(aligner.ophys_timestamps, aligner.stim_timestamps) # Old alignment assigned simultaneous stim frames to the previous ophys # frame. Methods should only differ when ophys and stim are identical. mismatch = old_align != new_align mis_o = aligner.ophys_timestamps[new_align[mismatch].astype(int)] mis_s = aligner.stim_timestamps[mismatch] assert np.all(mis_o == mis_s) # Occurence of mismatch should be rare assert len(mis_o) < 0.005 * len(aligner.ophys_timestamps)
def main(): parser = argparse.ArgumentParser("Generate brain observatory alignment.") parser.add_argument("input_json", type=str, help="path to input json" ) parser.add_argument("output_json", type=str, nargs="?", help="path to which output json will be written" ) parser.add_argument("--log-level", default=logging.DEBUG) parser.add_argument("--min-stimulus-delay", type=float, default=0.0, help="reject results if monitor delay less than this value (s)" ) parser.add_argument("--max-stimulus-delay", type=float, default=0.07, help="reject results if monitor delay greater than this value (s)" ) mod = PipelineModule("Generate brain observatory alignment.", parser) input_data = mod.input_data() writer = TimeSyncWriter(input_data.get("output_file"), mod.args.output_json) writer.validate_paths() aligner = ts.OphysTimeAligner( input_data.get("sync_file"), scanner=input_data.get("scanner", None), dff_file=input_data.get("dff_file", None), stimulus_pkl=input_data.get("stimulus_pkl", None), eye_video=input_data.get("eye_video", None), behavior_video=input_data.get("behavior_video", None), long_stim_threshold=input_data.get( "long_stim_threshold", ts.LONG_STIM_THRESHOLD ) ) outputs = run_ophys_time_sync( aligner, input_data.get("ophys_experiment_id"), mod.args.min_stimulus_delay, mod.args.max_stimulus_delay ) writer.write(outputs)
def test_regression_calculate_camera_alignment(nikon_input, scientifica_input): for input_data in [nikon_input, scientifica_input]: sync_file = input_data.pop("sync_file") aligner = ts.OphysTimeAligner(sync_file, **input_data) freq = aligner.dataset.meta_data['ni_daq']['counter_output_freq'] old_eye_align = sync_camera_stimulus(aligner.dataset, freq, 2, 1) # old alignment throws out the first ophys timestamp new_eye_align = ts.get_alignment_array(aligner.eye_video_timestamps, aligner.ophys_timestamps[1:], int_method=np.ceil) mismatch = np.where(old_eye_align[:, 0] != new_eye_align) mis_e = \ aligner.eye_video_timestamps[new_eye_align[mismatch].astype(int)] mis_o = aligner.ophys_timestamps[1:][mismatch] mis_o_plus = aligner.ophys_timestamps[1:][(mismatch[0] + 1, )] # New method should only disagree when old method was wrong (old method # set an eye tracking frame to an earlier ophys frame). assert np.all(mis_o < mis_e) assert np.all(mis_o_plus >= mis_e) # Occurence of mismatch should be rare assert len(mis_o) < 0.005 * len(aligner.ophys_timestamps[1:])