diff --git a/brainglobe_utils/image_io/load.py b/brainglobe_utils/image_io/load.py index 1e0e0fc..56fd3ac 100644 --- a/brainglobe_utils/image_io/load.py +++ b/brainglobe_utils/image_io/load.py @@ -198,6 +198,9 @@ def load_img_stack( logging.debug(f"Loading: {stack_path}") stack = tifffile.imread(stack_path) + if stack.ndim != 3: + raise ImageIOLoadException(error_type="2D tiff") + # Downsampled plane by plane because the 3D downsampling in scipy etc # uses too much RAM @@ -218,11 +221,10 @@ def load_img_stack( logging.debug("Converting downsampled stack to array") stack = np.array(downsampled_stack) - if stack.ndim == 3: - # stack = np.rollaxis(stack, 0, 3) - if z_scaling_factor != 1: - logging.debug("Downsampling stack in Z") - stack = scale_z(stack, z_scaling_factor) + # stack = np.rollaxis(stack, 0, 3) + if z_scaling_factor != 1: + logging.debug("Downsampling stack in Z") + stack = scale_z(stack, z_scaling_factor) return stack @@ -534,9 +536,9 @@ def threaded_load_from_sequence( stack_shapes = set() for i in range(len(stacks)): stacks[i] = stacks[i].result() - stack_shapes.add(stacks[i].shape) + stack_shapes.add(stacks[i].shape[0:2]) - # Raise an error if the shapes of all stacks aren't the same + # Raise an error if the x/y shape of all stacks aren't the same if len(stack_shapes) > 1: raise ImageIOLoadException("sequence_shape") @@ -616,8 +618,8 @@ 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 files), without loading - the whole image. + Returns the size of an image (which is a list of 2D tiff files), + without loading the whole image. Parameters ---------- @@ -641,7 +643,7 @@ def get_size_image_from_file_paths(file_path, file_extension="tif"): logging.debug( "Loading file: {} to check raw image size" "".format(img_paths[0]) ) - image_0 = load_any(img_paths[0]) + image_0 = tifffile.imread(img_paths[0]) y_shape, x_shape = image_0.shape image_shape = {"x": x_shape, "y": y_shape, "z": z_shape} diff --git a/brainglobe_utils/image_io/utils.py b/brainglobe_utils/image_io/utils.py index aa2873a..7a96d47 100644 --- a/brainglobe_utils/image_io/utils.py +++ b/brainglobe_utils/image_io/utils.py @@ -1,25 +1,20 @@ -import traceback - import psutil from scipy.ndimage import zoom class ImageIOLoadException(Exception): """ - Custom exception class for errors found loading image with - image_io.load.load_any. + Custom exception class for errors found loading images with + image_io.load. - If the passed target brain directory contains only a single - .tiff, alert the user. - Otherwise, alert the user there was an issue loading the file and - including the full traceback + Alerts the user of: loading a directory containing only a single .tiff, + loading a single 2D .tiff, loading an image sequence where all 2D images + don't have the same shape, lack of memory to complete loading. Set the error message to self.message to read during testing. """ - def __init__( - self, error_type=None, base_error=None, total_size=None, free_mem=None - ): + def __init__(self, error_type=None, total_size=None, free_mem=None): if error_type == "single_tiff": self.message = ( "Attempted to load directory containing " @@ -29,6 +24,9 @@ def __init__( "not supported." ) + elif error_type == "2D tiff": + self.message = "Single 2D .tiff file input is not supported." + elif error_type == "sequence_shape": self.message = ( "Attempted to load an image sequence where individual 2D " @@ -46,14 +44,9 @@ def __init__( f" Needed {total_size}, only {free_mem} " f"available." ) - elif base_error is not None: - original_traceback = "".join( - traceback.format_tb(base_error.__traceback__) - + [base_error.__str__()] - ) + else: self.message = ( - f"{original_traceback}\nFile failed to load with " - "brainglobe_utils.image_io. " + "File failed to load with brainglobe_utils.image_io." ) super().__init__(self.message) diff --git a/tests/tests/test_image_io.py b/tests/tests/test_image_io.py index e5397fe..e720cd5 100644 --- a/tests/tests/test_image_io.py +++ b/tests/tests/test_image_io.py @@ -21,15 +21,6 @@ def array_3d(array_2d): return volume -@pytest.fixture(params=["2D", "3D"]) -def image_array(request, array_2d, array_3d): - """Create both a 2D and 3D array of 32-bit integers""" - if request.param == "2D": - return array_2d - else: - return array_3d - - @pytest.fixture() def txt_path(tmp_path, array_3d): """ @@ -71,9 +62,9 @@ def shuffled_txt_path(txt_path): @pytest.mark.parametrize("use_path", [True, False], ids=["Path", "String"]) -def test_tiff_io(tmp_path, image_array, use_path): +def test_tiff_io(tmp_path, array_3d, use_path): """ - Test that a 2D/3D tiff can be written and read correctly, using string + Test that a 3D tiff can be written and read correctly, using string or pathlib.Path input. """ filename = "image_array.tiff" @@ -82,10 +73,10 @@ def test_tiff_io(tmp_path, image_array, use_path): else: dest_path = str(tmp_path / filename) - save.to_tiff(image_array, dest_path) + save.to_tiff(array_3d, dest_path) reloaded = load.load_img_stack(dest_path, 1, 1, 1) - assert (reloaded == image_array).all() + assert (reloaded == array_3d).all() @pytest.mark.parametrize( @@ -142,6 +133,17 @@ def test_tiff_sequence_io(tmp_path, array_3d, load_parallel, use_path): assert (reloaded_array == array_3d).all() +def test_2d_tiff(tmp_path, array_2d): + """ + Test that an error is thrown when loading a single 2D tiff + """ + image_path = tmp_path / "image.tif" + save.to_tiff(array_2d, image_path) + + with pytest.raises(utils.ImageIOLoadException): + load.load_any(image_path) + + @pytest.mark.parametrize( "x_scaling_factor, y_scaling_factor, z_scaling_factor", [(1, 1, 1), (0.5, 0.5, 1), (0.25, 0.25, 0.25)],