From e48da37a15917b95f83143d1736a2f8f4713940f Mon Sep 17 00:00:00 2001 From: Jennings Zhang Date: Fri, 1 Apr 2022 14:27:53 -0400 Subject: [PATCH 1/4] MincHeader.get_xyzt_units() --- nibabel/minc1.py | 26 +++++++++++++++++++++++++- nibabel/tests/test_minc1.py | 4 ++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/nibabel/minc1.py b/nibabel/minc1.py index c172f40a8e..9ce0b2a80b 100644 --- a/nibabel/minc1.py +++ b/nibabel/minc1.py @@ -11,6 +11,7 @@ from numbers import Integral import numpy as np +from typing import Tuple from .externals.netcdf import netcdf_file @@ -115,6 +116,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 @@ -289,6 +293,17 @@ 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 @@ -297,6 +312,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): @@ -334,7 +352,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 829523c4c9..3e48209a89 100644 --- a/nibabel/tests/test_minc1.py +++ b/nibabel/tests/test_minc1.py @@ -215,3 +215,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') From d81a026c4ede56e1643cc5c9a328bfd7fb3d8e51 Mon Sep 17 00:00:00 2001 From: Jennings Zhang Date: Fri, 1 Apr 2022 15:24:19 -0400 Subject: [PATCH 2/4] Lint flake8 --- nibabel/minc1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nibabel/minc1.py b/nibabel/minc1.py index 9ce0b2a80b..e85e671f37 100644 --- a/nibabel/minc1.py +++ b/nibabel/minc1.py @@ -302,7 +302,8 @@ def __init__(self, self._units = units def copy(self): - return self.__class__(self.get_data_dtype(), self.get_data_shape(), self.get_zooms(), self._units) + 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 """ From 35c3e78c001f40e09401386d9d7bc7481f4b7364 Mon Sep 17 00:00:00 2001 From: Jennings Zhang Date: Fri, 1 Apr 2022 15:45:55 -0400 Subject: [PATCH 3/4] Minc2Header.get_xyzt_units() --- nibabel/minc2.py | 8 +++++++- nibabel/tests/test_minc2.py | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/nibabel/minc2.py b/nibabel/minc2.py index a71ec7c693..58ff252091 100644 --- a/nibabel/minc2.py +++ b/nibabel/minc2.py @@ -169,7 +169,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_minc2.py b/nibabel/tests/test_minc2.py index 1842ca02f9..3b0930c927 100644 --- a/nibabel/tests/test_minc2.py +++ b/nibabel/tests/test_minc2.py @@ -112,3 +112,7 @@ class TestMinc2Image(tm2.TestMinc1Image): image_class = Minc2Image 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') From ca618008bae934413089cb917d8e8d9195643a0d Mon Sep 17 00:00:00 2001 From: Jennings Zhang Date: Fri, 18 Oct 2024 12:52:20 -0400 Subject: [PATCH 4/4] MNT: Replace typing.Tuple with tuple --- nibabel/minc1.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nibabel/minc1.py b/nibabel/minc1.py index 8c65c66507..acbc649bae 100644 --- a/nibabel/minc1.py +++ b/nibabel/minc1.py @@ -13,7 +13,6 @@ from numbers import Integral import numpy as np -from typing import Tuple from .externals.netcdf import netcdf_file from .fileslice import canonical_slicers @@ -113,7 +112,7 @@ def get_affine(self): aff[:nspatial, nspatial] = origin return aff - def get_units(self) -> Tuple[str, str, str]: + 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): @@ -308,7 +307,7 @@ 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]: + def get_xyzt_units(self) -> tuple[str, str]: return self._units, 'unknown'