diff --git a/fsl/data/image.py b/fsl/data/image.py index c6f83c5988b1317ca1e2b691f499d5c339b10d26..c03618d856931ee96c0ab037ba530aa42d4c1a76 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -771,60 +771,64 @@ class Image(Nifti): calcRange=True, indexed=False, threaded=False, + dataSource=None, **kwargs): """Create an ``Image`` object with the given image data or file name. - :arg image: A string containing the name of an image file to load, - or a :mod:`numpy` array, or a :mod:`nibabel` image - object. - - :arg name: A name for the image. - - :arg header: If not ``None``, assumed to be a - :class:`nibabel.nifti1.Nifti1Header` or - :class:`nibabel.nifti2.Nifti2Header` to be used as the - image header. Not applied to images loaded from file, - or existing :mod:`nibabel` images. - - :arg xform: A :math:`4\\times 4` affine transformation matrix - which transforms voxel coordinates into real world - coordinates. If not provided, and a ``header`` is - provided, the transformation in the header is used. - If neither a ``xform`` nor a ``header`` are provided, - an identity matrix is used. If both a ``xform`` and a - ``header`` are provided, the ``xform`` is used in - preference to the header transformation. - - :arg loadData: If ``True`` (the default) the image data is loaded - in to memory. Otherwise, only the image header - information is read, and the image data is kept - from disk. In either case, the image data is - accessed through an :class:`.ImageWrapper` instance. - The data may be loaded into memory later on via the - :meth:`loadData` method. - - :arg calcRange: If ``True`` (the default), the image range is - calculated immediately (vi a call to - :meth:`calcRange`). Otherwise, the image range is - incrementally updated as more data is read from memory - or disk. - - :arg indexed: If ``True``, and the file is gzipped, it is opened - using the :mod:`indexed_gzip` package. Otherwise the - file is opened by ``nibabel``. Ignored if ``loadData`` - is ``True``. - - :arg threaded: If ``True``, the :class:`.ImageWrapper` will use a - separate thread for data range calculation. Defaults - to ``False``. Ignored if ``loadData`` is ``True``. + :arg image: A string containing the name of an image file to load, + or a :mod:`numpy` array, or a :mod:`nibabel` image + object. + + :arg name: A name for the image. + + :arg header: If not ``None``, assumed to be a + :class:`nibabel.nifti1.Nifti1Header` or + :class:`nibabel.nifti2.Nifti2Header` to be used as the + image header. Not applied to images loaded from file, + or existing :mod:`nibabel` images. + + :arg xform: A :math:`4\\times 4` affine transformation matrix + which transforms voxel coordinates into real world + coordinates. If not provided, and a ``header`` is + provided, the transformation in the header is used. + If neither a ``xform`` nor a ``header`` are provided, + an identity matrix is used. If both a ``xform`` and a + ``header`` are provided, the ``xform`` is used in + preference to the header transformation. + + :arg loadData: If ``True`` (the default) the image data is loaded + in to memory. Otherwise, only the image header + information is read, and the image data is kept + from disk. In either case, the image data is + accessed through an :class:`.ImageWrapper` instance. + The data may be loaded into memory later on via the + :meth:`loadData` method. + + :arg calcRange: If ``True`` (the default), the image range is + calculated immediately (vi a call to + :meth:`calcRange`). Otherwise, the image range is + incrementally updated as more data is read from memory + or disk. + + :arg indexed: If ``True``, and the file is gzipped, it is opened + using the :mod:`indexed_gzip` package. Otherwise the + file is opened by ``nibabel``. Ignored if ``loadData`` + is ``True``. + + :arg threaded: If ``True``, the :class:`.ImageWrapper` will use a + separate thread for data range calculation. Defaults + to ``False``. Ignored if ``loadData`` is ``True``. + + :arg dataSource: If ``image`` is not a file name, this argument may be + used to specify the file from which the image was + loaded. All other arguments are passed through to the ``nibabel.load`` function (if it is called). """ - nibImage = None - dataSource = None - fileobj = None + nibImage = None + fileobj = None if loadData: indexed = False @@ -1132,6 +1136,10 @@ class Image(Nifti): filename = op.abspath(filename) + # make sure the extension is specified + if not looksLikeImage(filename): + filename = addExt(filename, mustExist=False) + log.debug('Saving {} to {}'.format(self.name, filename)) # If this Image is not managing its diff --git a/fsl/data/mghimage.py b/fsl/data/mghimage.py index c1530d493993253962493dd9776611145a888235..ba352c3e64f396b788f9b613383a55cbdb6a49c1 100644 --- a/fsl/data/mghimage.py +++ b/fsl/data/mghimage.py @@ -19,6 +19,7 @@ import os.path as op import six import nibabel as nib +import fsl.utils.path as fslpath import fsl.data.image as fslimage @@ -54,7 +55,7 @@ class MGHImage(fslimage.Image): name = op.basename(filename) image = nib.load(image) else: - name = None + name = 'MGH image' filename = None data = image.get_data() @@ -63,12 +64,26 @@ class MGHImage(fslimage.Image): fslimage.Image.__init__(self, data, xform=affine, - name=name) + name=name, + dataSource=filename) if filename is not None: self.setMeta('mghImageFile', filename) + def save(self, filename=None): + """Overrides :meth:`.Image.save`. If a ``filename`` is not provided, + converts the original (MGH) file name into a NIFTI filename, before + passing it to the :meth:`.Image.save` method. + """ + if filename is None: + filename = self.dataSource + + filename = fslpath.removeExt(filename, ALLOWED_EXTENSIONS) + + return fslimage.Image.save(self, filename) + + @property def mghImageFile(self): """If this ``MGHImage`` was loaded from a file, returns the file