Skip to content
Snippets Groups Projects
Commit 9d5c986e authored by Paul McCarthy's avatar Paul McCarthy :mountain_bicyclist:
Browse files

Merge branch 'rf/resample_$d' into 'master'

Rf/resample $d

See merge request fsl/fslpy!86
parents 16496d1d 9b9d4460
No related branches found
No related tags found
No related merge requests found
Pipeline #2959 canceled
...@@ -21,6 +21,8 @@ Changed ...@@ -21,6 +21,8 @@ Changed
* Minimum required version of ``nibabel`` is now 2.3. * Minimum required version of ``nibabel`` is now 2.3.
* The :class:`.Image` class now fully delegates to ``nibabel`` for managing * The :class:`.Image` class now fully delegates to ``nibabel`` for managing
file handles. file handles.
* The :meth:`.Image.resample` method now supports images with more than three
dimensions.
Removed Removed
...@@ -38,6 +40,7 @@ Fixed ...@@ -38,6 +40,7 @@ Fixed
* Make sure that FEAT ``Cluster`` objects (created by the * Make sure that FEAT ``Cluster`` objects (created by the
:func:`.loadClusterResults` function) contain ``p`` and ``logp`` attributes, :func:`.loadClusterResults` function) contain ``p`` and ``logp`` attributes,
even when cluster thresholding was not used. even when cluster thresholding was not used.
* Fix to the :class:`.ImageWrapper` regarding complex data types.
1.13.0 (Thursday 22nd November 2018) 1.13.0 (Thursday 22nd November 2018)
......
...@@ -1173,7 +1173,7 @@ class Image(Nifti): ...@@ -1173,7 +1173,7 @@ class Image(Nifti):
order=1, order=1,
smooth=True): smooth=True):
"""Returns a copy of the data in this ``Image``, resampled to the """Returns a copy of the data in this ``Image``, resampled to the
specified ``shape``. specified ``newShape``.
:arg newShape: Desired shape. May containg floating point values, :arg newShape: Desired shape. May containg floating point values,
in which case the resampled image will have shape in which case the resampled image will have shape
...@@ -1182,7 +1182,7 @@ class Image(Nifti): ...@@ -1182,7 +1182,7 @@ class Image(Nifti):
:arg sliceobj: Slice into this ``Image``. If ``None``, the whole :arg sliceobj: Slice into this ``Image``. If ``None``, the whole
image is resampled, and it is assumed that it has the image is resampled, and it is assumed that it has the
same number of dimensions as ``shape``. A same number of dimensions as ``newShape``. A
:exc:`ValueError` is raised if this is not the case. :exc:`ValueError` is raised if this is not the case.
:arg dtype: ``numpy`` data type of the resampled data. If ``None``, :arg dtype: ``numpy`` data type of the resampled data. If ``None``,
...@@ -1201,12 +1201,12 @@ class Image(Nifti): ...@@ -1201,12 +1201,12 @@ class Image(Nifti):
:returns: A tuple containing: :returns: A tuple containing:
- A ``numpy`` array of shape ``shape``, containing an - A ``numpy`` array of shape ``newShape``, containing
interpolated copy of the data in this ``Image``. an interpolated copy of the data in this ``Image``.
- A ``numpy`` array of shape ``(4, 4)``, containing the - A ``numpy`` array of shape ``(4, 4)``, containing the
adjusted voxel-to-world transformation for the resampled adjusted voxel-to-world transformation for the spatial
data. dimensions of the resampled data.
""" """
if sliceobj is None: sliceobj = slice(None) if sliceobj is None: sliceobj = slice(None)
...@@ -1225,7 +1225,7 @@ class Image(Nifti): ...@@ -1225,7 +1225,7 @@ class Image(Nifti):
ratio = oldShape / newShape ratio = oldShape / newShape
newShape = np.array(np.round(newShape), dtype=np.int) newShape = np.array(np.round(newShape), dtype=np.int)
scale = transform.scaleOffsetXform(ratio, 0) scale = np.diag(ratio)
# If interpolating and smoothing, we apply a # If interpolating and smoothing, we apply a
# gaussian filter along axes with a resampling # gaussian filter along axes with a resampling
...@@ -1242,7 +1242,7 @@ class Image(Nifti): ...@@ -1242,7 +1242,7 @@ class Image(Nifti):
data = ndimage.gaussian_filter(data, sigma) data = ndimage.gaussian_filter(data, sigma)
data = ndimage.affine_transform(data, data = ndimage.affine_transform(data,
scale[:3, :3], scale,
output_shape=newShape, output_shape=newShape,
order=order) order=order)
...@@ -1250,6 +1250,7 @@ class Image(Nifti): ...@@ -1250,6 +1250,7 @@ class Image(Nifti):
# puts the resampled image into the # puts the resampled image into the
# same world coordinate system as this # same world coordinate system as this
# image. # image.
scale = transform.scaleOffsetXform(ratio[:3], 0)
xform = transform.concat(self.voxToWorldMat, scale) xform = transform.concat(self.voxToWorldMat, scale)
else: else:
xform = self.voxToWorldMat xform = self.voxToWorldMat
......
...@@ -301,7 +301,14 @@ class ImageWrapper(notifier.Notifier): ...@@ -301,7 +301,14 @@ class ImageWrapper(notifier.Notifier):
# Internally, we calculate and store the # Internally, we calculate and store the
# data range for each volume/slice/vector # data range for each volume/slice/vector
self.__volRanges = np.zeros((nvols, 2), dtype=np.float32) #
# We use nan as a placeholder, so the
# dtype must be non-integral
dtype = self.__image.get_data_dtype()
if np.issubdtype(dtype, np.integer):
dtype = np.float32
self.__volRanges = np.zeros((nvols, 2),
dtype=dtype)
self.__coverage[ :] = np.nan self.__coverage[ :] = np.nan
self.__volRanges[:] = np.nan self.__volRanges[:] = np.nan
......
...@@ -1078,6 +1078,12 @@ def test_image_resample(seed): ...@@ -1078,6 +1078,12 @@ def test_image_resample(seed):
make_random_image(fname, shape) make_random_image(fname, shape)
img = fslimage.Image(fname, mmap=False) img = fslimage.Image(fname, mmap=False)
# bad shape
with pytest.raises(ValueError):
img.resample((10, 10))
with pytest.raises(ValueError):
img.resample((10, 10, 10, 10))
# resampling to the same shape should be a no-op # resampling to the same shape should be a no-op
samei, samex = img.resample(shape) samei, samex = img.resample(shape)
assert np.all(samei == img[:]) assert np.all(samei == img[:])
...@@ -1134,18 +1140,50 @@ def test_image_resample(seed): ...@@ -1134,18 +1140,50 @@ def test_image_resample(seed):
assert np.all(np.isclose(resvals, origvals)) assert np.all(np.isclose(resvals, origvals))
# Test a 4D image del img
img = None
def test_image_resample_4d(seed):
fname = 'test.nii.gz'
with tempdir():
make_random_image(fname, (10, 10, 10, 10)) make_random_image(fname, (10, 10, 10, 10))
# resample one volume
img = fslimage.Image(fname) img = fslimage.Image(fname)
slc = (slice(None), slice(None), slice(None), 3) slc = (slice(None), slice(None), slice(None), 3)
resampled = img.resample(img.shape[:3], slc)[0] resampled = img.resample(img.shape[:3], slc)[0]
assert np.all(resampled == img[..., 3]) assert np.all(resampled == img[..., 3])
# resample up
resampled = img.resample((15, 15, 15), slc)[0] resampled = img.resample((15, 15, 15), slc)[0]
assert tuple(resampled.shape) == (15, 15, 15) assert tuple(resampled.shape) == (15, 15, 15)
# resample down
resampled = img.resample((5, 5, 5), slc)[0]
assert tuple(resampled.shape) == (5, 5, 5)
# resample the entire image
resampled = img.resample((15, 15, 15, 10), None)[0]
assert tuple(resampled.shape) == (15, 15, 15, 10)
resampled = img.resample((5, 5, 5, 10), None)[0]
assert tuple(resampled.shape) == (5, 5, 5, 10)
# resample along the fourth dim
resampled = img.resample((15, 15, 15, 15), None)[0]
assert tuple(resampled.shape) == (15, 15, 15, 15)
resampled = img.resample((5, 5, 5, 15), None)[0]
assert tuple(resampled.shape) == (5, 5, 5, 15)
del img del img
del resampled
img = None img = None
resampled = None
def test_Image_init_xform_nifti1(): _test_Image_init_xform(1) def test_Image_init_xform_nifti1(): _test_Image_init_xform(1)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment