Skip to content

Commit e3fd6f8

Browse files
committed
wip: add a flag to reuse freesurfer as is, for longitudinal base case
1 parent bfcc1a8 commit e3fd6f8

File tree

4 files changed

+109
-55
lines changed

4 files changed

+109
-55
lines changed

smriprep/cli/run.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,12 @@ def get_parser():
205205
help="Path to existing FreeSurfer subjects directory to reuse. "
206206
"(default: OUTPUT_DIR/freesurfer)",
207207
)
208-
208+
g_fs.add_argument(
209+
"--fs-reuse-base",
210+
action="store_true",
211+
dest="fs_reuse_base",
212+
help="Reuse freesurfer base template (from longitudinal preprocessing)",
213+
)
209214
# Surface generation xor
210215
g_surfs = parser.add_argument_group("Surface preprocessing options")
211216
g_surfs.add_argument(
@@ -570,6 +575,7 @@ def build_workflow(opts, retval):
570575
freesurfer=opts.run_reconall,
571576
fs_subjects_dir=opts.fs_subjects_dir,
572577
hires=opts.hires,
578+
fs_reuse_base=opts.fs_reuse_base,
573579
layout=layout,
574580
longitudinal=opts.longitudinal,
575581
low_mem=opts.low_mem,

smriprep/workflows/anatomical.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def init_anat_preproc_wf(
6666
bids_root,
6767
freesurfer,
6868
hires,
69+
fs_reuse_base,
6970
longitudinal,
7071
t1w,
7172
t2w,
@@ -129,6 +130,8 @@ def init_anat_preproc_wf(
129130
at the very least)
130131
hires : :obj:`bool`
131132
Enable sub-millimeter preprocessing in FreeSurfer
133+
fs_reuse_base : bool
134+
Adjust pipeline to reuse base template of existing longitudinal freesurfer
132135
longitudinal : :obj:`bool`
133136
Create unbiased structural template, regardless of number of inputs
134137
(may increase runtime)
@@ -527,7 +530,7 @@ def _check_img(img):
527530

528531
# 5. Surface reconstruction (--fs-no-reconall not set)
529532
surface_recon_wf = init_surface_recon_wf(
530-
name="surface_recon_wf", omp_nthreads=omp_nthreads, hires=hires
533+
name="surface_recon_wf", omp_nthreads=omp_nthreads, hires=hires, fs_reuse_base=fs_reuse_base,
531534
)
532535
applyrefined = pe.Node(fsl.ApplyMask(), name="applyrefined")
533536
sphere_reg_wf = init_sphere_reg_wf(msm_sulc=msm_sulc, sloppy=sloppy, name="sphere_reg_wf")

smriprep/workflows/base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def init_smriprep_wf(
4949
freesurfer,
5050
fs_subjects_dir,
5151
hires,
52+
fs_reuse_base,
5253
layout,
5354
longitudinal,
5455
low_mem,
@@ -177,6 +178,7 @@ def init_smriprep_wf(
177178
freesurfer=freesurfer,
178179
fast_track=fast_track,
179180
hires=hires,
181+
fs_reuse_base=fs_reuse_base,
180182
layout=layout,
181183
longitudinal=longitudinal,
182184
low_mem=low_mem,
@@ -212,6 +214,7 @@ def init_single_subject_wf(
212214
fast_track,
213215
freesurfer,
214216
hires,
217+
fs_reuse_base,
215218
layout,
216219
longitudinal,
217220
low_mem,
@@ -280,6 +283,8 @@ def init_single_subject_wf(
280283
Enable FreeSurfer surface reconstruction (may increase runtime)
281284
hires : :obj:`bool`
282285
Enable sub-millimeter preprocessing in FreeSurfer
286+
fs_reuse_base : bool
287+
Adjust pipeline to reuse base template of existing longitudinal freesurfer
283288
layout : BIDSLayout object
284289
BIDS dataset layout
285290
longitudinal : :obj:`bool`
@@ -415,6 +420,7 @@ def init_single_subject_wf(
415420
existing_derivatives=deriv_cache,
416421
freesurfer=freesurfer,
417422
hires=hires,
423+
fs_reuse_base=fs_reuse_base,
418424
longitudinal=longitudinal,
419425
msm_sulc=msm_sulc,
420426
name="anat_preproc_wf",

smriprep/workflows/surfaces.py

Lines changed: 92 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
from ..interfaces.workbench import CreateSignedDistanceVolume
5454

5555

56-
def init_surface_recon_wf(*, omp_nthreads, hires, name="surface_recon_wf"):
56+
def init_surface_recon_wf(*, omp_nthreads, hires, fs_reuse_base, name="surface_recon_wf"):
5757
r"""
5858
Reconstruct anatomical surfaces using FreeSurfer's ``recon-all``.
5959
@@ -124,6 +124,8 @@ def init_surface_recon_wf(*, omp_nthreads, hires, name="surface_recon_wf"):
124124
Maximum number of threads an individual process may use
125125
hires : bool
126126
Enable sub-millimeter preprocessing in FreeSurfer
127+
fs_reuse_base : bool
128+
Adjust pipeline to reuse base template of existing longitudinal freesurfer
127129
128130
Inputs
129131
------
@@ -222,85 +224,122 @@ def init_surface_recon_wf(*, omp_nthreads, hires, name="surface_recon_wf"):
222224
name="outputnode",
223225
)
224226

225-
recon_config = pe.Node(FSDetectInputs(hires_enabled=hires), name="recon_config")
226-
227-
fov_check = pe.Node(niu.Function(function=_check_cw256), name="fov_check")
228-
fov_check.inputs.default_flags = ['-noskullstrip', '-noT2pial', '-noFLAIRpial']
229-
230-
autorecon1 = pe.Node(
231-
ReconAll(directive="autorecon1", openmp=omp_nthreads),
232-
name="autorecon1",
233-
n_procs=omp_nthreads,
234-
mem_gb=5,
235-
)
236-
autorecon1.interface._can_resume = False
237-
autorecon1.interface._always_run = True
238-
239-
skull_strip_extern = pe.Node(FSInjectBrainExtracted(), name="skull_strip_extern")
240-
241227
fsnative2t1w_xfm = pe.Node(
242228
RobustRegister(auto_sens=True, est_int_scale=True), name="fsnative2t1w_xfm"
243229
)
244230
t1w2fsnative_xfm = pe.Node(LTAConvert(out_lta=True, invert=True), name="t1w2fsnative_xfm")
245231

246-
autorecon_resume_wf = init_autorecon_resume_wf(omp_nthreads=omp_nthreads)
247232
gifti_surface_wf = init_gifti_surface_wf()
248233

249234
aseg_to_native_wf = init_segs_to_native_wf()
250235
aparc_to_native_wf = init_segs_to_native_wf(segmentation="aparc_aseg")
251236
refine = pe.Node(RefineBrainMask(), name="refine")
252237

238+
if not fs_reuse_base:
239+
240+
recon_config = pe.Node(FSDetectInputs(hires_enabled=hires), name="recon_config")
241+
242+
fov_check = pe.Node(niu.Function(function=_check_cw256), name="fov_check")
243+
fov_check.inputs.default_flags = ['-noskullstrip', '-noT2pial', '-noFLAIRpial']
244+
245+
autorecon1 = pe.Node(
246+
ReconAll(directive="autorecon1", openmp=omp_nthreads),
247+
name="autorecon1",
248+
n_procs=omp_nthreads,
249+
mem_gb=5,
250+
)
251+
autorecon1.interface._can_resume = False
252+
autorecon1.interface._always_run = True
253+
254+
skull_strip_extern = pe.Node(FSInjectBrainExtracted(), name="skull_strip_extern")
255+
256+
autorecon_resume_wf = init_autorecon_resume_wf(omp_nthreads=omp_nthreads)
257+
258+
# fmt:off
259+
workflow.connect(
260+
# Configuration
261+
(inputnode, recon_config, [('t1w', 't1w_list'),
262+
('t2w', 't2w_list'),
263+
('flair', 'flair_list')]),
264+
# Passing subjects_dir / subject_id enforces serial order
265+
(inputnode, autorecon1, [('subjects_dir', 'subjects_dir'),
266+
('subject_id', 'subject_id')]),
267+
(autorecon1, skull_strip_extern, [('subjects_dir', 'subjects_dir'),
268+
('subject_id', 'subject_id')]),
269+
(skull_strip_extern, autorecon_resume_wf, [('subjects_dir', 'inputnode.subjects_dir'),
270+
('subject_id', 'inputnode.subject_id')]),
271+
(autorecon_resume_wf, gifti_surface_wf, [
272+
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
273+
('outputnode.subject_id', 'inputnode.subject_id')]),
274+
# Reconstruction phases
275+
(inputnode, autorecon1, [('t1w', 'T1_files')]),
276+
(inputnode, fov_check, [('t1w', 'in_files')]),
277+
(fov_check, autorecon1, [('out', 'flags')]),
278+
(recon_config, autorecon1, [('t2w', 'T2_file'),
279+
('flair', 'FLAIR_file'),
280+
('hires', 'hires'),
281+
# First run only (recon-all saves expert options)
282+
('mris_inflate', 'mris_inflate')]),
283+
(inputnode, skull_strip_extern, [('skullstripped_t1', 'in_brain')]),
284+
(recon_config, autorecon_resume_wf, [('use_t2w', 'inputnode.use_T2'),
285+
('use_flair', 'inputnode.use_FLAIR')]),
286+
(autorecon1, fsnative2t1w_xfm, [('T1', 'source_file')]),
287+
288+
289+
(autorecon_resume_wf, aseg_to_native_wf, [
290+
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
291+
('outputnode.subject_id', 'inputnode.subject_id')]),
292+
293+
(autorecon_resume_wf, aparc_to_native_wf, [
294+
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
295+
('outputnode.subject_id', 'inputnode.subject_id')]),
296+
# Output
297+
(autorecon_resume_wf, outputnode, [('outputnode.subjects_dir', 'subjects_dir'),
298+
('outputnode.subject_id', 'subject_id')]),
299+
)
300+
# fmt:on
301+
else:
302+
fs_base_inputs = pe.Node(nio.FreeSurferSource())
303+
# fmt:off
304+
workflow.connect([
305+
(inputnode, fs_base_inputs, [('subjects_dir', 'subjects_dir'),
306+
('subject_id', 'subject_id')]),
307+
(inputnode, gifti_surface_wf, [
308+
('subjects_dir', 'inputnode.subjects_dir'),
309+
('subject_id', 'inputnode.subject_id')]),
310+
(fs_base_inputs, fsnative2t1w_xfm, [('T1', 'source_file')]),
311+
312+
(inputnode, aparc_to_native_wf, [('corrected_t1', 'inputnode.in_file')]),
313+
(inputnode, aparc_to_native_wf, [
314+
('subjects_dir', 'inputnode.subjects_dir'),
315+
('subject_id', 'inputnode.subject_id')]),
316+
(fsnative2t1w_xfm, aparc_to_native_wf, [
317+
('out_reg_file', 'inputnode.fsnative2t1w_xfm')]),
318+
(aseg_to_native_wf, refine, [('outputnode.out_file', 'in_aseg')]),
319+
320+
# Output
321+
(inputnode, outputnode, [('subjects_dir', 'subjects_dir'),
322+
('subject_id', 'subject_id')]),
323+
])
324+
# fmt:on
325+
253326
# fmt:off
254327
workflow.connect([
255-
# Configuration
256-
(inputnode, recon_config, [('t1w', 't1w_list'),
257-
('t2w', 't2w_list'),
258-
('flair', 'flair_list')]),
259-
# Passing subjects_dir / subject_id enforces serial order
260-
(inputnode, autorecon1, [('subjects_dir', 'subjects_dir'),
261-
('subject_id', 'subject_id')]),
262-
(autorecon1, skull_strip_extern, [('subjects_dir', 'subjects_dir'),
263-
('subject_id', 'subject_id')]),
264-
(skull_strip_extern, autorecon_resume_wf, [('subjects_dir', 'inputnode.subjects_dir'),
265-
('subject_id', 'inputnode.subject_id')]),
266-
(autorecon_resume_wf, gifti_surface_wf, [
267-
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
268-
('outputnode.subject_id', 'inputnode.subject_id')]),
269-
# Reconstruction phases
270-
(inputnode, autorecon1, [('t1w', 'T1_files')]),
271-
(inputnode, fov_check, [('t1w', 'in_files')]),
272-
(fov_check, autorecon1, [('out', 'flags')]),
273-
(recon_config, autorecon1, [('t2w', 'T2_file'),
274-
('flair', 'FLAIR_file'),
275-
('hires', 'hires'),
276-
# First run only (recon-all saves expert options)
277-
('mris_inflate', 'mris_inflate')]),
278-
(inputnode, skull_strip_extern, [('skullstripped_t1', 'in_brain')]),
279-
(recon_config, autorecon_resume_wf, [('use_t2w', 'inputnode.use_T2'),
280-
('use_flair', 'inputnode.use_FLAIR')]),
281328
# Construct transform from FreeSurfer conformed image to sMRIPrep
282329
# reoriented image
283330
(inputnode, fsnative2t1w_xfm, [('t1w', 'target_file')]),
284-
(autorecon1, fsnative2t1w_xfm, [('T1', 'source_file')]),
285331
(fsnative2t1w_xfm, t1w2fsnative_xfm, [('out_reg_file', 'in_lta')]),
286332
# Refine ANTs mask, deriving new mask from FS' aseg
287333
(inputnode, refine, [('corrected_t1', 'in_anat'),
288334
('ants_segs', 'in_ants')]),
289335
(inputnode, aseg_to_native_wf, [('corrected_t1', 'inputnode.in_file')]),
290-
(autorecon_resume_wf, aseg_to_native_wf, [
291-
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
292-
('outputnode.subject_id', 'inputnode.subject_id')]),
293336
(fsnative2t1w_xfm, aseg_to_native_wf, [('out_reg_file', 'inputnode.fsnative2t1w_xfm')]),
294337
(inputnode, aparc_to_native_wf, [('corrected_t1', 'inputnode.in_file')]),
295-
(autorecon_resume_wf, aparc_to_native_wf, [
296-
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
297-
('outputnode.subject_id', 'inputnode.subject_id')]),
338+
298339
(fsnative2t1w_xfm, aparc_to_native_wf, [('out_reg_file', 'inputnode.fsnative2t1w_xfm')]),
299340
(aseg_to_native_wf, refine, [('outputnode.out_file', 'in_aseg')]),
300341

301342
# Output
302-
(autorecon_resume_wf, outputnode, [('outputnode.subjects_dir', 'subjects_dir'),
303-
('outputnode.subject_id', 'subject_id')]),
304343
(gifti_surface_wf, outputnode, [('outputnode.surfaces', 'surfaces'),
305344
('outputnode.morphometrics', 'morphometrics'),
306345
('outputnode.midthickness', 'midthickness'),

0 commit comments

Comments
 (0)