a b/ants/registration/apply_transforms.py
1
2
3
__all__ = ['apply_transforms',
4
           'apply_transforms_to_points']
5
6
import os
7
8
import ants
9
from ants.internal import get_lib_fn, process_arguments
10
11
12
def apply_transforms(fixed, moving, transformlist,
13
                     interpolator='linear', imagetype=0,
14
                     whichtoinvert=None, compose=None,
15
                     defaultvalue=0, singleprecision=False, verbose=False, **kwargs):
16
    """
17
    Apply a transform list to map an image from one domain to another.
18
    In image registration, one computes mappings between (usually) pairs
19
    of images. These transforms are often a sequence of increasingly
20
    complex maps, e.g. from translation, to rigid, to affine to deformation.
21
    The list of such transforms is passed to this function to interpolate one
22
    image domain into the next image domain, as below. The order matters
23
    strongly and the user is advised to familiarize with the standards
24
    established in examples.
25
26
    ANTsR function: `antsApplyTransforms`
27
28
    Arguments
29
    ---------
30
    fixed : ANTsImage
31
        fixed image defining domain into which the moving image is transformed. The output will
32
        have the same pixel type as this image.
33
34
    moving : AntsImage
35
        moving image to be mapped to fixed space.
36
37
    transformlist : list of strings
38
        list of transforms generated by ants.registration where each transform is a filename.
39
40
    interpolator : string
41
        Choice of interpolator. Supports partial matching.
42
            linear
43
            nearestNeighbor
44
            multiLabel for label images (deprecated, prefer genericLabel)
45
            gaussian
46
            bSpline
47
            cosineWindowedSinc
48
            welchWindowedSinc
49
            hammingWindowedSinc
50
            lanczosWindowedSinc
51
            genericLabel use this for label images
52
53
    imagetype : integer
54
        choose 0/1/2/3 mapping to scalar/vector/tensor/time-series
55
56
    whichtoinvert : list of booleans (optional)
57
        Must be same length as transformlist.
58
        whichtoinvert[i] is True if transformlist[i] is a matrix,
59
        and the matrix should be inverted. If transformlist[i] is a
60
        warp field, whichtoinvert[i] must be False.
61
        If the transform list is a matrix followed by a warp field,
62
        whichtoinvert defaults to (True,False). Otherwise it defaults
63
        to [False]*len(transformlist)).
64
65
    compose : string (optional)
66
        if it is a string pointing to a valid file location,
67
        this will force the function to return a composite transformation filename.
68
69
    defaultvalue : scalar
70
        Default voxel value for mappings outside the image domain.
71
72
    singleprecision : boolean
73
        if True, use float32 for computations. This is useful for reducing memory
74
        usage for large datasets, at the cost of precision.
75
76
    verbose : boolean
77
        print command and run verbose application of transform.
78
79
    kwargs : keyword arguments
80
        extra parameters
81
82
    Returns
83
    -------
84
    ANTsImage or string (transformation filename)
85
86
    Example
87
    -------
88
    >>> import ants
89
    >>> fixed = ants.image_read( ants.get_ants_data('r16') )
90
    >>> moving = ants.image_read( ants.get_ants_data('r64') )
91
    >>> fixed = ants.resample_image(fixed, (64,64), 1, 0)
92
    >>> moving = ants.resample_image(moving, (64,64), 1, 0)
93
    >>> mytx = ants.registration(fixed=fixed , moving=moving ,
94
                                 type_of_transform = 'SyN' )
95
    >>> mywarpedimage = ants.apply_transforms( fixed=fixed, moving=moving,
96
                                               transformlist=mytx['fwdtransforms'] )
97
    """
98
99
    if not isinstance(transformlist, (tuple, list)) and (transformlist is not None):
100
        transformlist = [transformlist]
101
102
    accepted_interpolators = {"linear", "nearestNeighbor", "multiLabel", "gaussian",
103
                        "bSpline", "cosineWindowedSinc", "welchWindowedSinc",
104
                        "hammingWindowedSinc", "lanczosWindowedSinc", "genericLabel"}
105
106
    if interpolator not in accepted_interpolators:
107
        raise ValueError('interpolator not supported - see %s' % accepted_interpolators)
108
109
    args = [fixed, moving, transformlist, interpolator]
110
111
    output_pixel_type = 'float' if singleprecision else 'double'
112
113
    if not isinstance(fixed, str):
114
        if ants.is_image(fixed) and ants.is_image(moving):
115
            for tl_path in transformlist:
116
                if not os.path.exists(tl_path):
117
                    raise Exception('Transform %s does not exist' % tl_path)
118
119
            inpixeltype = fixed.pixeltype
120
            fixed = fixed.clone(output_pixel_type)
121
            moving = moving.clone(output_pixel_type)
122
            warpedmovout = moving.clone(output_pixel_type)
123
            f = fixed
124
            m = moving
125
            if (moving.dimension == 4) and (fixed.dimension == 3) and (imagetype == 0):
126
                raise Exception('Set imagetype 3 to transform time series images.')
127
128
            wmo = warpedmovout
129
            mytx = []
130
            if whichtoinvert is None or (isinstance(whichtoinvert, (tuple,list)) and (sum([w is not None for w in whichtoinvert])==0)):
131
                if (len(transformlist) == 2) and ('.mat' in transformlist[0]) and ('.mat' not in transformlist[1]):
132
                    whichtoinvert = (True, False)
133
                else:
134
                    whichtoinvert = tuple([False]*len(transformlist))
135
136
            if len(whichtoinvert) != len(transformlist):
137
                raise ValueError('Transform list and inversion list must be the same length')
138
139
            for i in range(len(transformlist)):
140
                ismat = False
141
                if '.mat' in transformlist[i]:
142
                    ismat = True
143
                if whichtoinvert[i] and (not ismat):
144
                    raise ValueError('Cannot invert transform %i (%s) because it is not a matrix' % (i, transformlist[i]))
145
                if whichtoinvert[i]:
146
                    mytx = mytx + ['-t', '[%s,1]' % (transformlist[i])]
147
                else:
148
                    mytx = mytx + ['-t', transformlist[i]]
149
150
            if compose is None:
151
                args = ['-d', fixed.dimension,
152
                        '-i', m,
153
                        '-o', wmo,
154
                        '-r', f,
155
                        '-n', interpolator]
156
                args = args + mytx
157
            if compose:
158
                tfn = '%scomptx.nii.gz' % compose if not compose.endswith('.h5') else compose
159
            else:
160
                tfn = 'NA'
161
            if compose is not None:
162
                mycompo = '[%s,1]' % tfn
163
                args = ['-d', fixed.dimension,
164
                        '-i', m,
165
                        '-o', mycompo,
166
                        '-r', f,
167
                        '-n', interpolator]
168
                args = args + mytx
169
170
            myargs = process_arguments(args)
171
172
            myverb = int(verbose)
173
            if verbose:
174
                print(myargs)
175
176
            processed_args = myargs + ['-z', str(1), '-v', str(myverb), '--float', str(int(singleprecision)), '-e', str(imagetype), '-f', str(defaultvalue)]
177
            libfn = get_lib_fn('antsApplyTransforms')
178
            libfn(processed_args)
179
180
            if compose is None:
181
                return warpedmovout.clone(inpixeltype)
182
            else:
183
                if os.path.exists(tfn):
184
                    return tfn
185
                else:
186
                    return None
187
188
        else:
189
            return 1
190
    else:
191
        args = args + ['-z', str(1), '--float', str(int(singleprecision)), '-e', imagetype, '-f', defaultvalue]
192
        processed_args = process_arguments(args)
193
        libfn = get_lib_fn('antsApplyTransforms')
194
        libfn(processed_args)
195
196
197
198
199
200
201
def apply_transforms_to_points( dim, points, transformlist,
202
                     whichtoinvert=None, verbose=False ):
203
    """
204
     Apply a transform list to map a pointset from one domain to
205
     another. In registration, one computes mappings between pairs of
206
     domains. These transforms are often a sequence of increasingly
207
     complex maps, e.g. from translation, to rigid, to affine to
208
     deformation.  The list of such transforms is passed to this
209
     function to interpolate one image domain into the next image
210
     domain, as below.  The order matters strongly and the user is
211
     advised to familiarize with the standards established in examples.
212
     Importantly, point mapping goes the opposite direction of image
213
     mapping, for both reasons of convention and engineering.
214
215
    ANTsR function: `antsApplyTransformsToPoints`
216
217
    Arguments
218
    ---------
219
    dim: integer
220
         dimensionality of the transformation.
221
222
    points: data frame
223
          moving point set with n-points in rows of at least dim
224
          columns - we maintain extra information in additional
225
          columns. this should be a data frame with columns names x, y, z, t.
226
227
    transformlist : list of strings
228
        list of transforms generated by ants.registration where each transform is a filename.
229
230
    whichtoinvert : list of booleans (optional)
231
        Must be same length as transformlist.
232
        whichtoinvert[i] is True if transformlist[i] is a matrix,
233
        and the matrix should be inverted. If transformlist[i] is a
234
        warp field, whichtoinvert[i] must be False.
235
        If the transform list is a matrix followed by a warp field,
236
        whichtoinvert defaults to (True,False). Otherwise it defaults
237
        to [False]*len(transformlist)).
238
239
    verbose : boolean
240
241
    Returns
242
    -------
243
    data frame of transformed points
244
245
    Example
246
    -------
247
    >>> import ants
248
    >>> fixed = ants.image_read( ants.get_ants_data('r16') )
249
    >>> moving = ants.image_read( ants.get_ants_data('r27') )
250
    >>> reg = ants.registration( fixed, moving, 'Affine' )
251
    >>> d = {'x': [128, 127], 'y': [101, 111]}
252
    >>> pts = pd.DataFrame(data=d)
253
    >>> ptsw = ants.apply_transforms_to_points( 2, pts, reg['fwdtransforms'])
254
    """
255
256
    if not isinstance(transformlist, (tuple, list)) and (transformlist is not None):
257
        transformlist = [transformlist]
258
259
    args = [dim, points, transformlist, whichtoinvert]
260
261
    for tl_path in transformlist:
262
        if not os.path.exists(tl_path):
263
            raise Exception('Transform %s does not exist' % tl_path)
264
265
    mytx = []
266
267
    if whichtoinvert is None or (isinstance(whichtoinvert, (tuple,list)) and (sum([w is not None for w in whichtoinvert])==0)):
268
        if (len(transformlist) == 2) and ('.mat' in transformlist[0]) and ('.mat' not in transformlist[1]):
269
            whichtoinvert = (True, False)
270
        else:
271
            whichtoinvert = tuple([False]*len(transformlist))
272
273
    if len(whichtoinvert) != len(transformlist):
274
        raise ValueError('Transform list and inversion list must be the same length')
275
276
    for i in range(len(transformlist)):
277
        ismat = False
278
        if '.mat' in transformlist[i]:
279
            ismat = True
280
        if whichtoinvert[i] and (not ismat):
281
            raise ValueError('Cannot invert transform %i (%s) because it is not a matrix' % (i, transformlist[i]))
282
        if whichtoinvert[i]:
283
            mytx = mytx + ['-t', '[%s,1]' % (transformlist[i])]
284
        else:
285
            mytx = mytx + ['-t', transformlist[i]]
286
    if dim == 2:
287
        pointsSub = points[['x','y']]
288
    if dim == 3:
289
        pointsSub = points[['x','y','z']]
290
    if dim == 4:
291
        pointsSub = points[['x','y','z','t']]
292
    pointImage = ants.make_image( pointsSub.shape, pointsSub.values.flatten())
293
    pointsOut = pointImage.clone()
294
    args = ['-d', dim,
295
            '-i', pointImage,
296
            '-o', pointsOut ]
297
    args = args + mytx
298
    myargs = process_arguments(args)
299
300
    myverb = int(verbose)
301
    if verbose:
302
        print(myargs)
303
304
    processed_args = myargs + [ '-f', str(1), '--precision', str(0)]
305
    libfn = get_lib_fn('antsApplyTransformsToPoints')
306
    libfn(processed_args)
307
    mynp = pointsOut.numpy()
308
    pointsOutDF = points.copy()
309
    pointsOutDF['x'] = mynp[:,0]
310
    if dim >= 2:
311
        pointsOutDF['y'] = mynp[:,1]
312
    if dim >= 3:
313
        pointsOutDF['z'] = mynp[:,2]
314
    if dim >= 4:
315
        pointsOutDF['t'] = mynp[:,3]
316
    return pointsOutDF