diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ddf56a5b72b4449b4079c1c63c8ebbb5f00f9d38..bdc60a5bc84f7cc3bc6e612de03c8727441c77ef 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,12 +10,26 @@ Added ^^^^^ +* New :func:`.cifti` module, providing classes and functions for working with + `CIFTI <https://www.nitrc.org/projects/cifti/>`_ data. * New :func:`.winpath` and :func:`wslpath` functions for working with paths when using FSL in a Windows Subsystem for Linux (WSL) environment. * New :func:`.wslcmd` function for generating a path to a FSL command installed in a WSL environment. * New :meth:`.Platform.fslwsl` attribute for detecting whether FSL is installed in a WSL environment. +* New :meth:`.Image.niftiDataType` property. +* The :class:`.FileTree` class has been updated to allow creation of + deep copies via the new :meth:`.FileTree.copy` method. + + +Changed +^^^^^^^ + + +* :func:`.Image` objects created from ``numpy`` arrays will be NIFTI1 or + NIFTI2, depending on the value of the ``$FSLOUTPUTTYPE`` environment + variable. Fixed diff --git a/fsl/data/constants.py b/fsl/data/constants.py index 5a870ccf03f5dd457fd39bd70137b9448891282f..00b60acb22f0e9fb71d4608f0a7d791d7d893dbd 100644 --- a/fsl/data/constants.py +++ b/fsl/data/constants.py @@ -98,6 +98,36 @@ NIFTI_UNITS_PPM = 40 NIFTI_UNITS_RADS = 48 +# NIFTI datatype codes +NIFTI_DT_NONE = 0 +NIFTI_DT_UNKNOWN = 0 +NIFTI_DT_BINARY = 1 +NIFTI_DT_UNSIGNED_CHAR = 2 +NIFTI_DT_SIGNED_SHORT = 4 +NIFTI_DT_SIGNED_INT = 8 +NIFTI_DT_FLOAT = 16 +NIFTI_DT_COMPLEX = 32 +NIFTI_DT_DOUBLE = 64 +NIFTI_DT_RGB = 128 +NIFTI_DT_ALL = 255 +NIFTI_DT_UINT8 = 2 +NIFTI_DT_INT16 = 4 +NIFTI_DT_INT32 = 8 +NIFTI_DT_FLOAT32 = 16 +NIFTI_DT_COMPLEX64 = 32 +NIFTI_DT_FLOAT64 = 64 +NIFTI_DT_RGB24 = 128 +NIFTI_DT_INT8 = 256 +NIFTI_DT_UINT16 = 512 +NIFTI_DT_UINT32 = 768 +NIFTI_DT_INT64 = 1024 +NIFTI_DT_UINT64 = 1280 +NIFTI_DT_FLOAT128 = 1536 +NIFTI_DT_COMPLEX128 = 1792 +NIFTI_DT_COMPLEX256 = 2048 +NIFTI_DT_RGBA32 = 2304 + + # NIFTI file intent codes NIFTI_INTENT_NONE = 0 NIFTI_INTENT_CORREL = 2 diff --git a/fsl/data/image.py b/fsl/data/image.py index eb9c468cc50a9831833b50bbe3cd758576297e18..a12f39b12eec26064096a23dcc9365726588e3d2 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -39,7 +39,6 @@ import json import string import logging import tempfile -import warnings import six import numpy as np @@ -560,6 +559,11 @@ class Nifti(notifier.Notifier, meta.Meta): """Returns the NIFTI intent code of this image. """ return self.header.get('intent_code', constants.NIFTI_INTENT_NONE) + @property + def niftiDataType(self): + """Returns the NIFTI data type code of this image. """ + return self.header.get('datatype', constants.NIFTI_DT_UNKNOWN) + @intent.setter def intent(self, val): @@ -1094,11 +1098,12 @@ class Image(Nifti): if header is not None: xform = header.get_best_affine() else: xform = np.identity(4) - # We default to NIFTI1 and not - # NIFTI2, because the rest of - # FSL is not yet NIFTI2 compatible. + # default to NIFTI1 if FSLOUTPUTTYPE + # is not set, just to be safe. if header is None: - ctr = nib.nifti1.Nifti1Image + outputType = os.environ.get('FSLOUTPUTTYPE', 'NIFTI_GZ') + if 'NIFTI2' in outputType: ctr = nib.Nifti2Image + else: ctr = nib.Nifti1Image # make sure that the data type is correct, # in case this header was passed in from @@ -1643,9 +1648,14 @@ def defaultExt(): # TODO: Add analyze support. options = { - 'NIFTI' : '.nii', - 'NIFTI_PAIR' : '.img', - 'NIFTI_GZ' : '.nii.gz', + 'NIFTI' : '.nii', + 'NIFTI2' : '.nii', + 'NIFTI_GZ' : '.nii.gz', + 'NIFTI2_GZ' : '.nii.gz', + 'NIFTI_PAIR' : '.img', + 'NIFTI2_PAIR' : '.img', + 'NIFTI_PAIR_GZ' : '.img.gz', + 'NIFTI2_PAIR_GZ' : '.img.gz', } outputType = os.environ.get('FSLOUTPUTTYPE', 'NIFTI_GZ') diff --git a/tests/test_image.py b/tests/test_image.py index 75ec686a9cdfba1d682d7b992fdd9820e1573512..b7de2def252e4f8bf4edcf672ebb773eafc38ee5 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -246,6 +246,11 @@ def _test_Image_atts(imgtype): allowedExts = fslimage.ALLOWED_EXTENSIONS fileGroups = fslimage.FILE_GROUPS + typeMap = {np.uint8 : constants.NIFTI_DT_UINT8, + np.int16 : constants.NIFTI_DT_INT16, + np.int32 : constants.NIFTI_DT_INT32, + np.float32 : constants.NIFTI_DT_FLOAT32, + np.float64 : constants.NIFTI_DT_FLOAT64} # (file, dims, pixdims, dtype) dtypes = [np.uint8, np.int16, np.int32, np.float32, np.double] @@ -307,14 +312,15 @@ def _test_Image_atts(imgtype): assert tuple(i.nibImage.shape) == tuple(dims) assert tuple(i.nibImage.header.get_zooms()) == tuple(pixdims) - assert i.nvals == 1 - assert i.ndim == expndims - assert i.dtype == dtype - assert i.name == op.basename(path) - assert i.dataSource == fslpath.addExt(path, - allowedExts=allowedExts, - mustExist=True, - fileGroups=fileGroups) + assert i.nvals == 1 + assert i.ndim == expndims + assert i.dtype == dtype + assert i.niftiDataType == typeMap[dtype] + assert i.name == op.basename(path) + assert i.dataSource == fslpath.addExt(path, + allowedExts=allowedExts, + mustExist=True, + fileGroups=fileGroups) i = None @@ -493,8 +499,9 @@ def test_splitExt(): def test_defaultExt(): - fslOutputTypes = ['NIFTI', 'NIFTI_PAIR', 'NIFTI_GZ'] - exts = ['.nii', '.img', '.nii.gz'] + fslOutputTypes = ['NIFTI', 'NIFTI_PAIR', 'NIFTI_GZ', 'NIFTI_PAIR_GZ', + 'NIFTI2', 'NIFTI2_PAIR', 'NIFTI2_GZ', 'NIFTI2_PAIR_GZ'] + exts = ['.nii', '.img', '.nii.gz', '.img.gz'] * 2 os.environ.pop('FSLOUTPUTTYPE', None) assert fslimage.defaultExt() == '.nii.gz' @@ -506,6 +513,35 @@ def test_defaultExt(): assert fslimage.defaultExt() == e +def test_defaultImageType(): + + fslOutputTypes = [None, + 'NIFTI', 'NIFTI_PAIR', 'NIFTI_GZ', 'NIFTI_PAIR_GZ', + 'NIFTI2', 'NIFTI2_PAIR', 'NIFTI2_GZ', 'NIFTI2_PAIR_GZ'] + exts = ['.nii.gz'] + \ + ['.nii', '.img', '.nii.gz', '.img.gz'] * 2 + + with tempdir(): + for o, e in zip(fslOutputTypes, exts): + + if o is None: + os.environ.pop('FSLOUTPUTTYPE', None) + else: + os.environ['FSLOUTPUTTYPE'] = o + + if o is None or 'NIFTI2' not in o: + exptype = nib.Nifti1Image + else: + exptype = nib.Nifti2Image + + img = fslimage.Image(np.random.randint(1, 10, (30, 30, 30))) + + assert type(img.nibImage) == exptype + + img.save('image') + assert op.exists('image' + e) + + def test_fixExt(): with tempdir():