Skip to content

Commit

Permalink
added support for multiple frames per slice
Browse files Browse the repository at this point in the history
  • Loading branch information
pauladkisson committed Sep 22, 2023
1 parent 3bc556d commit 36105b9
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ def parse_matlab_vector(matlab_vector: str) -> list:
vector = vector.split(",")
elif " " in vector:
vector = vector.split(" ")
elif len(vector) == 1:
pass
else:
raise ValueError(f"Could not parse vector from {matlab_vector}.")
vector = [int(x.strip()) for x in vector if x != ""]
Expand Down Expand Up @@ -398,10 +400,15 @@ def __init__(
The underlying data is stored in a round-robin format collapsed into 3 dimensions (frames, rows, columns).
I.e. the first frame of each channel and each plane is stored, and then the second frame of each channel and
each plane, etc.
Ex. for 2 channels and 2 planes:
[channel_1_plane_1_frame_1, channel_2_plane_1_frame_1, channel_1_plane_2_frame_1, channel_2_plane_2_frame_1,
channel_1_plane_1_frame_2, channel_2_plane_1_frame_2, channel_1_plane_2_frame_2, channel_2_plane_2_frame_2, ...
channel_1_plane_1_frame_N, channel_2_plane_1_frame_N, channel_1_plane_2_frame_N, channel_2_plane_2_frame_N]
If framesPerSlice > 1, then multiple frames are acquired per slice before moving to the next slice.
Ex. for 2 channels, 2 planes, and 2 framesPerSlice:
```
[channel_1_plane_1_frame_1, channel_2_plane_1_frame_1, channel_1_plane_1_frame_2, channel_2_plane_1_frame_2,
channel_1_plane_2_frame_1, channel_2_plane_2_frame_1, channel_1_plane_2_frame_2, channel_2_plane_2_frame_2,
channel_1_plane_1_frame_3, channel_2_plane_1_frame_3, channel_1_plane_1_frame_4, channel_2_plane_1_frame_4,
channel_1_plane_2_frame_3, channel_2_plane_2_frame_3, channel_1_plane_2_frame_4, channel_2_plane_2_frame_4, ...
channel_1_plane_1_frame_N, channel_2_plane_1_frame_N, channel_1_plane_2_frame_N, channel_2_plane_2_frame_N]
```
This file structured is accessed by ScanImageTiffImagingExtractor for a single channel and plane.
Parameters
Expand Down Expand Up @@ -431,12 +438,6 @@ def __init__(
if plane_name not in self._plane_names:
raise ValueError(f"Plane name ({plane_name}) not found in plane names ({self._plane_names}).")
self.plane = self._plane_names.index(plane_name)
if self._frames_per_slice != 1:
warn(
"Multiple frames per slice have not been tested and may produce incorrect output. "
"Please raise an issue to request this feature: "
"https://github.com/catalystneuro/roiextractors/issues "
)

valid_suffixes = [".tiff", ".tif", ".TIFF", ".TIF"]
if self.file_path.suffix not in valid_suffixes:
Expand All @@ -450,10 +451,13 @@ def __init__(
shape = io.shape() # [frames, rows, columns]
if len(shape) == 3:
self._total_num_frames, self._num_rows, self._num_columns = shape
self._num_raw_per_plane = self._frames_per_slice * self._num_channels
self._num_raw_per_cycle = self._num_raw_per_plane * self._num_planes
self._num_frames = self._total_num_frames // (self._num_planes * self._num_channels)
self._num_cycles = self._total_num_frames // self._num_raw_per_cycle
else:
raise NotImplementedError(
"Extractor cannot handle 4D TIFF data. Please raise an issue to request this feature: "
"Extractor cannot handle 4D ScanImageTiff data. Please raise an issue to request this feature: "
"https://github.com/catalystneuro/roiextractors/issues "
)

Expand Down Expand Up @@ -527,8 +531,13 @@ def get_video(self, start_frame=None, end_frame=None) -> np.ndarray:
raw_end = np.min([raw_end, self._total_num_frames])
with ScanImageTiffReader(filename=str(self.file_path)) as io:
raw_video = io.data(beg=raw_start, end=raw_end)
video = raw_video[:: self._num_channels]
video = video[:: self._num_planes]
start_cycle = raw_start // self._num_raw_per_cycle
end_cycle = raw_end // self._num_raw_per_cycle
index = []
for i in range(end_cycle - start_cycle):
for j in range(self._frames_per_slice):
index.append(i * self._num_raw_per_cycle + j * self._num_channels)
video = raw_video[index]
return video

def get_image_size(self) -> Tuple[int, int]:
Expand Down Expand Up @@ -573,12 +582,24 @@ def frame_to_raw_index(self, frame):
The underlying data is stored in a round-robin format collapsed into 3 dimensions (frames, rows, columns).
I.e. the first frame of each channel and each plane is stored, and then the second frame of each channel and
each plane, etc.
Ex. for 2 channels and 2 planes:
[channel_1_plane_1_frame_1, channel_2_plane_1_frame_1, channel_1_plane_2_frame_1, channel_2_plane_2_frame_1,
channel_1_plane_1_frame_2, channel_2_plane_1_frame_2, channel_1_plane_2_frame_2, channel_2_plane_2_frame_2, ...
channel_1_plane_1_frame_N, channel_2_plane_1_frame_N, channel_1_plane_2_frame_N, channel_2_plane_2_frame_N]
If framesPerSlice > 1, then multiple frames are acquired per slice before moving to the next slice.
Ex. for 2 channels, 2 planes, and 2 framesPerSlice:
```
[channel_1_plane_1_frame_1, channel_2_plane_1_frame_1, channel_1_plane_1_frame_2, channel_2_plane_1_frame_2,
channel_1_plane_2_frame_1, channel_2_plane_2_frame_1, channel_1_plane_2_frame_2, channel_2_plane_2_frame_2,
channel_1_plane_1_frame_3, channel_2_plane_1_frame_3, channel_1_plane_1_frame_4, channel_2_plane_1_frame_4,
channel_1_plane_2_frame_3, channel_2_plane_2_frame_3, channel_1_plane_2_frame_4, channel_2_plane_2_frame_4, ...
channel_1_plane_1_frame_N, channel_2_plane_1_frame_N, channel_1_plane_2_frame_N, channel_2_plane_2_frame_N]
```
"""
raw_index = (frame * self._num_planes * self._num_channels) + (self.plane * self._num_channels) + self.channel
cycle = frame // self._frames_per_slice
frame_in_cycle = frame % self._frames_per_slice
raw_index = (
cycle * self._num_raw_per_cycle
+ self.plane * self._num_raw_per_plane
+ frame_in_cycle * self._num_channels
+ self.channel
)
return raw_index


Expand Down
13 changes: 5 additions & 8 deletions tests/test_scanimagetiffimagingextractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

scan_image_path = OPHYS_DATA_PATH / "imaging_datasets" / "ScanImage"
test_files = [
"scanimage_20220801_volume.tif",
"scanimage_20220801_multivolume.tif",
# "scanimage_20220801_volume.tif",
# "scanimage_20220801_multivolume.tif",
# "roi.tif",
"scanimage_20230119_adesnik_00001.tif",
]
file_paths = [scan_image_path / test_file for test_file in test_files]
Expand Down Expand Up @@ -197,13 +198,9 @@ def test_get_num_channels(scan_image_tiff_single_plane_imaging_extractor):


def test_get_channel_names(scan_image_tiff_single_plane_imaging_extractor):
channel_names = scan_image_tiff_single_plane_imaging_extractor.get_channel_names()
num_channels = scan_image_tiff_single_plane_imaging_extractor.get_num_channels()
file_path = str(scan_image_tiff_single_plane_imaging_extractor.file_path)
with ScanImageTiffReader(file_path) as io:
metadata_string = io.metadata()
metadata_dict = metadata_string_to_dict(metadata_string)
assert channel_names == metadata_dict["SI.hChannels.channelName"].split("'")[1::2][:num_channels]
channel_names = ScanImageTiffSinglePlaneImagingExtractor.get_channel_names(file_path)
assert channel_names == ["Channel 1", "Channel 2"]


def test_get_num_planes(scan_image_tiff_single_plane_imaging_extractor):
Expand Down

0 comments on commit 36105b9

Please sign in to comment.