Skip to content

Commit 0a44004

Browse files
committed
FIX: Handle fieldmap orientations apart from R/LAS
1 parent b047077 commit 0a44004

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

sdcflows/interfaces/bspline.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,6 @@ def _fix_topup_fieldcoeff(in_coeff, fmap_ref, pe_dir, out_file=None):
518518
from pathlib import Path
519519
import numpy as np
520520
import nibabel as nb
521-
from sdcflows.utils.tools import ensure_positive_cosines
522521

523522
if out_file is None:
524523
out_file = Path("coefficients.nii.gz").absolute()
@@ -528,28 +527,45 @@ def _fix_topup_fieldcoeff(in_coeff, fmap_ref, pe_dir, out_file=None):
528527

529528
# Load coefficients
530529
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+
531547
# Coefficients - flip LR and overwrite coeffnii variable
532548
# Internal data orientation of FSL is LAS, so coefficients will be LR flipped,
533549
# and because the affine does not encode orientation (factors instead), this flip
534550
# always is implicit.
535551
# If the PE direction is x/i, the flip in the axis direction causes that the
536552
# 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]
539555
coeffnii = coeffnii.__class__(
540556
reverse_pe * np.flip(np.asanyarray(coeffnii.dataobj), axis=lr_axis),
541557
coeffnii.affine,
542558
coeffnii.header,
543559
)
544560
# Ensure reference has positive director cosines
545-
refnii, ref_axcodes = ensure_positive_cosines(refnii)
561+
refnii_ras = nb.as_closest_canonical(refnii)
546562

547563
# Get matrix of B-Spline control knots
548564
coeff_shape = np.array(coeffnii.shape[:3])
549565
# Get factors (w.r.t. reference's pixel sizes) to calculate separation btw control points
550566
factors = np.array(coeffnii.header.get_zooms()[:3])
551567
# Shape checking
552-
ref_shape = np.array(refnii.shape[:3])
568+
ref_shape = np.array(refnii_ras.shape[:3])
553569
exp_shape = ref_shape // factors + 3 * (factors > 1)
554570
if not np.all(coeff_shape == exp_shape):
555571
raise ValueError(
@@ -559,8 +575,8 @@ def _fix_topup_fieldcoeff(in_coeff, fmap_ref, pe_dir, out_file=None):
559575

560576
# Contextualize the control points in space with a proper NIfTI affine
561577
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))
564580
c_coeff = nb.affines.apply_affine(newaff, 0.5 * (coeff_shape - 1))
565581
newaff[:3, 3] = c_ref - c_coeff
566582

sdcflows/utils/tools.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ def ensure_positive_cosines(img):
7070
"""
7171
import nibabel as nb
7272

73-
img_axcodes = nb.aff2axcodes(img.affine)
74-
in_ornt = nb.orientations.axcodes2ornt(img_axcodes)
73+
in_ornt = nb.io_orientation(img.affine)
74+
img_axcodes = nb.orientations.ornt2axcodes(in_ornt)
7575
out_ornt = in_ornt.copy()
7676
out_ornt[:, 1] = 1
7777
ornt_xfm = nb.orientations.ornt_transform(in_ornt, out_ornt)

0 commit comments

Comments
 (0)