11"""Read/write FSL's transforms."""
22import os
3+ import warnings
34import numpy as np
45from pathlib import Path
6+ from nibabel import load as _nbload
57from nibabel .affines import voxel_sizes
68
79from .base import BaseLinearTransformList , LinearParameters , TransformFileError
@@ -24,7 +26,19 @@ def to_string(self):
2426
2527 @classmethod
2628 def from_ras (cls , ras , moving = None , reference = None ):
27- """Create an ITK affine from a nitransform's RAS+ matrix."""
29+ """Create an FSL affine from a nitransform's RAS+ matrix."""
30+ if moving is None :
31+ warnings .warn (
32+ "[Converting FSL to RAS] moving not provided, using reference as moving"
33+ )
34+ moving = reference
35+
36+ if reference is None :
37+ raise ValueError ("Cannot build FSL linear transform without a reference" )
38+
39+ reference = _ensure_image (reference )
40+ moving = _ensure_image (moving )
41+
2842 # Adjust for reference image offset and orientation
2943 refswp , refspc = _fsl_aff_adapt (reference )
3044 pre = reference .affine .dot (np .linalg .inv (refspc ).dot (np .linalg .inv (refswp )))
@@ -55,6 +69,35 @@ def from_string(cls, string):
5569 )
5670 return tf
5771
72+ def to_ras (self , moving = None , reference = None ):
73+ """Return a nitransforms internal RAS+ matrix."""
74+ if moving is None :
75+ warnings .warn (
76+ "Converting FSL to RAS: moving image not provided, using reference."
77+ )
78+ moving = reference
79+
80+ if reference is None :
81+ raise ValueError ("Cannot build FSL linear transform without a reference" )
82+
83+ reference = _ensure_image (reference )
84+ moving = _ensure_image (moving )
85+
86+ refswp , refspc = _fsl_aff_adapt (reference )
87+ pre = reference .affine .dot (np .linalg .inv (refspc ).dot (np .linalg .inv (refswp )))
88+
89+ # Adjust for moving image offset and orientation
90+ movswp , movspc = _fsl_aff_adapt (moving )
91+ post = np .linalg .inv (movswp ).dot (movspc .dot (np .linalg .inv (moving .affine )))
92+
93+ mat = self .structarr ["parameters" ].T
94+
95+ return (
96+ np .linalg .inv (post )
97+ @ np .swapaxes (np .linalg .inv (mat ), 0 , 1 )
98+ @ np .linalg .inv (pre )
99+ )
100+
58101
59102class FSLLinearTransformArray (BaseLinearTransformList ):
60103 """A string-based structure for series of FSL linear transforms."""
@@ -144,3 +187,9 @@ def _fsl_aff_adapt(space):
144187 swp [0 , 0 ] = - 1.0
145188 swp [0 , 3 ] = (space .shape [0 ] - 1 ) * zooms [0 ]
146189 return swp , np .diag (zooms )
190+
191+
192+ def _ensure_image (img ):
193+ if isinstance (img , (str , Path )):
194+ return _nbload (img )
195+ return img
0 commit comments