From 5f3980e051d2eabd90dbada48c440513431cc8c0 Mon Sep 17 00:00:00 2001 From: pauladkisson Date: Tue, 26 Sep 2023 14:56:41 -0700 Subject: [PATCH] Added tests for scanimage utils --- .../scanimagetiff_utils.py | 15 +- tests/test_scanimage_utils.py | 128 ++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 tests/test_scanimage_utils.py diff --git a/src/roiextractors/extractors/tiffimagingextractors/scanimagetiff_utils.py b/src/roiextractors/extractors/tiffimagingextractors/scanimagetiff_utils.py index ac3d8ef7..49f1c981 100644 --- a/src/roiextractors/extractors/tiffimagingextractors/scanimagetiff_utils.py +++ b/src/roiextractors/extractors/tiffimagingextractors/scanimagetiff_utils.py @@ -27,7 +27,7 @@ def extract_extra_metadata( Notes ----- - Known to work on SI versions v3.8.0, v2019bR0, and v2022.0.0. + Known to work on SI versions v3.8.0, v2019bR0, v2022.0.0, and v2023.0.0 """ ScanImageTiffReader = _get_scanimage_reader() io = ScanImageTiffReader(str(file_path)) @@ -102,7 +102,7 @@ def parse_metadata(metadata: dict) -> dict: Notes ----- - Known to work on SI versions v2019bR0 and v2022.0.0. + Known to work on SI versions v2019bR0, v2022.0.0, and v2023.0.0. Fails on v3.8.0. SI.hChannels.channelsActive = string of MATLAB-style vector with channel integers (see parse_matlab_vector). SI.hChannels.channelName = "{'channel_name_1' 'channel_name_2' ... 'channel_name_M'}" where M is the number of channels (active or not). @@ -167,9 +167,19 @@ def extract_timestamps_from_file(file_path: PathType) -> np.ndarray: ------- timestamps : numpy.ndarray Array of frame timestamps in seconds. + + Raises + ------ + AssertionError + If the frame timestamps are not found in the TIFF file. + + Notes + ----- + Known to work on SI versions v2019bR0, v2022.0.0, and v2023.0.0. Fails on v3.8.0. """ ScanImageTiffReader = _get_scanimage_reader() io = ScanImageTiffReader(str(file_path)) + assert "frameTimestamps_sec" in io.description(iframe=0), "frameTimestamps_sec not found in TIFF file" num_frames = io.shape()[0] timestamps = np.zeros(num_frames) for iframe in range(num_frames): @@ -179,4 +189,5 @@ def extract_timestamps_from_file(file_path: PathType) -> np.ndarray: if "frameTimestamps_sec" in line: timestamps[iframe] = float(line.split("=")[1].strip()) break + return timestamps diff --git a/tests/test_scanimage_utils.py b/tests/test_scanimage_utils.py new file mode 100644 index 00000000..4b65b0cf --- /dev/null +++ b/tests/test_scanimage_utils.py @@ -0,0 +1,128 @@ +import pytest +from numpy.testing import assert_array_equal +from ScanImageTiffReader import ScanImageTiffReader +from roiextractors.extractors.tiffimagingextractors.scanimagetiff_utils import ( + _get_scanimage_reader, + extract_extra_metadata, + parse_matlab_vector, + parse_metadata, + parse_metadata_v3_8, + extract_timestamps_from_file, +) + +from .setup_paths import OPHYS_DATA_PATH + + +def test_get_scanimage_reader(): + ScanImageTiffReader = _get_scanimage_reader() + assert ScanImageTiffReader is not None + + +@pytest.mark.parametrize( + "filename, expected_key, expected_value", + [ + ("sample_scanimage_version_3_8.tiff", "state.software.version", "3.8"), + ("scanimage_20220801_single.tif", "SI.VERSION_MAJOR", "2022"), + ("scanimage_20220923_roi.tif", "SI.VERSION_MAJOR", "2023"), + ], +) +def test_extract_extra_metadata(filename, expected_key, expected_value): + file_path = OPHYS_DATA_PATH / "imaging_datasets" / "ScanImage" / filename + metadata = extract_extra_metadata(file_path) + assert metadata[expected_key] == expected_value + + +@pytest.mark.parametrize( + "matlab_vector, expected_vector", + [ + ("[1 2 3]", [1, 2, 3]), + ("[1,2,3]", [1, 2, 3]), + ("[1, 2, 3]", [1, 2, 3]), + ("[1;2;3]", [1, 2, 3]), + ("[1; 2; 3]", [1, 2, 3]), + ], +) +def test_parse_matlab_vector(matlab_vector, expected_vector): + vector = parse_matlab_vector(matlab_vector) + assert vector == expected_vector + + +@pytest.mark.parametrize( + "filename, expected_metadata", + [ + ( + "scanimage_20220801_single.tif", + { + "sampling_frequency": 15.2379, + "num_channels": 1, + "num_planes": 20, + "frames_per_slice": 24, + "channel_names": ["Channel 1"], + }, + ), + ( + "scanimage_20220923_roi.tif", + { + "sampling_frequency": 29.1248, + "num_channels": 2, + "num_planes": 2, + "frames_per_slice": 2, + "channel_names": ["Channel 1", "Channel 4"], + }, + ), + ], +) +def test_parse_metadata(filename, expected_metadata): + file_path = OPHYS_DATA_PATH / "imaging_datasets" / "ScanImage" / filename + metadata = extract_extra_metadata(file_path) + metadata = parse_metadata(metadata) + assert metadata == expected_metadata + + +def test_parse_metadata_v3_8(): + file_path = OPHYS_DATA_PATH / "imaging_datasets" / "ScanImage" / "sample_scanimage_version_3_8.tiff" + metadata = extract_extra_metadata(file_path) + metadata = parse_metadata_v3_8(metadata) + expected_metadata = {"sampling_frequency": 3.90625, "num_channels": 1, "num_planes": 1} + assert metadata == expected_metadata + + +@pytest.mark.parametrize( + "filename, expected_timestamps", + [ + ("scanimage_20220801_single.tif", [0.45951611, 0.98468446, 1.50985974]), + ( + "scanimage_20220923_roi.tif", + [ + 0.0, + 0.0, + 0.03433645, + 0.03433645, + 1.04890375, + 1.04890375, + 1.08324025, + 1.08324025, + 2.12027815, + 2.12027815, + 2.15461465, + 2.15461465, + 2.7413649, + 2.7413649, + 2.7757014, + 2.7757014, + 3.23987545, + 3.23987545, + 3.27421195, + 3.27421195, + 3.844804, + 3.844804, + 3.87914055, + 3.87914055, + ], + ), + ], +) +def test_extract_timestamps_from_file(filename, expected_timestamps): + file_path = OPHYS_DATA_PATH / "imaging_datasets" / "ScanImage" / filename + timestamps = extract_timestamps_from_file(file_path) + assert_array_equal(timestamps, expected_timestamps)