diff --git a/nibabel/minc1.py b/nibabel/minc1.py index d0b9fd537..acbc649ba 100644 --- a/nibabel/minc1.py +++ b/nibabel/minc1.py @@ -112,6 +112,9 @@ def get_affine(self): aff[:nspatial, nspatial] = origin return aff + def get_units(self) -> tuple[str, str, str]: + return tuple(d.units.decode('utf-8') for d in self._dims) + def _get_valid_range(self): """Return valid range for image data @@ -284,6 +287,18 @@ class MincHeader(SpatialHeader): # We don't use the data layout - this just in case we do later data_layout = 'C' + def __init__(self, + data_dtype=np.float32, + shape=(0,), + zooms=None, + units='unknown'): + super().__init__(data_dtype, shape, zooms) + self._units = units + + def copy(self): + return self.__class__(self.get_data_dtype(), self.get_data_shape(), self.get_zooms(), + self._units) + def data_to_fileobj(self, data, fileobj, rescale=True): """See Header class for an implementation we can't use""" raise NotImplementedError @@ -292,6 +307,9 @@ def data_from_fileobj(self, fileobj): """See Header class for an implementation we can't use""" raise NotImplementedError + def get_xyzt_units(self) -> tuple[str, str]: + return self._units, 'unknown' + class Minc1Header(MincHeader): @classmethod @@ -330,7 +348,13 @@ def from_file_map(klass, file_map, *, mmap=True, keep_file_open=None): data_dtype = minc_file.get_data_dtype() shape = minc_file.get_data_shape() zooms = minc_file.get_zooms() - header = klass.header_class(data_dtype, shape, zooms) + unit = 'unknown' + units = minc_file.get_units() + if units: + if any(u != units[0] for u in units[1:]): + raise ValueError(f'Image has different units for different dimensions: {units}') + unit = units[0] + header = klass.header_class(data_dtype, shape, zooms, unit) data = klass.ImageArrayProxy(minc_file) return klass(data, affine, header, extra=None, file_map=file_map) diff --git a/nibabel/minc2.py b/nibabel/minc2.py index 161be5c11..e91d6ad75 100644 --- a/nibabel/minc2.py +++ b/nibabel/minc2.py @@ -176,7 +176,13 @@ def from_file_map(klass, file_map, *, mmap=True, keep_file_open=None): data_dtype = minc_file.get_data_dtype() shape = minc_file.get_data_shape() zooms = minc_file.get_zooms() - header = klass.header_class(data_dtype, shape, zooms) + unit = 'unknown' + units = minc_file.get_units() + if units: + if any(u != units[0] for u in units[1:]): + raise ValueError(f'Image has different units for different dimensions: {units}') + unit = units[0] + header = klass.header_class(data_dtype, shape, zooms, unit) data = klass.ImageArrayProxy(minc_file) return klass(data, affine, header, extra=None, file_map=file_map) diff --git a/nibabel/tests/test_minc1.py b/nibabel/tests/test_minc1.py index 8f88bf802..62999141f 100644 --- a/nibabel/tests/test_minc1.py +++ b/nibabel/tests/test_minc1.py @@ -214,3 +214,7 @@ def test_data_to_from_fileobj(self): img.header.data_to_fileobj(arr, bio) with pytest.raises(NotImplementedError): img.header.data_from_fileobj(bio) + + def test_xyzt_units(self): + img = self.module.load(self.eg_images[0]) + assert img.header.get_xyzt_units() == ('mm', 'unknown') diff --git a/nibabel/tests/test_minc2.py b/nibabel/tests/test_minc2.py index 4c2973a72..fd8f273b4 100644 --- a/nibabel/tests/test_minc2.py +++ b/nibabel/tests/test_minc2.py @@ -123,6 +123,10 @@ class TestMinc2Image(tm2.TestMinc1Image): eg_images = (pjoin(data_path, 'small.mnc'),) module = minc2 + def test_xyzt_units(self): + img = self.module.load(self.eg_images[0]) + assert img.header.get_xyzt_units() == ('mm', 'unknown') + def test_bad_diminfo(): fname = pjoin(data_path, 'minc2_baddim.mnc') @@ -131,3 +135,4 @@ def test_bad_diminfo(): # We interpret an invalid spacing as absent, but warn. with pytest.warns(UserWarning): Minc2Image.from_filename(fname) +