22from math import pi
33import numpy as np
44from nibabel .affines import (
5- from_matvec ,
5+ apply_affine ,
66 obliquity ,
77 voxel_sizes ,
88)
@@ -39,25 +39,8 @@ def to_string(self, banner=True):
3939 @classmethod
4040 def from_ras (cls , ras , moving = None , reference = None ):
4141 """Create an AFNI affine from a nitransform's RAS+ matrix."""
42- pre = LPS
43- post = LPS
44-
45- if reference is not None :
46- reference = _ensure_image (reference )
47-
48- if reference is not None and _is_oblique (reference .affine ):
49- print ("Reference affine axes are oblique." )
50- ras = ras @ _afni_warpdrive (reference .affine , ras = True , forward = False )
51-
52- if moving is not None :
53- moving = _ensure_image (moving )
54-
55- if moving is not None and _is_oblique (moving .affine ):
56- print ("Moving affine axes are oblique." )
57- ras = _afni_warpdrive (reference .affine , ras = True ) @ ras
58-
5942 # swapaxes is necessary, as axis 0 encodes series of transforms
60- parameters = np .swapaxes (post @ ras @ pre , 0 , 1 )
43+ parameters = np .swapaxes (LPS @ ras @ LPS , 0 , 1 )
6144
6245 tf = cls ()
6346 tf .structarr ["parameters" ] = parameters .T
@@ -89,23 +72,8 @@ def from_string(cls, string):
8972
9073 def to_ras (self , moving = None , reference = None ):
9174 """Return a nitransforms internal RAS+ matrix."""
92- pre = LPS
93- post = LPS
94-
95- if reference is not None :
96- reference = _ensure_image (reference )
97-
98- if reference is not None and _is_oblique (reference .affine ):
99- raise NotImplementedError
100-
101- if moving is not None :
102- moving = _ensure_image (moving )
103-
104- if moving is not None and _is_oblique (moving .affine ):
105- raise NotImplementedError
106-
10775 # swapaxes is necessary, as axis 0 encodes series of transforms
108- return post @ np .swapaxes (self .structarr ["parameters" ].T , 0 , 1 ) @ pre
76+ return LPS @ np .swapaxes (self .structarr ["parameters" ].T , 0 , 1 ) @ LPS
10977
11078
11179class AFNILinearTransformArray (BaseLinearTransformList ):
@@ -183,7 +151,7 @@ def _is_oblique(affine, thres=OBLIQUITY_THRESHOLD_DEG):
183151 return (obliquity (affine ).min () * 180 / pi ) > thres
184152
185153
186- def _afni_warpdrive (nii , forward = True , ras = False ):
154+ def _afni_warpdrive (oblique , shape , forward = True , ras = False ):
187155 """
188156 Calculate AFNI's ``WARPDRIVE_MATVEC_FOR_000000`` (de)obliquing affine.
189157
@@ -204,15 +172,18 @@ def _afni_warpdrive(nii, forward=True, ras=False):
204172 to be oblique.
205173
206174 """
207- oblique = nii .affine
208- plumb = oblique [:3 , :3 ] / np .abs (oblique [:3 , :3 ]).max (0 )
209- plumb [np .abs (plumb ) < 1.0 ] = 0
210- plumb *= voxel_sizes (oblique )
211-
212- R = from_matvec (plumb @ np .linalg .inv (oblique [:3 , :3 ]), (0 , 0 , 0 ))
213- plumb_orig = np .linalg .inv (R [:3 , :3 ]) @ oblique [:3 , 3 ]
214- print (plumb_orig )
215- R [:3 , 3 ] = R [:3 , :3 ] @ (plumb_orig - oblique [:3 , 3 ])
175+ shape = np .array (shape [:3 ])
176+ plumb_r = oblique [:3 , :3 ] / np .abs (oblique [:3 , :3 ]).max (0 )
177+ plumb_r [np .abs (plumb_r ) < 1.0 ] = 0
178+ plumb_r *= voxel_sizes (oblique )
179+ plumb = np .eye (4 )
180+ plumb [:3 , :3 ] = plumb_r
181+ obliq_o = apply_affine (oblique , 0.5 * (shape - 1 ))
182+ plumb_c = apply_affine (plumb , 0.5 * (shape - 1 ))
183+ plumb [:3 , 3 ] = - plumb_c + obliq_o
184+ print (obliq_o , apply_affine (plumb , 0.5 * (shape - 1 )))
185+
186+ R = plumb @ np .linalg .inv (oblique )
216187 if not ras :
217188 # Change sign to match AFNI's warpdrive_matvec signs
218189 B = np .ones ((2 , 2 ))
@@ -228,5 +199,8 @@ def _afni_header(nii, field="WARPDRIVE_MATVEC_FOR_000000"):
228199 root .find (f".//*[@atr_name='{ field } ']" ).text ,
229200 sep = "\n " ,
230201 dtype = "float32"
231- ).reshape ((3 , 4 ))
232- return np .vstack ((retval , (0 , 0 , 0 , 1 )))
202+ )
203+ if retval .size == 12 :
204+ return np .vstack ((retval .reshape ((3 , 4 )), (0 , 0 , 0 , 1 )))
205+
206+ return retval
0 commit comments