24
24
class Affine (TransformBase ):
25
25
"""Represents linear transforms on image data."""
26
26
27
- __slots__ = (' _matrix' , )
27
+ __slots__ = (" _matrix" , )
28
28
29
29
def __init__ (self , matrix = None , reference = None ):
30
30
"""
@@ -40,7 +40,7 @@ def __init__(self, matrix=None, reference=None):
40
40
41
41
Examples
42
42
--------
43
- >>> xfm = Affine(reference=datadir / ' someones_anatomy.nii.gz' )
43
+ >>> xfm = Affine(reference=datadir / " someones_anatomy.nii.gz" )
44
44
>>> xfm.matrix # doctest: +NORMALIZE_WHITESPACE
45
45
array([[1., 0., 0., 0.],
46
46
[0., 1., 0., 0.],
@@ -61,13 +61,17 @@ def __init__(self, matrix=None, reference=None):
61
61
if matrix is not None :
62
62
matrix = np .array (matrix )
63
63
if matrix .ndim != 2 :
64
- raise TypeError (' Affine should be 2D.' )
64
+ raise TypeError (" Affine should be 2D." )
65
65
elif matrix .shape [0 ] != matrix .shape [1 ]:
66
- raise TypeError (' Matrix is not square.' )
66
+ raise TypeError (" Matrix is not square." )
67
67
self ._matrix = matrix
68
68
69
- if np .any (self ._matrix [3 , :] != (0 , 0 , 0 , 1 )):
70
- raise ValueError ("Matrix does not represent a valid transform." )
69
+ if not np .allclose (self ._matrix [3 , :], (0 , 0 , 0 , 1 )):
70
+ raise ValueError ("""The last row of a homogeneus matrix \
71
+ should be (0, 0, 0, 1), got %s.""" % self ._matrix [3 , :])
72
+
73
+ # Normalize last row
74
+ self ._matrix [3 , :] = (0 , 0 , 0 , 1 )
71
75
72
76
def __eq__ (self , other ):
73
77
"""
@@ -83,7 +87,7 @@ def __eq__(self, other):
83
87
"""
84
88
_eq = np .allclose (self .matrix , other .matrix , rtol = EQUALITY_TOL )
85
89
if _eq and self ._reference != other ._reference :
86
- warnings .warn (' Affines are equal, but references do not match.' )
90
+ warnings .warn (" Affines are equal, but references do not match." )
87
91
return _eq
88
92
89
93
@property
@@ -125,16 +129,16 @@ def map(self, x, inverse=False):
125
129
126
130
def _to_hdf5 (self , x5_root ):
127
131
"""Serialize this object into the x5 file format."""
128
- xform = x5_root .create_dataset (' Transform' , data = [self ._matrix ])
129
- xform .attrs [' Type' ] = ' affine'
130
- x5_root .create_dataset (' Inverse' , data = [np .linalg .inv (self ._matrix )])
132
+ xform = x5_root .create_dataset (" Transform" , data = [self ._matrix ])
133
+ xform .attrs [" Type" ] = " affine"
134
+ x5_root .create_dataset (" Inverse" , data = [np .linalg .inv (self ._matrix )])
131
135
132
136
if self ._reference :
133
- self .reference ._to_hdf5 (x5_root .create_group (' Reference' ))
137
+ self .reference ._to_hdf5 (x5_root .create_group (" Reference" ))
134
138
135
- def to_filename (self , filename , fmt = 'X5' , moving = None ):
139
+ def to_filename (self , filename , fmt = "X5" , moving = None ):
136
140
"""Store the transform in BIDS-Transforms HDF5 file format (.x5)."""
137
- if fmt .lower () in [' itk' , ' ants' , ' elastix' ]:
141
+ if fmt .lower () in [" itk" , " ants" , " elastix" ]:
138
142
itkobj = io .itk .ITKLinearTransform .from_ras (self .matrix )
139
143
itkobj .to_filename (filename )
140
144
return filename
@@ -145,45 +149,45 @@ def to_filename(self, filename, fmt='X5', moving=None):
145
149
else :
146
150
moving = self .reference
147
151
148
- if fmt .lower () == ' afni' :
152
+ if fmt .lower () == " afni" :
149
153
afniobj = io .afni .AFNILinearTransform .from_ras (
150
154
self .matrix , moving = moving , reference = self .reference )
151
155
afniobj .to_filename (filename )
152
156
return filename
153
157
154
- if fmt .lower () == ' fsl' :
158
+ if fmt .lower () == " fsl" :
155
159
fslobj = io .fsl .FSLLinearTransform .from_ras (
156
160
self .matrix , moving = moving , reference = self .reference
157
161
)
158
162
fslobj .to_filename (filename )
159
163
return filename
160
164
161
- if fmt .lower () == 'fs' :
165
+ if fmt .lower () == "fs" :
162
166
# xform info
163
167
lt = io .LinearTransform ()
164
- lt [' sigma' ] = 1.
165
- lt [' m_L' ] = self .matrix
168
+ lt [" sigma" ] = 1.
169
+ lt [" m_L" ] = self .matrix
166
170
# Just for reference, nitransforms does not write VOX2VOX
167
- lt [' src' ] = io .VolumeGeometry .from_image (moving )
168
- lt [' dst' ] = io .VolumeGeometry .from_image (self .reference )
171
+ lt [" src" ] = io .VolumeGeometry .from_image (moving )
172
+ lt [" dst" ] = io .VolumeGeometry .from_image (self .reference )
169
173
# to make LTA file format
170
174
lta = io .LinearTransformArray ()
171
- lta [' type' ] = 1 # RAS2RAS
172
- lta [' xforms' ].append (lt )
175
+ lta [" type" ] = 1 # RAS2RAS
176
+ lta [" xforms" ].append (lt )
173
177
174
- with open (filename , 'w' ) as f :
178
+ with open (filename , "w" ) as f :
175
179
f .write (lta .to_string ())
176
180
return filename
177
181
178
182
raise NotImplementedError
179
183
180
184
@classmethod
181
- def from_filename (cls , filename , fmt = 'X5' ,
185
+ def from_filename (cls , filename , fmt = "X5" ,
182
186
reference = None , moving = None ):
183
187
"""Create an affine from a transform file."""
184
- if fmt .lower () in (' itk' , ' ants' , ' elastix' ):
188
+ if fmt .lower () in (" itk" , " ants" , " elastix" ):
185
189
_factory = io .itk .ITKLinearTransformArray
186
- elif fmt .lower () in (' lta' , 'fs' ):
190
+ elif fmt .lower () in (" lta" , "fs" ):
187
191
_factory = io .LinearTransformArray
188
192
else :
189
193
raise NotImplementedError
@@ -193,7 +197,7 @@ def from_filename(cls, filename, fmt='X5',
193
197
if cls == Affine :
194
198
if np .shape (matrix )[0 ] != 1 :
195
199
raise TypeError (
196
- ' Cannot load transform array "%s"' % filename )
200
+ " Cannot load transform array '%s'" % filename )
197
201
matrix = matrix [0 ]
198
202
return cls (matrix , reference = reference )
199
203
@@ -297,9 +301,9 @@ def map(self, x, inverse=False):
297
301
affine = np .linalg .inv (affine )
298
302
return np .swapaxes (affine .dot (coords ), 1 , 2 )
299
303
300
- def to_filename (self , filename , fmt = 'X5' , moving = None ):
304
+ def to_filename (self , filename , fmt = "X5" , moving = None ):
301
305
"""Store the transform in BIDS-Transforms HDF5 file format (.x5)."""
302
- if fmt .lower () in (' itk' , ' ants' , ' elastix' ):
306
+ if fmt .lower () in (" itk" , " ants" , " elastix" ):
303
307
itkobj = io .itk .ITKLinearTransformArray .from_ras (self .matrix )
304
308
itkobj .to_filename (filename )
305
309
return filename
@@ -310,41 +314,41 @@ def to_filename(self, filename, fmt='X5', moving=None):
310
314
else :
311
315
moving = self .reference
312
316
313
- if fmt .lower () == ' afni' :
317
+ if fmt .lower () == " afni" :
314
318
afniobj = io .afni .AFNILinearTransformArray .from_ras (
315
319
self .matrix , moving = moving , reference = self .reference )
316
320
afniobj .to_filename (filename )
317
321
return filename
318
322
319
- if fmt .lower () == ' fsl' :
323
+ if fmt .lower () == " fsl" :
320
324
fslobj = io .fsl .FSLLinearTransformArray .from_ras (
321
325
self .matrix , moving = moving , reference = self .reference
322
326
)
323
327
fslobj .to_filename (filename )
324
328
return filename
325
329
326
- if fmt .lower () in ('fs' , ' lta' ):
330
+ if fmt .lower () in ("fs" , " lta" ):
327
331
# xform info
328
332
# to make LTA file format
329
333
lta = io .LinearTransformArray ()
330
- lta [' type' ] = 1 # RAS2RAS
334
+ lta [" type" ] = 1 # RAS2RAS
331
335
for m in self .matrix :
332
336
lt = io .LinearTransform ()
333
- lt [' sigma' ] = 1.
334
- lt [' m_L' ] = m
337
+ lt [" sigma" ] = 1.
338
+ lt [" m_L" ] = m
335
339
# Just for reference, nitransforms does not write VOX2VOX
336
- lt [' src' ] = io .VolumeGeometry .from_image (moving )
337
- lt [' dst' ] = io .VolumeGeometry .from_image (self .reference )
338
- lta [' xforms' ].append (lt )
340
+ lt [" src" ] = io .VolumeGeometry .from_image (moving )
341
+ lt [" dst" ] = io .VolumeGeometry .from_image (self .reference )
342
+ lta [" xforms" ].append (lt )
339
343
340
- with open (filename , 'w' ) as f :
344
+ with open (filename , "w" ) as f :
341
345
f .write (lta .to_string ())
342
346
return filename
343
347
344
348
raise NotImplementedError
345
349
346
350
def apply (self , spatialimage , reference = None ,
347
- order = 3 , mode = ' constant' , cval = 0.0 , prefilter = True , output_dtype = None ):
351
+ order = 3 , mode = " constant" , cval = 0.0 , prefilter = True , output_dtype = None ):
348
352
"""
349
353
Apply a transformation to an image, resampling on the reference spatial object.
350
354
@@ -359,11 +363,11 @@ def apply(self, spatialimage, reference=None,
359
363
order : int, optional
360
364
The order of the spline interpolation, default is 3.
361
365
The order has to be in the range 0-5.
362
- mode : {' constant', ' reflect', ' nearest', ' mirror', ' wrap' }, optional
366
+ mode : {" constant", " reflect", " nearest", " mirror", " wrap" }, optional
363
367
Determines how the input image is extended when the resamplings overflows
364
- a border. Default is ' constant' .
368
+ a border. Default is " constant" .
365
369
cval : float, optional
366
- Constant value for ``mode=' constant' ``. Default is 0.0.
370
+ Constant value for ``mode=" constant" ``. Default is 0.0.
367
371
prefilter: bool, optional
368
372
Determines if the image's data array is prefiltered with
369
373
a spline filter before interpolation. The default is ``True``,
@@ -436,17 +440,17 @@ def apply(self, spatialimage, reference=None,
436
440
return resampled
437
441
438
442
439
- def load (filename , fmt = 'X5' , reference = None , moving = None ):
443
+ def load (filename , fmt = "X5" , reference = None , moving = None ):
440
444
"""
441
445
Load a linear transform file.
442
446
443
447
Examples
444
448
--------
445
- >>> xfm = load(datadir / ' affine-LAS.itk.tfm' , fmt=' itk' )
449
+ >>> xfm = load(datadir / " affine-LAS.itk.tfm" , fmt=" itk" )
446
450
>>> isinstance(xfm, Affine)
447
451
True
448
452
449
- >>> xfm = load(datadir / ' itktflist.tfm' , fmt=' itk' )
453
+ >>> xfm = load(datadir / " itktflist.tfm" , fmt=" itk" )
450
454
>>> isinstance(xfm, LinearTransformsMapping)
451
455
True
452
456
0 commit comments