Skip to content

Commit 431c9be

Browse files
authored
FIX: Correct fsnative <-> anatomical transforms (#223)
* FIX: Do not apply fsnative transform to GIFTIs * RF: Use identity matrix for fsnative xfm * RF: Use FreeSurferSource to parse outputs * FIX: Mirror smriprep implementation for fsnative<->anat xfms * FIX: surface workflow connections * STY: black * ENH: Re-enable mri_coreg * ENH: Update FS build * ENH: Use newly minted nipreps freesurfer distro
1 parent c9577ec commit 431c9be

File tree

3 files changed

+52
-100
lines changed

3 files changed

+52
-100
lines changed

Dockerfile

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -208,27 +208,7 @@ ENV FSLDIR="/opt/fsl" \
208208
LD_LIBRARY_PATH="/opt/fsl/lib:$LD_LIBRARY_PATH"
209209

210210
# Install FreeSurfer
211-
RUN apt update && \
212-
apt-get install -y --no-install-recommends \
213-
bc \
214-
libgomp1 \
215-
perl \
216-
tar \
217-
tcsh \
218-
wget \
219-
vim-common \
220-
libgl1-mesa-dev \
221-
libsm-dev \
222-
libxrender-dev \
223-
libxmu-dev \
224-
unzip \
225-
&& apt-get clean \
226-
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
227-
&& echo "Downloading FreeSurfer + InfantFS" \
228-
&& mkdir -p /opt/freesurfer \
229-
&& curl -fSLO --retry 5 https://github.com/nipreps-containers/freesurfer/releases/download/infant-min-4a14499-fix/infant-freesurfer_dev-4a14499-min.zip \
230-
&& unzip infant-freesurfer_dev-4a14499-min.zip -d /opt \
231-
&& rm infant-freesurfer_dev-4a14499-min.zip
211+
COPY --from=nipreps/freesurfer@sha256:2ddc13f0a07b09e282a85d0a1aa715967841b1f4b734371dde98da1d1e87bed8 /opt/freesurfer /opt/freesurfer
232212
ENV FREESURFER_HOME="/opt/freesurfer"
233213
ENV SUBJECTS_DIR="$FREESURFER_HOME/subjects" \
234214
FUNCTIONALS_DIR="$FREESURFER_HOME/sessions" \
Lines changed: 37 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
# Use infant_recon_all to generate subcortical segmentations and cortical parcellations
22

3-
from nipype.interfaces import freesurfer as fs
4-
from nipype.interfaces import utility as niu
5-
from nipype.pipeline import engine as pe
6-
from niworkflows.interfaces.freesurfer import PatchedLTAConvert as LTAConvert
7-
from smriprep.workflows.surfaces import init_gifti_surface_wf
8-
9-
from ...interfaces.freesurfer import InfantReconAll
10-
113

124
def init_infant_surface_recon_wf(*, age_months, use_aseg=False, name="infant_surface_recon_wf"):
5+
from nipype.interfaces import freesurfer as fs
6+
from nipype.interfaces import io as nio
7+
from nipype.interfaces import utility as niu
8+
from nipype.pipeline import engine as pe
139
from niworkflows.engine.workflows import LiterateWorkflow
10+
from niworkflows.interfaces.freesurfer import PatchedLTAConvert as LTAConvert
11+
from niworkflows.interfaces.freesurfer import (
12+
PatchedRobustRegister as RobustRegister,
13+
)
14+
from smriprep.workflows.surfaces import init_gifti_surface_wf
15+
16+
from nibabies.interfaces.freesurfer import InfantReconAll
1417

1518
wf = LiterateWorkflow(name=name)
1619
inputnode = pe.Node(
@@ -52,29 +55,24 @@ def init_infant_surface_recon_wf(*, age_months, use_aseg=False, name="infant_sur
5255

5356
# inject the intensity-normalized skull-stripped t1w from the brain extraction workflow
5457
recon = pe.Node(InfantReconAll(age=age_months), name="reconall")
58+
fssource = pe.Node(nio.FreeSurferSource(), name='fssource', run_without_submitting=True)
5559

56-
# these files are created by babyFS, but transforms are for masked anatomicals
57-
# https://github.com/freesurfer/freesurfer/blob/
58-
# 8b40551f096294cc6603ce928317b8df70bce23e/infant/infant_recon_all#L744
59-
# TODO: calculate full anat -> fsnative transform?
60-
get_tal_lta = pe.Node(
61-
niu.Function(function=_get_talairch_lta),
62-
name="get_tal_xfm",
63-
)
6460
fsnative2anat_xfm = pe.Node(
61+
RobustRegister(auto_sens=True, est_int_scale=True),
62+
name='fsnative2anat_xfm',
63+
)
64+
65+
anat2fsnative_xfm = pe.Node(
6566
LTAConvert(out_lta=True, invert=True),
66-
name="fsnative2anat_xfm",
67+
name="anat2fsnative_xfm",
6768
)
6869

6970
# convert generated surfaces to GIFTIs
7071
gifti_surface_wf = init_gifti_surface_wf()
7172

72-
get_aseg = pe.Node(niu.Function(function=_get_aseg), name="get_aseg")
73-
get_aparc = pe.Node(niu.Function(function=_get_aparc), name="get_aparc")
7473
aparc2nii = pe.Node(fs.MRIConvert(out_type="niigz"), name="aparc2nii")
7574

7675
if use_aseg:
77-
# TODO: Add precomputed segmentation upon new babyFS rel
7876
wf.connect(inputnode, "anat_aseg", recon, "aseg_file")
7977

8078
# fmt: off
@@ -94,39 +92,36 @@ def init_infant_surface_recon_wf(*, age_months, use_aseg=False, name="infant_sur
9492
('subject_id', 'subject_id'),
9593
(('outdir', _parent), 'subjects_dir'),
9694
]),
95+
(recon, fssource, [
96+
('subject_id', 'subject_id'),
97+
(('outdir', _parent), 'subjects_dir'),
98+
]),
9799
(recon, gifti_surface_wf, [
98100
('subject_id', 'inputnode.subject_id'),
99101
(('outdir', _parent), 'inputnode.subjects_dir'),
100102
]),
101-
(recon, get_aparc, [
102-
('outdir', 'fs_subject_dir'),
103+
(fssource, outputnode, [
104+
(('aseg', _replace_mgz), 'anat_aseg'),
103105
]),
104-
(recon, get_aseg, [
105-
('outdir', 'fs_subject_dir'),
106+
(inputnode, fsnative2anat_xfm, [('anat_skullstripped', 'target_file')]),
107+
(fssource, fsnative2anat_xfm, [
108+
(('norm', _replace_mgz), 'source_file'),
106109
]),
107-
(get_aseg, outputnode, [
108-
('out', 'anat_aseg'),
109-
]),
110-
(get_aparc, aparc2nii, [
111-
('out', 'in_file'),
110+
(fsnative2anat_xfm, anat2fsnative_xfm, [('out_reg_file', 'in_lta')]),
111+
(fssource, aparc2nii, [
112+
('aparc_aseg', 'in_file'),
112113
]),
113114
(aparc2nii, outputnode, [
114115
('out_file', 'anat_aparc'),
115116
]),
116-
(recon, get_tal_lta, [
117-
('outdir', 'fs_subject_dir'),
118-
]),
119-
(get_tal_lta, outputnode, [
120-
('out', 'anat2fsnative_xfm'),
121-
]),
122-
(get_tal_lta, fsnative2anat_xfm, [
123-
('out', 'in_lta'),
124-
]),
125117
(fsnative2anat_xfm, outputnode, [
126-
('out_lta', 'fsnative2anat_xfm'),
118+
('out_reg_file', 'fsnative2anat_xfm'),
119+
]),
120+
(anat2fsnative_xfm, outputnode, [
121+
('out_lta', 'anat2fsnative_xfm'),
127122
]),
128123
(fsnative2anat_xfm, gifti_surface_wf, [
129-
('out_lta', 'inputnode.fsnative2t1w_xfm')]),
124+
('out_reg_file', 'inputnode.fsnative2t1w_xfm')]),
130125
(gifti_surface_wf, outputnode, [
131126
('outputnode.surfaces', 'surfaces'),
132127
]),
@@ -149,31 +144,5 @@ def _gen_recon_dir(subjects_dir, subject_id):
149144
return str(p)
150145

151146

152-
def _get_talairch_lta(fs_subject_dir):
153-
"""Fetch pre-computed transform from infant_recon_all"""
154-
from pathlib import Path
155-
156-
xfm = Path(fs_subject_dir) / "mri" / "transforms" / "niftyreg_affine.lta"
157-
if not xfm.exists():
158-
raise FileNotFoundError("Could not find talairach transform.")
159-
return str(xfm.absolute())
160-
161-
162-
def _get_aseg(fs_subject_dir):
163-
"""Fetch infant_recon_all's aparc+aseg"""
164-
from pathlib import Path
165-
166-
aseg = Path(fs_subject_dir) / "mri" / "aseg.nii.gz"
167-
if not aseg.exists():
168-
raise FileNotFoundError("Could not find aseg.")
169-
return str(aseg)
170-
171-
172-
def _get_aparc(fs_subject_dir):
173-
"""Fetch infant_recon_all's aparc+aseg"""
174-
from pathlib import Path
175-
176-
aparc = Path(fs_subject_dir) / "mri" / "aparc+aseg.mgz"
177-
if not aparc.exists():
178-
raise FileNotFoundError("Could not find aparc.")
179-
return str(aparc)
147+
def _replace_mgz(in_file):
148+
return in_file.replace('.mgz', '.nii.gz')

nibabies/workflows/bold/registration.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,20 @@ def init_bold_reg_wf(
137137
name="outputnode",
138138
)
139139

140-
# MG: Default to FSL FLIRT to avoid https://github.com/nipreps/nibabies/issues/97
141-
# if freesurfer:
142-
# bbr_wf = init_bbreg_wf(use_bbr=use_bbr, bold2t1w_dof=bold2t1w_dof,
143-
# bold2t1w_init=bold2t1w_init, omp_nthreads=omp_nthreads)
144-
# else:
145-
bbr_wf = init_fsl_bbr_wf(
146-
use_bbr=use_bbr,
147-
bold2t1w_dof=bold2t1w_dof,
148-
bold2t1w_init=bold2t1w_init,
149-
sloppy=sloppy,
150-
)
140+
if freesurfer:
141+
bbr_wf = init_bbreg_wf(
142+
use_bbr=use_bbr,
143+
bold2t1w_dof=bold2t1w_dof,
144+
bold2t1w_init=bold2t1w_init,
145+
omp_nthreads=omp_nthreads,
146+
)
147+
else:
148+
bbr_wf = init_fsl_bbr_wf(
149+
use_bbr=use_bbr,
150+
bold2t1w_dof=bold2t1w_dof,
151+
bold2t1w_init=bold2t1w_init,
152+
sloppy=sloppy,
153+
)
151154

152155
# fmt: off
153156
workflow.connect([

0 commit comments

Comments
 (0)