From ad72b45d3f2e9140bbf114af4578f199ad20e3d6 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Wed, 27 Mar 2024 10:01:38 -0400 Subject: [PATCH 01/20] RF: Extend template workflow for other anatomical image types --- smriprep/workflows/outputs.py | 57 ++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index dee84d17a0..d0ab7111ad 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -230,36 +230,39 @@ def init_anat_reports_wf(*, spaces, freesurfer, output_dir, sloppy=False, name=' def init_ds_template_wf( *, - num_t1w, - output_dir, - name='ds_template_wf', + num_anat: int, + output_dir: str, + image_type: str = 'T1w', + name: str = 'ds_template_wf', ): """ Save the subject-specific template Parameters ---------- - num_t1w : :obj:`int` - Number of T1w images + num_anat : :obj:`int` + Number of anatomical images output_dir : :obj:`str` Directory in which to save derivatives + image_type + MR image type (T1w, T2w, etc.) name : :obj:`str` Workflow name (default: ds_template_wf) Inputs ------ source_files - List of input T1w images - t1w_ref_xfms - List of affine transforms to realign input T1w images - t1w_preproc - The T1w reference map, which is calculated as the average of bias-corrected - and preprocessed T1w images, defining the anatomical space. + List of input anatomical images + anat_ref_xfms + List of affine transforms to realign input anatomical images + anat_preproc + The anatomical reference map, which is calculated as the average of bias-corrected + and preprocessed anatomical images, defining the anatomical space. Outputs ------- - t1w_preproc - The location in the output directory of the preprocessed T1w image + anat_preproc + The location in the output directory of the preprocessed anatomical image """ workflow = Workflow(name=name) @@ -268,49 +271,49 @@ def init_ds_template_wf( niu.IdentityInterface( fields=[ 'source_files', - 't1w_ref_xfms', - 't1w_preproc', + 'anat_ref_xfms', + 'anat_preproc', ] ), name='inputnode', ) - outputnode = pe.Node(niu.IdentityInterface(fields=['t1w_preproc']), name='outputnode') + outputnode = pe.Node(niu.IdentityInterface(fields=['anat_preproc']), name='outputnode') - ds_t1w_preproc = pe.Node( + ds_anat_preproc = pe.Node( DerivativesDataSink(base_directory=output_dir, desc='preproc', compress=True), - name='ds_t1w_preproc', + name='ds_anat_preproc', run_without_submitting=True, ) - ds_t1w_preproc.inputs.SkullStripped = False + ds_anat_preproc.inputs.SkullStripped = False # fmt:off workflow.connect([ - (inputnode, ds_t1w_preproc, [('t1w_preproc', 'in_file'), + (inputnode, ds_anat_preproc, [('anat_preproc', 'in_file'), ('source_files', 'source_file')]), - (ds_t1w_preproc, outputnode, [('out_file', 't1w_preproc')]), + (ds_anat_preproc, outputnode, [('out_file', 'anat_preproc')]), ]) # fmt:on - if num_t1w > 1: + if num_anat > 1: # Please note the dictionary unpacking to provide the from argument. # It is necessary because from is a protected keyword (not allowed as argument name). - ds_t1w_ref_xfms = pe.MapNode( + ds_anat_ref_xfms = pe.MapNode( DerivativesDataSink( base_directory=output_dir, - to='T1w', + to=image_type, mode='image', suffix='xfm', extension='txt', **{'from': 'orig'}, ), iterfield=['source_file', 'in_file'], - name='ds_t1w_ref_xfms', + name='ds_anat_ref_xfms', run_without_submitting=True, ) # fmt:off workflow.connect([ - (inputnode, ds_t1w_ref_xfms, [('source_files', 'source_file'), - ('t1w_ref_xfms', 'in_file')]), + (inputnode, ds_anat_ref_xfms, [('source_files', 'source_file'), + ('anat_ref_xfms', 'in_file')]), ]) # fmt:on From e6e350151635440ded71c61830dac4b574532e84 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Wed, 3 Apr 2024 14:49:20 -0400 Subject: [PATCH 02/20] RF: More T1w -> anat substitutions --- smriprep/workflows/outputs.py | 82 +++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index d0ab7111ad..1c94959a94 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -342,7 +342,7 @@ def init_ds_mask_wf( Inputs ------ source_files - List of input T1w images + List of input anat images mask_file Mask to save @@ -370,7 +370,7 @@ def init_ds_mask_wf( suffix='mask', compress=True, ), - name='ds_t1w_mask', + name='ds_anat_mask', run_without_submitting=True, ) if mask_type == 'brain': @@ -391,7 +391,7 @@ def init_ds_mask_wf( return workflow -def init_ds_dseg_wf(*, output_dir, name='ds_dseg_wf'): +def init_ds_dseg_wf(*, output_dir: str, name: str = 'ds_dseg_wf'): """ Save discrete segmentations @@ -405,23 +405,23 @@ def init_ds_dseg_wf(*, output_dir, name='ds_dseg_wf'): Inputs ------ source_files - List of input T1w images - t1w_dseg - Segmentation in T1w space + List of input anatomical images + anat_dseg + Segmentation in anatomical space Outputs ------- - t1w_dseg + anat_dseg The location in the output directory of the discrete segmentation """ workflow = Workflow(name=name) inputnode = pe.Node( - niu.IdentityInterface(fields=['source_files', 't1w_dseg']), + niu.IdentityInterface(fields=['source_files', 'anat_dseg']), name='inputnode', ) - outputnode = pe.Node(niu.IdentityInterface(fields=['t1w_dseg']), name='outputnode') + outputnode = pe.Node(niu.IdentityInterface(fields=['anat_dseg']), name='outputnode') ds_t1w_dseg = pe.Node( DerivativesDataSink( @@ -430,22 +430,27 @@ def init_ds_dseg_wf(*, output_dir, name='ds_dseg_wf'): compress=True, dismiss_entities=['desc'], ), - name='ds_t1w_dseg', + name='ds_anat_dseg', run_without_submitting=True, ) # fmt:off workflow.connect([ - (inputnode, ds_t1w_dseg, [('t1w_dseg', 'in_file'), + (inputnode, ds_t1w_dseg, [('anat_dseg', 'in_file'), ('source_files', 'source_file')]), - (ds_t1w_dseg, outputnode, [('out_file', 't1w_dseg')]), + (ds_t1w_dseg, outputnode, [('out_file', 'anat_dseg')]), ]) # fmt:on return workflow -def init_ds_tpms_wf(*, output_dir, name='ds_tpms_wf', tpm_labels=BIDS_TISSUE_ORDER): +def init_ds_tpms_wf( + *, + output_dir: str, + name: str = 'ds_tpms_wf', + tpm_labels: tuple = BIDS_TISSUE_ORDER, +): """ Save tissue probability maps @@ -461,23 +466,23 @@ def init_ds_tpms_wf(*, output_dir, name='ds_tpms_wf', tpm_labels=BIDS_TISSUE_ORD Inputs ------ source_files - List of input T1w images - t1w_tpms - Tissue probability maps in T1w space + List of input anatomical images + anat_tpms + Tissue probability maps in anatomical space Outputs ------- - t1w_tpms + anat_tpms The location in the output directory of the tissue probability maps """ workflow = Workflow(name=name) inputnode = pe.Node( - niu.IdentityInterface(fields=['source_files', 't1w_tpms']), + niu.IdentityInterface(fields=['source_files', 'anat_tpms']), name='inputnode', ) - outputnode = pe.Node(niu.IdentityInterface(fields=['t1w_tpms']), name='outputnode') + outputnode = pe.Node(niu.IdentityInterface(fields=['anat_tpms']), name='outputnode') ds_t1w_tpms = pe.Node( DerivativesDataSink( @@ -486,16 +491,16 @@ def init_ds_tpms_wf(*, output_dir, name='ds_tpms_wf', tpm_labels=BIDS_TISSUE_ORD compress=True, dismiss_entities=['desc'], ), - name='ds_t1w_tpms', + name='ds_anat_tpms', run_without_submitting=True, ) ds_t1w_tpms.inputs.label = tpm_labels # fmt:off workflow.connect([ - (inputnode, ds_t1w_tpms, [('t1w_tpms', 'in_file'), + (inputnode, ds_t1w_tpms, [('anat_tpms', 'in_file'), ('source_files', 'source_file')]), - (ds_t1w_tpms, outputnode, [('out_file', 't1w_tpms')]), + (ds_t1w_tpms, outputnode, [('out_file', 'anat_tpms')]), ]) # fmt:on @@ -504,8 +509,9 @@ def init_ds_tpms_wf(*, output_dir, name='ds_tpms_wf', tpm_labels=BIDS_TISSUE_ORD def init_ds_template_registration_wf( *, - output_dir, - name='ds_template_registration_wf', + output_dir: str, + image_type: ty.Literal['T1w', 'T2w'] = 'T1w', + name: str = 'ds_template_registration_wf', ): """ Save template registration transforms @@ -514,6 +520,8 @@ def init_ds_template_registration_wf( ---------- output_dir : :obj:`str` Directory in which to save derivatives + image_type : :obj:`str` + Anatomical image type (T1w, T2w, etc) name : :obj:`str` Workflow name (default: anat_derivatives_wf) @@ -522,7 +530,7 @@ def init_ds_template_registration_wf( template Template space and specifications source_files - List of input T1w images + List of input anatomical images anat2std_xfm Nonlinear spatial transform to resample imaging data given in anatomical space into standard space. @@ -541,44 +549,44 @@ def init_ds_template_registration_wf( name='outputnode', ) - ds_std2t1w_xfm = pe.MapNode( + ds_std2anat_xfm = pe.MapNode( DerivativesDataSink( base_directory=output_dir, - to='T1w', + to=image_type, mode='image', suffix='xfm', dismiss_entities=['desc'], ), iterfield=('in_file', 'from'), - name='ds_std2t1w_xfm', + name='ds_std2anat_xfm', run_without_submitting=True, ) - ds_t1w2std_xfm = pe.MapNode( + ds_anat2std_xfm = pe.MapNode( DerivativesDataSink( base_directory=output_dir, mode='image', suffix='xfm', dismiss_entities=['desc'], - **{'from': 'T1w'}, + **{'from': image_type}, ), iterfield=('in_file', 'to'), - name='ds_t1w2std_xfm', + name='ds_anat2std_xfm', run_without_submitting=True, ) # fmt:off workflow.connect([ - (inputnode, ds_t1w2std_xfm, [ + (inputnode, ds_anat2std_xfm, [ ('anat2std_xfm', 'in_file'), (('template', _combine_cohort), 'to'), ('source_files', 'source_file')]), - (inputnode, ds_std2t1w_xfm, [ + (inputnode, ds_std2anat_xfm, [ ('std2anat_xfm', 'in_file'), (('template', _combine_cohort), 'from'), ('source_files', 'source_file')]), - (ds_t1w2std_xfm, outputnode, [('out_file', 'anat2std_xfm')]), - (ds_std2t1w_xfm, outputnode, [('out_file', 'std2anat_xfm')]), + (ds_anat2std_xfm, outputnode, [('out_file', 'anat2std_xfm')]), + (ds_std2anat_xfm, outputnode, [('out_file', 'std2anat_xfm')]), ]) # fmt:on @@ -587,8 +595,8 @@ def init_ds_template_registration_wf( def init_ds_fs_registration_wf( *, - output_dir, - name='ds_fs_registration_wf', + output_dir: str, + name: str = 'ds_fs_registration_wf', ): """ Save rigid registration between subject anatomical template and FreeSurfer T1.mgz From 54fc2ff47e972757c8828598566a6dc584eefc34 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 11 Apr 2024 16:01:36 -0400 Subject: [PATCH 03/20] RF: Make more functions anatagnostic --- smriprep/workflows/outputs.py | 62 ++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index 1c94959a94..b98dc79554 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -423,7 +423,7 @@ def init_ds_dseg_wf(*, output_dir: str, name: str = 'ds_dseg_wf'): ) outputnode = pe.Node(niu.IdentityInterface(fields=['anat_dseg']), name='outputnode') - ds_t1w_dseg = pe.Node( + ds_anat_dseg = pe.Node( DerivativesDataSink( base_directory=output_dir, suffix='dseg', @@ -436,9 +436,9 @@ def init_ds_dseg_wf(*, output_dir: str, name: str = 'ds_dseg_wf'): # fmt:off workflow.connect([ - (inputnode, ds_t1w_dseg, [('anat_dseg', 'in_file'), + (inputnode, ds_anat_dseg, [('anat_dseg', 'in_file'), ('source_files', 'source_file')]), - (ds_t1w_dseg, outputnode, [('out_file', 'anat_dseg')]), + (ds_anat_dseg, outputnode, [('out_file', 'anat_dseg')]), ]) # fmt:on @@ -484,7 +484,7 @@ def init_ds_tpms_wf( ) outputnode = pe.Node(niu.IdentityInterface(fields=['anat_tpms']), name='outputnode') - ds_t1w_tpms = pe.Node( + ds_anat_tpms = pe.Node( DerivativesDataSink( base_directory=output_dir, suffix='probseg', @@ -494,13 +494,13 @@ def init_ds_tpms_wf( name='ds_anat_tpms', run_without_submitting=True, ) - ds_t1w_tpms.inputs.label = tpm_labels + ds_anat_tpms.inputs.label = tpm_labels # fmt:off workflow.connect([ - (inputnode, ds_t1w_tpms, [('anat_tpms', 'in_file'), + (inputnode, ds_anat_tpms, [('anat_tpms', 'in_file'), ('source_files', 'source_file')]), - (ds_t1w_tpms, outputnode, [('out_file', 'anat_tpms')]), + (ds_anat_tpms, outputnode, [('out_file', 'anat_tpms')]), ]) # fmt:on @@ -596,10 +596,12 @@ def init_ds_template_registration_wf( def init_ds_fs_registration_wf( *, output_dir: str, + image_type: ty.Literal['T1w', 'T2w'] = 'T1w', name: str = 'ds_fs_registration_wf', ): """ - Save rigid registration between subject anatomical template and FreeSurfer T1.mgz + Save rigid registration between subject anatomical template and either + FreeSurfer T1.mgz or T2.mgz Parameters ---------- @@ -611,17 +613,17 @@ def init_ds_fs_registration_wf( Inputs ------ source_files - List of input T1w images - fsnative2t1w_xfm + List of input anatomical images + fsnative2anat_xfm LTA-style affine matrix translating from FreeSurfer-conformed - subject space to T1w + subject space to T1/T2 Outputs ------- - t1w2fsnative_xfm - LTA-style affine matrix translating from T1w to + anat2fsnative_xfm + LTA-style affine matrix translating from T1/T2 to FreeSurfer-conformed subject space - fsnative2t1w_xfm + fsnative2anat_xfm LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w @@ -629,11 +631,11 @@ def init_ds_fs_registration_wf( workflow = Workflow(name=name) inputnode = pe.Node( - niu.IdentityInterface(fields=['source_files', 'fsnative2t1w_xfm']), + niu.IdentityInterface(fields=['source_files', 'fsnative2anat_xfm']), name='inputnode', ) outputnode = pe.Node( - niu.IdentityInterface(fields=['fsnative2t1w_xfm', 't1w2fsnative_xfm']), + niu.IdentityInterface(fields=['fsnative2anat_xfm', 'anat2fsnative_xfm']), name='outputnode', ) @@ -641,40 +643,40 @@ def init_ds_fs_registration_wf( # FS native space transforms lta2itk = pe.Node(ConcatenateXFMs(inverse=True), name='lta2itk', run_without_submitting=True) - ds_t1w_fsnative = pe.Node( + ds_anat_fsnative = pe.Node( DerivativesDataSink( base_directory=output_dir, mode='image', to='fsnative', suffix='xfm', extension='txt', - **{'from': 'T1w'}, + **{'from': image_type}, ), - name='ds_t1w_fsnative', + name='ds_anat_fsnative', run_without_submitting=True, ) - ds_fsnative_t1w = pe.Node( + ds_fsnative_anat = pe.Node( DerivativesDataSink( base_directory=output_dir, mode='image', - to='T1w', + to=image_type, suffix='xfm', extension='txt', **{'from': 'fsnative'}, ), - name='ds_fsnative_t1w', + name='ds_fsnative_anat', run_without_submitting=True, ) # fmt:off workflow.connect([ - (inputnode, lta2itk, [('fsnative2t1w_xfm', 'in_xfms')]), - (inputnode, ds_t1w_fsnative, [('source_files', 'source_file')]), - (lta2itk, ds_t1w_fsnative, [('out_inv', 'in_file')]), - (inputnode, ds_fsnative_t1w, [('source_files', 'source_file')]), - (lta2itk, ds_fsnative_t1w, [('out_xfm', 'in_file')]), - (ds_fsnative_t1w, outputnode, [('out_file', 'fsnative2t1w_xfm')]), - (ds_t1w_fsnative, outputnode, [('out_file', 't1w2fsnative_xfm')]), + (inputnode, lta2itk, [('fsnative2anat_xfm', 'in_xfms')]), + (inputnode, ds_anat_fsnative, [('source_files', 'source_file')]), + (lta2itk, ds_anat_fsnative, [('out_inv', 'in_file')]), + (inputnode, ds_fsnative_anat, [('source_files', 'source_file')]), + (lta2itk, ds_fsnative_anat, [('out_xfm', 'in_file')]), + (ds_fsnative_anat, outputnode, [('out_file', 'fsnative2anat_xfm')]), + (ds_anat_fsnative, outputnode, [('out_file', 'anat2fsnative_xfm')]), ]) # fmt:on return workflow @@ -704,7 +706,7 @@ def init_ds_surfaces_wf( Inputs ------ source_files - List of input T1w images + List of input anatomical images ```` Left and right GIFTIs for each surface passed to ``surfaces`` From 3889004cef9b3457aeb85136614171df07fca040 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 11 Apr 2024 16:50:16 -0400 Subject: [PATCH 04/20] RF: More t1w->anat --- smriprep/workflows/surfaces.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/smriprep/workflows/surfaces.py b/smriprep/workflows/surfaces.py index 12e78c501a..42918dd2b2 100644 --- a/smriprep/workflows/surfaces.py +++ b/smriprep/workflows/surfaces.py @@ -863,8 +863,8 @@ def init_gifti_surfaces_wf( ``lh/rh.inflated``, and ``lh/rh.white``. Vertex coordinates are :py:class:`transformed - ` to align with native T1w space - when ``fsnative2t1w_xfm`` is provided. + ` to align with native anatomical space + when ``fsnative2anat_xfm`` is provided. Workflow Graph .. workflow:: @@ -880,7 +880,7 @@ def init_gifti_surfaces_wf( FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID - fsnative2t1w_xfm + fsnative2anat_xfm LTA formatted affine transform file Outputs @@ -896,7 +896,7 @@ def init_gifti_surfaces_wf( workflow = Workflow(name=name) inputnode = pe.Node( - niu.IdentityInterface(['subjects_dir', 'subject_id', 'fsnative2t1w_xfm']), + niu.IdentityInterface(['subjects_dir', 'subject_id', 'fsnative2anat_xfm']), name='inputnode', ) outputnode = pe.Node(niu.IdentityInterface(['surfaces', *surfaces]), name='outputnode') @@ -931,7 +931,7 @@ def init_gifti_surfaces_wf( ('subject_id', 'subject_id'), ]), (inputnode, fix_surfs, [ - ('fsnative2t1w_xfm', 'transform_file'), + ('fsnative2anat_xfm', 'transform_file'), ]), (get_surfaces, surface_list, [ (surf, f'in{i}') for i, surf in enumerate(surfaces, start=1) @@ -973,8 +973,6 @@ def init_gifti_morphometrics_wf( FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID - fsnative2t1w_xfm - LTA formatted affine transform file (inverse) Outputs ------- From ce09d87082496440feffb9711744d5964ce245b6 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 18 Apr 2024 16:02:38 -0400 Subject: [PATCH 05/20] RF: Generalize anatomical reference used in surface workflows --- smriprep/workflows/surfaces.py | 98 ++++++++++++++++------------------ 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/smriprep/workflows/surfaces.py b/smriprep/workflows/surfaces.py index 42918dd2b2..10f93f2da1 100644 --- a/smriprep/workflows/surfaces.py +++ b/smriprep/workflows/surfaces.py @@ -43,6 +43,7 @@ ) from niworkflows.interfaces.freesurfer import PatchedRobustRegister as RobustRegister from niworkflows.interfaces.nitransforms import ConcatenateXFMs +from niworkflows.interfaces.patches import FreeSurferSource from niworkflows.interfaces.utility import KeySelect from niworkflows.interfaces.workbench import ( MetricDilate, @@ -228,7 +229,7 @@ def init_surface_recon_wf( autorecon_resume_wf = init_autorecon_resume_wf(omp_nthreads=omp_nthreads) - get_surfaces = pe.Node(nio.FreeSurferSource(), name='get_surfaces') + get_surfaces = pe.Node(FreeSurferSource(), name='get_surfaces') midthickness = pe.MapNode( MakeMidthickness(thickness=True, distance=0.5, out_name='midthickness'), @@ -284,7 +285,7 @@ def init_surface_recon_wf( ]) # fmt:skip else: # Pretend to be the autorecon1 node so fsnative2t1w_xfm gets run ASAP - fs_base_inputs = autorecon1 = pe.Node(nio.FreeSurferSource(), name='fs_base_inputs') + fs_base_inputs = autorecon1 = pe.Node(FreeSurferSource(), name='fs_base_inputs') workflow.connect([ (inputnode, fs_base_inputs, [ @@ -328,7 +329,9 @@ def init_surface_recon_wf( return workflow -def init_refinement_wf(*, name='refinement_wf'): +def init_refinement_wf( + *, image_type: ty.Literal['T1w', 'T2w'] = 'T1w', name: str = 'refinement_wf' +) -> Workflow: r""" Refine ANTs brain extraction with FreeSurfer segmentation @@ -346,20 +349,12 @@ def init_refinement_wf(*, name='refinement_wf'): FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID - fsnative2t1w_xfm - LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w + fsnative2anat_xfm + LTA-style affine matrix translating from FreeSurfer-conformed subject space to anatomical reference_image Input - t2w - List of T2-weighted structural images (only first used) - flair - List of FLAIR images - skullstripped_t1 - Skull-stripped T1-weighted image (or mask of image) ants_segs Brain tissue segmentation from ANTS ``antsBrainExtraction.sh`` - corrected_t1 - INU-corrected, merged T1-weighted image subjects_dir FreeSurfer SUBJECTS_DIR subject_id @@ -367,14 +362,6 @@ def init_refinement_wf(*, name='refinement_wf'): Outputs ------- - subjects_dir - FreeSurfer SUBJECTS_DIR - subject_id - FreeSurfer subject ID - t1w2fsnative_xfm - LTA-style affine matrix translating from T1w to FreeSurfer-conformed subject space - fsnative2t1w_xfm - LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w out_brainmask Refined brainmask, derived from FreeSurfer's ``aseg`` volume @@ -397,7 +384,7 @@ def init_refinement_wf(*, name='refinement_wf'): fields=[ 'reference_image', 'ants_segs', - 'fsnative2t1w_xfm', + 'fsnative2anat_xfm', 'subjects_dir', 'subject_id', ] @@ -413,24 +400,22 @@ def init_refinement_wf(*, name='refinement_wf'): name='outputnode', ) - aseg_to_native_wf = init_segs_to_native_wf() + aseg_to_native_wf = init_segs_to_native_wf(image_type=image_type) refine = pe.Node(RefineBrainMask(), name='refine') - # fmt:off workflow.connect([ # Refine ANTs mask, deriving new mask from FS' aseg (inputnode, aseg_to_native_wf, [ ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('reference_image', 'inputnode.in_file'), - ('fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('fsnative2t1w_xfm', 'inputnode.fsnative2anat_xfm'), ]), (inputnode, refine, [('reference_image', 'in_anat'), ('ants_segs', 'in_ants')]), (aseg_to_native_wf, refine, [('outputnode.out_file', 'in_aseg')]), (refine, outputnode, [('out_file', 'out_brainmask')]), - ]) - # fmt:on + ]) # fmt:skip return workflow @@ -610,8 +595,8 @@ def _dedup(in_list): def init_surface_derivatives_wf( *, - cifti_output: ty.Literal['91k', '170k', False] = False, - name='surface_derivatives_wf', + image_type: ty.Literal['T1w', 'T2w'] = 'T1w', + name: str = 'surface_derivatives_wf', ): r""" Generate sMRIPrep derivatives from FreeSurfer derivatives @@ -627,9 +612,9 @@ def init_surface_derivatives_wf( Inputs ------ reference - Reference image in native T1w space, for defining a resampling grid - fsnative2t1w_xfm - LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w + Reference image in native anatomical space, for defining a resampling grid + fsnative2anat_xfm + LTA-style affine matrix translating from FreeSurfer-conformed subject space to anatomical subjects_dir FreeSurfer SUBJECTS_DIR subject_id @@ -643,9 +628,9 @@ def init_surface_derivatives_wf( morphometrics GIFTIs of cortical thickness, curvature, and sulcal depth out_aseg - FreeSurfer's aseg segmentation, in native T1w space + FreeSurfer's aseg segmentation, in native anatomical space out_aparc - FreeSurfer's aparc+aseg segmentation, in native T1w space + FreeSurfer's aparc+aseg segmentation, in native anatomical space See also -------- @@ -659,7 +644,7 @@ def init_surface_derivatives_wf( fields=[ 'subjects_dir', 'subject_id', - 'fsnative2t1w_xfm', + 'fsnative2anat_xfm', 'reference', ] ), @@ -679,8 +664,8 @@ def init_surface_derivatives_wf( gifti_surfaces_wf = init_gifti_surfaces_wf(surfaces=['inflated']) gifti_morph_wf = init_gifti_morphometrics_wf(morphometrics=['curv']) - aseg_to_native_wf = init_segs_to_native_wf() - aparc_to_native_wf = init_segs_to_native_wf(segmentation='aparc_aseg') + aseg_to_native_wf = init_segs_to_native_wf(image_type=image_type) + aparc_to_native_wf = init_segs_to_native_wf(image_type=image_type, segmentation='aparc_aseg') # fmt:off workflow.connect([ @@ -688,7 +673,7 @@ def init_surface_derivatives_wf( (inputnode, gifti_surfaces_wf, [ ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), - ('fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('fsnative2anat_xfm', 'inputnode.fsnative2anat_xfm'), ]), (inputnode, gifti_morph_wf, [ ('subjects_dir', 'inputnode.subjects_dir'), @@ -698,13 +683,13 @@ def init_surface_derivatives_wf( ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('reference', 'inputnode.in_file'), - ('fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('fsnative2anat_xfm', 'inputnode.fsnative2anat_xfm'), ]), (inputnode, aparc_to_native_wf, [ ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('reference', 'inputnode.in_file'), - ('fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('fsnative2anat_xfm', 'inputnode.fsnative2anat_xfm'), ]), # Output @@ -1000,7 +985,7 @@ def init_gifti_morphometrics_wf( name='outputnode', ) - get_subject = pe.Node(nio.FreeSurferSource(), name='get_surfaces') + get_subject = pe.Node(FreeSurferSource(), name='get_surfaces') morphometry_list = pe.Node( niu.Merge(len(morphometrics), ravel_inputs=True), @@ -1182,7 +1167,12 @@ def init_hcp_morphometrics_wf( return workflow -def init_segs_to_native_wf(*, name='segs_to_native', segmentation='aseg'): +def init_segs_to_native_wf( + *, + image_type: ty.Literal['T1w', 'T2w'] = 'T1w', + segmentation: ty.Literal['aseg', 'aparc_aseg', 'wmparc'] = 'aseg', + name: str = 'segs_to_native_wf', +) -> Workflow: """ Get a segmentation from FreeSurfer conformed space into native T1w space. @@ -1196,19 +1186,21 @@ def init_segs_to_native_wf(*, name='segs_to_native', segmentation='aseg'): Parameters ---------- + image_type + MR anatomical image type ('T1w' or 'T2w') segmentation The name of a segmentation ('aseg' or 'aparc_aseg' or 'wmparc') Inputs ------ in_file - Anatomical, merged T1w image after INU correction + Anatomical, merged anatomical image after INU correction subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID - fsnative2t1w_xfm - LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w + fsnative2anat_xfm + LTA-style affine matrix translating from FreeSurfer-conformed subject space to anatomical Outputs ------- @@ -1218,16 +1210,16 @@ def init_segs_to_native_wf(*, name='segs_to_native', segmentation='aseg'): """ workflow = Workflow(name=f'{name}_{segmentation}') inputnode = pe.Node( - niu.IdentityInterface(['in_file', 'subjects_dir', 'subject_id', 'fsnative2t1w_xfm']), + niu.IdentityInterface(['in_file', 'subjects_dir', 'subject_id', 'fsnative2anat_xfm']), name='inputnode', ) outputnode = pe.Node(niu.IdentityInterface(['out_file']), name='outputnode') # Extract the aseg and aparc+aseg outputs - fssource = pe.Node(nio.FreeSurferSource(), name='fs_datasource') + fssource = pe.Node(FreeSurferSource(), name='fs_datasource') lta = pe.Node(ConcatenateXFMs(out_fmt='fs'), name='lta', run_without_submitting=True) - # Resample from T1.mgz to T1w.nii.gz, applying any offset in fsnative2t1w_xfm, + # Resample from T1.mgz to T1w.nii.gz, applying any offset in fsnative2anat_xfm, # and convert to NIfTI while we're at it resample = pe.Node( fs.ApplyVolTransform(transformed_file='seg.nii.gz', interp='nearest'), @@ -1252,20 +1244,20 @@ def _sel(x): segmentation = (segmentation, _sel) - # fmt:off + anat = 'T2' if image_type == 'T2w' else 'T1' + workflow.connect([ (inputnode, fssource, [ ('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (inputnode, lta, [('in_file', 'reference'), - ('fsnative2t1w_xfm', 'in_xfms')]), - (fssource, lta, [('T1', 'moving')]), + ('fsnative2anat_xfm', 'in_xfms')]), + (fssource, lta, [(anat, 'moving')]), (inputnode, resample, [('in_file', 'target_file')]), (fssource, resample, [(segmentation, 'source_file')]), (lta, resample, [('out_xfm', 'lta_file')]), (resample, outputnode, [('transformed_file', 'out_file')]), - ]) - # fmt:on + ]) # fmt:skip return workflow From 3701d43697c67a06841730ae1a4830ddb924db27 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 18 Apr 2024 17:47:31 -0400 Subject: [PATCH 06/20] RF: Alter io fields within fit workflow --- smriprep/workflows/anatomical.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index f879b2ea7e..f1154a938b 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -355,7 +355,7 @@ def init_anat_preproc_wf( ('outputnode.t1w_preproc', 'inputnode.reference'), ('outputnode.subjects_dir', 'inputnode.subjects_dir'), ('outputnode.subject_id', 'inputnode.subject_id'), - ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2anat_xfm'), ]), (anat_fit_wf, ds_surfaces_wf, [ ('outputnode.t1w_valid_list', 'inputnode.source_files'), @@ -765,7 +765,7 @@ def init_anat_fit_wf( longitudinal=longitudinal, omp_nthreads=omp_nthreads, num_files=num_t1w, - contrast='T1w', + image_type='T1w', name='anat_template_wf', ) ds_template_wf = init_ds_template_wf(output_dir=output_dir, num_t1w=num_t1w) @@ -1088,10 +1088,10 @@ def init_anat_fit_wf( ('source_files', 'inputnode.source_files'), ]), (surface_recon_wf, ds_fs_registration_wf, [ - ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2anat_xfm'), ]), (ds_fs_registration_wf, outputnode, [ - ('outputnode.fsnative2t1w_xfm', 'fsnative2t1w_xfm'), + ('outputnode.fsnative2anat_xfm', 'fsnative2t1w_xfm'), ]), ]) # fmt:on @@ -1114,7 +1114,7 @@ def init_anat_fit_wf( (surface_recon_wf, refinement_wf, [ ('outputnode.subjects_dir', 'inputnode.subjects_dir'), ('outputnode.subject_id', 'inputnode.subject_id'), - ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2anat_xfm'), ]), (t1w_buffer, refinement_wf, [ ('t1w_preproc', 'inputnode.reference_image'), @@ -1135,7 +1135,7 @@ def init_anat_fit_wf( longitudinal=longitudinal, omp_nthreads=omp_nthreads, num_files=len(t2w), - contrast='T2w', + image_type='T2w', name='t2w_template_wf', ) bbreg = pe.Node( @@ -1224,7 +1224,7 @@ def init_anat_fit_wf( (surface_recon_wf, gifti_surfaces_wf, [ ('outputnode.subject_id', 'inputnode.subject_id'), ('outputnode.subjects_dir', 'inputnode.subjects_dir'), - ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm'), + ('outputnode.fsnative2t1w_xfm', 'inputnode.fsnative2anat_xfm'), ]), (gifti_surfaces_wf, surfaces_buffer, [ (f'outputnode.{surf}', surf) for surf in surfs @@ -1375,7 +1375,7 @@ def init_anat_template_wf( longitudinal: bool, omp_nthreads: int, num_files: int, - contrast: str, + image_type: ty.Literal['T1w', 'T2w'] = 'T1w', name: str = 'anat_template_wf', ): """ @@ -1400,8 +1400,8 @@ def init_anat_template_wf( Maximum number of threads an individual process may use num_files : :obj:`int` Number of images - contrast : :obj:`str` - Name of contrast, for reporting purposes, e.g., T1w, T2w, PDw + image_type : :obj:`str` + MR image type (T1w, T2w, etc.) name : :obj:`str`, optional Workflow name (default: anat_template_wf) @@ -1427,8 +1427,8 @@ def init_anat_template_wf( if num_files > 1: fs_ver = fs.Info().looseversion() or '(version unknown)' workflow.__desc__ = f"""\ -An anatomical {contrast}-reference map was computed after registration of -{num_files} {contrast} images (after INU-correction) using +An anatomical {image_type}-reference map was computed after registration of +{num_files} {image} images (after INU-correction) using `mri_robust_template` [FreeSurfer {fs_ver}, @fs_template]. """ From 4614c94ce7b0838a0de85a2d43ca0d7b7b90c10f Mon Sep 17 00:00:00 2001 From: mathiasg Date: Mon, 22 Apr 2024 16:20:56 -0400 Subject: [PATCH 07/20] RF: anat_second_derivatives_wf -> ds_fs_segs_wf --- smriprep/workflows/outputs.py | 67 +++++++++++------------------------ 1 file changed, 20 insertions(+), 47 deletions(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index b98dc79554..6aff884a22 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -1031,13 +1031,11 @@ def init_ds_anat_volumes_wf( return workflow -def init_anat_second_derivatives_wf( +def init_ds_fs_segs_wf( *, bids_root: str, output_dir: str, - cifti_output: ty.Literal['91k', '170k', False], - name='anat_second_derivatives_wf', - tpm_labels=BIDS_TISSUE_ORDER, + name='ds_fs_segs_wf', ): """ Set up a battery of datasinks to store derivatives in the right location. @@ -1049,41 +1047,18 @@ def init_anat_second_derivatives_wf( output_dir : :obj:`str` Directory in which to save derivatives name : :obj:`str` - Workflow name (default: anat_derivatives_wf) - tpm_labels : :obj:`tuple` - Tissue probability maps in order + Workflow name (default: ds_anat_segs_wf) Inputs ------ + anat_fs_aparc + FreeSurfer's aparc+aseg segmentation, in native anatomical space + anat_fs_aseg + FreeSurfer's aseg segmentation, in native anatomical space + source_files + List of input anatomical images template Template space and specifications - source_files - List of input T1w images - t1w_preproc - The T1w reference map, which is calculated as the average of bias-corrected - and preprocessed T1w images, defining the anatomical space. - t1w_mask - Mask of the ``t1w_preproc`` - t1w_dseg - Segmentation in T1w space - t1w_tpms - Tissue probability maps in T1w space - anat2std_xfm - Nonlinear spatial transform to resample imaging data given in anatomical space - into standard space. - surfaces - GIFTI surfaces (gray/white boundary, midthickness, pial, inflated) - morphometrics - GIFTIs of cortical thickness, curvature, and sulcal depth - t1w_fs_aseg - FreeSurfer's aseg segmentation, in native T1w space - t1w_fs_aparc - FreeSurfer's aparc+aseg segmentation, in native T1w space - cifti_morph - Morphometric CIFTI-2 dscalar files - cifti_metadata - JSON files containing metadata dictionaries - """ workflow = Workflow(name=name) @@ -1092,8 +1067,8 @@ def init_anat_second_derivatives_wf( fields=[ 'template', 'source_files', - 't1w_fs_aseg', - 't1w_fs_aparc', + 'anat_fs_aseg', + 'anat_fs_aparc', ] ), name='inputnode', @@ -1103,27 +1078,25 @@ def init_anat_second_derivatives_wf( raw_sources.inputs.bids_root = bids_root # Parcellations - ds_t1w_fsaseg = pe.Node( + ds_anat_fsaseg = pe.Node( DerivativesDataSink(base_directory=output_dir, desc='aseg', suffix='dseg', compress=True), - name='ds_t1w_fsaseg', + name='ds_anat_fsaseg', run_without_submitting=True, ) - ds_t1w_fsparc = pe.Node( + ds_anat_fsparc = pe.Node( DerivativesDataSink( base_directory=output_dir, desc='aparcaseg', suffix='dseg', compress=True ), - name='ds_t1w_fsparc', + name='ds_anat_fsparc', run_without_submitting=True, ) - # fmt:off workflow.connect([ - (inputnode, ds_t1w_fsaseg, [('t1w_fs_aseg', 'in_file'), - ('source_files', 'source_file')]), - (inputnode, ds_t1w_fsparc, [('t1w_fs_aparc', 'in_file'), - ('source_files', 'source_file')]), - ]) - # fmt:on + (inputnode, ds_anat_fsaseg, [('anat_fs_aseg', 'in_file'), + ('source_files', 'source_file')]), + (inputnode, ds_anat_fsparc, [('anat_fs_aparc', 'in_file'), + ('source_files', 'source_file')]), + ]) # fmt:skip return workflow From 277ad07ab7ad3e6dd318438f76989a1d2dc5a24b Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Apr 2024 11:15:39 -0400 Subject: [PATCH 08/20] FIX: Remnants of second_derivatives_wf, remove unused input --- smriprep/workflows/anatomical.py | 13 ++++++------- smriprep/workflows/outputs.py | 3 --- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index f1154a938b..b902d82610 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -61,10 +61,10 @@ from .fit.registration import init_register_template_wf from .outputs import ( init_anat_reports_wf, - init_anat_second_derivatives_wf, init_ds_anat_volumes_wf, init_ds_dseg_wf, init_ds_fs_registration_wf, + init_ds_fs_segs_wf, init_ds_grayord_metrics_wf, init_ds_mask_wf, init_ds_surface_metrics_wf, @@ -335,10 +335,9 @@ def init_anat_preproc_wf( ]) # fmt:skip if freesurfer: - anat_second_derivatives_wf = init_anat_second_derivatives_wf( + ds_fs_segs_wf = init_ds_fs_segs_wf( bids_root=bids_root, output_dir=output_dir, - cifti_output=cifti_output, ) surface_derivatives_wf = init_surface_derivatives_wf( cifti_output=cifti_output, @@ -369,12 +368,12 @@ def init_anat_preproc_wf( (surface_derivatives_wf, ds_curv_wf, [ ('outputnode.curv', 'inputnode.curv'), ]), - (anat_fit_wf, anat_second_derivatives_wf, [ + (anat_fit_wf, ds_fs_segs_wf, [ ('outputnode.t1w_valid_list', 'inputnode.source_files'), ]), - (surface_derivatives_wf, anat_second_derivatives_wf, [ - ('outputnode.out_aseg', 'inputnode.t1w_fs_aseg'), - ('outputnode.out_aparc', 'inputnode.t1w_fs_aparc'), + (surface_derivatives_wf, ds_fs_segs_wf, [ + ('outputnode.out_aseg', 'inputnode.anat_fs_aseg'), + ('outputnode.out_aparc', 'inputnode.anat_fs_aparc'), ]), (surface_derivatives_wf, outputnode, [ ('outputnode.out_aseg', 't1w_aseg'), diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index 6aff884a22..caaa672f7a 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -1057,15 +1057,12 @@ def init_ds_fs_segs_wf( FreeSurfer's aseg segmentation, in native anatomical space source_files List of input anatomical images - template - Template space and specifications """ workflow = Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface( fields=[ - 'template', 'source_files', 'anat_fs_aseg', 'anat_fs_aparc', From 17e873bc1e2aa6edbc08d5f1cee2a884957b6197 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Sat, 27 Apr 2024 23:04:57 -0400 Subject: [PATCH 09/20] FIX: Remove unused parameter, add conditional for dhcpAsym --- smriprep/workflows/outputs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index caaa672f7a..cbaadfc1b4 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -684,7 +684,6 @@ def init_ds_fs_registration_wf( def init_ds_surfaces_wf( *, - bids_root: str, output_dir: str, surfaces: list[str], name='ds_surfaces_wf', @@ -744,6 +743,8 @@ def init_ds_surfaces_wf( ds_surf.inputs.desc = 'reg' if surf == 'sphere_reg_fsLR': ds_surf.inputs.space = 'fsLR' + elif surf == 'sphere_reg_dhcpAsym': + ds_surf.inputs.space = 'dhcpAsym' # fmt:off workflow.connect([ From 5c7f02b43efdfc5b478d0c3aa7b79cef0da8108f Mon Sep 17 00:00:00 2001 From: mathiasg Date: Sat, 27 Apr 2024 23:31:26 -0400 Subject: [PATCH 10/20] RF: Diversify ds_anat_volumes_wf anatomical inputs --- smriprep/workflows/outputs.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index cbaadfc1b4..acb0ef6d77 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -909,11 +909,11 @@ def init_ds_anat_volumes_wf( fields=[ # Original T1w image 'source_files', - # T1w-space images - 't1w_preproc', - 't1w_mask', - 't1w_dseg', - 't1w_tpms', + # anat-space images + 'anat_preproc', + 'anat_mask', + 'anat_dseg', + 'anat_tpms', # Template 'ref_file', 'anat2std_xfm', @@ -932,7 +932,7 @@ def init_ds_anat_volumes_wf( gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.01) # Mask T1w preproc images - mask_t1w = pe.Node(ApplyMask(), name='mask_t1w') + mask_anat = pe.Node(ApplyMask(), name='mask_anat') # Resample T1w-space inputs anat2std_t1w = pe.Node( @@ -993,15 +993,15 @@ def init_ds_anat_volumes_wf( ('ref_file', 'fixed_image'), (('resolution', _is_native), 'keep_native'), ]), - (inputnode, mask_t1w, [ - ('t1w_preproc', 'in_file'), - ('t1w_mask', 'in_mask'), + (inputnode, mask_anat, [ + ('anat_preproc', 'in_file'), + ('anat_mask', 'in_mask'), ]), - (mask_t1w, anat2std_t1w, [('out_file', 'input_image')]), - (inputnode, anat2std_mask, [('t1w_mask', 'input_image')]), - (inputnode, anat2std_dseg, [('t1w_dseg', 'input_image')]), - (inputnode, anat2std_tpms, [('t1w_tpms', 'input_image')]), - (inputnode, gen_ref, [('t1w_preproc', 'moving_image')]), + (mask_anat, anat2std_t1w, [('out_file', 'input_image')]), + (inputnode, anat2std_mask, [('anat_mask', 'input_image')]), + (inputnode, anat2std_dseg, [('anat_dseg', 'input_image')]), + (inputnode, anat2std_tpms, [('anat_tpms', 'input_image')]), + (inputnode, gen_ref, [('anat_preproc', 'moving_image')]), (anat2std_t1w, ds_std_t1w, [('output_image', 'in_file')]), (anat2std_mask, ds_std_mask, [('output_image', 'in_file')]), (anat2std_dseg, ds_std_dseg, [('output_image', 'in_file')]), From bbddb114c965115647ddb726d8d743a083b64dae Mon Sep 17 00:00:00 2001 From: mathiasg Date: Tue, 30 Apr 2024 22:33:57 -0400 Subject: [PATCH 11/20] ENH: Allow inclusion of additional BIDS entities to outputs --- smriprep/workflows/outputs.py | 47 +++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index acb0ef6d77..d9a94035cc 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -324,7 +324,8 @@ def init_ds_mask_wf( *, bids_root: str, output_dir: str, - mask_type: str, + mask_type: ty.Literal['brain', 'roi'], + extra_entities: dict | None = None, name='ds_mask_wf', ): """ @@ -336,6 +337,8 @@ def init_ds_mask_wf( Root path of BIDS dataset output_dir : :obj:`str` Directory in which to save derivatives + extra_entities : :obj:`dict` or None + Additional entities to add to filename name : :obj:`str` Workflow name (default: ds_mask_wf) @@ -363,12 +366,15 @@ def init_ds_mask_wf( raw_sources = pe.Node(niu.Function(function=_bids_relative), name='raw_sources') raw_sources.inputs.bids_root = bids_root + extra_entities = extra_entities or {} + ds_mask = pe.Node( DerivativesDataSink( base_directory=output_dir, desc=mask_type, suffix='mask', compress=True, + **extra_entities, ), name='ds_anat_mask', run_without_submitting=True, @@ -391,7 +397,12 @@ def init_ds_mask_wf( return workflow -def init_ds_dseg_wf(*, output_dir: str, name: str = 'ds_dseg_wf'): +def init_ds_dseg_wf( + *, + output_dir: str, + extra_entities: dict | None = None, + name: str = 'ds_dseg_wf', +): """ Save discrete segmentations @@ -399,6 +410,8 @@ def init_ds_dseg_wf(*, output_dir: str, name: str = 'ds_dseg_wf'): ---------- output_dir : :obj:`str` Directory in which to save derivatives + extra_entities : :obj:`dict` or None + Additional entities to add to filename name : :obj:`str` Workflow name (default: ds_dseg_wf) @@ -423,12 +436,15 @@ def init_ds_dseg_wf(*, output_dir: str, name: str = 'ds_dseg_wf'): ) outputnode = pe.Node(niu.IdentityInterface(fields=['anat_dseg']), name='outputnode') + extra_entities = extra_entities or {} + ds_anat_dseg = pe.Node( DerivativesDataSink( base_directory=output_dir, suffix='dseg', compress=True, dismiss_entities=['desc'], + **extra_entities, ), name='ds_anat_dseg', run_without_submitting=True, @@ -448,6 +464,7 @@ def init_ds_dseg_wf(*, output_dir: str, name: str = 'ds_dseg_wf'): def init_ds_tpms_wf( *, output_dir: str, + extra_entities: dict | None = None, name: str = 'ds_tpms_wf', tpm_labels: tuple = BIDS_TISSUE_ORDER, ): @@ -458,6 +475,8 @@ def init_ds_tpms_wf( ---------- output_dir : :obj:`str` Directory in which to save derivatives + extra_entities : :obj:`dict` or None + Additional entities to add to filename name : :obj:`str` Workflow name (default: anat_derivatives_wf) tpm_labels : :obj:`tuple` @@ -484,12 +503,15 @@ def init_ds_tpms_wf( ) outputnode = pe.Node(niu.IdentityInterface(fields=['anat_tpms']), name='outputnode') + extra_entities = extra_entities or {} + ds_anat_tpms = pe.Node( DerivativesDataSink( base_directory=output_dir, suffix='probseg', compress=True, dismiss_entities=['desc'], + **extra_entities, ), name='ds_anat_tpms', run_without_submitting=True, @@ -907,7 +929,7 @@ def init_ds_anat_volumes_wf( inputnode = pe.Node( niu.IdentityInterface( fields=[ - # Original T1w image + # Original anat image 'source_files', # anat-space images 'anat_preproc', @@ -1036,6 +1058,7 @@ def init_ds_fs_segs_wf( *, bids_root: str, output_dir: str, + extra_entities: dict | None = None, name='ds_fs_segs_wf', ): """ @@ -1047,6 +1070,8 @@ def init_ds_fs_segs_wf( Root path of BIDS dataset output_dir : :obj:`str` Directory in which to save derivatives + extra_entities : :obj:`dict` or None + Additional entities to add to filename name : :obj:`str` Workflow name (default: ds_anat_segs_wf) @@ -1075,15 +1100,27 @@ def init_ds_fs_segs_wf( raw_sources = pe.Node(niu.Function(function=_bids_relative), name='raw_sources') raw_sources.inputs.bids_root = bids_root + extra_entities = extra_entities or {} + # Parcellations ds_anat_fsaseg = pe.Node( - DerivativesDataSink(base_directory=output_dir, desc='aseg', suffix='dseg', compress=True), + DerivativesDataSink( + base_directory=output_dir, + desc='aseg', + suffix='dseg', + compress=True, + **extra_entities, + ), name='ds_anat_fsaseg', run_without_submitting=True, ) ds_anat_fsparc = pe.Node( DerivativesDataSink( - base_directory=output_dir, desc='aparcaseg', suffix='dseg', compress=True + base_directory=output_dir, + desc='aparcaseg', + suffix='dseg', + compress=True, + **extra_entities, ), name='ds_anat_fsparc', run_without_submitting=True, From 00259715e5855f2b35f37779c31d86d871021cdf Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 13:04:18 -0400 Subject: [PATCH 12/20] FIX: ds_template_wf connections, parameter --- smriprep/workflows/anatomical.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index b902d82610..fbb1947216 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -767,7 +767,7 @@ def init_anat_fit_wf( image_type='T1w', name='anat_template_wf', ) - ds_template_wf = init_ds_template_wf(output_dir=output_dir, num_t1w=num_t1w) + ds_template_wf = init_ds_template_wf(output_dir=output_dir, num_anat=num_t1w) # fmt:off workflow.connect([ @@ -780,11 +780,11 @@ def init_anat_fit_wf( ('outputnode.out_report', 'inputnode.t1w_conform_report'), ]), (anat_template_wf, ds_template_wf, [ - ('outputnode.anat_realign_xfm', 'inputnode.t1w_ref_xfms'), + ('outputnode.anat_realign_xfm', 'inputnode.anat_ref_xfms'), ]), (sourcefile_buffer, ds_template_wf, [('source_files', 'inputnode.source_files')]), - (t1w_buffer, ds_template_wf, [('t1w_preproc', 'inputnode.t1w_preproc')]), - (ds_template_wf, outputnode, [('outputnode.t1w_preproc', 't1w_preproc')]), + (t1w_buffer, ds_template_wf, [('t1w_preproc', 'inputnode.anat_preproc')]), + (ds_template_wf, outputnode, [('outputnode.anat_preproc', 't1w_preproc')]), ]) # fmt:on else: From 45fd797de47330100235d318f9af2c6643df73ea Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 13:11:00 -0400 Subject: [PATCH 13/20] FIX: ds_dseg_wf connections --- smriprep/workflows/anatomical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index fbb1947216..71596b647c 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -953,8 +953,8 @@ def init_anat_fit_wf( workflow.connect([ (fast, lut_t1w_dseg, [('partial_volume_map', 'in_dseg')]), (sourcefile_buffer, ds_dseg_wf, [('source_files', 'inputnode.source_files')]), - (lut_t1w_dseg, ds_dseg_wf, [('out', 'inputnode.t1w_dseg')]), - (ds_dseg_wf, seg_buffer, [('outputnode.t1w_dseg', 't1w_dseg')]), + (lut_t1w_dseg, ds_dseg_wf, [('out', 'inputnode.anat_dseg')]), + (ds_dseg_wf, seg_buffer, [('outputnode.anat_dseg', 't1w_dseg')]), ]) if not have_tpms: ds_tpms_wf = init_ds_tpms_wf(output_dir=output_dir) From 8d2322feb529bd0abae3e9faa3f1b75583d622c9 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 13:21:09 -0400 Subject: [PATCH 14/20] FIX: ds_tpms_wf connections --- smriprep/workflows/anatomical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index 71596b647c..f3897a3711 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -961,8 +961,8 @@ def init_anat_fit_wf( workflow.connect([ (fast, fast2bids, [('partial_volume_files', 'inlist')]), (sourcefile_buffer, ds_tpms_wf, [('source_files', 'inputnode.source_files')]), - (fast2bids, ds_tpms_wf, [('out', 'inputnode.t1w_tpms')]), - (ds_tpms_wf, seg_buffer, [('outputnode.t1w_tpms', 't1w_tpms')]), + (fast2bids, ds_tpms_wf, [('out', 'inputnode.anat_tpms')]), + (ds_tpms_wf, seg_buffer, [('outputnode.anat_tpms', 't1w_tpms')]), ]) # fmt:on else: From 79f3b17c964e6ca2ff897e697fa17856770a34c2 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 13:39:15 -0400 Subject: [PATCH 15/20] FIX: Drop removed parameter --- smriprep/workflows/anatomical.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index f3897a3711..982ef70aa8 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -339,12 +339,8 @@ def init_anat_preproc_wf( bids_root=bids_root, output_dir=output_dir, ) - surface_derivatives_wf = init_surface_derivatives_wf( - cifti_output=cifti_output, - ) - ds_surfaces_wf = init_ds_surfaces_wf( - bids_root=bids_root, output_dir=output_dir, surfaces=['inflated'] - ) + surface_derivatives_wf = init_surface_derivatives_wf() + ds_surfaces_wf = init_ds_surfaces_wf(output_dir=output_dir, surfaces=['inflated']) ds_curv_wf = init_ds_surface_metrics_wf( bids_root=bids_root, output_dir=output_dir, metrics=['curv'], name='ds_curv_wf' ) @@ -1215,9 +1211,7 @@ def init_anat_fit_wf( LOGGER.info(f'ANAT Stage 8: Creating GIFTI surfaces for {surfs + spheres}') if surfs: gifti_surfaces_wf = init_gifti_surfaces_wf(surfaces=surfs) - ds_surfaces_wf = init_ds_surfaces_wf( - bids_root=bids_root, output_dir=output_dir, surfaces=surfs - ) + ds_surfaces_wf = init_ds_surfaces_wf(output_dir=output_dir, surfaces=surfs) # fmt:off workflow.connect([ (surface_recon_wf, gifti_surfaces_wf, [ @@ -1239,7 +1233,7 @@ def init_anat_fit_wf( surfaces=spheres, to_scanner=False, name='gifti_spheres_wf' ) ds_spheres_wf = init_ds_surfaces_wf( - bids_root=bids_root, output_dir=output_dir, surfaces=spheres, name='ds_spheres_wf' + output_dir=output_dir, surfaces=spheres, name='ds_spheres_wf' ) # fmt:off workflow.connect([ @@ -1315,7 +1309,6 @@ def init_anat_fit_wf( LOGGER.info('ANAT Stage 9: Creating fsLR registration sphere') fsLR_reg_wf = init_fsLR_reg_wf() ds_fsLR_reg_wf = init_ds_surfaces_wf( - bids_root=bids_root, output_dir=output_dir, surfaces=['sphere_reg_fsLR'], name='ds_fsLR_reg_wf', @@ -1340,7 +1333,6 @@ def init_anat_fit_wf( LOGGER.info('ANAT Stage 10: Creating MSM-Sulc registration sphere') msm_sulc_wf = init_msm_sulc_wf(sloppy=sloppy) ds_msmsulc_wf = init_ds_surfaces_wf( - bids_root=bids_root, output_dir=output_dir, surfaces=['sphere_reg_msm'], name='ds_msmsulc_wf', From a66b7af35904a8a6ee9fdbd9b3eb91be4302d0d8 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 13:40:26 -0400 Subject: [PATCH 16/20] FIX: Add potential mask typing --- smriprep/workflows/outputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index d9a94035cc..67cd236492 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -324,7 +324,7 @@ def init_ds_mask_wf( *, bids_root: str, output_dir: str, - mask_type: ty.Literal['brain', 'roi'], + mask_type: ty.Literal['brain', 'roi', 'ribbon'], extra_entities: dict | None = None, name='ds_mask_wf', ): From 4b7e22f30dfa5bf56840569cb2c921985f860b86 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 13:51:37 -0400 Subject: [PATCH 17/20] FIX: ds_std_volumes_wf connections --- smriprep/workflows/anatomical.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index 982ef70aa8..3cb12cebfd 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -287,7 +287,6 @@ def init_anat_preproc_wf( ds_std_volumes_wf = init_ds_anat_volumes_wf( bids_root=bids_root, output_dir=output_dir, - name='ds_std_volumes_wf', ) workflow.connect([ @@ -320,10 +319,10 @@ def init_anat_preproc_wf( ]), (anat_fit_wf, ds_std_volumes_wf, [ ('outputnode.t1w_valid_list', 'inputnode.source_files'), - ('outputnode.t1w_preproc', 'inputnode.t1w_preproc'), - ('outputnode.t1w_mask', 'inputnode.t1w_mask'), - ('outputnode.t1w_dseg', 'inputnode.t1w_dseg'), - ('outputnode.t1w_tpms', 'inputnode.t1w_tpms'), + ('outputnode.t1w_preproc', 'inputnode.anat_preproc'), + ('outputnode.t1w_mask', 'inputnode.anat_mask'), + ('outputnode.t1w_dseg', 'inputnode.anat_dseg'), + ('outputnode.t1w_tpms', 'inputnode.anat_tpms'), ]), (template_iterator_wf, ds_std_volumes_wf, [ ('outputnode.std_t1w', 'inputnode.ref_file'), From b03477875a6ffa0a3c36f9d955951bca98f3b63f Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 14:37:57 -0400 Subject: [PATCH 18/20] FIX: Refinement connection --- smriprep/workflows/surfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smriprep/workflows/surfaces.py b/smriprep/workflows/surfaces.py index 10f93f2da1..c2a43b4f73 100644 --- a/smriprep/workflows/surfaces.py +++ b/smriprep/workflows/surfaces.py @@ -409,7 +409,7 @@ def init_refinement_wf( ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('reference_image', 'inputnode.in_file'), - ('fsnative2t1w_xfm', 'inputnode.fsnative2anat_xfm'), + ('fsnative2anat_xfm', 'inputnode.fsnative2anat_xfm'), ]), (inputnode, refine, [('reference_image', 'in_anat'), ('ants_segs', 'in_ants')]), From 0e1b7cbde251ffa44d4a98120f71b634b9fcc79f Mon Sep 17 00:00:00 2001 From: mathiasg Date: Thu, 25 Jul 2024 14:51:26 -0400 Subject: [PATCH 19/20] DOC: Modify function signature --- smriprep/workflows/anatomical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index 3cb12cebfd..b4943275fd 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -1378,7 +1378,7 @@ def init_anat_template_wf( from smriprep.workflows.anatomical import init_anat_template_wf wf = init_anat_template_wf( - longitudinal=False, omp_nthreads=1, num_files=1, contrast="T1w" + longitudinal=False, omp_nthreads=1, num_files=1, image_type="T1w" ) Parameters From 4a8a782ef603fc6049c037454c809f54f9875529 Mon Sep 17 00:00:00 2001 From: mathiasg Date: Fri, 26 Jul 2024 15:44:52 -0400 Subject: [PATCH 20/20] ENH: Require specifying image type for various datasink workflows --- smriprep/workflows/anatomical.py | 12 ++++++++---- smriprep/workflows/outputs.py | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index b4943275fd..78e19ad8bd 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -762,7 +762,9 @@ def init_anat_fit_wf( image_type='T1w', name='anat_template_wf', ) - ds_template_wf = init_ds_template_wf(output_dir=output_dir, num_anat=num_t1w) + ds_template_wf = init_ds_template_wf( + output_dir=output_dir, num_anat=num_t1w, image_type='T1w' + ) # fmt:off workflow.connect([ @@ -992,7 +994,9 @@ def init_anat_fit_wf( omp_nthreads=omp_nthreads, templates=templates, ) - ds_template_registration_wf = init_ds_template_registration_wf(output_dir=output_dir) + ds_template_registration_wf = init_ds_template_registration_wf( + output_dir=output_dir, image_type='T1w' + ) # fmt:off workflow.connect([ @@ -1075,7 +1079,7 @@ def init_anat_fit_wf( fsnative_xfms = precomputed.get('transforms', {}).get('fsnative') if not fsnative_xfms: - ds_fs_registration_wf = init_ds_fs_registration_wf(output_dir=output_dir) + ds_fs_registration_wf = init_ds_fs_registration_wf(output_dir=output_dir, image_type='T1w') # fmt:off workflow.connect([ (sourcefile_buffer, ds_fs_registration_wf, [ @@ -1365,7 +1369,7 @@ def init_anat_template_wf( longitudinal: bool, omp_nthreads: int, num_files: int, - image_type: ty.Literal['T1w', 'T2w'] = 'T1w', + image_type: ty.Literal['T1w', 'T2w'], name: str = 'anat_template_wf', ): """ diff --git a/smriprep/workflows/outputs.py b/smriprep/workflows/outputs.py index 67cd236492..0396a8000c 100644 --- a/smriprep/workflows/outputs.py +++ b/smriprep/workflows/outputs.py @@ -232,7 +232,7 @@ def init_ds_template_wf( *, num_anat: int, output_dir: str, - image_type: str = 'T1w', + image_type: ty.Literal['T1w', 'T2w'], name: str = 'ds_template_wf', ): """ @@ -532,7 +532,7 @@ def init_ds_tpms_wf( def init_ds_template_registration_wf( *, output_dir: str, - image_type: ty.Literal['T1w', 'T2w'] = 'T1w', + image_type: ty.Literal['T1w', 'T2w'], name: str = 'ds_template_registration_wf', ): """ @@ -618,7 +618,7 @@ def init_ds_template_registration_wf( def init_ds_fs_registration_wf( *, output_dir: str, - image_type: ty.Literal['T1w', 'T2w'] = 'T1w', + image_type: ty.Literal['T1w', 'T2w'], name: str = 'ds_fs_registration_wf', ): """