|
17 | 17 | import numpy.linalg as npl |
18 | 18 | from numpy.compat.py3k import asstr |
19 | 19 |
|
| 20 | +from .arrayproxy import get_obj_dtype |
20 | 21 | from .optpkg import optional_package |
21 | 22 | from .filebasedimages import SerializableImage |
22 | 23 | from .volumeutils import Recoder, make_dt_codes, endian_codes |
@@ -881,6 +882,51 @@ def set_data_shape(self, shape): |
881 | 882 | shape = (-1, 1, 1) + shape[3:] |
882 | 883 | super(Nifti1Header, self).set_data_shape(shape) |
883 | 884 |
|
| 885 | + def set_data_dtype(self, datatype): |
| 886 | + """ Set numpy dtype for data from code or dtype or type |
| 887 | +
|
| 888 | + Using :py:class:`int` or ``"int"`` is disallowed, as these types |
| 889 | + will be interpreted as ``np.int64``, which is almost never desired. |
| 890 | + ``np.int64`` is permitted for those intent on making poor choices. |
| 891 | +
|
| 892 | + Examples |
| 893 | + -------- |
| 894 | + >>> hdr = Nifti1Header() |
| 895 | + >>> hdr.set_data_dtype(np.uint8) |
| 896 | + >>> hdr.get_data_dtype() |
| 897 | + dtype('uint8') |
| 898 | + >>> hdr.set_data_dtype(np.dtype(np.uint8)) |
| 899 | + >>> hdr.get_data_dtype() |
| 900 | + dtype('uint8') |
| 901 | + >>> hdr.set_data_dtype('implausible') #doctest: +IGNORE_EXCEPTION_DETAIL |
| 902 | + Traceback (most recent call last): |
| 903 | + ... |
| 904 | + HeaderDataError: data dtype "implausible" not recognized |
| 905 | + >>> hdr.set_data_dtype('none') #doctest: +IGNORE_EXCEPTION_DETAIL |
| 906 | + Traceback (most recent call last): |
| 907 | + ... |
| 908 | + HeaderDataError: data dtype "none" known but not supported |
| 909 | + >>> hdr.set_data_dtype(np.void) #doctest: +IGNORE_EXCEPTION_DETAIL |
| 910 | + Traceback (most recent call last): |
| 911 | + ... |
| 912 | + HeaderDataError: data dtype "<type 'numpy.void'>" known but not supported |
| 913 | + >>> hdr.set_data_dtype('int') #doctest: +IGNORE_EXCEPTION_DETAIL |
| 914 | + Traceback (most recent call last): |
| 915 | + ... |
| 916 | + ValueError: Invalid data type 'int'. Specify a sized integer, e.g., 'uint8' or numpy.int16. |
| 917 | + >>> hdr.set_data_dtype(int) #doctest: +IGNORE_EXCEPTION_DETAIL |
| 918 | + Traceback (most recent call last): |
| 919 | + ... |
| 920 | + ValueError: Invalid data type 'int'. Specify a sized integer, e.g., 'uint8' or numpy.int16. |
| 921 | + >>> hdr.set_data_dtype('int64') |
| 922 | + >>> hdr.get_data_dtype() == np.dtype('int64') |
| 923 | + True |
| 924 | + """ |
| 925 | + if not isinstance(datatype, np.dtype) and datatype in (int, "int"): |
| 926 | + raise ValueError(f"Invalid data type {datatype!r}. Specify a sized integer, " |
| 927 | + "e.g., 'uint8' or numpy.int16.") |
| 928 | + super().set_data_dtype(datatype) |
| 929 | + |
884 | 930 | def get_qform_quaternion(self): |
885 | 931 | """ Compute quaternion from b, c, d of quaternion |
886 | 932 |
|
@@ -1754,12 +1800,27 @@ class Nifti1Pair(analyze.AnalyzeImage): |
1754 | 1800 | rw = True |
1755 | 1801 |
|
1756 | 1802 | def __init__(self, dataobj, affine, header=None, |
1757 | | - extra=None, file_map=None): |
| 1803 | + extra=None, file_map=None, dtype=None): |
| 1804 | + # Special carve-out for 64 bit integers |
| 1805 | + # See GitHub issues |
| 1806 | + # * https://github.com/nipy/nibabel/issues/1046 |
| 1807 | + # * https://github.com/nipy/nibabel/issues/1089 |
| 1808 | + # This only applies to NIfTI because the parent Analyze formats did |
| 1809 | + # not support 64-bit integer data, so `set_data_dtype(int64)` would |
| 1810 | + # already fail. |
| 1811 | + danger_dts = (np.dtype("int64"), np.dtype("uint64")) |
| 1812 | + if header is None and dtype is None and get_obj_dtype(dataobj) in danger_dts: |
| 1813 | + msg = (f"Image data has type {dataobj.dtype}, which may cause " |
| 1814 | + "incompatibilities with other tools. This will error in " |
| 1815 | + "NiBabel 5.0. This warning can be silenced " |
| 1816 | + f"by passing the dtype argument to {self.__class__.__name__}().") |
| 1817 | + warnings.warn(msg, FutureWarning, stacklevel=2) |
1758 | 1818 | super(Nifti1Pair, self).__init__(dataobj, |
1759 | 1819 | affine, |
1760 | 1820 | header, |
1761 | 1821 | extra, |
1762 | | - file_map) |
| 1822 | + file_map, |
| 1823 | + dtype) |
1763 | 1824 | # Force set of s/q form when header is None unless affine is also None |
1764 | 1825 | if header is None and affine is not None: |
1765 | 1826 | self._affine2header() |
@@ -1865,7 +1926,7 @@ def set_qform(self, affine, code=None, strip_shears=True, **kwargs): |
1865 | 1926 |
|
1866 | 1927 | Examples |
1867 | 1928 | -------- |
1868 | | - >>> data = np.arange(24).reshape((2,3,4)) |
| 1929 | + >>> data = np.arange(24, dtype='f4').reshape((2,3,4)) |
1869 | 1930 | >>> aff = np.diag([2, 3, 4, 1]) |
1870 | 1931 | >>> img = Nifti1Pair(data, aff) |
1871 | 1932 | >>> img.get_qform() |
@@ -1948,7 +2009,7 @@ def set_sform(self, affine, code=None, **kwargs): |
1948 | 2009 |
|
1949 | 2010 | Examples |
1950 | 2011 | -------- |
1951 | | - >>> data = np.arange(24).reshape((2,3,4)) |
| 2012 | + >>> data = np.arange(24, dtype='f4').reshape((2,3,4)) |
1952 | 2013 | >>> aff = np.diag([2, 3, 4, 1]) |
1953 | 2014 | >>> img = Nifti1Pair(data, aff) |
1954 | 2015 | >>> img.get_sform() |
|
0 commit comments