diff --git a/monai/transforms/spatial/functional.py b/monai/transforms/spatial/functional.py index 68e66fd29c..967020a3bf 100644 --- a/monai/transforms/spatial/functional.py +++ b/monai/transforms/spatial/functional.py @@ -162,8 +162,11 @@ def spatial_resample( in_spatial_size = torch.tensor(original_spatial_shape[:spatial_rank]) if isinstance(spatial_size, int) and (spatial_size == -1): # using the input spatial size spatial_size = in_spatial_size - elif spatial_size is None and spatial_rank > 1: # auto spatial size - spatial_size, _ = compute_shape_offset(in_spatial_size, src_affine, dst_affine) # type: ignore + elif spatial_size is None: + if spatial_rank > 1: # auto spatial size + spatial_size, _ = compute_shape_offset(in_spatial_size, src_affine, dst_affine) # type: ignore + else: + spatial_size = in_spatial_size spatial_size = torch.tensor( fall_back_tuple(ensure_tuple(spatial_size)[:spatial_rank], in_spatial_size, lambda x: x is not None and x >= 0) ) diff --git a/tests/data/test_nifti_rw.py b/tests/data/test_nifti_rw.py index cd75bef93d..47370b284e 100644 --- a/tests/data/test_nifti_rw.py +++ b/tests/data/test_nifti_rw.py @@ -249,6 +249,26 @@ def test_write_5d(self): ) np.testing.assert_allclose(out.affine, np.diag([1.4, 2, 2, 1]), atol=1e-4, rtol=1e-4) + @parameterized.expand([((64, 64),), ((8, 64, 64),)]) + def test_write_without_original_affine_and_spatial_shape(self, shape): + """Verify channel-less NIfTI writing preserves shape, voxel values, and affine without target metadata. + + Args: + shape: Shape of the channel-less 2D or 3D input array. + """ + data = np.arange(np.prod(shape), dtype=np.float32).reshape(shape) + with tempfile.TemporaryDirectory() as out_dir: + image_name = os.path.join(out_dir, "test.nii") + writer_obj = NibabelWriter(output_dtype=np.float32) + writer_obj.set_data_array(data, channel_dim=None, spatial_ndim=None) + writer_obj.set_metadata({"affine": np.eye(4), "original_affine": None}) + writer_obj.write(image_name) + + saved = nib.load(image_name) + self.assertEqual(saved.shape, data.shape) + np.testing.assert_array_equal(saved.get_fdata(), data) + np.testing.assert_allclose(saved.affine, np.eye(4)) + if __name__ == "__main__": unittest.main() diff --git a/tests/transforms/test_spatial_resample.py b/tests/transforms/test_spatial_resample.py index 0fad4090e2..5b70c92a6f 100644 --- a/tests/transforms/test_spatial_resample.py +++ b/tests/transforms/test_spatial_resample.py @@ -222,6 +222,20 @@ def test_inverse(self, img, device, data_param, expected_output): expected_affine = to_affine_nd(len(out.affine) - 1, torch.eye(4)) assert_allclose(out.affine, expected_affine) + @parameterized.expand([(False, None), (False, -1), (True, None), (True, -1)]) + def test_1d_unchanged(self, lazy, spatial_size): + """Verify rank-one no-op resampling preserves values, pending shape, and affine. + + Args: + lazy: Whether to record the resampling lazily instead of executing eagerly. + spatial_size: Output size setting, either None or -1, expected to preserve the input size. + """ + img = MetaTensor(torch.arange(8, dtype=torch.float32).reshape(1, 8)) + result = SpatialResample(lazy=lazy)(img, spatial_size=spatial_size) + assert_allclose(result, img) + assert_allclose(result.peek_pending_affine(), img.affine) + self.assertEqual(tuple(result.peek_pending_shape()), (8,)) + def test_unchange(self): for i, p in enumerate(TEST_NDARRAYS_ALL): set_track_meta(i % 2)