From d998feafff9dc7119310ea745415b08efbf71ff4 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd Date: Mon, 25 Oct 2021 16:22:13 +0200 Subject: [PATCH 1/5] squeeze non-spatial dims in nib-reader Signed-off-by: Bryn Lloyd --- monai/data/image_reader.py | 4 ++++ tests/test_load_image.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index 303091f0d8..016661fd7c 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -395,6 +395,10 @@ def get_data(self, img): header["affine"] = self._get_affine(i) header["spatial_shape"] = self._get_spatial_shape(i) data = self._get_array_data(i) + # squeez any non-spatial dimension + for d in range(len(data.shape), len(header["spatial_shape"]), -1): + if data.shape[d - 1] == 1: + data = data.squeeze(axis=d - 1) img_array.append(data) header["original_channel_dim"] = "no_channel" if len(data.shape) == len(header["spatial_shape"]) else -1 _copy_compatible_dict(header, compatible_meta) diff --git a/tests/test_load_image.py b/tests/test_load_image.py index 3f78d3892d..c34cac6012 100644 --- a/tests/test_load_image.py +++ b/tests/test_load_image.py @@ -170,6 +170,20 @@ def test_itk_reader_multichannel(self): np.testing.assert_allclose(result[:, :, 1], test_image[:, :, 1].T) np.testing.assert_allclose(result[:, :, 2], test_image[:, :, 2].T) + def test_load_nifty_multichannel(self): + test_image = np.random.randint(0, 256, size=(31, 64, 16, 2)).astype("uint8") + with tempfile.TemporaryDirectory() as tempdir: + filename = os.path.join(tempdir, "test_image.nii.gz") + itk_np_view = itk.image_view_from_array(test_image, is_vector=True) + itk.imwrite(itk_np_view, filename) + itk_img, itk_header = LoadImage(reader=ITKReader())(Path(filename)) + self.assertTupleEqual(tuple(itk_header["spatial_shape"]), (16, 64, 31)) + self.assertTupleEqual(tuple(itk_img.shape), (16, 64, 31, 2)) + + nib_image, nib_header = LoadImage(reader=NibabelReader())(Path(filename)) + self.assertTupleEqual(tuple(nib_header["spatial_shape"]), (16, 64, 31)) + self.assertTupleEqual(tuple(nib_image.shape), (16, 64, 31, 2)) + def test_load_png(self): spatial_size = (256, 224) test_image = np.random.randint(0, 256, size=spatial_size) From 4fa97b38144ab69bb0eb0f05292cc11176f6e359 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd Date: Tue, 26 Oct 2021 21:47:34 +0200 Subject: [PATCH 2/5] fix: add squeeze_non_spatial_dims=False option Signed-off-by: Bryn Lloyd --- monai/data/image_reader.py | 17 ++++++++++++----- tests/test_load_image.py | 5 +++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index 016661fd7c..567274a2fe 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -328,9 +328,16 @@ class NibabelReader(ImageReader): """ - def __init__(self, as_closest_canonical: bool = False, dtype: DtypeLike = np.float32, **kwargs): + def __init__( + self, + as_closest_canonical: bool = False, + squeeze_non_spatial_dim: bool = False, + dtype: DtypeLike = np.float32, + **kwargs, + ): super().__init__() self.as_closest_canonical = as_closest_canonical + self.squeeze_non_spatial_dim = squeeze_non_spatial_dim self.dtype = dtype self.kwargs = kwargs @@ -395,10 +402,10 @@ def get_data(self, img): header["affine"] = self._get_affine(i) header["spatial_shape"] = self._get_spatial_shape(i) data = self._get_array_data(i) - # squeez any non-spatial dimension - for d in range(len(data.shape), len(header["spatial_shape"]), -1): - if data.shape[d - 1] == 1: - data = data.squeeze(axis=d - 1) + if self.squeeze_non_spatial_dim: + for d in range(len(data.shape), len(header["spatial_shape"]), -1): + if data.shape[d - 1] == 1: + data = data.squeeze(axis=d - 1) img_array.append(data) header["original_channel_dim"] = "no_channel" if len(data.shape) == len(header["spatial_shape"]) else -1 _copy_compatible_dict(header, compatible_meta) diff --git a/tests/test_load_image.py b/tests/test_load_image.py index c34cac6012..444ab0a476 100644 --- a/tests/test_load_image.py +++ b/tests/test_load_image.py @@ -171,16 +171,17 @@ def test_itk_reader_multichannel(self): np.testing.assert_allclose(result[:, :, 2], test_image[:, :, 2].T) def test_load_nifty_multichannel(self): - test_image = np.random.randint(0, 256, size=(31, 64, 16, 2)).astype("uint8") + test_image = np.random.randint(0, 256, size=(31, 64, 16, 2)).astype(np.float32) with tempfile.TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, "test_image.nii.gz") itk_np_view = itk.image_view_from_array(test_image, is_vector=True) itk.imwrite(itk_np_view, filename) + itk_img, itk_header = LoadImage(reader=ITKReader())(Path(filename)) self.assertTupleEqual(tuple(itk_header["spatial_shape"]), (16, 64, 31)) self.assertTupleEqual(tuple(itk_img.shape), (16, 64, 31, 2)) - nib_image, nib_header = LoadImage(reader=NibabelReader())(Path(filename)) + nib_image, nib_header = LoadImage(reader=NibabelReader(squeeze_non_spatial_dim=True))(Path(filename)) self.assertTupleEqual(tuple(nib_header["spatial_shape"]), (16, 64, 31)) self.assertTupleEqual(tuple(nib_image.shape), (16, 64, 31, 2)) From bda427798788b25ccfabeceec2de334371b18930 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd Date: Tue, 26 Oct 2021 22:10:13 +0200 Subject: [PATCH 3/5] fix: add docstring for arg Signed-off-by: Bryn Lloyd --- monai/data/image_reader.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index 567274a2fe..f46a50028a 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -323,6 +323,7 @@ class NibabelReader(ImageReader): Args: as_closest_canonical: if True, load the image as closest to canonical axis format. + squeeze_non_spatial_dim: if True, non-spatial singletons will be squezed, e.g. (256,256,1,3) -> (256,256,3) kwargs: additional args for `nibabel.load` API. more details about available args: https://github.com/nipy/nibabel/blob/master/nibabel/loadsave.py @@ -331,13 +332,13 @@ class NibabelReader(ImageReader): def __init__( self, as_closest_canonical: bool = False, - squeeze_non_spatial_dim: bool = False, + squeeze_non_spatial_dims: bool = False, dtype: DtypeLike = np.float32, **kwargs, ): super().__init__() self.as_closest_canonical = as_closest_canonical - self.squeeze_non_spatial_dim = squeeze_non_spatial_dim + self.squeeze_non_spatial_dims = squeeze_non_spatial_dims self.dtype = dtype self.kwargs = kwargs @@ -402,7 +403,7 @@ def get_data(self, img): header["affine"] = self._get_affine(i) header["spatial_shape"] = self._get_spatial_shape(i) data = self._get_array_data(i) - if self.squeeze_non_spatial_dim: + if self.squeeze_non_spatial_dims: for d in range(len(data.shape), len(header["spatial_shape"]), -1): if data.shape[d - 1] == 1: data = data.squeeze(axis=d - 1) From 2aa6413126e6824b60feee444baea8eda3bea7bc Mon Sep 17 00:00:00 2001 From: Bryn Lloyd Date: Tue, 26 Oct 2021 22:14:18 +0200 Subject: [PATCH 4/5] fix: add docstring for arg Signed-off-by: Bryn Lloyd --- tests/test_load_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_load_image.py b/tests/test_load_image.py index 444ab0a476..f718c019ec 100644 --- a/tests/test_load_image.py +++ b/tests/test_load_image.py @@ -181,7 +181,7 @@ def test_load_nifty_multichannel(self): self.assertTupleEqual(tuple(itk_header["spatial_shape"]), (16, 64, 31)) self.assertTupleEqual(tuple(itk_img.shape), (16, 64, 31, 2)) - nib_image, nib_header = LoadImage(reader=NibabelReader(squeeze_non_spatial_dim=True))(Path(filename)) + nib_image, nib_header = LoadImage(reader=NibabelReader(squeeze_non_spatial_dims=True))(Path(filename)) self.assertTupleEqual(tuple(nib_header["spatial_shape"]), (16, 64, 31)) self.assertTupleEqual(tuple(nib_image.shape), (16, 64, 31, 2)) From 025be3d72a1b1842b3bb68630247b26a67788a7c Mon Sep 17 00:00:00 2001 From: Bryn Lloyd Date: Tue, 26 Oct 2021 22:59:05 +0200 Subject: [PATCH 5/5] fix: typos Signed-off-by: Bryn Lloyd --- monai/data/image_reader.py | 2 +- tests/test_load_image.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index f46a50028a..9c212784ca 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -323,7 +323,7 @@ class NibabelReader(ImageReader): Args: as_closest_canonical: if True, load the image as closest to canonical axis format. - squeeze_non_spatial_dim: if True, non-spatial singletons will be squezed, e.g. (256,256,1,3) -> (256,256,3) + squeeze_non_spatial_dims: if True, non-spatial singletons will be squeezed, e.g. (256,256,1,3) -> (256,256,3) kwargs: additional args for `nibabel.load` API. more details about available args: https://github.com/nipy/nibabel/blob/master/nibabel/loadsave.py diff --git a/tests/test_load_image.py b/tests/test_load_image.py index f718c019ec..687829dfa8 100644 --- a/tests/test_load_image.py +++ b/tests/test_load_image.py @@ -170,7 +170,7 @@ def test_itk_reader_multichannel(self): np.testing.assert_allclose(result[:, :, 1], test_image[:, :, 1].T) np.testing.assert_allclose(result[:, :, 2], test_image[:, :, 2].T) - def test_load_nifty_multichannel(self): + def test_load_nifti_multichannel(self): test_image = np.random.randint(0, 256, size=(31, 64, 16, 2)).astype(np.float32) with tempfile.TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, "test_image.nii.gz")