diff --git a/brainglobe_utils/image_io/load.py b/brainglobe_utils/image_io/load.py index 33e5acf..2ad4969 100644 --- a/brainglobe_utils/image_io/load.py +++ b/brainglobe_utils/image_io/load.py @@ -665,14 +665,15 @@ def load_from_paths_sequence( def get_size_image_from_file_paths(file_path, file_extension="tif"): """ - Returns the size of an image (which is a list of 2D tiff files), - without loading the whole image. + Returns the size of an image (which is a list of 2D tiff files or a + single-file tif stack), without loading the whole image. Parameters ---------- file_path : str or pathlib.Path - Filepath of text file containing paths of all 2D files, or - filepath of a directory containing all 2D files. + Filepath of text file containing paths of all 2D files, a + filepath of a directory containing all 2D files, or a single + tiff file z-stack. file_extension : str, optional Optional file extension (if a directory is passed). @@ -683,6 +684,42 @@ def get_size_image_from_file_paths(file_path, file_extension="tif"): Dict of image sizes. """ file_path = Path(file_path) + if file_path.suffix in [".tif", ".tiff"]: + # read just the metadata + with tifffile.TiffFile(file_path) as tiff: + if not len(tiff.series): + raise ValueError( + f"Attempted to load {file_path} but didn't find a z-stack" + ) + if len(tiff.series) != 1: + raise ValueError( + f"Attempted to load {file_path} but found multiple stacks" + ) + + shape = tiff.series[0].shape + axes = tiff.series[0].axes.lower() + + if len(shape) != 3: + raise ValueError( + f"Attempted to load {file_path} but didn't find a " + f"3-dimensional stack. Found {axes} axes " + f"with shape {shape}" + ) + # axes is e.g. "zxy" + if set(axes) == {"x", "y", "z"}: + image_shape = {ax: n for ax, n in zip(axes, shape)} + return image_shape + else: # metadata does not specify axes as expected, + logging.debug( + f"Axis metadata is {axes}, " + "which is not the expected set of x,y,z in any order. " + "Assume z,y,x" + ) + image_shape = { + ax: n + for ax, n in zip(["z", "y", "x"], tiff.series[0].shape) + } + return image_shape img_paths = get_sorted_file_paths(file_path, file_extension=file_extension) z_shape = len(img_paths) diff --git a/brainglobe_utils/image_io/save.py b/brainglobe_utils/image_io/save.py index 613b805..2115f07 100644 --- a/brainglobe_utils/image_io/save.py +++ b/brainglobe_utils/image_io/save.py @@ -58,7 +58,12 @@ def to_tiff(img_volume, dest_path, photometric="minisblack"): Use 'minisblack' (default) for grayscale and 'rgb' for rgb """ dest_path = Path(dest_path) - tifffile.imwrite(dest_path, img_volume, photometric=photometric) + tifffile.imwrite( + dest_path, + img_volume, + photometric=photometric, + metadata={"axes": "ZYX"}, + ) def to_tiffs(img_volume, path_prefix, path_suffix="", extension=".tif"): diff --git a/tests/tests/test_image_io.py b/tests/tests/test_image_io.py index e720cd5..0f38c39 100644 --- a/tests/tests/test_image_io.py +++ b/tests/tests/test_image_io.py @@ -350,6 +350,19 @@ def test_image_size_dir(tmp_path, array_3d): assert image_shape["z"] == array_3d.shape[0] +def test_image_size_tiff_stack(tmp_path, array_3d): + """ + Test that image size can be detected from a directory of 2D tiffs + """ + filename = tmp_path.joinpath("image_size_tiff_stack.tif") + save.save_any(array_3d, filename) + + image_shape = load.get_size_image_from_file_paths(filename) + assert image_shape["x"] == array_3d.shape[2] + assert image_shape["y"] == array_3d.shape[1] + assert image_shape["z"] == array_3d.shape[0] + + def test_image_size_txt(txt_path, array_3d): """ Test that image size can be detected from a text file containing the paths