From b55a02e111ee4a4a177a2753c55437c882f205e1 Mon Sep 17 00:00:00 2001 From: advcu987 Date: Wed, 8 Jan 2025 13:33:21 +0100 Subject: [PATCH 1/6] Fixes #8267 . Fix channel-wise intensity normalization for integer type inputs. Signed-off-by: Adrian Voicu --- monai/transforms/intensity/array.py | 2 ++ tests/test_normalize_intensity.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 20000c52c4..aefe5697b8 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -907,6 +907,8 @@ def __call__(self, img: NdarrayOrTensor) -> NdarrayOrTensor: if self.divisor is not None and len(self.divisor) != len(img): raise ValueError(f"img has {len(img)} channels, but divisor has {len(self.divisor)} components.") + img, *_ = convert_data_type(img, dtype=torch.float32) + for i, d in enumerate(img): img[i] = self._normalize( # type: ignore d, diff --git a/tests/test_normalize_intensity.py b/tests/test_normalize_intensity.py index 72ebf579e1..7efd0d83e5 100644 --- a/tests/test_normalize_intensity.py +++ b/tests/test_normalize_intensity.py @@ -108,6 +108,27 @@ def test_channel_wise(self, im_type): normalized = normalizer(input_data) assert_allclose(normalized, im_type(expected), type_test="tensor") + @parameterized.expand([[p] for p in TEST_NDARRAYS]) + def test_channel_wise_int(self, im_type): + normalizer = NormalizeIntensity(nonzero=True, channel_wise=True) + input_data = im_type(torch.arange(1, 25).reshape(2, 3, 4)) + expected = np.array( + [ + [ + [-1.593255, -1.3035723, -1.0138896, -0.7242068], + [-0.4345241, -0.1448414, 0.1448414, 0.4345241], + [0.7242068, 1.0138896, 1.3035723, 1.593255], + ], + [ + [-1.593255, -1.3035723, -1.0138896, -0.7242068], + [-0.4345241, -0.1448414, 0.1448414, 0.4345241], + [0.7242068, 1.0138896, 1.3035723, 1.593255], + ], + ] + ) + normalized = normalizer(input_data) + assert_allclose(normalized, im_type(expected), type_test="tensor", rtol=1e-7, atol=1e-7) # tolerance + @parameterized.expand([[p] for p in TEST_NDARRAYS]) def test_value_errors(self, im_type): input_data = im_type(np.array([[0.0, 3.0, 0.0, 4.0], [0.0, 4.0, 0.0, 5.0]])) From 28a0df1aacda74cfe8a846e34a31a8b0a1ce76d3 Mon Sep 17 00:00:00 2001 From: advcu987 Date: Wed, 8 Jan 2025 13:36:42 +0100 Subject: [PATCH 2/6] Revert " Fixes #8267 ." This reverts commit b55a02e111ee4a4a177a2753c55437c882f205e1. --- monai/transforms/intensity/array.py | 2 -- tests/test_normalize_intensity.py | 21 --------------------- 2 files changed, 23 deletions(-) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index aefe5697b8..20000c52c4 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -907,8 +907,6 @@ def __call__(self, img: NdarrayOrTensor) -> NdarrayOrTensor: if self.divisor is not None and len(self.divisor) != len(img): raise ValueError(f"img has {len(img)} channels, but divisor has {len(self.divisor)} components.") - img, *_ = convert_data_type(img, dtype=torch.float32) - for i, d in enumerate(img): img[i] = self._normalize( # type: ignore d, diff --git a/tests/test_normalize_intensity.py b/tests/test_normalize_intensity.py index 7efd0d83e5..72ebf579e1 100644 --- a/tests/test_normalize_intensity.py +++ b/tests/test_normalize_intensity.py @@ -108,27 +108,6 @@ def test_channel_wise(self, im_type): normalized = normalizer(input_data) assert_allclose(normalized, im_type(expected), type_test="tensor") - @parameterized.expand([[p] for p in TEST_NDARRAYS]) - def test_channel_wise_int(self, im_type): - normalizer = NormalizeIntensity(nonzero=True, channel_wise=True) - input_data = im_type(torch.arange(1, 25).reshape(2, 3, 4)) - expected = np.array( - [ - [ - [-1.593255, -1.3035723, -1.0138896, -0.7242068], - [-0.4345241, -0.1448414, 0.1448414, 0.4345241], - [0.7242068, 1.0138896, 1.3035723, 1.593255], - ], - [ - [-1.593255, -1.3035723, -1.0138896, -0.7242068], - [-0.4345241, -0.1448414, 0.1448414, 0.4345241], - [0.7242068, 1.0138896, 1.3035723, 1.593255], - ], - ] - ) - normalized = normalizer(input_data) - assert_allclose(normalized, im_type(expected), type_test="tensor", rtol=1e-7, atol=1e-7) # tolerance - @parameterized.expand([[p] for p in TEST_NDARRAYS]) def test_value_errors(self, im_type): input_data = im_type(np.array([[0.0, 3.0, 0.0, 4.0], [0.0, 4.0, 0.0, 5.0]])) From bdb36041efd9a4e8e6f27a5a08910196e211a83c Mon Sep 17 00:00:00 2001 From: advcu987 Date: Wed, 8 Jan 2025 13:40:16 +0100 Subject: [PATCH 3/6] Fixes #8267 . Fix channel-wise intensity normalization for integer type inputs. Signed-off-by: Adrian Voicu --- monai/transforms/intensity/array.py | 2 ++ tests/test_normalize_intensity.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 20000c52c4..aefe5697b8 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -907,6 +907,8 @@ def __call__(self, img: NdarrayOrTensor) -> NdarrayOrTensor: if self.divisor is not None and len(self.divisor) != len(img): raise ValueError(f"img has {len(img)} channels, but divisor has {len(self.divisor)} components.") + img, *_ = convert_data_type(img, dtype=torch.float32) + for i, d in enumerate(img): img[i] = self._normalize( # type: ignore d, diff --git a/tests/test_normalize_intensity.py b/tests/test_normalize_intensity.py index 72ebf579e1..7efd0d83e5 100644 --- a/tests/test_normalize_intensity.py +++ b/tests/test_normalize_intensity.py @@ -108,6 +108,27 @@ def test_channel_wise(self, im_type): normalized = normalizer(input_data) assert_allclose(normalized, im_type(expected), type_test="tensor") + @parameterized.expand([[p] for p in TEST_NDARRAYS]) + def test_channel_wise_int(self, im_type): + normalizer = NormalizeIntensity(nonzero=True, channel_wise=True) + input_data = im_type(torch.arange(1, 25).reshape(2, 3, 4)) + expected = np.array( + [ + [ + [-1.593255, -1.3035723, -1.0138896, -0.7242068], + [-0.4345241, -0.1448414, 0.1448414, 0.4345241], + [0.7242068, 1.0138896, 1.3035723, 1.593255], + ], + [ + [-1.593255, -1.3035723, -1.0138896, -0.7242068], + [-0.4345241, -0.1448414, 0.1448414, 0.4345241], + [0.7242068, 1.0138896, 1.3035723, 1.593255], + ], + ] + ) + normalized = normalizer(input_data) + assert_allclose(normalized, im_type(expected), type_test="tensor", rtol=1e-7, atol=1e-7) # tolerance + @parameterized.expand([[p] for p in TEST_NDARRAYS]) def test_value_errors(self, im_type): input_data = im_type(np.array([[0.0, 3.0, 0.0, 4.0], [0.0, 4.0, 0.0, 5.0]])) From 3656b99867002dc7302086165e1342b4a32ece6f Mon Sep 17 00:00:00 2001 From: advcu987 Date: Wed, 8 Jan 2025 14:00:01 +0100 Subject: [PATCH 4/6] :DCO Remediation Commit for advcu987 I, advcu987 , hereby add my Signed-off-by to this commit: b55a02e111ee4a4a177a2753c55437c882f205e1 I, advcu987 , hereby add my Signed-off-by to this commit: 28a0df1aacda74cfe8a846e34a31a8b0a1ce76d3 I, advcu987 , hereby add my Signed-off-by to this commit: bdb36041efd9a4e8e6f27a5a08910196e211a83c Signed-off-by: advcu987 From 3c58135fb7c545ab36d9f4df1d4c4fdfb4286d25 Mon Sep 17 00:00:00 2001 From: advcu <65158236+advcu987@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:39:09 +0100 Subject: [PATCH 5/6] Update monai/transforms/intensity/array.py Thanks eric for the suggestion Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: advcu <65158236+advcu987@users.noreply.github.com> --- monai/transforms/intensity/array.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index aefe5697b8..beb14b76e3 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -907,7 +907,8 @@ def __call__(self, img: NdarrayOrTensor) -> NdarrayOrTensor: if self.divisor is not None and len(self.divisor) != len(img): raise ValueError(f"img has {len(img)} channels, but divisor has {len(self.divisor)} components.") - img, *_ = convert_data_type(img, dtype=torch.float32) + if not img.dtype.is_floating_point: + img, *_ = convert_data_type(img, dtype=torch.float32) for i, d in enumerate(img): img[i] = self._normalize( # type: ignore From 63bd2b7c0c9b3b5b0077ace37a9336fb1099962a Mon Sep 17 00:00:00 2001 From: advcu987 Date: Sat, 18 Jan 2025 23:33:32 +0100 Subject: [PATCH 6/6] Add class comment. Signed-off-by: advcu987 --- monai/transforms/intensity/array.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index beb14b76e3..8fe658ad3e 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -821,6 +821,7 @@ class NormalizeIntensity(Transform): mean and std on each channel separately. When `channel_wise` is True, the first dimension of `subtrahend` and `divisor` should be the number of image channels if they are not None. + If the input is not of floating point type, it will be converted to float32 Args: subtrahend: the amount to subtract by (usually the mean).