Skip to content

Commit af7194a

Browse files
authored
Merge pull request #2239 from emdupre/fix/meepi-t1-trans
REF/FIX: Simplify transform aggregation in resampling, pass identity transforms for multi-echo cases
2 parents c717c6b + c2228f0 commit af7194a

File tree

5 files changed

+55
-118
lines changed

5 files changed

+55
-118
lines changed

fmriprep/workflows/bold/base.py

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,6 @@ def init_func_preproc_wf(bold_file):
340340
# apply BOLD registration to T1w
341341
bold_t1_trans_wf = init_bold_t1_trans_wf(name='bold_t1_trans_wf',
342342
freesurfer=freesurfer,
343-
use_fieldwarp=bool(fmaps),
344-
multiecho=multiecho,
345343
mem_gb=mem_gb['resampled'],
346344
omp_nthreads=omp_nthreads,
347345
use_compression=False)
@@ -398,10 +396,12 @@ def init_func_preproc_wf(bold_file):
398396
debug=config.execution.debug)
399397

400398
# MULTI-ECHO EPI DATA #############################################
401-
if multiecho:
399+
if multiecho: # instantiate relevant interfaces, imports
402400
from niworkflows.func.util import init_skullstrip_bold_wf
403401
skullstrip_bold_wf = init_skullstrip_bold_wf(name='skullstrip_bold_wf')
404402

403+
split_opt_comb = bold_split.clone(name='split_opt_comb')
404+
405405
inputnode.inputs.bold_file = ref_file # Replace reference w first echo
406406

407407
join_echos = pe.JoinNode(niu.IdentityInterface(fields=['bold_files']),
@@ -415,13 +415,6 @@ def init_func_preproc_wf(bold_file):
415415
omp_nthreads=omp_nthreads,
416416
name='bold_t2smap_wf')
417417

418-
workflow.connect([
419-
(skullstrip_bold_wf, join_echos, [
420-
('outputnode.skull_stripped_file', 'bold_files')]),
421-
(join_echos, bold_t2s_wf, [
422-
('bold_files', 'inputnode.bold_file')]),
423-
])
424-
425418
# MAIN WORKFLOW STRUCTURE #######################################################
426419
workflow.connect([
427420
(inputnode, t1w_brain, [('t1w_preproc', 'in_file'),
@@ -450,8 +443,6 @@ def init_func_preproc_wf(bold_file):
450443
('t1w_aparc', 'inputnode.t1w_aparc')]),
451444
(t1w_brain, bold_t1_trans_wf, [
452445
('out_file', 'inputnode.t1w_brain')]),
453-
# unused if multiecho, but this is safe
454-
(bold_hmc_wf, bold_t1_trans_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]),
455446
(bold_reg_wf, outputnode, [
456447
('outputnode.itk_bold_to_t1', 'bold2anat_xfm'),
457448
('outputnode.itk_t1_to_bold', 'anat2bold_xfm')]),
@@ -470,7 +461,6 @@ def init_func_preproc_wf(bold_file):
470461
('outputnode.ref_image_brain', 'inputnode.epi_brain'),
471462
('outputnode.bold_mask', 'inputnode.epi_mask')]),
472463
(bold_sdc_wf, bold_t1_trans_wf, [
473-
('outputnode.out_warp', 'inputnode.fieldwarp'),
474464
('outputnode.epi_mask', 'inputnode.ref_bold_mask'),
475465
('outputnode.epi_brain', 'inputnode.ref_bold_brain')]),
476466
(bold_sdc_wf, bold_bold_trans_wf, [
@@ -516,20 +506,34 @@ def init_func_preproc_wf(bold_file):
516506
('outputnode.bold', 'inputnode.bold')]),
517507
(bold_split, bold_t1_trans_wf, [
518508
('out_files', 'inputnode.bold_split')]),
509+
(bold_hmc_wf, bold_t1_trans_wf, [
510+
('outputnode.xforms', 'inputnode.hmc_xforms')]),
511+
(bold_sdc_wf, bold_t1_trans_wf, [
512+
('outputnode.out_warp', 'inputnode.fieldwarp')])
519513
])
520-
else: # for meepi, create and use optimal combination
514+
else: # for meepi, use optimal combination
521515
workflow.connect([
522516
# update name source for optimal combination
523517
(inputnode, func_derivatives_wf, [
524518
(('bold_file', combine_meepi_source), 'inputnode.source_file')]),
525519
(bold_bold_trans_wf, skullstrip_bold_wf, [
526520
('outputnode.bold', 'inputnode.in_file')]),
521+
(skullstrip_bold_wf, join_echos, [
522+
('outputnode.skull_stripped_file', 'bold_files')]),
523+
(join_echos, bold_t2s_wf, [
524+
('bold_files', 'inputnode.bold_file')]),
527525
(bold_t2s_wf, bold_confounds_wf, [
528526
('outputnode.bold', 'inputnode.bold')]),
529-
(bold_t2s_wf, bold_t1_trans_wf, [
530-
('outputnode.bold', 'inputnode.bold_split')]),
527+
(bold_t2s_wf, split_opt_comb, [
528+
('outputnode.bold', 'in_file')]),
529+
(split_opt_comb, bold_t1_trans_wf, [
530+
('out_files', 'inputnode.bold_split')]),
531531
])
532532

533+
# Already applied in bold_bold_trans_wf, which inputs to bold_t2s_wf
534+
bold_t1_trans_wf.inputs.inputnode.fieldwarp = 'identity'
535+
bold_t1_trans_wf.inputs.inputnode.hmc_xforms = 'identity'
536+
533537
if fmaps:
534538
from sdcflows.workflows.outputs import init_sdc_unwarp_report_wf
535539
# Report on BOLD correction
@@ -628,7 +632,6 @@ def init_func_preproc_wf(bold_file):
628632
spaces=spaces,
629633
name='bold_std_trans_wf',
630634
use_compression=not config.execution.low_mem,
631-
use_fieldwarp=bool(fmaps),
632635
)
633636
workflow.connect([
634637
(inputnode, bold_std_trans_wf, [
@@ -637,14 +640,10 @@ def init_func_preproc_wf(bold_file):
637640
('bold_file', 'inputnode.name_source'),
638641
('t1w_aseg', 'inputnode.bold_aseg'),
639642
('t1w_aparc', 'inputnode.bold_aparc')]),
640-
(bold_hmc_wf, bold_std_trans_wf, [
641-
('outputnode.xforms', 'inputnode.hmc_xforms')]),
642643
(bold_reg_wf, bold_std_trans_wf, [
643644
('outputnode.itk_bold_to_t1', 'inputnode.itk_bold_to_t1')]),
644645
(bold_bold_trans_wf, bold_std_trans_wf, [
645646
('outputnode.bold_mask', 'inputnode.bold_mask')]),
646-
(bold_sdc_wf, bold_std_trans_wf, [
647-
('outputnode.out_warp', 'inputnode.fieldwarp')]),
648647
(bold_std_trans_wf, outputnode, [('outputnode.bold_std', 'bold_std'),
649648
('outputnode.bold_std_ref', 'bold_std_ref'),
650649
('outputnode.bold_mask_std', 'bold_mask_std')]),
@@ -664,18 +663,22 @@ def init_func_preproc_wf(bold_file):
664663
if not multiecho:
665664
workflow.connect([
666665
(bold_split, bold_std_trans_wf, [
667-
('out_files', 'inputnode.bold_split')])
666+
('out_files', 'inputnode.bold_split')]),
667+
(bold_sdc_wf, bold_std_trans_wf, [
668+
('outputnode.out_warp', 'inputnode.fieldwarp')]),
669+
(bold_hmc_wf, bold_std_trans_wf, [
670+
('outputnode.xforms', 'inputnode.hmc_xforms')]),
668671
])
669672
else:
670-
split_opt_comb = bold_split.clone(name='split_opt_comb')
671673
workflow.connect([
672-
(bold_t2s_wf, split_opt_comb, [
673-
('outputnode.bold', 'in_file')]),
674674
(split_opt_comb, bold_std_trans_wf, [
675-
('out_files', 'inputnode.bold_split')
676-
])
675+
('out_files', 'inputnode.bold_split')])
677676
])
678677

678+
# Already applied in bold_bold_trans_wf, which inputs to bold_t2s_wf
679+
bold_std_trans_wf.inputs.inputnode.fieldwarp = 'identity'
680+
bold_std_trans_wf.inputs.inputnode.hmc_xforms = 'identity'
681+
679682
# func_derivatives_wf internally parametrizes over snapshotted spaces.
680683
workflow.connect([
681684
(bold_std_trans_wf, func_derivatives_wf, [
@@ -693,7 +696,6 @@ def init_func_preproc_wf(bold_file):
693696
mem_gb=mem_gb['resampled'],
694697
metadata=metadata,
695698
omp_nthreads=omp_nthreads,
696-
use_fieldwarp=bool(fmaps),
697699
err_on_aroma_warn=config.workflow.aroma_err_on_warn,
698700
aroma_melodic_dim=config.workflow.aroma_melodic_dim,
699701
name='ica_aroma_wf')

fmriprep/workflows/bold/confounds.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,6 @@ def init_ica_aroma_wf(
548548
err_on_aroma_warn=False,
549549
name='ica_aroma_wf',
550550
susan_fwhm=6.0,
551-
use_fieldwarp=True,
552551
):
553552
"""
554553
Build a workflow that runs `ICA-AROMA`_.
@@ -601,8 +600,6 @@ def init_ica_aroma_wf(
601600
susan_fwhm : :obj:`float`
602601
Kernel width (FWHM in mm) for the smoothing step with
603602
FSL ``susan`` (default: 6.0mm)
604-
use_fieldwarp : :obj:`bool`
605-
Include SDC warp in single-shot transform from BOLD to MNI
606603
err_on_aroma_warn : :obj:`bool`
607604
Do not fail on ICA-AROMA errors
608605
aroma_melodic_dim : :obj:`int`
@@ -628,8 +625,6 @@ def init_ica_aroma_wf(
628625
BOLD series mask in template space
629626
hmc_xforms
630627
List of affine transforms aligning each volume to ``ref_image`` in ITK format
631-
fieldwarp
632-
a :abbr:`DFM (displacements field map)` in ITK format
633628
movpar_file
634629
SPM-formatted motion parameters file
635630

fmriprep/workflows/bold/registration.py

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ def _bold_reg_suffix(fallback, freesurfer):
175175
return workflow
176176

177177

178-
def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, multiecho=False, use_fieldwarp=False,
179-
use_compression=True, name='bold_t1_trans_wf'):
178+
def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, use_compression=True,
179+
name='bold_t1_trans_wf'):
180180
"""
181181
Co-register the reference BOLD image to T1w-space.
182182
@@ -196,10 +196,6 @@ def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, multiecho=False, use
196196
----------
197197
freesurfer : :obj:`bool`
198198
Enable FreeSurfer functional registration (bbregister)
199-
use_fieldwarp : :obj:`bool`
200-
Include SDC warp in single-shot transform from BOLD to T1
201-
multiecho : :obj:`bool`
202-
If multiecho data was supplied, HMC already performed
203199
mem_gb : :obj:`float`
204200
Size of BOLD file in GB
205201
omp_nthreads : :obj:`int`
@@ -327,38 +323,18 @@ def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, multiecho=False, use
327323
# Generate a reference on the target T1w space
328324
gen_final_ref = init_bold_reference_wf(omp_nthreads, pre_mask=True)
329325

330-
if not multiecho:
331-
# Merge transforms placing the head motion correction last
332-
nforms = 2 + int(use_fieldwarp)
333-
merge_xforms = pe.Node(niu.Merge(nforms), name='merge_xforms',
334-
run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB)
335-
if use_fieldwarp:
336-
workflow.connect([
337-
(inputnode, merge_xforms, [('fieldwarp', 'in2')])
338-
])
339-
340-
workflow.connect([
341-
# merge transforms
342-
(inputnode, merge_xforms, [
343-
('hmc_xforms', 'in%d' % nforms),
344-
('itk_bold_to_t1', 'in1')]),
345-
(merge_xforms, bold_to_t1w_transform, [('out', 'transforms')]),
346-
(inputnode, bold_to_t1w_transform, [('bold_split', 'input_image')]),
347-
])
348-
349-
else:
350-
from nipype.interfaces.fsl import Split as FSLSplit
351-
bold_split = pe.Node(FSLSplit(dimension='t'), name='bold_split',
352-
mem_gb=DEFAULT_MEMORY_MIN_GB)
353-
354-
workflow.connect([
355-
(inputnode, bold_split, [('bold_split', 'in_file')]),
356-
(bold_split, bold_to_t1w_transform, [('out_files', 'input_image')]),
357-
(inputnode, bold_to_t1w_transform, [('itk_bold_to_t1', 'transforms')]),
358-
])
326+
# Merge transforms placing the head motion correction last
327+
merge_xforms = pe.Node(niu.Merge(3), name='merge_xforms',
328+
run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB)
359329

360330
workflow.connect([
361331
(inputnode, merge, [('name_source', 'header_source')]),
332+
(inputnode, merge_xforms, [
333+
('hmc_xforms', 'in3'), # May be 'identity' if HMC already applied
334+
('fieldwarp', 'in2'), # May be 'identity' if SDC already applied
335+
('itk_bold_to_t1', 'in1')]),
336+
(inputnode, bold_to_t1w_transform, [('bold_split', 'input_image')]),
337+
(merge_xforms, bold_to_t1w_transform, [('out', 'transforms')]),
362338
(gen_ref, bold_to_t1w_transform, [('out_file', 'reference_image')]),
363339
(bold_to_t1w_transform, merge, [('out_files', 'in_files')]),
364340
(merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]),

fmriprep/workflows/bold/resampling.py

Lines changed: 13 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
from nipype.pipeline import engine as pe
1515
from nipype.interfaces import utility as niu, freesurfer as fs
16-
from nipype.interfaces.fsl import Split as FSLSplit
1716
import nipype.interfaces.workbench as wb
1817

1918

@@ -164,7 +163,6 @@ def init_bold_std_trans_wf(
164163
spaces,
165164
name='bold_std_trans_wf',
166165
use_compression=True,
167-
use_fieldwarp=False,
168166
):
169167
"""
170168
Sample fMRI into standard space with a single-step resampling of the original BOLD series.
@@ -215,8 +213,6 @@ def init_bold_std_trans_wf(
215213
Name of workflow (default: ``bold_std_trans_wf``)
216214
use_compression : :obj:`bool`
217215
Save registered BOLD series as ``.nii.gz``
218-
use_fieldwarp : :obj:`bool`
219-
Include SDC warp in single-shot transform from BOLD to MNI
220216
221217
Inputs
222218
------
@@ -335,13 +331,8 @@ def init_bold_std_trans_wf(
335331
mask_merge_tfms = pe.Node(niu.Merge(2), name='mask_merge_tfms', run_without_submitting=True,
336332
mem_gb=DEFAULT_MEMORY_MIN_GB)
337333

338-
nxforms = 3 + use_fieldwarp
339-
merge_xforms = pe.Node(niu.Merge(nxforms), name='merge_xforms',
334+
merge_xforms = pe.Node(niu.Merge(4), name='merge_xforms',
340335
run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB)
341-
workflow.connect([(inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nxforms)])])
342-
343-
if use_fieldwarp:
344-
workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in3')])])
345336

346337
bold_to_std_transform = pe.Node(
347338
MultiApplyTransforms(interpolation="LanczosWindowedSinc", float=True, copy_dtype=True),
@@ -361,8 +352,9 @@ def init_bold_std_trans_wf(
361352
('templates', 'keys')]),
362353
(inputnode, mask_std_tfm, [('bold_mask', 'input_image')]),
363354
(inputnode, gen_ref, [(('bold_split', _first), 'moving_image')]),
364-
(inputnode, merge_xforms, [
365-
(('itk_bold_to_t1', _aslist), 'in2')]),
355+
(inputnode, merge_xforms, [('hmc_xforms', 'in4'),
356+
('fieldwarp', 'in3'),
357+
(('itk_bold_to_t1', _aslist), 'in2')]),
366358
(inputnode, merge, [('name_source', 'header_source')]),
367359
(inputnode, mask_merge_tfms, [(('itk_bold_to_t1', _aslist), 'in2')]),
368360
(inputnode, bold_to_std_transform, [('bold_split', 'input_image')]),
@@ -431,7 +423,6 @@ def init_bold_preproc_trans_wf(mem_gb, omp_nthreads,
431423
name='bold_preproc_trans_wf',
432424
use_compression=True,
433425
use_fieldwarp=False,
434-
split_file=False,
435426
interpolation='LanczosWindowedSinc'):
436427
"""
437428
Resample in native (original) space.
@@ -459,9 +450,6 @@ def init_bold_preproc_trans_wf(mem_gb, omp_nthreads,
459450
Save registered BOLD series as ``.nii.gz``
460451
use_fieldwarp : :obj:`bool`
461452
Include SDC warp in single-shot transform from BOLD to MNI
462-
split_file : :obj:`bool`
463-
Whether the input file should be splitted (it is a 4D file)
464-
or it is a list of 3D files (default ``False``, do not split)
465453
interpolation : :obj:`str`
466454
Interpolation type to be used by ANTs' ``applyTransforms``
467455
(default ``'LanczosWindowedSinc'``)
@@ -518,19 +506,26 @@ def init_bold_preproc_trans_wf(mem_gb, omp_nthreads,
518506
niu.IdentityInterface(fields=['bold', 'bold_mask', 'bold_ref', 'bold_ref_brain']),
519507
name='outputnode')
520508

509+
merge_xforms = pe.Node(niu.Merge(2), name='merge_xforms',
510+
run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB)
511+
521512
bold_transform = pe.Node(
522513
MultiApplyTransforms(interpolation=interpolation, float=True, copy_dtype=True),
523514
name='bold_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads)
524515

525-
merge = pe.Node(Merge(compress=use_compression), name='merge',
526-
mem_gb=mem_gb * 3)
516+
merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb * 3)
527517

528518
# Generate a new BOLD reference
529519
bold_reference_wf = init_bold_reference_wf(omp_nthreads=omp_nthreads)
530520
bold_reference_wf.__desc__ = None # Unset description to avoid second appearance
531521

532522
workflow.connect([
523+
(inputnode, merge_xforms, [('fieldwarp', 'in1'),
524+
('hmc_xforms', 'in2')]),
525+
(inputnode, bold_transform, [('bold_file', 'input_image'),
526+
(('bold_file', _first), 'reference_image')]),
533527
(inputnode, merge, [('name_source', 'header_source')]),
528+
(merge_xforms, bold_transform, [('out', 'transforms')]),
534529
(bold_transform, merge, [('out_files', 'in_files')]),
535530
(merge, bold_reference_wf, [('out_file', 'inputnode.bold_file')]),
536531
(merge, outputnode, [('out_file', 'bold')]),
@@ -540,37 +535,6 @@ def init_bold_preproc_trans_wf(mem_gb, omp_nthreads,
540535
('outputnode.bold_mask', 'bold_mask')]),
541536
])
542537

543-
# Input file is not splitted
544-
if split_file:
545-
bold_split = pe.Node(FSLSplit(dimension='t'), name='bold_split',
546-
mem_gb=mem_gb * 3)
547-
workflow.connect([
548-
(inputnode, bold_split, [('bold_file', 'in_file')]),
549-
(bold_split, bold_transform, [
550-
('out_files', 'input_image'),
551-
(('out_files', _first), 'reference_image'),
552-
])
553-
])
554-
else:
555-
workflow.connect([
556-
(inputnode, bold_transform, [('bold_file', 'input_image'),
557-
(('bold_file', _first), 'reference_image')]),
558-
])
559-
560-
if use_fieldwarp:
561-
merge_xforms = pe.Node(niu.Merge(2), name='merge_xforms',
562-
run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB)
563-
workflow.connect([
564-
(inputnode, merge_xforms, [('fieldwarp', 'in1'),
565-
('hmc_xforms', 'in2')]),
566-
(merge_xforms, bold_transform, [('out', 'transforms')]),
567-
])
568-
else:
569-
def _aslist(val):
570-
return [val]
571-
workflow.connect([
572-
(inputnode, bold_transform, [(('hmc_xforms', _aslist), 'transforms')]),
573-
])
574538
return workflow
575539

576540

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ install_requires =
3434
pybids >= 0.10.2
3535
pyyaml
3636
requests
37-
sdcflows ~= 1.3.1
37+
sdcflows @ git+https://github.com/nipreps/sdcflows.git@master
3838
smriprep @ git+https://github.com/nipreps/smriprep.git@master
3939
tedana >= 0.0.9a1, < 0.0.10
4040
templateflow ~= 0.6

0 commit comments

Comments
 (0)