From 68b48c48c09a0ec2a4fb1326210f85f72fc2f3bd Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Tue, 23 Jul 2024 15:18:51 +0100 Subject: [PATCH] MNT: Work-around nibabel auto-rescaling logic so that data/slope/intercept in imcp'd images is not modified --- fsl/utils/imcp.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/fsl/utils/imcp.py b/fsl/utils/imcp.py index be4b62393..2cf3651c6 100644 --- a/fsl/utils/imcp.py +++ b/fsl/utils/imcp.py @@ -50,7 +50,8 @@ def imcp(src, :func:`~fsl.data.image.defaultOutputType`. If the source file does not have the same type as the default extension, it will be converted. If ``False``, the - source file type is not changed. + source file type is used, and the destination file type + (if specified) is ignored. :arg move: If ``True``, the files are moved, instead of being copied. See :func:`immv`. @@ -143,7 +144,7 @@ def imcp(src, img = nib.load(src) - # Force conversion to specific data type if + # Force conversion to specific file format if # necessary. The file format (pair, gzipped # or not) is taken care of automatically by # nibabel @@ -151,7 +152,26 @@ def imcp(src, elif 'NIFTI2' in destType.name: cls = nib.Nifti2Image elif 'NIFTI' in destType.name: cls = nib.Nifti1Image - img = cls(np.asanyarray(img.dataobj), None, header=img.header) + # The default behaviour of nibabel when saving + # is to rescale the image data to the full range + # of the data type, and then set the scl_slope/ + # inter header fields accordingly. This is highly + # disruptive in many circumstances. Fortunately: + # - The nibabel ArrayProxy class provides a + # get_unscaled method, which allows us to + # bypass the rescaling at load time. + # - Explicitly setting the slope and intercept + # on the header allows us to bypass rescaling + # at save time. + # + # https://github.com/nipy/nibabel/issues/1001 + # https://neurostars.org/t/preserve-datatype-and-precision-with-nibabel/27641/2 + slope = img.dataobj.slope + inter = img.dataobj.inter + data = np.asanyarray(img.dataobj.get_unscaled(), + dtype=img.get_data_dtype()) + img = cls(data, None, header=img.header) + img.header.set_slope_inter(slope, inter) nib.save(img, dest) # Make sure the image reference is cleared, and -- GitLab