Source code for ants.registration.apply_transforms
__all__ = ['apply_transforms',
'apply_transforms_to_points']
import os
import ants
from ants.internal import get_lib_fn, process_arguments
[docs]
def apply_transforms(fixed, moving, transformlist,
interpolator='linear', imagetype=0,
whichtoinvert=None, compose=None,
defaultvalue=0, singleprecision=False, verbose=False, **kwargs):
"""
Apply a transform list to map an image from one domain to another.
In image registration, one computes mappings between (usually) pairs
of images. These transforms are often a sequence of increasingly
complex maps, e.g. from translation, to rigid, to affine to deformation.
The list of such transforms is passed to this function to interpolate one
image domain into the next image domain, as below. The order matters
strongly and the user is advised to familiarize with the standards
established in examples.
ANTsR function: `antsApplyTransforms`
Arguments
---------
fixed : ANTsImage
fixed image defining domain into which the moving image is transformed. The output will
have the same pixel type as this image.
moving : AntsImage
moving image to be mapped to fixed space.
transformlist : list of strings
list of transforms generated by ants.registration where each transform is a filename.
interpolator : string
Choice of interpolator. Supports partial matching.
linear
nearestNeighbor
multiLabel for label images (deprecated, prefer genericLabel)
gaussian
bSpline
cosineWindowedSinc
welchWindowedSinc
hammingWindowedSinc
lanczosWindowedSinc
genericLabel use this for label images
imagetype : integer
Integer mapping to image types as one of
0 scalar (default)
1 spatial vector in index coordinates
2 diffusion tensor in index coordinates
3 time series
4 multi-channel image (vector-valued pixels, but not spatial vectors)
6 spatial vector in physical coordinates, such as an ANTs/ITK displacement field
Default is 0 (scalar).
Index-based vectors (imagetype=1) and tensors (imagetype=2) will be rebased into the fixed index space.
They will also be reoriented to account for the physical rotation in the transform.
Physical space vectors (imagetype=6) will remain in physical coordinates, but will be reoriented to maintain
appropriate alignment after transformation.
Tensors are interpolated using a log-Euclidean framework.
Vectors are linearly interpolated component-wise. This is not optimal but probably acceptable if the neighborhood
orientations are similar. If your vector data is axial (ie, the sign is undefined) use nearestNeighbor interpolation,
or project vectors into a single hemisphere before interpolation.
whichtoinvert : list of booleans (optional)
Must be same length as transformlist.
whichtoinvert[i] is True if transformlist[i] is a matrix,
and the matrix should be inverted. If transformlist[i] is a
warp field, whichtoinvert[i] must be False.
If the transform list is a matrix followed by a warp field,
whichtoinvert defaults to (True,False). Otherwise it defaults
to [False]*len(transformlist)).
compose : string (optional)
if it is a string pointing to a valid file location,
this will force the function to return a composite transformation filename.
defaultvalue : scalar
Default voxel value for mappings outside the image domain.
singleprecision : boolean
if True, use float32 for computations. This is useful for reducing memory
usage for large datasets, at the cost of precision.
verbose : boolean
print command and run verbose application of transform.
kwargs : keyword arguments
extra parameters
Returns
-------
ANTsImage or string (transformation filename)
Example
-------
>>> import ants
>>> fixed = ants.image_read( ants.get_ants_data('r16') )
>>> moving = ants.image_read( ants.get_ants_data('r64') )
>>> fixed = ants.resample_image(fixed, (64,64), 1, 0)
>>> moving = ants.resample_image(moving, (64,64), 1, 0)
>>> mytx = ants.registration(fixed=fixed , moving=moving ,
type_of_transform = 'SyN' )
>>> mywarpedimage = ants.apply_transforms( fixed=fixed, moving=moving,
transformlist=mytx['fwdtransforms'] )
"""
if not isinstance(transformlist, (tuple, list)) and (transformlist is not None):
transformlist = [transformlist]
accepted_interpolators = {"linear", "nearestNeighbor", "multiLabel", "gaussian",
"bSpline", "cosineWindowedSinc", "welchWindowedSinc",
"hammingWindowedSinc", "lanczosWindowedSinc", "genericLabel"}
if interpolator not in accepted_interpolators:
raise ValueError('interpolator not supported - see %s' % accepted_interpolators)
args = [fixed, moving, transformlist, interpolator]
output_pixel_type = 'float' if singleprecision else 'double'
if not isinstance(fixed, str):
if ants.is_image(fixed) and ants.is_image(moving):
for tl_path in transformlist:
if not os.path.exists(tl_path):
raise Exception('Transform %s does not exist' % tl_path)
inpixeltype = fixed.pixeltype
fixed = fixed.clone(output_pixel_type)
moving = moving.clone(output_pixel_type)
warpedmovout = moving.clone(output_pixel_type)
f = fixed
m = moving
if (moving.dimension == 4) and (fixed.dimension == 3) and (imagetype == 0):
raise Exception('Set imagetype 3 to transform time series images.')
wmo = warpedmovout
mytx = []
if whichtoinvert is None or (isinstance(whichtoinvert, (tuple,list)) and (sum([w is not None for w in whichtoinvert])==0)):
if (len(transformlist) == 2) and ('.mat' in transformlist[0]) and ('.mat' not in transformlist[1]):
whichtoinvert = (True, False)
else:
whichtoinvert = tuple([False]*len(transformlist))
if len(whichtoinvert) != len(transformlist):
raise ValueError('Transform list and inversion list must be the same length')
for i in range(len(transformlist)):
ismat = False
if '.mat' in transformlist[i]:
ismat = True
if whichtoinvert[i] and (not ismat):
raise ValueError('Cannot invert transform %i (%s) because it is not a matrix' % (i, transformlist[i]))
if whichtoinvert[i]:
mytx = mytx + ['-t', '[%s,1]' % (transformlist[i])]
else:
mytx = mytx + ['-t', transformlist[i]]
if compose is None:
args = ['-d', fixed.dimension,
'-i', m,
'-o', wmo,
'-r', f,
'-n', interpolator]
args = args + mytx
if compose:
tfn = '%scomptx.nii.gz' % compose if not compose.endswith('.h5') else compose
else:
tfn = 'NA'
if compose is not None:
mycompo = '[%s,1]' % tfn
args = ['-d', fixed.dimension,
'-i', m,
'-o', mycompo,
'-r', f,
'-n', interpolator]
args = args + mytx
myargs = process_arguments(args)
myverb = int(verbose)
if verbose:
print(myargs)
processed_args = myargs + ['-z', str(1), '-v', str(myverb), '--float', str(int(singleprecision)), '-e', str(imagetype), '-f', str(defaultvalue)]
libfn = get_lib_fn('antsApplyTransforms')
retval = libfn(processed_args)
if retval != 0:
raise RuntimeError('antsApplyTransforms returned non-zero exit code %d' % retval)
if compose is None:
return warpedmovout.clone(inpixeltype)
else:
if os.path.exists(tfn):
return tfn
else:
return None
else:
raise ValueError('fixed and moving must be ANTsImage objects')
else:
args = args + ['-z', str(1), '--float', str(int(singleprecision)), '-e', imagetype, '-f', defaultvalue]
processed_args = process_arguments(args)
libfn = get_lib_fn('antsApplyTransforms')
libfn(processed_args)
def apply_transforms_to_points( dim, points, transformlist,
whichtoinvert=None, verbose=False ):
"""
Apply a transform list to map a pointset from one domain to
another. In registration, one computes mappings between pairs of
domains. These transforms are often a sequence of increasingly
complex maps, e.g. from translation, to rigid, to affine to
deformation. The list of such transforms is passed to this
function to interpolate one image domain into the next image
domain, as below. The order matters strongly and the user is
advised to familiarize with the standards established in examples.
Importantly, point mapping goes the opposite direction of image
mapping, for both reasons of convention and engineering.
ANTsR function: `antsApplyTransformsToPoints`
Arguments
---------
dim: integer
dimensionality of the transformation.
points: data frame
moving point set with n-points in rows of at least dim
columns - we maintain extra information in additional
columns. this should be a data frame with columns names x, y, z, t.
transformlist : list of strings
list of transforms generated by ants.registration where each transform is a filename.
whichtoinvert : list of booleans (optional)
Must be same length as transformlist.
whichtoinvert[i] is True if transformlist[i] is a matrix,
and the matrix should be inverted. If transformlist[i] is a
warp field, whichtoinvert[i] must be False.
If the transform list is a matrix followed by a warp field,
whichtoinvert defaults to (True,False). Otherwise it defaults
to [False]*len(transformlist)).
verbose : boolean
Returns
-------
data frame of transformed points
Example
-------
>>> import ants
>>> fixed = ants.image_read( ants.get_ants_data('r16') )
>>> moving = ants.image_read( ants.get_ants_data('r27') )
>>> reg = ants.registration( fixed, moving, 'Affine' )
>>> d = {'x': [128, 127], 'y': [101, 111]}
>>> pts = pd.DataFrame(data=d)
>>> ptsw = ants.apply_transforms_to_points( 2, pts, reg['fwdtransforms'])
"""
if not isinstance(transformlist, (tuple, list)) and (transformlist is not None):
transformlist = [transformlist]
args = [dim, points, transformlist, whichtoinvert]
for tl_path in transformlist:
if not os.path.exists(tl_path):
raise Exception('Transform %s does not exist' % tl_path)
mytx = []
if whichtoinvert is None or (isinstance(whichtoinvert, (tuple,list)) and (sum([w is not None for w in whichtoinvert])==0)):
if (len(transformlist) == 2) and ('.mat' in transformlist[0]) and ('.mat' not in transformlist[1]):
whichtoinvert = (True, False)
else:
whichtoinvert = tuple([False]*len(transformlist))
if len(whichtoinvert) != len(transformlist):
raise ValueError('Transform list and inversion list must be the same length')
for i in range(len(transformlist)):
ismat = False
if '.mat' in transformlist[i]:
ismat = True
if whichtoinvert[i] and (not ismat):
raise ValueError('Cannot invert transform %i (%s) because it is not a matrix' % (i, transformlist[i]))
if whichtoinvert[i]:
mytx = mytx + ['-t', '[%s,1]' % (transformlist[i])]
else:
mytx = mytx + ['-t', transformlist[i]]
if dim == 2:
pointsSub = points[['x','y']]
if dim == 3:
pointsSub = points[['x','y','z']]
if dim == 4:
pointsSub = points[['x','y','z','t']]
pointImage = ants.make_image( pointsSub.shape, pointsSub.values.flatten())
pointsOut = pointImage.clone()
args = ['-d', dim,
'-i', pointImage,
'-o', pointsOut ]
args = args + mytx
myargs = process_arguments(args)
myverb = int(verbose)
if verbose:
print(myargs)
processed_args = myargs + [ '-f', str(1), '--precision', str(0)]
libfn = get_lib_fn('antsApplyTransformsToPoints')
retval = libfn(processed_args)
if retval != 0:
raise RuntimeError('antsApplyTransformsToPoints returned non-zero exit code %d' % retval)
mynp = pointsOut.numpy()
pointsOutDF = points.copy()
pointsOutDF['x'] = mynp[:,0]
if dim >= 2:
pointsOutDF['y'] = mynp[:,1]
if dim >= 3:
pointsOutDF['z'] = mynp[:,2]
if dim >= 4:
pointsOutDF['t'] = mynp[:,3]
return pointsOutDF