From 4819be2526d3cb5d092327e88f03d5ba5a095547 Mon Sep 17 00:00:00 2001 From: pauladkisson Date: Fri, 15 Sep 2023 15:57:01 -0700 Subject: [PATCH] refactored init to manually accept args rather than automatically parsing from metadata to support old testing file --- .../scanimagetiffimagingextractor.py | 60 +++++++++++++++---- tests/temp_test_scanimage.py | 30 ++++++++-- tests/test_scan_image_tiff.py | 2 +- 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/roiextractors/extractors/tiffimagingextractors/scanimagetiffimagingextractor.py b/src/roiextractors/extractors/tiffimagingextractors/scanimagetiffimagingextractor.py index f134f53a..963867e4 100644 --- a/src/roiextractors/extractors/tiffimagingextractors/scanimagetiffimagingextractor.py +++ b/src/roiextractors/extractors/tiffimagingextractors/scanimagetiffimagingextractor.py @@ -38,6 +38,30 @@ def extract_extra_metadata( return extra_metadata +def parse_metadata(metadata): + """Parse metadata dictionary to extract relevant information. + + Notes + ----- + SI.hChannels.channelsActive = '[1;2;...;N]' where N is the number of active channels. + SI.hChannels.channelName = "{'channel_name_1' 'channel_name_2' ... 'channel_name_M'}" + where M is the number of channels (active or not). + """ + sampling_frequency = float(metadata["SI.hRoiManager.scanVolumeRate"]) + num_channels = len(metadata["SI.hChannels.channelsActive"].split(";")) + num_planes = int(metadata["SI.hStackManager.numSlices"]) + frames_per_slice = int(metadata["SI.hStackManager.framesPerSlice"]) + channel_names = metadata["SI.hChannels.channelName"].split("'")[1::2][:num_channels] + metadata_parsed = dict( + sampling_frequency=sampling_frequency, + num_channels=num_channels, + num_planes=num_planes, + frames_per_slice=frames_per_slice, + channel_names=channel_names, + ) + return metadata_parsed + + class ScanImageTiffImagingExtractor(ImagingExtractor): """Specialized extractor for reading TIFF files produced via ScanImage.""" @@ -45,7 +69,15 @@ class ScanImageTiffImagingExtractor(ImagingExtractor): is_writable = True mode = "file" - def __init__(self, file_path: PathType) -> None: + def __init__( + self, + file_path: PathType, + sampling_frequency: float, + num_channels: Optional[int] = 1, + num_planes: Optional[int] = 1, + frames_per_slice: Optional[int] = 1, + channel_names: Optional[list] = None, + ) -> None: """Create a ScanImageTiffImagingExtractor instance from a TIFF file produced by ScanImage. The underlying data is stored in a round-robin format collapsed into 3 dimensions (frames, rows, columns). @@ -62,24 +94,29 @@ def __init__(self, file_path: PathType) -> None: ---------- file_path : PathType Path to the TIFF file. + sampling_frequency : float + Sampling frequency of each plane (scanVolumeRate) in Hz. + num_channels : int, optional + Number of active channels that were acquired (default=1). + num_planes : int, optional + Number of depth planes that were acquired (default=1). + frames_per_slice : int, optional + Number of frames per depth plane that were acquired (default=1). + channel_names : list, optional + Names of the channels that were acquired (default=None). """ super().__init__() self.file_path = Path(file_path) - ScanImageTiffReader = _get_scanimage_reader() - extra_metadata = extract_extra_metadata(file_path) - # SI.hChannels.channelsActive = '[1;2;...;N]' where N is the number of active channels - self._num_channels = len(extra_metadata["SI.hChannels.channelsActive"].split(";")) - self._num_planes = int(extra_metadata["SI.hStackManager.numSlices"]) - frames_per_slice = int(extra_metadata["SI.hStackManager.framesPerSlice"]) + self._sampling_frequency = sampling_frequency + self.metadata = extract_extra_metadata(file_path) + self._num_channels = num_channels + self._num_planes = num_planes if frames_per_slice != 1: raise NotImplementedError( "Extractor cannot handle multiple frames per slice. Please raise an issue to request this feature: " "https://github.com/catalystneuro/roiextractors/issues " ) - self._sampling_frequency = float(extra_metadata["SI.hRoiManager.scanVolumeRate"]) - # SI.hChannels.channelName = "{'channel_name_1' 'channel_name_2' ... 'channel_name_N'}" - # where N is the number of channels (active or not) - self._channel_names = extra_metadata["SI.hChannels.channelName"].split("'")[1::2][: self._num_channels] + self._channel_names = channel_names valid_suffixes = [".tiff", ".tif", ".TIFF", ".TIF"] if self.file_path.suffix not in valid_suffixes: @@ -88,6 +125,7 @@ def __init__(self, file_path: PathType) -> None: f"Suffix ({self.file_path.suffix}) is not of type {suffix_string}! " f"The {self.extractor_name}Extractor may not be appropriate for the file." ) + ScanImageTiffReader = _get_scanimage_reader() with ScanImageTiffReader(str(self.file_path)) as io: shape = io.shape() # [frames, rows, columns] if len(shape) == 3: diff --git a/tests/temp_test_scanimage.py b/tests/temp_test_scanimage.py index 16cc2c81..81e2e4f3 100644 --- a/tests/temp_test_scanimage.py +++ b/tests/temp_test_scanimage.py @@ -1,20 +1,42 @@ # scratch file to test scanimage tiff extractor from roiextractors import ScanImageTiffImagingExtractor +from roiextractors.extractors.tiffimagingextractors.scanimagetiffimagingextractor import ( + extract_extra_metadata, + parse_metadata, +) def main(): + example_test = "/Users/pauladkisson/Documents/CatalystNeuro/ROIExtractors/ophys_testing_data/imaging_datasets/ScanImage/sample_scanimage_version_3_8.tiff" example_holo = "/Volumes/T7/CatalystNeuro/NWB/MouseV1/raw-tiffs/2ret/20230119_w57_1_2ret_00001.tif" example_single = "/Users/pauladkisson/Documents/CatalystNeuro/ROIExtractors/ophys_testing_data/imaging_datasets/ScanImage/scanimage_20220801_single.tif" example_volume = "/Users/pauladkisson/Documents/CatalystNeuro/ROIExtractors/ophys_testing_data/imaging_datasets/ScanImage/scanimage_20220801_volume.tif" example_multivolume = "/Users/pauladkisson/Documents/CatalystNeuro/ROIExtractors/ophys_testing_data/imaging_datasets/ScanImage/scanimage_20220801_multivolume.tif" - extractor = ScanImageTiffImagingExtractor(file_path=example_holo) + extractor = ScanImageTiffImagingExtractor(file_path=example_test, sampling_frequency=30.0) + print("Example test file loads!") + + metadata = extract_extra_metadata(example_holo) + metadata_parsed = parse_metadata(metadata) + extractor = ScanImageTiffImagingExtractor(file_path=example_holo, **metadata_parsed) print("Example holographic file loads!") - extractor = ScanImageTiffImagingExtractor(file_path=example_single) + + metadata = extract_extra_metadata(example_single) + metadata_parsed = parse_metadata(metadata) + metadata_parsed["frames_per_slice"] = 1 # Multiple frames per slice is not supported yet + extractor = ScanImageTiffImagingExtractor(file_path=example_single, **metadata_parsed) print("Example single-plane file loads!") - extractor = ScanImageTiffImagingExtractor(file_path=example_volume) + + metadata = extract_extra_metadata(example_volume) + metadata_parsed = parse_metadata(metadata) + metadata_parsed["frames_per_slice"] = 1 # Multiple frames per slice is not supported yet + extractor = ScanImageTiffImagingExtractor(file_path=example_volume, **metadata_parsed) print("Example volume file loads!") - extractor = ScanImageTiffImagingExtractor(file_path=example_multivolume) + + metadata = extract_extra_metadata(example_multivolume) + metadata_parsed = parse_metadata(metadata) + metadata_parsed["frames_per_slice"] = 1 # Multiple frames per slice is not supported yet + extractor = ScanImageTiffImagingExtractor(file_path=example_multivolume, **metadata_parsed) print("Example multivolume file loads!") diff --git a/tests/test_scan_image_tiff.py b/tests/test_scan_image_tiff.py index ded5f759..a0d7c25d 100644 --- a/tests/test_scan_image_tiff.py +++ b/tests/test_scan_image_tiff.py @@ -17,7 +17,7 @@ class TestScanImageTiffExtractor(TestCase): def setUpClass(cls): cls.file_path = OPHYS_DATA_PATH / "imaging_datasets" / "Tif" / "sample_scanimage.tiff" cls.tmpdir = Path(mkdtemp()) - cls.imaging_extractor = ScanImageTiffImagingExtractor(file_path=cls.file_path) + cls.imaging_extractor = ScanImageTiffImagingExtractor(file_path=cls.file_path, sampling_frequency=30.0) with ScanImageTiffReader(filename=str(cls.imaging_extractor.file_path)) as io: cls.data = io.data()