Skip to content

Commit 5678af4

Browse files
committed
Avoid eager C-order copies in NibabelReader (Fixes: #8107)
Nibabel exposes NIfTI voxel buffers in their native Fortran layout, but MONAI was forcing np.asanyarray(img.dataobj, order="C") in NibabelReader._get_array_data(). For compressed .nii.gz inputs that adds a full dense memory reorder on top of the file read/decompression step, which is the hot path reported in issue #8107. Drop the forced C-order conversion and keep nibabel's native array layout instead. Downstream MONAI conversion paths already handle contiguity when they actually need it, so the reader does not need to pay that cost eagerly at load time. Add a regression test that loads a small NIfTI image through NibabelReader and asserts the returned data is still correct while preserving the native F-contiguous layout. This guards against reintroducing the eager copy in the reader path. Signed-off-by: Soumya Snigdha Kundu <[email protected]>
1 parent 55c724f commit 5678af4

2 files changed

Lines changed: 22 additions & 1 deletion

File tree

monai/data/image_reader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ def _get_array_data(self, img, filename):
12171217
data_offset = img.dataobj.offset
12181218
data_dtype = img.dataobj.dtype
12191219
return image[data_offset:].view(data_dtype).reshape(data_shape, order="F")
1220-
return np.asanyarray(img.dataobj, order="C")
1220+
return np.asanyarray(img.dataobj)
12211221

12221222

12231223
class NumpyReader(ImageReader):

tests/data/test_init_reader.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111

1212
from __future__ import annotations
1313

14+
import os
15+
import tempfile
1416
import unittest
1517

18+
import numpy as np
19+
1620
from monai.data import ITKReader, NibabelReader, NrrdReader, NumpyReader, PILReader, PydicomReader
1721
from monai.transforms import LoadImage, LoadImaged
1822
from tests.test_utils import SkipIfNoModule
@@ -76,6 +80,23 @@ def test_readers_to_gpu(self):
7680
inst = NibabelReader(to_gpu=to_gpu)
7781
self.assertIsInstance(inst, NibabelReader)
7882

83+
@SkipIfNoModule("nibabel")
84+
def test_nibabel_reader_avoids_eager_c_order_copy(self):
85+
import nibabel as nib
86+
87+
test_image = np.arange(2 * 3 * 4, dtype=np.int16).reshape(2, 3, 4)
88+
with tempfile.TemporaryDirectory() as tempdir:
89+
filename = os.path.join(tempdir, "test_image.nii.gz")
90+
nib.save(nib.Nifti1Image(test_image, np.eye(4)), filename)
91+
92+
reader = NibabelReader(mmap=False)
93+
img = reader.read(filename)
94+
data, _ = reader.get_data(img)
95+
96+
np.testing.assert_array_equal(data, test_image)
97+
self.assertTrue(data.flags.f_contiguous)
98+
self.assertFalse(data.flags.c_contiguous)
99+
79100

80101
if __name__ == "__main__":
81102
unittest.main()

0 commit comments

Comments
 (0)