From fb53aee50fb8d0b73a200334b4297ac445fd9ddf Mon Sep 17 00:00:00 2001 From: Rifa Ferzana <78103780+Rifa-111@users.noreply.github.com> Date: Sun, 8 Nov 2026 00:47:34 +0000 Subject: [PATCH 1/2] Fix ShiftIntensity safe integer offset overflow Promote integer tensors before adding offsets when safe=True to avoid wraparound before dtype conversion, and cover signed/unsigned cases. Signed-off-by: Rifa Ferzana <78103780+Rifa-111@users.noreply.github.com> --- monai/transforms/intensity/array.py | 5 ++++- tests/transforms/test_shift_intensity.py | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 0421d344923..f93c5f8d3e7 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -252,7 +252,10 @@ def __call__(self, img: NdarrayOrTensor, offset: float | None = None) -> Ndarray img = convert_to_tensor(img, track_meta=get_track_meta()) offset = self.offset if offset is None else offset - out = img + offset + # Promote integer inputs before addition when safe=True. Otherwise integer + # offsets can overflow in the input dtype before safe conversion clamps. + computation_img = img.to(torch.float64) if self.safe and not img.is_floating_point() else img + out = computation_img + offset out, *_ = convert_data_type(data=out, dtype=img.dtype, safe=self.safe) return out diff --git a/tests/transforms/test_shift_intensity.py b/tests/transforms/test_shift_intensity.py index 70a6c7bf799..4b626fc5eb6 100644 --- a/tests/transforms/test_shift_intensity.py +++ b/tests/transforms/test_shift_intensity.py @@ -14,12 +14,32 @@ import unittest import numpy as np +import torch from monai.transforms import ShiftIntensity from tests.test_utils import NumpyImageTestCase2D class TestShiftIntensity(NumpyImageTestCase2D): + def test_safe_integer_offset_clamps_unsigned_image(self): + image = torch.tensor([0, 100, 255], dtype=torch.uint8) + for offset, expected in ( + (1, [1, 101, 255]), + (-1, [0, 99, 254]), + (1.0, [1, 101, 255]), + (-1.0, [0, 99, 254]), + ): + with self.subTest(offset=offset): + result = ShiftIntensity(offset=offset, safe=True)(image) + torch.testing.assert_close(result, torch.tensor(expected, dtype=torch.uint8)) + + def test_safe_integer_offset_clamps_signed_image(self): + image = torch.tensor([-128, 0, 127], dtype=torch.int8) + result = ShiftIntensity(offset=1, safe=True)(image) + torch.testing.assert_close(result, torch.tensor([-127, 1, 127], dtype=torch.int8)) + result = ShiftIntensity(offset=-1, safe=True)(image) + torch.testing.assert_close(result, torch.tensor([-128, -1, 126], dtype=torch.int8)) + def test_value(self): shifter = ShiftIntensity(offset=1.0) result = shifter(self.imt) From 63f1803787c4e85b96ed57f5f1acde067a67b65c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Nov 2026 00:50:50 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/transforms/test_shift_intensity.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/transforms/test_shift_intensity.py b/tests/transforms/test_shift_intensity.py index 4b626fc5eb6..4346f9adc90 100644 --- a/tests/transforms/test_shift_intensity.py +++ b/tests/transforms/test_shift_intensity.py @@ -23,12 +23,7 @@ class TestShiftIntensity(NumpyImageTestCase2D): def test_safe_integer_offset_clamps_unsigned_image(self): image = torch.tensor([0, 100, 255], dtype=torch.uint8) - for offset, expected in ( - (1, [1, 101, 255]), - (-1, [0, 99, 254]), - (1.0, [1, 101, 255]), - (-1.0, [0, 99, 254]), - ): + for offset, expected in ((1, [1, 101, 255]), (-1, [0, 99, 254]), (1.0, [1, 101, 255]), (-1.0, [0, 99, 254])): with self.subTest(offset=offset): result = ShiftIntensity(offset=offset, safe=True)(image) torch.testing.assert_close(result, torch.tensor(expected, dtype=torch.uint8))