@@ -518,7 +518,6 @@ def _fix_topup_fieldcoeff(in_coeff, fmap_ref, pe_dir, out_file=None):
518
518
from pathlib import Path
519
519
import numpy as np
520
520
import nibabel as nb
521
- from sdcflows .utils .tools import ensure_positive_cosines
522
521
523
522
if out_file is None :
524
523
out_file = Path ("coefficients.nii.gz" ).absolute ()
@@ -528,28 +527,45 @@ def _fix_topup_fieldcoeff(in_coeff, fmap_ref, pe_dir, out_file=None):
528
527
529
528
# Load coefficients
530
529
coeffnii = nb .load (in_coeff )
530
+
531
+ # Orientations are [[axis, flip], [axis, flip], [axis, flip]] arrays,
532
+ # where RAS is "no change"
533
+ # e.g. PSL == [[1, -1], [2, 1], [0, -1]]
534
+ ref_ornt = nb .io_orientation (refnii .affine )
535
+ coeff_ornt = nb .io_orientation (coeffnii .affine )
536
+ # Get ref_ornt relative to coeff_ornt instead of relative to RAS
537
+ ref2coeff = nb .orientations .ornt_transform (ref_ornt , coeff_ornt ).astype (int )
538
+
539
+ # Find the axis index and flip of PE direction in reference space
540
+ ref_pe_axis = "ijk" .find (pe_dir [0 ])
541
+ if ref_pe_axis == - 1 :
542
+ ref_pe_axis = "xyz" .index (pe_dir [0 ])
543
+
544
+ # Transform to coefficient space
545
+ coeff_pe_axis = "ijk" [ref2coeff [ref_pe_axis , 0 ]]
546
+
531
547
# Coefficients - flip LR and overwrite coeffnii variable
532
548
# Internal data orientation of FSL is LAS, so coefficients will be LR flipped,
533
549
# and because the affine does not encode orientation (factors instead), this flip
534
550
# always is implicit.
535
551
# If the PE direction is x/i, the flip in the axis direction causes that the
536
552
# fieldmap estimation must also be inverted in direction (multiply by -1.0)
537
- reverse_pe = - 1.0 if "i" in pe_dir . replace ( "x" , "i" ) else 1.0
538
- lr_axis = "" . join ( nb . aff2axcodes ( coeffnii . affine )). index ( "R" )
553
+ reverse_pe = - 1.0 if coeff_pe_axis == "i" else 1.0
554
+ lr_axis = np . nonzero ( coeff_ornt [:, 0 ] == 0 )[ 0 ]
539
555
coeffnii = coeffnii .__class__ (
540
556
reverse_pe * np .flip (np .asanyarray (coeffnii .dataobj ), axis = lr_axis ),
541
557
coeffnii .affine ,
542
558
coeffnii .header ,
543
559
)
544
560
# Ensure reference has positive director cosines
545
- refnii , ref_axcodes = ensure_positive_cosines (refnii )
561
+ refnii_ras = nb . as_closest_canonical (refnii )
546
562
547
563
# Get matrix of B-Spline control knots
548
564
coeff_shape = np .array (coeffnii .shape [:3 ])
549
565
# Get factors (w.r.t. reference's pixel sizes) to calculate separation btw control points
550
566
factors = np .array (coeffnii .header .get_zooms ()[:3 ])
551
567
# Shape checking
552
- ref_shape = np .array (refnii .shape [:3 ])
568
+ ref_shape = np .array (refnii_ras .shape [:3 ])
553
569
exp_shape = ref_shape // factors + 3 * (factors > 1 )
554
570
if not np .all (coeff_shape == exp_shape ):
555
571
raise ValueError (
@@ -559,8 +575,8 @@ def _fix_topup_fieldcoeff(in_coeff, fmap_ref, pe_dir, out_file=None):
559
575
560
576
# Contextualize the control points in space with a proper NIfTI affine
561
577
newaff = np .eye (4 )
562
- newaff [:3 , :3 ] = refnii .affine [:3 , :3 ] * factors
563
- c_ref = nb .affines .apply_affine (refnii .affine , 0.5 * (ref_shape - 1 ))
578
+ newaff [:3 , :3 ] = refnii_ras .affine [:3 , :3 ] * factors
579
+ c_ref = nb .affines .apply_affine (refnii_ras .affine , 0.5 * (ref_shape - 1 ))
564
580
c_coeff = nb .affines .apply_affine (newaff , 0.5 * (coeff_shape - 1 ))
565
581
newaff [:3 , 3 ] = c_ref - c_coeff
566
582
0 commit comments