diff --git a/.gitignore b/.gitignore index bbd0a11b43..df018f0ead 100644 --- a/.gitignore +++ b/.gitignore @@ -84,4 +84,3 @@ Thumbs.db doc/source/reference venv/ .buildbot.patch -nibabel/maths/bspline.c diff --git a/nibabel/transform/__init__.py b/nibabel/transform/__init__.py index 323a353cc7..be52d50c42 100644 --- a/nibabel/transform/__init__.py +++ b/nibabel/transform/__init__.py @@ -6,7 +6,8 @@ # copyright and license terms. # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -"""Geometric transforms +""" +Geometric transforms. .. currentmodule:: nibabel.transform @@ -15,7 +16,6 @@ transform """ -from __future__ import absolute_import from .linear import Affine from .nonlinear import DeformationFieldTransform diff --git a/nibabel/transform/base.py b/nibabel/transform/base.py index 832e4a1164..ada8c6a799 100644 --- a/nibabel/transform/base.py +++ b/nibabel/transform/base.py @@ -6,8 +6,7 @@ # copyright and license terms. # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -''' Common interface for transforms ''' -from __future__ import division, print_function, absolute_import +"""Common interface for transforms.""" import numpy as np import h5py @@ -15,7 +14,8 @@ class ImageSpace(object): - '''Class to represent spaces of gridded data (images)''' + """Class to represent spaces of gridded data (images).""" + __slots__ = ['_affine', '_shape', '_ndim', '_ndindex', '_coords', '_nvox', '_inverse'] @@ -92,9 +92,8 @@ def __eq__(self, other): class TransformBase(object): - ''' - Abstract image class to represent transforms - ''' + """Abstract image class to represent transforms.""" + __slots__ = ['_reference'] def __init__(self): @@ -117,11 +116,11 @@ def ndim(self): def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, output_dtype=None): - '''Resample the moving image in reference space + """ + Resample the moving image in reference space. Parameters ---------- - moving : `spatialimage` The image object containing the data to be resampled in reference space @@ -144,16 +143,14 @@ def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, Returns ------- - moved_image : `spatialimage` The moving imaged after resampling to reference space. - ''' - + """ + moving_data = np.asanyarray(moving.dataobj) if output_dtype is None: - output_dtype = moving.header.get_data_dtype() + output_dtype = moving_data.dtype - moving_data = moving.get_data() moved = ndi.geometric_transform( moving_data, mapping=self.map_voxel, @@ -171,22 +168,19 @@ def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, return moved_image def map_point(self, coords): - '''Find the coordinates in moving space corresponding to the - input reference coordinates''' + """Apply y = f(x), where x is the argument `coords`.""" raise NotImplementedError def map_voxel(self, index, moving=None): - '''Find the voxel indices in the moving image corresponding to the - input reference voxel''' + """Apply ijk' = f_ijk((i, j, k)), equivalent to the above with indexes.""" raise NotImplementedError def _to_hdf5(self, x5_root): - '''Serialize this object into the x5 file format''' + """Serialize this object into the x5 file format.""" raise NotImplementedError def to_filename(self, filename, fmt='X5'): - '''Store the transform in BIDS-Transforms HDF5 file format (.x5). - ''' + """Store the transform in BIDS-Transforms HDF5 file format (.x5).""" with h5py.File(filename, 'w') as out_file: out_file.attrs['Format'] = 'X5' out_file.attrs['Version'] = np.uint16(1) diff --git a/nibabel/transform/linear.py b/nibabel/transform/linear.py index d5b17dfef8..f33f6342a1 100644 --- a/nibabel/transform/linear.py +++ b/nibabel/transform/linear.py @@ -6,8 +6,7 @@ # copyright and license terms. # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -''' Linear transforms ''' -from __future__ import division, print_function, absolute_import +"""Linear transforms.""" import sys import numpy as np from scipy import ndimage as ndi @@ -21,15 +20,16 @@ class Affine(TransformBase): - '''Represents linear transforms on image data''' + """Represents linear transforms on image data.""" + __slots__ = ['_matrix'] def __init__(self, matrix=None, reference=None): - '''Initialize a transform + """ + Initialize a linear transform. Parameters ---------- - matrix : ndarray The inverse coordinate transformation matrix **in physical coordinates**, mapping coordinates from *reference* space @@ -38,7 +38,6 @@ def __init__(self, matrix=None, reference=None): Examples -------- - >>> xfm = Affine([[1, 0, 0, 4], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) >>> xfm.matrix # doctest: +NORMALIZE_WHITESPACE array([[1, 0, 0, 4], @@ -46,7 +45,8 @@ def __init__(self, matrix=None, reference=None): [0, 0, 1, 0], [0, 0, 0, 1]]) - ''' + """ + super(Affine, self).__init__() if matrix is None: matrix = [np.eye(4)] @@ -56,7 +56,6 @@ def __init__(self, matrix=None, reference=None): self._matrix = np.array(matrix) assert self._matrix.ndim == 3, 'affine matrix should be 3D' assert self._matrix.shape[-2] == self._matrix.shape[-1], 'affine matrix is not square' - super(Affine, self).__init__() if reference: if isinstance(reference, str): @@ -69,11 +68,11 @@ def matrix(self): def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, output_dtype=None): - '''Resample the moving image in reference space + """ + Resample the moving image in reference space. Parameters ---------- - moving : `spatialimage` The image object containing the data to be resampled in reference space @@ -96,21 +95,19 @@ def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, Returns ------- - moved_image : `spatialimage` The moving imaged after resampling to reference space. Examples -------- - >>> import nibabel as nib >>> xfm = Affine([[1, 0, 0, 4], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) >>> ref = nib.load('image.nii.gz') >>> xfm.reference = ref >>> xfm.resample(ref, order=0) - ''' + """ if output_dtype is None: output_dtype = moving.header.get_data_dtype() @@ -163,6 +160,7 @@ def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, return moved_image def map_point(self, coords, index=0, forward=True): + """Apply y = f(x), where x is the argument `coords`.""" coords = np.array(coords) if coords.shape[0] == self._matrix[index].shape[0] - 1: coords = np.append(coords, [1]) @@ -170,6 +168,7 @@ def map_point(self, coords, index=0, forward=True): return affine.dot(coords)[:-1] def map_voxel(self, index, nindex=0, moving=None): + """Apply ijk' = f_ijk((i, j, k)), equivalent to the above with indexes.""" try: reference = self.reference except ValueError: @@ -191,6 +190,7 @@ def map_voxel(self, index, nindex=0, moving=None): return tuple(matrix.dot(index)[:-1]) def _to_hdf5(self, x5_root): + """Serialize this object into the x5 file format.""" xform = x5_root.create_dataset('Transform', data=self._matrix) xform.attrs['Type'] = 'affine' x5_root.create_dataset('Inverse', data=np.linalg.inv(self._matrix)) @@ -199,9 +199,7 @@ def _to_hdf5(self, x5_root): self.reference._to_hdf5(x5_root.create_group('Reference')) def to_filename(self, filename, fmt='X5', moving=None): - '''Store the transform in BIDS-Transforms HDF5 file format (.x5). - ''' - + """Store the transform in BIDS-Transforms HDF5 file format (.x5).""" if fmt.lower() in ['itk', 'ants', 'elastix', 'nifty']: with open(filename, 'w') as f: f.write('#Insight Transform File V1.0\n') @@ -257,8 +255,7 @@ def to_filename(self, filename, fmt='X5', moving=None): def load(filename, fmt='X5', reference=None): - ''' Load a linear transform ''' - + """Load a linear transform.""" if fmt.lower() in ['itk', 'ants', 'elastix', 'nifty']: with open(filename) as itkfile: itkxfm = itkfile.read().splitlines() @@ -293,7 +290,10 @@ def load(filename, fmt='X5', reference=None): def _fsl_aff_adapt(space): - """Calculates a matrix to convert from the original RAS image + """ + Adapt FSL affines. + + Calculates a matrix to convert from the original RAS image coordinates to FSL's internal coordinate system of transforms """ aff = space.affine diff --git a/nibabel/transform/nonlinear.py b/nibabel/transform/nonlinear.py index a538628d99..e607bd144f 100644 --- a/nibabel/transform/nonlinear.py +++ b/nibabel/transform/nonlinear.py @@ -6,8 +6,7 @@ # copyright and license terms. # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -''' Common interface for transforms ''' -from __future__ import division, print_function, absolute_import +"""Nonlinear transforms.""" import numpy as np from scipy import ndimage as ndi # from gridbspline.maths import cubic @@ -19,14 +18,13 @@ class DeformationFieldTransform(TransformBase): - '''Represents a dense field of displacements (one vector per voxel)''' + """Represents a dense field of displacements (one vector per voxel).""" + __slots__ = ['_field', '_moving', '_moving_space'] __s = (slice(None), ) def __init__(self, field, reference=None): - ''' - Create a dense deformation field transform - ''' + """Create a dense deformation field transform.""" super(DeformationFieldTransform, self).__init__() self._field = field.get_data() @@ -103,11 +101,11 @@ def _cache_moving(self, moving): def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, output_dtype=None): - ''' + """ + Resample the `moving` image applying the deformation field. Examples -------- - >>> import numpy as np >>> import nibabel as nb >>> ref = nb.load('t1_weighted.nii.gz') @@ -118,16 +116,18 @@ def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, >>> new = xfm.resample(ref) >>> new.to_filename('deffield.nii.gz') - ''' + """ self._cache_moving(moving) return super(DeformationFieldTransform, self).resample( moving, order=order, mode=mode, cval=cval, prefilter=prefilter) def map_voxel(self, index, moving=None): + """Apply ijk' = f_ijk((i, j, k)), equivalent to the above with indexes.""" return tuple(self._moving[index + self.__s]) def map_coordinates(self, coordinates, order=3, mode='mirror', cval=0.0, prefilter=True): + """Apply y = f(x), where x is the argument `coords`.""" coordinates = np.array(coordinates) # Extract shapes and dimensions, then flatten ndim = coordinates.shape[-1] @@ -154,11 +154,13 @@ def map_coordinates(self, coordinates, order=3, mode='mirror', cval=0.0, class BSplineFieldTransform(TransformBase): + """Represent a nonlinear transform parameterized by BSpline basis.""" + __slots__ = ['_coeffs', '_knots', '_refknots', '_order', '_moving'] __s = (slice(None), ) def __init__(self, reference, coefficients, order=3): - '''Create a smooth deformation field using B-Spline basis''' + """Create a smooth deformation field using B-Spline basis.""" super(BSplineFieldTransform, self).__init__() self._order = order self.reference = reference @@ -216,16 +218,16 @@ def _interp_transform(self, coords): return self.reference.inverse.dot(np.hstack((coords, 1)))[:3] def map_voxel(self, index, moving=None): - '''Find the corresponding coordinates for a voxel in reference space''' + """Apply ijk' = f_ijk((i, j, k)), equivalent to the above with indexes.""" return tuple(self._moving[index + self.__s]) def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, output_dtype=None): - ''' + """ + Resample the `moving` image applying the deformation field. Examples -------- - >>> import numpy as np >>> import nibabel as nb >>> ref = nb.load('t1_weighted.nii.gz') @@ -238,7 +240,7 @@ def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True, >>> new = xfm.resample(ref) >>> new.to_filename('deffield.nii.gz') - ''' + """ self._cache_moving() return super(BSplineFieldTransform, self).resample( moving, order=order, mode=mode, cval=cval, prefilter=prefilter)