diff --git a/fsl/data/atlases.py b/fsl/data/atlases.py index 59122134fdacf6da3d2fc940ad996ede9f0af969..6ed038f1a15edbe4acd090b7bc4af55f4778d852 100644 --- a/fsl/data/atlases.py +++ b/fsl/data/atlases.py @@ -675,6 +675,42 @@ class Atlas(fslimage.Image): return self.desc.find(*args, **kwargs) + def prepareMask(self, mask): + """Makes sure that the given mask has the same resolution as this + atlas, so it can be used for querying. Used by the + :meth:`.LabelAtlas.maskLabels` and + :meth:`.ProbabilisticAtlas.maskProportions` methods. + + :arg mask: A :class:`.Image` + + :returns: A ``numpy`` array containing the resampled mask data. + + :raises: A :exc:`MaskError` if the mask is not in the same space as + this atlas, or does not have three dimensions. + """ + + # Make sure that the mask has the same + # number of voxels as the atlas image. + # Use nearest neighbour interpolation + # for resampling, as it is most likely + # that the mask is binary. + try: + mask, xform = mask.resample(self.shape[:3], + dtype=np.float32, + order=0) + + except ValueError: + raise MaskError('Mask has wrong number of dimensions') + + # TODO allow non-aligned mask - as long as it overlaps + # in world coordinates, it should be allowed + if not fslimage.Image(mask, xform=xform).sameSpace(self): + raise MaskError('Mask is not in the same space as atlas') + + return mask + + + class MaskError(Exception): """Exception raised by the :meth:`LabelAtlas.maskLabel` and :meth:`ProbabilisticAtlas.maskProportions` when a mask is provided which @@ -781,23 +817,10 @@ class LabelAtlas(Atlas): associated with each returned value. """ - # Make sure that the mask has the same - # number of voxels as the atlas image. - # Use nearest neighbour interpolation - # for resampling, as it is most likely - # that the mask is binary. - mask, xform = mask.resample(self.shape[:3], dtype=np.float32, order=0) - - if not fslimage.Image(mask, xform=xform).sameSpace(self): - raise MaskError('Mask is not in the same space as atlas') - - # TODO allow non-aligned mask - as long as it overlaps - # in world coordinates, it should be allowed - - # Extract the values that are in # the mask, and their corresponding # mask weights + mask = self.prepareMask(mask) boolmask = mask > 0 vals = self[boolmask] weights = mask[boolmask] @@ -924,13 +947,7 @@ class ProbabilisticAtlas(Atlas): props = [] - # Make sure that the mask has the same - # number of voxels as the atlas image - mask, xform = mask.resample(self.shape[:3], dtype=np.float32, order=0) - - if not fslimage.Image(mask, xform=xform).sameSpace(self): - raise MaskError('Mask is not in the same space as atlas') - + mask = self.prepareMask(mask) boolmask = mask > 0 weights = mask[boolmask] weightsum = weights.sum() diff --git a/fsl/data/image.py b/fsl/data/image.py index b1c93c0873ab37fd2e620e0d391b65f276d86df3..3b3e870415c36118ac66c77cee5c7236907e7290 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -1149,7 +1149,8 @@ class Image(Nifti): :arg sliceobj: Slice into this ``Image``. If ``None``, the whole image is resampled, and it is assumed that it has the - same number of dimensions as ``shape``. + same number of dimensions as ``shape``. A + :exc:`ValueError` is raised if this is not the case. :arg dtype: ``numpy`` data type of the resampled data. If ``None``, the :meth:`dtype` of this ``Image`` is used. @@ -1184,6 +1185,9 @@ class Image(Nifti): oldShape = np.array(data.shape, dtype=np.float) newShape = np.array(newShape, dtype=np.float) + if len(oldShape) != len(newShape): + raise ValueError('Shapes don\'t match') + if not np.all(np.isclose(oldShape, newShape)): ratio = oldShape / newShape