From b3d45a05d94efc1a06298f939a22aff10d2cd738 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Thu, 28 Mar 2024 12:45:44 -0400 Subject: [PATCH 1/3] Add Sources to functional GIFTI outputs. --- fmriprep/workflows/bold/base.py | 11 ++++++++++- fmriprep/workflows/bold/outputs.py | 9 +++++++++ fmriprep/workflows/bold/resampling.py | 13 +++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py index 105ce6fdb..ae6a1c380 100644 --- a/fmriprep/workflows/bold/base.py +++ b/fmriprep/workflows/bold/base.py @@ -504,7 +504,6 @@ def init_bold_wf( output_dir=fmriprep_dir, name='bold_surf_wf', ) - bold_surf_wf.inputs.inputnode.source_file = bold_file workflow.connect([ (inputnode, bold_surf_wf, [ ('subjects_dir', 'inputnode.subjects_dir'), @@ -514,6 +513,16 @@ def init_bold_wf( (bold_anat_wf, bold_surf_wf, [('outputnode.bold_file', 'inputnode.bold_t1w')]), ]) # fmt:skip + if nonstd_spaces.intersection(('anat', 'T1w')): + # Source file should be output T1w-space volumetric file + workflow.connect([ + (ds_bold_t1_wf, bold_surf_wf, [ + ('outputnode.bold_file', 'inputnode.source_file'), + ]), + ]) # fmt:skip + else: + bold_surf_wf.inputs.inputnode.source_file = bold_file + if config.workflow.cifti_output: from .resampling import ( init_bold_fsLR_resampling_wf, diff --git a/fmriprep/workflows/bold/outputs.py b/fmriprep/workflows/bold/outputs.py index 89931ef0b..23accb8cf 100644 --- a/fmriprep/workflows/bold/outputs.py +++ b/fmriprep/workflows/bold/outputs.py @@ -746,6 +746,14 @@ def init_ds_volumes_wf( ), name='inputnode', ) + outputnode = pe.Node( + niu.IdentityInterface( + fields=[ + 'bold', + ], + ), + name='outputnode', + ) sources = pe.Node( BIDSURI( @@ -790,6 +798,7 @@ def init_ds_volumes_wf( ('cohort', 'cohort'), ('resolution', 'resolution'), ]), + (ds_bold, outputnode, [('out_file', 'bold')]), ]) # fmt:skip resample_ref = pe.Node( diff --git a/fmriprep/workflows/bold/resampling.py b/fmriprep/workflows/bold/resampling.py index c15cf1ff6..6acbff4ba 100644 --- a/fmriprep/workflows/bold/resampling.py +++ b/fmriprep/workflows/bold/resampling.py @@ -43,7 +43,9 @@ from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.interfaces.freesurfer import MedialNaNs +from ... import config from ...config import DEFAULT_MEMORY_MIN_GB +from ...interfaces.bids import BIDSURI from ...interfaces.workbench import MetricDilate, MetricMask, MetricResample from ...utils.bids import dismiss_echo from .outputs import prepare_timing_parameters @@ -139,6 +141,15 @@ def init_bold_surf_wf( itersource = pe.Node(niu.IdentityInterface(fields=['target']), name='itersource') itersource.iterables = [('target', surface_spaces)] + surfs_sources = pe.Node( + BIDSURI( + numinputs=1, + dataset_links=config.execution.dataset_links, + out_dir=str(config.execution.fmriprep_dir.absolute()), + ), + name='surfs_sources', + ) + get_fsnative = pe.Node(FreeSurferSource(), name='get_fsnative', run_without_submitting=True) def select_target(subject_id, space): @@ -212,6 +223,8 @@ def select_target(subject_id, space): (itk2lta, sampler, [('out_inv', 'reg_file')]), (targets, sampler, [('out', 'target_subject')]), (inputnode, ds_bold_surfs, [('source_file', 'source_file')]), + (inputnode, surfs_sources, [('source_file', 'in1')]), + (surfs_sources, ds_bold_surfs, [('out', 'Sources')]), (itersource, ds_bold_surfs, [('target', 'space')]), (update_metadata, ds_bold_surfs, [('out_file', 'in_file')]), ]) # fmt:skip From 8cc05430312885675fa1e438ff700c676e496ac7 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Thu, 28 Mar 2024 13:27:00 -0400 Subject: [PATCH 2/3] Improve source tracking for GIFTIs. --- fmriprep/workflows/bold/base.py | 32 ++++++++++++++++++++++----- fmriprep/workflows/bold/resampling.py | 19 ++++++++-------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py index ae6a1c380..c6c198e01 100644 --- a/fmriprep/workflows/bold/base.py +++ b/fmriprep/workflows/bold/base.py @@ -504,6 +504,7 @@ def init_bold_wf( output_dir=fmriprep_dir, name='bold_surf_wf', ) + bold_surf_wf.inputs.inputnode.source_file = bold_file workflow.connect([ (inputnode, bold_surf_wf, [ ('subjects_dir', 'inputnode.subjects_dir'), @@ -514,14 +515,35 @@ def init_bold_wf( ]) # fmt:skip if nonstd_spaces.intersection(('anat', 'T1w')): - # Source file should be output T1w-space volumetric file + # Source file should be output T1w-space volumetric file and fsnative2t1w_xfm + merge_surface_sources = pe.Node( + niu.Merge(2), + name='merge_surface_sources', + run_without_submitting=True, + ) workflow.connect([ - (ds_bold_t1_wf, bold_surf_wf, [ - ('outputnode.bold_file', 'inputnode.source_file'), - ]), + (ds_bold_t1_wf, merge_surface_sources, [('outputnode.bold', 'in1')]), + (inputnode, merge_surface_sources, [('fsnative2t1w_xfm', 'in2')]), ]) # fmt:skip else: - bold_surf_wf.inputs.inputnode.source_file = bold_file + # sources are bold_file, motion_xfm, boldref2anat_xfm, fsnative2t1w_xfm + merge_surface_sources = pe.Node( + niu.Merge(4), + name='merge_surface_sources', + run_without_submitting=True, + ) + merge_surface_sources.inputs.in1 = bold_file + workflow.connect([ + (bold_fit_wf, merge_surface_sources, [ + ('outputnode.motion_xfm', 'in2'), + ('outputnode.boldref2anat_xfm', 'in3'), + ]), + (inputnode, merge_surface_sources, [ + ('fsnative2t1w_xfm', 'in4'), + ]), + ]) # fmt:skip + + workflow.connect([(merge_surface_sources, bold_surf_wf, [('out', 'inputnode.sources')])]) if config.workflow.cifti_output: from .resampling import ( diff --git a/fmriprep/workflows/bold/resampling.py b/fmriprep/workflows/bold/resampling.py index 6acbff4ba..696318ff4 100644 --- a/fmriprep/workflows/bold/resampling.py +++ b/fmriprep/workflows/bold/resampling.py @@ -95,6 +95,8 @@ def init_bold_surf_wf( ------ source_file Original BOLD series + sources + List of files used to create the output files. bold_t1w Motion-corrected BOLD series in T1 space subjects_dir @@ -130,6 +132,7 @@ def init_bold_surf_wf( niu.IdentityInterface( fields=[ 'source_file', + 'sources', 'bold_t1w', 'subject_id', 'subjects_dir', @@ -223,7 +226,7 @@ def select_target(subject_id, space): (itk2lta, sampler, [('out_inv', 'reg_file')]), (targets, sampler, [('out', 'target_subject')]), (inputnode, ds_bold_surfs, [('source_file', 'source_file')]), - (inputnode, surfs_sources, [('source_file', 'in1')]), + (inputnode, surfs_sources, [('sources', 'in1')]), (surfs_sources, ds_bold_surfs, [('out', 'Sources')]), (itersource, ds_bold_surfs, [('target', 'space')]), (update_metadata, ds_bold_surfs, [('out_file', 'in_file')]), @@ -501,14 +504,12 @@ def _calc_lower_thr(in_stats): ) # apply goodvoxels ribbon mask to bold - workflow.connect( - [ - (goodvoxels_mask, goodvoxels_ribbon_mask, [('out_file', 'in_file')]), - (ribbon_boldsrc_xfm, goodvoxels_ribbon_mask, [('output_image', 'mask_file')]), - (goodvoxels_mask, outputnode, [('out_file', 'goodvoxels_mask')]), - (goodvoxels_ribbon_mask, outputnode, [('out_file', 'goodvoxels_ribbon')]), - ] - ) + workflow.connect([ + (goodvoxels_mask, goodvoxels_ribbon_mask, [('out_file', 'in_file')]), + (ribbon_boldsrc_xfm, goodvoxels_ribbon_mask, [('output_image', 'mask_file')]), + (goodvoxels_mask, outputnode, [('out_file', 'goodvoxels_mask')]), + (goodvoxels_ribbon_mask, outputnode, [('out_file', 'goodvoxels_ribbon')]), + ]) # fmt:skip return workflow From 79eb79e541e673c7469a451bdab0d851b3f2b79e Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 29 Mar 2024 12:57:37 -0400 Subject: [PATCH 3/3] Drop T1w-space output as a surface Source. --- fmriprep/workflows/bold/base.py | 44 +++++++++++------------------- fmriprep/workflows/bold/outputs.py | 9 ------ 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py index c6c198e01..92cf55338 100644 --- a/fmriprep/workflows/bold/base.py +++ b/fmriprep/workflows/bold/base.py @@ -514,34 +514,22 @@ def init_bold_wf( (bold_anat_wf, bold_surf_wf, [('outputnode.bold_file', 'inputnode.bold_t1w')]), ]) # fmt:skip - if nonstd_spaces.intersection(('anat', 'T1w')): - # Source file should be output T1w-space volumetric file and fsnative2t1w_xfm - merge_surface_sources = pe.Node( - niu.Merge(2), - name='merge_surface_sources', - run_without_submitting=True, - ) - workflow.connect([ - (ds_bold_t1_wf, merge_surface_sources, [('outputnode.bold', 'in1')]), - (inputnode, merge_surface_sources, [('fsnative2t1w_xfm', 'in2')]), - ]) # fmt:skip - else: - # sources are bold_file, motion_xfm, boldref2anat_xfm, fsnative2t1w_xfm - merge_surface_sources = pe.Node( - niu.Merge(4), - name='merge_surface_sources', - run_without_submitting=True, - ) - merge_surface_sources.inputs.in1 = bold_file - workflow.connect([ - (bold_fit_wf, merge_surface_sources, [ - ('outputnode.motion_xfm', 'in2'), - ('outputnode.boldref2anat_xfm', 'in3'), - ]), - (inputnode, merge_surface_sources, [ - ('fsnative2t1w_xfm', 'in4'), - ]), - ]) # fmt:skip + # sources are bold_file, motion_xfm, boldref2anat_xfm, fsnative2t1w_xfm + merge_surface_sources = pe.Node( + niu.Merge(4), + name='merge_surface_sources', + run_without_submitting=True, + ) + merge_surface_sources.inputs.in1 = bold_file + workflow.connect([ + (bold_fit_wf, merge_surface_sources, [ + ('outputnode.motion_xfm', 'in2'), + ('outputnode.boldref2anat_xfm', 'in3'), + ]), + (inputnode, merge_surface_sources, [ + ('fsnative2t1w_xfm', 'in4'), + ]), + ]) # fmt:skip workflow.connect([(merge_surface_sources, bold_surf_wf, [('out', 'inputnode.sources')])]) diff --git a/fmriprep/workflows/bold/outputs.py b/fmriprep/workflows/bold/outputs.py index 242c512f6..e41f356ba 100644 --- a/fmriprep/workflows/bold/outputs.py +++ b/fmriprep/workflows/bold/outputs.py @@ -746,14 +746,6 @@ def init_ds_volumes_wf( ), name='inputnode', ) - outputnode = pe.Node( - niu.IdentityInterface( - fields=[ - 'bold', - ], - ), - name='outputnode', - ) sources = pe.Node( BIDSURI( @@ -799,7 +791,6 @@ def init_ds_volumes_wf( ('resolution', 'resolution'), ]), (sources, ds_bold, [('out', 'Sources')]), - (ds_bold, outputnode, [('out_file', 'bold')]), ]) # fmt:skip resample_ref = pe.Node(