From b9ac1c402619a6e415118f51b27c4f28f67d8d4d Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Tue, 24 Sep 2024 13:38:39 +0100 Subject: [PATCH 1/9] ensure camera times have correct length for plotting --- ibllib/io/extractors/video_motion.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ibllib/io/extractors/video_motion.py b/ibllib/io/extractors/video_motion.py index 6ca26863b..8a27643cd 100644 --- a/ibllib/io/extractors/video_motion.py +++ b/ibllib/io/extractors/video_motion.py @@ -466,12 +466,14 @@ def fix_keys(alf_object): # nans but if this is too many we reject the wheel alignment based on the qc self.ttl_times = self.ttls self.times = np.r_[self.ttl_times, np.full((np.abs(self.tdiff)), np.nan)] + self.camera_times = np.r_[self.camera_times, np.full((np.abs(self.tdiff)), np.nan)] self.short_flag = True elif self.tdiff > 0: # In this case there are more ttls than camera frames. This happens often, for now we remove the first # tdiff ttls from the ttls self.ttl_times = self.ttls[self.tdiff:] self.times = self.ttls[self.tdiff:] + self.camera_times = self.camera_times[self.tdiff:] self.short_flag = False # Compute the frame rate of the camera From 080b148b39ae0a4b30025e872718959d60d86227 Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Tue, 24 Sep 2024 13:39:15 +0100 Subject: [PATCH 2/9] ensure qc is updated on alyx --- ibllib/pipes/video_tasks.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ibllib/pipes/video_tasks.py b/ibllib/pipes/video_tasks.py index 0edd41b8d..969a72697 100644 --- a/ibllib/pipes/video_tasks.py +++ b/ibllib/pipes/video_tasks.py @@ -190,7 +190,8 @@ def run_qc(self, camera_data=None, update=True): if camera_data is None: camera_data, _ = self.extract_camera(save=False) qc = run_camera_qc( - self.session_path, self.cameras, one=self.one, camlog=True, sync_collection=self.sync_collection, sync_type=self.sync) + self.session_path, self.cameras, one=self.one, camlog=True, sync_collection=self.sync_collection, sync_type=self.sync, + update=update) return qc def _run(self, update=True, **kwargs): @@ -308,7 +309,8 @@ def run_qc(self, camera_data=None, update=True): if camera_data is None: camera_data, _ = self.extract_camera(save=False) qc = run_camera_qc( - self.session_path, self.cameras, one=self.one, sync_collection=self.sync_collection, sync_type=self.sync) + self.session_path, self.cameras, one=self.one, sync_collection=self.sync_collection, sync_type=self.sync, + update=update) return qc def _run(self, update=True, **kwargs): From b07f792b4f68d76fca242ccdb4ca36101e282ee6 Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Tue, 24 Sep 2024 13:53:35 +0100 Subject: [PATCH 3/9] populate qc with string --- ibllib/qc/camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibllib/qc/camera.py b/ibllib/qc/camera.py index 433f31ce6..970dbcbdf 100644 --- a/ibllib/qc/camera.py +++ b/ibllib/qc/camera.py @@ -421,7 +421,7 @@ def is_metric(x): if update: extended = { - k: spec.QC.NOT_SET if v is None else v + k: spec.QC.NOT_SET.name if v is None else v.name for k, v in self.metrics.items() } self.update_extended_qc(extended) From 598aeebbb80c3d8ef476491b5efac4da7c192e2b Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Tue, 24 Sep 2024 16:41:27 +0100 Subject: [PATCH 4/9] account for tuple qc results --- ibllib/qc/camera.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ibllib/qc/camera.py b/ibllib/qc/camera.py index 970dbcbdf..dedd6c9a5 100644 --- a/ibllib/qc/camera.py +++ b/ibllib/qc/camera.py @@ -420,10 +420,15 @@ def is_metric(x): outcome = max(map(spec.QC.validate, values)) if update: - extended = { - k: spec.QC.NOT_SET.name if v is None else v.name - for k, v in self.metrics.items() - } + extended = dict() + for k, v in self.metrics.items(): + if v is None: + extended[k] = spec.QC.NOT_SET.name + elif isinstance(v, tuple): + extended[k] = tuple(i.name if isinstance(i, spec.QC) else i for i in v) + else: + extended[k] = v.name + self.update_extended_qc(extended) self.update(outcome, namespace) return outcome, self.metrics From 6470b5eb1302e4140334121b9e83bf69eb427991 Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Tue, 24 Sep 2024 21:33:18 +0100 Subject: [PATCH 5/9] account for no camera times --- ibllib/io/extractors/video_motion.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ibllib/io/extractors/video_motion.py b/ibllib/io/extractors/video_motion.py index 8a27643cd..fe27978fc 100644 --- a/ibllib/io/extractors/video_motion.py +++ b/ibllib/io/extractors/video_motion.py @@ -442,7 +442,6 @@ def fix_keys(alf_object): # Compute wheel velocity self.wheel_vel, _ = wh.velocity_filtered(wheel_pos, 1000) # Load in original camera times - self.camera_times = alfio.load_file_content(next(alf_path.rglob(f'_ibl_{self.label}Camera.times*.npy'))) self.camera_path = str(next(self.session_path.joinpath('raw_video_data').glob(f'_iblrig_{self.label}Camera.raw*.mp4'))) self.camera_meta = vidio.get_video_meta(self.camera_path) @@ -461,19 +460,25 @@ def fix_keys(alf_object): # Check if the ttl and video sizes match up self.tdiff = self.ttls.size - self.camera_meta['length'] + # Load in original camera times if available otherwise set to ttls + camera_times = next(alf_path.rglob(f'_ibl_{self.label}Camera.times*.npy'), None) + self.camera_times = alfio.load_file_content(camera_times) if camera_times else self.ttls + if self.tdiff < 0: # In this case there are fewer ttls than camera frames. This is not ideal, for now we pad the ttls with # nans but if this is too many we reject the wheel alignment based on the qc self.ttl_times = self.ttls self.times = np.r_[self.ttl_times, np.full((np.abs(self.tdiff)), np.nan)] - self.camera_times = np.r_[self.camera_times, np.full((np.abs(self.tdiff)), np.nan)] + if self.camera_times.size != self.camera_meta['length']: + self.camera_times = np.r_[self.camera_times, np.full((np.abs(self.tdiff)), np.nan)] self.short_flag = True elif self.tdiff > 0: # In this case there are more ttls than camera frames. This happens often, for now we remove the first # tdiff ttls from the ttls self.ttl_times = self.ttls[self.tdiff:] self.times = self.ttls[self.tdiff:] - self.camera_times = self.camera_times[self.tdiff:] + if self.camera_times.size != self.camera_meta['length']: + self.camera_times = self.camera_times[self.tdiff:] self.short_flag = False # Compute the frame rate of the camera From 87ccc96d0dacc8a26684863d7ee8b90b7bf13d5f Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Tue, 24 Sep 2024 22:20:44 +0100 Subject: [PATCH 6/9] pass in output_files to assert_expected --- ibllib/pipes/video_tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ibllib/pipes/video_tasks.py b/ibllib/pipes/video_tasks.py index 969a72697..8e7f02ec9 100644 --- a/ibllib/pipes/video_tasks.py +++ b/ibllib/pipes/video_tasks.py @@ -349,7 +349,7 @@ def signature(self): 'input_files': [(f'_iblrig_{cam}Camera.raw.mp4', self.device_collection, True) for cam in self.cameras], 'output_files': [(f'_ibl_{cam}Camera.dlc.pqt', 'alf', True) for cam in self.cameras] + [(f'{cam}Camera.ROIMotionEnergy.npy', 'alf', True) for cam in self.cameras] + - [(f'{cam}ROIMotionEnergy.position.npy', 'alf', True)for cam in self.cameras] + [(f'{cam}ROIMotionEnergy.position.npy', 'alf', True) for cam in self.cameras] } return signature @@ -524,7 +524,7 @@ def _run(self, overwrite=True, run_qc=True, plot_qc=True): """ # Check if output files exist locally - exist, output_files = self.assert_expected(self.signature['output_files'], silent=True) + exist, output_files = self.assert_expected(self.output_files, silent=True) if exist and not overwrite: _logger.warning('EphysPostDLC outputs exist and overwrite=False, skipping computations of outputs.') else: From 29ca0c0dbc1c8f9ae05911d9f07d4f2bc25e5e2d Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Wed, 25 Sep 2024 11:49:21 +0100 Subject: [PATCH 7/9] update wiring signature --- ibllib/pipes/video_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibllib/pipes/video_tasks.py b/ibllib/pipes/video_tasks.py index 8e7f02ec9..8bed2f311 100644 --- a/ibllib/pipes/video_tasks.py +++ b/ibllib/pipes/video_tasks.py @@ -285,7 +285,7 @@ def signature(self): [(f'_{self.sync_namespace}_sync.channels.npy', self.sync_collection, True), (f'_{self.sync_namespace}_sync.polarities.npy', self.sync_collection, True), (f'_{self.sync_namespace}_sync.times.npy', self.sync_collection, True), - ('*.wiring.json', self.sync_collection, True), + (f'_{self.sync_namespace}_*.wiring.json', self.sync_collection, True), ('*wheel.position.npy', 'alf', False), ('*wheel.timestamps.npy', 'alf', False), ('*experiment.description*', '', False)], From 77f2e97f78747e279aae452dfb6ec572c8af9703 Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Wed, 25 Sep 2024 11:57:03 +0100 Subject: [PATCH 8/9] add meta file --- ibllib/pipes/video_tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ibllib/pipes/video_tasks.py b/ibllib/pipes/video_tasks.py index 8bed2f311..9a163a4c5 100644 --- a/ibllib/pipes/video_tasks.py +++ b/ibllib/pipes/video_tasks.py @@ -285,7 +285,8 @@ def signature(self): [(f'_{self.sync_namespace}_sync.channels.npy', self.sync_collection, True), (f'_{self.sync_namespace}_sync.polarities.npy', self.sync_collection, True), (f'_{self.sync_namespace}_sync.times.npy', self.sync_collection, True), - (f'_{self.sync_namespace}_*.wiring.json', self.sync_collection, True), + (f'_{self.sync_namespace}_*.wiring.json', self.sync_collection, False), + (f'_{self.sync_namespace}_*.meta', self.sync_collection, True), ('*wheel.position.npy', 'alf', False), ('*wheel.timestamps.npy', 'alf', False), ('*experiment.description*', '', False)], From 637c1fb7338101cf7cd3f125a63155485aeced0a Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Thu, 26 Sep 2024 11:44:53 +0100 Subject: [PATCH 9/9] add motion energy and wheel to postdlc signatures --- ibllib/pipes/video_tasks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ibllib/pipes/video_tasks.py b/ibllib/pipes/video_tasks.py index 9a163a4c5..3c1f4d19f 100644 --- a/ibllib/pipes/video_tasks.py +++ b/ibllib/pipes/video_tasks.py @@ -507,8 +507,11 @@ def signature(self): # In particular the raw videos don't need to be downloaded as they can be streamed [(f'_iblrig_{cam}Camera.raw.mp4', self.device_collection, True) for cam in self.cameras] + [(f'{cam}ROIMotionEnergy.position.npy', 'alf', False) for cam in self.cameras] + + [(f'{cam}Camera.ROIMotionEnergy.npy', 'alf', False) for cam in self.cameras] + # The trials table is used in the DLC QC, however this is not an essential dataset - [('_ibl_trials.table.pqt', self.trials_collection, False)], + [('_ibl_trials.table.pqt', self.trials_collection, False), + ('_ibl_wheel.position.npy', self.trials_collection, False), + ('_ibl_wheel.timestamps.npy', self.trials_collection, False)], 'output_files': [(f'_ibl_{cam}Camera.features.pqt', 'alf', True) for cam in self.cameras] + [('licks.times.npy', 'alf', True)] }