def init_single_subject_wf( debug, freesurfer, fast_track, hires, layout, longitudinal, low_mem, name, omp_nthreads, output_dir, reportlets_dir, skull_strip_fixed_seed, skull_strip_mode, skull_strip_template, spaces, subject_id, bids_filters, ): """ Create a single subject workflow. This workflow organizes the preprocessing pipeline for a single subject. It collects and reports information about the subject, and prepares sub-workflows to perform anatomical and functional preprocessing. Anatomical preprocessing is performed in a single workflow, regardless of the number of sessions. Functional preprocessing is performed using a separate workflow for each individual BOLD series. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from collections import namedtuple from niworkflows.utils.spaces import SpatialReferences, Reference from smriprep.workflows.base import init_single_subject_wf BIDSLayout = namedtuple('BIDSLayout', ['root']) wf = init_single_subject_wf( debug=False, freesurfer=True, fast_track=False, hires=True, layout=BIDSLayout('.'), longitudinal=False, low_mem=False, name='single_subject_wf', omp_nthreads=1, output_dir='.', reportlets_dir='.', skull_strip_fixed_seed=False, skull_strip_mode='force', skull_strip_template=Reference('OASIS30ANTs'), spaces=SpatialReferences(spaces=['MNI152NLin2009cAsym', 'fsaverage5']), subject_id='test', bids_filters=None, ) Parameters ---------- debug : :obj:`bool` Enable debugging outputs freesurfer : :obj:`bool` Enable FreeSurfer surface reconstruction (may increase runtime) fast_track : :obj:`bool` If ``True``, attempt to collect previously run derivatives. hires : :obj:`bool` Enable sub-millimeter preprocessing in FreeSurfer layout : BIDSLayout object BIDS dataset layout longitudinal : :obj:`bool` Treat multiple sessions as longitudinal (may increase runtime) See sub-workflows for specific differences low_mem : :obj:`bool` Write uncompressed .nii files in some cases to reduce memory usage name : :obj:`str` Name of workflow omp_nthreads : :obj:`int` Maximum number of threads an individual process may use output_dir : :obj:`str` Directory in which to save derivatives reportlets_dir : :obj:`str` Directory in which to save reportlets skull_strip_fixed_seed : :obj:`bool` Do not use a random seed for skull-stripping - will ensure run-to-run replicability when used with --omp-nthreads 1 skull_strip_mode : :obj:`str` Determiner for T1-weighted skull stripping (`force` ensures skull stripping, `skip` ignores skull stripping, and `auto` automatically ignores skull stripping if pre-stripped brains are detected). skull_strip_template : :py:class:`~niworkflows.utils.spaces.Reference` Spatial reference to use in atlas-based brain extraction. spaces : :py:class:`~niworkflows.utils.spaces.SpatialReferences` Object containing standard and nonstandard space specifications. subject_id : :obj:`str` List of subject labels bids_filters : dict Provides finer specification of the pipeline input files through pybids entities filters. A dict with the following structure {<suffix>:{<entity>:<filter>,...},...} Inputs ------ subjects_dir FreeSurfer SUBJECTS_DIR """ from ..interfaces.reports import AboutSummary, SubjectSummary if name in ('single_subject_wf', 'single_subject_smripreptest_wf'): # for documentation purposes subject_data = { 't1w': ['/completely/made/up/path/sub-01_T1w.nii.gz'], } else: subject_data = collect_data(layout, subject_id, bids_filters=bids_filters)[0] if not subject_data['t1w']: raise Exception("No T1w images found for participant {}. " "All workflows require T1w images.".format(subject_id)) workflow = Workflow(name=name) workflow.__desc__ = """ Results included in this manuscript come from preprocessing performed using *sMRIPprep* {smriprep_ver} (@fmriprep1; @fmriprep2; RRID:SCR_016216), which is based on *Nipype* {nipype_ver} (@nipype1; @nipype2; RRID:SCR_002502). """.format(smriprep_ver=__version__, nipype_ver=nipype_ver) workflow.__postdesc__ = """ For more details of the pipeline, see [the section corresponding to workflows in *sMRIPrep*'s documentation]\ (https://smriprep.readthedocs.io/en/latest/workflows.html \ "sMRIPrep's documentation"). ### References """ deriv_cache = None if fast_track: from ..utils.bids import collect_derivatives std_spaces = spaces.get_spaces(nonstandard=False, dim=(3, )) deriv_cache = collect_derivatives( Path(output_dir) / 'smriprep', subject_id, std_spaces, freesurfer) inputnode = pe.Node(niu.IdentityInterface(fields=['subjects_dir']), name='inputnode') bidssrc = pe.Node(BIDSDataGrabber(subject_data=subject_data, anat_only=True), name='bidssrc') bids_info = pe.Node(BIDSInfo(bids_dir=layout.root), name='bids_info', run_without_submitting=True) summary = pe.Node( SubjectSummary(output_spaces=spaces.get_spaces(nonstandard=False)), name='summary', run_without_submitting=True) about = pe.Node(AboutSummary(version=__version__, command=' '.join(sys.argv)), name='about', run_without_submitting=True) ds_report_summary = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, desc='summary', keep_dtype=True), name='ds_report_summary', run_without_submitting=True) ds_report_about = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, desc='about', keep_dtype=True), name='ds_report_about', run_without_submitting=True) # Preprocessing of T1w (includes registration to MNI) anat_preproc_wf = init_anat_preproc_wf( bids_root=layout.root, debug=debug, existing_derivatives=deriv_cache, freesurfer=freesurfer, hires=hires, longitudinal=longitudinal, name="anat_preproc_wf", t1w=subject_data['t1w'], omp_nthreads=omp_nthreads, output_dir=output_dir, reportlets_dir=reportlets_dir, skull_strip_fixed_seed=skull_strip_fixed_seed, skull_strip_mode=skull_strip_mode, skull_strip_template=skull_strip_template, spaces=spaces, ) workflow.connect([ (inputnode, anat_preproc_wf, [('subjects_dir', 'inputnode.subjects_dir')]), (bidssrc, bids_info, [(('t1w', fix_multi_T1w_source_name), 'in_file') ]), (inputnode, summary, [('subjects_dir', 'subjects_dir')]), (bidssrc, summary, [('t1w', 't1w'), ('t2w', 't2w')]), (bids_info, summary, [('subject', 'subject_id')]), (bids_info, anat_preproc_wf, [(('subject', _prefix), 'inputnode.subject_id')]), (bidssrc, anat_preproc_wf, [('t1w', 'inputnode.t1w'), ('t2w', 'inputnode.t2w'), ('roi', 'inputnode.roi'), ('flair', 'inputnode.flair')]), (bidssrc, ds_report_summary, [(('t1w', fix_multi_T1w_source_name), 'source_file')]), (summary, ds_report_summary, [('out_report', 'in_file')]), (bidssrc, ds_report_about, [(('t1w', fix_multi_T1w_source_name), 'source_file')]), (about, ds_report_about, [('out_report', 'in_file')]), ]) return workflow
def create_register_func_to_mni(name='register_func_to_mni'): """ Registers a functional scan in native space to MNI standard space. This is meant to be used after create_nonlinear_register() has been run and relies on some of it's outputs. Parameters ---------- name : string, optional Name of the workflow. Returns ------- register_func_to_mni : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: inputspec.func : string (nifti file) Input functional scan to be registered to MNI space inputspec.mni : string (nifti file) Reference MNI file inputspec.anat : string (nifti file) Corresponding anatomical scan of subject inputspec.interp : string Type of interpolation to use ('trilinear' or 'nearestneighbour' or 'sinc') inputspec.anat_to_mni_nonlinear_xfm : string (warp file) Corresponding anatomical native space to MNI warp file inputspec.anat_to_mni_linear_xfm : string (mat file) Corresponding anatomical native space to MNI mat file Workflow Outputs:: outputspec.func_to_anat_linear_xfm : string (mat file) Affine transformation from functional to anatomical native space outputspec.func_to_mni_linear_xfm : string (mat file) Affine transformation from functional to MNI space outputspec.mni_to_func_linear_xfm : string (mat file) Affine transformation from MNI to functional space outputspec.mni_func : string (nifti file) Functional scan registered to MNI standard space Workflow Graph: .. image:: ../images/register_func_to_mni.dot.png :width: 500 Detailed Workflow Graph: .. image:: ../images/register_func_to_mni_detailed.dot.png :width: 500 """ register_func_to_mni = pe.Workflow(name=name) inputspec = pe.Node(util.IdentityInterface(fields=[ 'func', 'mni', 'anat', 'interp', 'anat_to_mni_nonlinear_xfm', 'anat_to_mni_linear_xfm' ]), name='inputspec') outputspec = pe.Node(util.IdentityInterface(fields=[ 'func_to_anat_linear_xfm', 'func_to_mni_linear_xfm', 'mni_to_func_linear_xfm', 'mni_func' ]), name='outputspec') linear_reg = pe.Node(interface=fsl.FLIRT(), name='linear_func_to_anat') linear_reg.inputs.cost = 'corratio' linear_reg.inputs.dof = 6 mni_warp = pe.Node(interface=fsl.ApplyWarp(), name='mni_warp') mni_affine = pe.Node(interface=fsl.ConvertXFM(), name='mni_affine') mni_affine.inputs.concat_xfm = True register_func_to_mni.connect(linear_reg, 'out_matrix_file', mni_affine, 'in_file2') register_func_to_mni.connect(inputspec, 'anat_to_mni_linear_xfm', mni_affine, 'in_file') register_func_to_mni.connect(mni_affine, 'out_file', outputspec, 'func_to_mni_linear_xfm') inv_mni_affine = pe.Node(interface=fsl.ConvertXFM(), name='inv_mni_affine') inv_mni_affine.inputs.invert_xfm = True register_func_to_mni.connect(mni_affine, 'out_file', inv_mni_affine, 'in_file') register_func_to_mni.connect(inv_mni_affine, 'out_file', outputspec, 'mni_to_func_linear_xfm') register_func_to_mni.connect(inputspec, 'func', linear_reg, 'in_file') register_func_to_mni.connect(inputspec, 'anat', linear_reg, 'reference') register_func_to_mni.connect(inputspec, 'interp', linear_reg, 'interp') register_func_to_mni.connect(inputspec, 'func', mni_warp, 'in_file') register_func_to_mni.connect(inputspec, 'mni', mni_warp, 'ref_file') register_func_to_mni.connect(inputspec, 'anat_to_mni_nonlinear_xfm', mni_warp, 'field_file') register_func_to_mni.connect(linear_reg, 'out_matrix_file', mni_warp, 'premat') register_func_to_mni.connect(linear_reg, 'out_matrix_file', outputspec, 'func_to_anat_linear_xfm') register_func_to_mni.connect(mni_warp, 'out_file', outputspec, 'mni_func') return register_func_to_mni
def create_bbregister_func_to_anat(name='bbregister_func_to_anat'): """ Registers a functional scan in native space to structural. This is meant to be used after create_nonlinear_register() has been run and relies on some of it's outputs. Parameters ---------- name : string, optional Name of the workflow. Returns ------- register_func_to_anat : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: inputspec.func : string (nifti file) Input functional scan to be registered to MNI space inputspec.anat_skull : string (nifti file) Corresponding full-head scan of subject inputspec.linear_reg_matrix : string (mat file) Affine matrix from linear functional to anatomical registration inputspec.anat_wm_segmentation : string (nifti file) White matter segmentation probability mask in anatomical space inputspec.bbr_schedule : string (.sch file) Boundary based registration schedule file for flirt command Workflow Outputs:: outputspec.func_to_anat_linear_xfm : string (mat file) Affine transformation from functional to anatomical native space outputspec.anat_func : string (nifti file) Functional data in anatomical space """ register_bbregister_func_to_anat = pe.Workflow(name=name) inputspec = pe.Node(util.IdentityInterface(fields=[ 'func', 'anat_skull', 'linear_reg_matrix', 'anat_wm_segmentation', 'bbr_schedule' ]), name='inputspec') outputspec = pe.Node( util.IdentityInterface(fields=[ 'func_to_anat_linear_xfm', #'func_to_mni_linear_xfm', #'mni_to_func_linear_xfm', #'anat_wm_edge', 'anat_func' ]), name='outputspec') wm_bb_mask = pe.Node(interface=fsl.ImageMaths(), name='wm_bb_mask') wm_bb_mask.inputs.op_string = '-thr 0.5 -bin' register_bbregister_func_to_anat.connect(inputspec, 'anat_wm_segmentation', wm_bb_mask, 'in_file') def wm_bb_edge_args(mas_file): return '-edge -bin -mas ' + mas_file #wm_bb_edge = pe.Node(interface=fsl.ImageMaths(), # name='wm_bb_edge') #register_func_to_mni.connect(wm_bb_mask, 'out_file', # wm_bb_edge, 'in_file') #register_func_to_mni.connect(wm_bb_mask, ('out_file', wm_bb_edge_args), # wm_bb_edge, 'op_string') def bbreg_args(bbreg_target): return '-cost bbr -wmseg ' + bbreg_target bbreg_func_to_anat = pe.Node(interface=fsl.FLIRT(), name='bbreg_func_to_anat') bbreg_func_to_anat.inputs.dof = 6 register_bbregister_func_to_anat.connect(inputspec, 'bbr_schedule', bbreg_func_to_anat, 'schedule') register_bbregister_func_to_anat.connect(wm_bb_mask, ('out_file', bbreg_args), bbreg_func_to_anat, 'args') register_bbregister_func_to_anat.connect(inputspec, 'func', bbreg_func_to_anat, 'in_file') register_bbregister_func_to_anat.connect(inputspec, 'anat_skull', bbreg_func_to_anat, 'reference') register_bbregister_func_to_anat.connect(inputspec, 'linear_reg_matrix', bbreg_func_to_anat, 'in_matrix_file') register_bbregister_func_to_anat.connect(bbreg_func_to_anat, 'out_matrix_file', outputspec, 'func_to_anat_linear_xfm') register_bbregister_func_to_anat.connect(bbreg_func_to_anat, 'out_file', outputspec, 'anat_func') #register_func_to_mni.connect(wm_bb_edge, 'out_file', # outputspec, 'anat_wm_edge') return register_bbregister_func_to_anat
def fmri_qc_workflow(dataset, settings, name='funcMRIQC'): """ The fMRI qc workflow .. workflow:: import os.path as op from mriqc.workflows.functional import fmri_qc_workflow datadir = op.abspath('data') wf = fmri_qc_workflow([op.join(datadir, 'sub-001/func/sub-001_task-rest_bold.nii.gz')], settings={'bids_dir': datadir, 'output_dir': op.abspath('out'), 'no_sub': True}) """ workflow = pe.Workflow(name=name) biggest_file_gb = settings.get("biggest_file_size_gb", 1) # Define workflow, inputs and outputs # 0. Get data, put it in RAS orientation inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') WFLOGGER.info('Building fMRI QC workflow, datasets list: %s', [ str(Path(d).relative_to(settings['bids_dir'])) for d in sorted(dataset) ]) inputnode.iterables = [('in_file', dataset)] outputnode = pe.Node(niu.IdentityInterface( fields=['qc', 'mosaic', 'out_group', 'out_dvars', 'out_fd']), name='outputnode') non_steady_state_detector = pe.Node(nac.NonSteadyStateDetector(), name="non_steady_state_detector") sanitize = pe.Node(niutils.SanitizeImage(), name="sanitize", mem_gb=biggest_file_gb * 4.0) sanitize.inputs.max_32bit = settings.get("float32", DEFAULTS['float32']) # Workflow -------------------------------------------------------- # 1. HMC: head motion correct if settings.get('hmc_fsl', False): hmcwf = hmc_mcflirt(settings) else: hmcwf = hmc_afni(settings, st_correct=settings.get('correct_slice_timing', False), despike=settings.get('despike', False), deoblique=settings.get('deoblique', False), start_idx=settings.get('start_idx', None), stop_idx=settings.get('stop_idx', None)) # Set HMC settings hmcwf.inputs.inputnode.fd_radius = settings.get('fd_radius', DEFAULT_FD_RADIUS) mean = pe.Node( afni.TStat( # 2. Compute mean fmri options='-mean', outputtype='NIFTI_GZ'), name='mean', mem_gb=biggest_file_gb * 1.5) skullstrip_epi = fmri_bmsk_workflow(use_bet=True) # EPI to MNI registration ema = epi_mni_align(settings) # Compute TSNR using nipype implementation tsnr = pe.Node(nac.TSNR(), name='compute_tsnr', mem_gb=biggest_file_gb * 2.5) # 7. Compute IQMs iqmswf = compute_iqms(settings) # Reports repwf = individual_reports(settings) workflow.connect([ (inputnode, iqmswf, [('in_file', 'inputnode.in_file')]), (inputnode, sanitize, [('in_file', 'in_file')]), (inputnode, non_steady_state_detector, [('in_file', 'in_file')]), (non_steady_state_detector, sanitize, [('n_volumes_to_discard', 'n_volumes_to_discard')]), (sanitize, hmcwf, [('out_file', 'inputnode.in_file')]), (mean, skullstrip_epi, [('out_file', 'inputnode.in_file')]), (hmcwf, mean, [('outputnode.out_file', 'in_file')]), (hmcwf, tsnr, [('outputnode.out_file', 'in_file')]), (mean, ema, [('out_file', 'inputnode.epi_mean')]), (skullstrip_epi, ema, [('outputnode.out_file', 'inputnode.epi_mask')]), (sanitize, iqmswf, [('out_file', 'inputnode.in_ras')]), (mean, iqmswf, [('out_file', 'inputnode.epi_mean')]), (hmcwf, iqmswf, [('outputnode.out_file', 'inputnode.hmc_epi'), ('outputnode.out_fd', 'inputnode.hmc_fd')]), (skullstrip_epi, iqmswf, [('outputnode.out_file', 'inputnode.brainmask')]), (tsnr, iqmswf, [('tsnr_file', 'inputnode.in_tsnr')]), (sanitize, repwf, [('out_file', 'inputnode.in_ras')]), (mean, repwf, [('out_file', 'inputnode.epi_mean')]), (tsnr, repwf, [('stddev_file', 'inputnode.in_stddev')]), (skullstrip_epi, repwf, [('outputnode.out_file', 'inputnode.brainmask') ]), (hmcwf, repwf, [('outputnode.out_fd', 'inputnode.hmc_fd'), ('outputnode.out_file', 'inputnode.hmc_epi')]), (ema, repwf, [('outputnode.epi_parc', 'inputnode.epi_parc'), ('outputnode.report', 'inputnode.mni_report')]), (non_steady_state_detector, iqmswf, [('n_volumes_to_discard', 'inputnode.exclude_index')]), (iqmswf, repwf, [('outputnode.out_file', 'inputnode.in_iqms'), ('outputnode.out_dvars', 'inputnode.in_dvars'), ('outputnode.outliers', 'inputnode.outliers')]), (hmcwf, outputnode, [('outputnode.out_fd', 'out_fd')]), ]) if settings.get('fft_spikes_detector', False): workflow.connect([ (iqmswf, repwf, [('outputnode.out_spikes', 'inputnode.in_spikes'), ('outputnode.out_fft', 'inputnode.in_fft')]), ]) if settings.get('ica', False): melodic = pe.Node(nws.MELODICRPT(no_bet=True, no_mask=True, no_mm=True, generate_report=True), name="ICA", mem_gb=biggest_file_gb * 5) workflow.connect([(sanitize, melodic, [('out_file', 'in_files')]), (skullstrip_epi, melodic, [('outputnode.out_file', 'report_mask')]), (melodic, repwf, [('out_report', 'inputnode.ica_report')])]) # Upload metrics if not settings.get('no_sub', False): from ..interfaces.webapi import UploadIQMs upldwf = pe.Node(UploadIQMs(), name='UploadMetrics') upldwf.inputs.url = settings.get('webapi_url') if settings.get('webapi_port'): upldwf.inputs.port = settings.get('webapi_port') upldwf.inputs.email = settings.get('email') upldwf.inputs.strict = settings.get('upload_strict', False) workflow.connect([ (iqmswf, upldwf, [('outputnode.out_file', 'in_iqms')]), ]) return workflow
def epi_mni_align(settings, name='SpatialNormalization'): """ Uses FSL FLIRT with the BBR cost function to find the transform that maps the EPI space into the MNI152-nonlinear-symmetric atlas. The input epi_mean is the averaged and brain-masked EPI timeseries Returns the EPI mean resampled in MNI space (for checking out registration) and the associated "lobe" parcellation in EPI space. .. workflow:: from mriqc.workflows.functional import epi_mni_align wf = epi_mni_align({}) """ from niworkflows.data import get_mni_icbm152_nlin_asym_09c as get_template from niworkflows.interfaces.registration import (RobustMNINormalizationRPT as RobustMNINormalization) from pkg_resources import resource_filename as pkgrf # Get settings testing = settings.get('testing', False) n_procs = settings.get('n_procs', 1) ants_nthreads = settings.get('ants_nthreads', DEFAULTS['ants_nthreads']) # Init template mni_template = get_template() workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['epi_mean', 'epi_mask']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['epi_mni', 'epi_parc', 'report']), name='outputnode') epimask = pe.Node(fsl.ApplyMask(), name='EPIApplyMask') n4itk = pe.Node(ants.N4BiasFieldCorrection(dimension=3), name='SharpenEPI') norm = pe.Node(RobustMNINormalization( num_threads=ants_nthreads, float=settings.get('ants_float', False), template='mni_icbm152_nlin_asym_09c', reference_image=pkgrf('mriqc', 'data/mni/2mm_T2_brain.nii.gz'), flavor='testing' if testing else 'precise', moving='EPI', generate_report=True, ), name='EPI2MNI', num_threads=n_procs, mem_gb=3) # Warp segmentation into EPI space invt = pe.Node(ants.ApplyTransforms(float=True, input_image=op.join( mni_template, '1mm_parc.nii.gz'), dimension=3, default_value=0, interpolation='NearestNeighbor'), name='ResampleSegmentation') workflow.connect([ (inputnode, invt, [('epi_mean', 'reference_image')]), (inputnode, n4itk, [('epi_mean', 'input_image')]), (inputnode, epimask, [('epi_mask', 'mask_file')]), (n4itk, epimask, [('output_image', 'in_file')]), (epimask, norm, [('out_file', 'moving_image')]), (norm, invt, [('inverse_composite_transform', 'transforms')]), (invt, outputnode, [('output_image', 'epi_parc')]), (norm, outputnode, [('warped_image', 'epi_mni'), ('out_report', 'report')]), ]) return workflow
def init_infant_anat_wf( *, age_months, ants_affine_init, t1w, t2w, anat_modality, bids_root, existing_derivatives, freesurfer, longitudinal, omp_nthreads, output_dir, segmentation_atlases, skull_strip_mode, skull_strip_template, sloppy, spaces, name="infant_anat_wf", ): """ - T1w reference: realigning and then averaging anatomical images. - Brain extraction and INU (bias field) correction. - Brain tissue segmentation. - Spatial normalization to standard spaces. - Surface reconstruction with FreeSurfer_. Outputs ------- anat_preproc The anatomical reference map, which is calculated as the average of bias-corrected and preprocessed anatomical images, defining the anatomical space. anat_brain Skull-stripped ``anat_preproc`` anat_mask Brain (binary) mask estimated by brain extraction. anat_dseg Brain tissue segmentation of the preprocessed structural image, including gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF). anat_tpms List of tissue probability maps corresponding to ``t1w_dseg``. std_preproc T1w reference resampled in one or more standard spaces. std_mask Mask of skull-stripped template, in MNI space std_dseg Segmentation, resampled into MNI space std_tpms List of tissue probability maps in MNI space subjects_dir FreeSurfer SUBJECTS_DIR anat2std_xfm Nonlinear spatial transform to resample imaging data given in anatomical space into standard space. std2anat_xfm Inverse transform of the above. subject_id FreeSurfer subject ID anat2fsnative_xfm LTA-style affine matrix translating from T1w to FreeSurfer-conformed subject space fsnative2anat_xfm LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w surfaces GIFTI surfaces (gray/white boundary, midthickness, pial, inflated) """ from nipype.interfaces.base import Undefined from nipype.interfaces.ants.base import Info as ANTsInfo from niworkflows.interfaces.images import ValidateImage from smriprep.workflows.anatomical import init_anat_template_wf, _probseg_fast2bids, _pop from smriprep.workflows.norm import init_anat_norm_wf from smriprep.workflows.outputs import ( init_anat_reports_wf, init_anat_derivatives_wf, ) from ...utils.misc import fix_multi_source_name from .brain_extraction import init_infant_brain_extraction_wf from .segmentation import init_anat_seg_wf from .surfaces import init_infant_surface_recon_wf # for now, T1w only num_t1w = len(t1w) if t1w else 0 num_t2w = len(t2w) if t2w else 0 wf = pe.Workflow(name=name) desc = """Anatomical data preprocessing : """ desc += f"""\ A total of {num_t1w} T1w and {num_t2w} T2w images were found within the input BIDS dataset.""" inputnode = pe.Node( niu.IdentityInterface( fields=["t1w", "t2w", "subject_id", "subjects_dir" ]), # FLAIR / ROI? name="inputnode", ) outputnode = pe.Node( niu.IdentityInterface(fields=[ "anat_preproc", "anat_brain", "anat_mask", "anat_dseg", "anat_tpms", "anat_ref_xfms", "std_preproc", "std_brain", "std_dseg", "std_tpms", "subjects_dir", "subject_id", "anat2std_xfm", "std2anat_xfm", "anat2fsnative_xfm", "fsnative2anat_xfm", "surfaces", "anat_aseg", "anat_aparc", ]), name="outputnode", ) # Connect reportlets workflows anat_reports_wf = init_anat_reports_wf( freesurfer=freesurfer, output_dir=output_dir, ) if existing_derivatives: raise NotImplementedError("Reusing derivatives is not yet supported.") desc += """ All of the T1-weighted images were corrected for intensity non-uniformity (INU) """ if num_t1w > 1 else """\ The T1-weighted (T1w) image was corrected for intensity non-uniformity (INU) """ desc += """\ with `N4BiasFieldCorrection` [@n4], distributed with ANTs {ants_ver} \ [@ants, RRID:SCR_004757]""" desc += '.\n' if num_t1w > 1 else ", and used as T1w-reference throughout the workflow.\n" desc += """\ The T1w-reference was then skull-stripped with a modified implementation of the `antsBrainExtraction.sh` workflow (from ANTs), using {skullstrip_tpl} as target template. Brain tissue segmentation of cerebrospinal fluid (CSF), white-matter (WM) and gray-matter (GM) was performed on the brain-extracted T1w using ANTs JointFusion, distributed with ANTs {ants_ver}. """ wf.__desc__ = desc.format( ants_ver=ANTsInfo.version() or '(version unknown)', skullstrip_tpl=skull_strip_template.fullname, ) # Define output workflows anat_reports_wf = init_anat_reports_wf(freesurfer=freesurfer, output_dir=output_dir) # HACK: remove resolution from TFSelect anat_reports_wf.get_node('tf_select').inputs.resolution = Undefined anat_derivatives_wf = init_anat_derivatives_wf( bids_root=bids_root, freesurfer=freesurfer, num_t1w=num_t1w, output_dir=output_dir, spaces=spaces, ) # HACK: remove resolution from TFSelect anat_derivatives_wf.get_node('select_tpl').inputs.resolution = Undefined # Multiple T1w files -> generate average reference t1w_template_wf = init_anat_template_wf( longitudinal=False, omp_nthreads=omp_nthreads, num_t1w=num_t1w, ) use_t2w = False if num_t2w: t2w_template_wf = init_t2w_template_wf( longitudinal=longitudinal, omp_nthreads=omp_nthreads, num_t2w=num_t2w, ) wf.connect(inputnode, 't2w', t2w_template_wf, 'inputnode.t2w') # TODO: determine cutoff (< 8 months) use_t2w = True anat_validate = pe.Node( ValidateImage(), name='anat_validate', run_without_submitting=True, ) # INU + Brain Extraction if skull_strip_mode != 'force': raise NotImplementedError("Skull stripping is currently required.") brain_extraction_wf = init_infant_brain_extraction_wf( age_months=age_months, mri_scheme=anat_modality.capitalize(), ants_affine_init=ants_affine_init, skull_strip_template=skull_strip_template.space, template_specs=skull_strip_template.spec, omp_nthreads=omp_nthreads, output_dir=Path(output_dir), sloppy=sloppy, use_t2w=use_t2w, ) # Ensure single outputs be_buffer = pe.Node( niu.IdentityInterface(fields=["anat_preproc", "anat_brain"]), name='be_buffer') # Segmentation - initial implementation should be simple: JLF anat_seg_wf = init_anat_seg_wf( age_months=age_months, anat_modality=anat_modality.capitalize(), template_dir=segmentation_atlases, sloppy=sloppy, omp_nthreads=omp_nthreads, ) # Spatial normalization (requires segmentation) anat_norm_wf = init_anat_norm_wf( debug=sloppy, omp_nthreads=omp_nthreads, templates=spaces.get_spaces(nonstandard=False, dim=(3, )), ) # HACK: remove resolution from TFSelect anat_norm_wf.get_node('tf_select').inputs.resolution = Undefined # HACK: requires patched niworkflows to allow setting resolution to none anat_norm_wf.get_node('registration').inputs.template_resolution = None # fmt: off if use_t2w: wf.connect(t2w_template_wf, 'outputnode.t2w_ref', brain_extraction_wf, 'inputnode.t2w') wf.connect([ (inputnode, t1w_template_wf, [ ('t1w', 'inputnode.t1w'), ]), (t1w_template_wf, outputnode, [ ('outputnode.t1w_realign_xfm', 'anat_ref_xfms'), ]), (t1w_template_wf, anat_validate, [ ('outputnode.t1w_ref', 'in_file'), ]), (anat_validate, brain_extraction_wf, [ ('out_file', 'inputnode.t1w'), ]), (brain_extraction_wf, be_buffer, [ (('outputnode.t1w_corrected', _pop), 'anat_preproc'), (('outputnode.t1w_corrected_brain', _pop), 'anat_brain'), (('outputnode.t1w_mask', _pop), 'anat_mask'), ]), (be_buffer, outputnode, [ ('anat_preproc', 'anat_preproc'), ('anat_brain', 'anat_brain'), ('anat_mask', 'anat_mask'), ]), (be_buffer, anat_seg_wf, [ ('anat_brain', 'inputnode.anat_brain'), ]), (anat_seg_wf, outputnode, [ ('outputnode.anat_dseg', 'anat_dseg'), ]), (anat_seg_wf, anat_norm_wf, [ ('outputnode.anat_dseg', 'inputnode.moving_segmentation'), ('outputnode.anat_tpms', 'inputnode.moving_tpms'), ]), (be_buffer, anat_norm_wf, [ ('anat_preproc', 'inputnode.moving_image'), ('anat_mask', 'inputnode.moving_mask'), ]), (anat_norm_wf, outputnode, [ ('poutputnode.standardized', 'std_preproc'), ('poutputnode.std_mask', 'std_mask'), ('poutputnode.std_dseg', 'std_dseg'), ('poutputnode.std_tpms', 'std_tpms'), ('outputnode.template', 'template'), ('outputnode.anat2std_xfm', 'anat2std_xfm'), ('outputnode.std2anat_xfm', 'std2anat_xfm'), ]), ( inputnode, anat_norm_wf, [ (('t1w', fix_multi_source_name), 'inputnode.orig_t1w'), # anat_validate? not used... ]), ]) wf.connect([ # reports (inputnode, anat_reports_wf, [ ('t1w', 'inputnode.source_file'), ]), (outputnode, anat_reports_wf, [ ('anat_preproc', 'inputnode.t1w_preproc'), ('anat_mask', 'inputnode.t1w_mask'), ('anat_dseg', 'inputnode.t1w_dseg'), ('std_preproc', 'inputnode.std_t1w'), ('std_mask', 'inputnode.std_mask'), ]), (t1w_template_wf, anat_reports_wf, [ ('outputnode.out_report', 'inputnode.t1w_conform_report'), ]), (anat_norm_wf, anat_reports_wf, [ ('poutputnode.template', 'inputnode.template'), ]), # derivatives (t1w_template_wf, anat_derivatives_wf, [ ('outputnode.t1w_valid_list', 'inputnode.source_files'), ('outputnode.t1w_realign_xfm', 'inputnode.t1w_ref_xfms'), ]), (be_buffer, anat_derivatives_wf, [ ('anat_mask', 'inputnode.t1w_mask'), ('anat_preproc', 'inputnode.t1w_preproc'), ]), (anat_norm_wf, anat_derivatives_wf, [ ('outputnode.template', 'inputnode.template'), ('outputnode.anat2std_xfm', 'inputnode.anat2std_xfm'), ('outputnode.std2anat_xfm', 'inputnode.std2anat_xfm'), ]), (anat_seg_wf, anat_derivatives_wf, [ ('outputnode.anat_dseg', 'inputnode.t1w_dseg'), ('outputnode.anat_tpms', 'inputnode.t1w_tpms'), ]), ]) if not freesurfer: return wf # FreeSurfer surfaces surface_recon_wf = init_infant_surface_recon_wf( age_months=age_months, use_aseg=bool(segmentation_atlases), ) wf.connect([ (anat_seg_wf, surface_recon_wf, [ ('outputnode.anat_aseg', 'inputnode.anat_aseg'), ]), (inputnode, surface_recon_wf, [ ('subject_id', 'inputnode.subject_id'), ('subjects_dir', 'inputnode.subjects_dir'), ('t2w', 'inputnode.t2w'), ]), (anat_validate, surface_recon_wf, [ ('out_file', 'inputnode.anat_orig'), ]), (be_buffer, surface_recon_wf, [ ('anat_brain', 'inputnode.anat_skullstripped'), ('anat_preproc', 'inputnode.anat_preproc'), ]), (surface_recon_wf, outputnode, [ ('outputnode.subjects_dir', 'subjects_dir'), ('outputnode.subject_id', 'subject_id'), ('outputnode.anat2fsnative_xfm', 'anat2fsnative_xfm'), ('outputnode.fsnative2anat_xfm', 'fsnative2anat_xfm'), ('outputnode.surfaces', 'surfaces'), ('outputnode.anat_aparc', 'anat_aparc'), ('outputnode.anat_aseg', 'anat_aseg'), ]), (surface_recon_wf, anat_reports_wf, [ ('outputnode.subject_id', 'inputnode.subject_id'), ('outputnode.subjects_dir', 'inputnode.subjects_dir'), ]), (surface_recon_wf, anat_derivatives_wf, [ ('outputnode.anat_aseg', 'inputnode.t1w_fs_aseg'), ('outputnode.anat_aparc', 'inputnode.t1w_fs_aparc'), ('outputnode.anat2fsnative_xfm', 'inputnode.t1w2fsnative_xfm'), ('outputnode.fsnative2anat_xfm', 'inputnode.fsnative2t1w_xfm'), ('outputnode.surfaces', 'inputnode.surfaces'), ]), ]) # fmt: on return wf
def compute_iqms(settings, name='ComputeIQMs'): """ Workflow that actually computes the IQMs .. workflow:: from mriqc.workflows.functional import compute_iqms wf = compute_iqms(settings={'output_dir': 'out'}) """ from .utils import _tofloat from ..interfaces.transitional import GCOR biggest_file_gb = settings.get("biggest_file_size_gb", 1) workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_file', 'in_ras', 'epi_mean', 'brainmask', 'hmc_epi', 'hmc_fd', 'fd_thres', 'in_tsnr', 'metadata', 'exclude_index' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_file', 'out_dvars', 'outliers', 'out_spikes', 'out_fft']), name='outputnode') # Set FD threshold inputnode.inputs.fd_thres = settings.get('fd_thres', 0.2) # Compute DVARS dvnode = pe.Node(nac.ComputeDVARS(save_plot=False, save_all=True), name='ComputeDVARS', mem_gb=biggest_file_gb * 3) # AFNI quality measures fwhm_interface = get_fwhmx() fwhm = pe.Node(fwhm_interface, name='smoothness') # fwhm.inputs.acf = True # add when AFNI >= 16 outliers = pe.Node(afni.OutlierCount(fraction=True, out_file='outliers.out'), name='outliers', mem_gb=biggest_file_gb * 2.5) quality = pe.Node(afni.QualityIndex(automask=True), out_file='quality.out', name='quality', mem_gb=biggest_file_gb * 3) gcor = pe.Node(GCOR(), name='gcor', mem_gb=biggest_file_gb * 2) measures = pe.Node(FunctionalQC(), name='measures', mem_gb=biggest_file_gb * 3) workflow.connect([(inputnode, dvnode, [('hmc_epi', 'in_file'), ('brainmask', 'in_mask')]), (inputnode, measures, [('epi_mean', 'in_epi'), ('brainmask', 'in_mask'), ('hmc_epi', 'in_hmc'), ('hmc_fd', 'in_fd'), ('fd_thres', 'fd_thres'), ('in_tsnr', 'in_tsnr')]), (inputnode, fwhm, [('epi_mean', 'in_file'), ('brainmask', 'mask')]), (inputnode, quality, [('hmc_epi', 'in_file')]), (inputnode, outliers, [('hmc_epi', 'in_file'), ('brainmask', 'mask')]), (inputnode, gcor, [('hmc_epi', 'in_file'), ('brainmask', 'mask')]), (dvnode, measures, [('out_all', 'in_dvars')]), (fwhm, measures, [(('fwhm', _tofloat), 'in_fwhm')]), (dvnode, outputnode, [('out_all', 'out_dvars')]), (outliers, outputnode, [('out_file', 'outliers')])]) # Add metadata meta = pe.Node(ReadSidecarJSON(), name='metadata', run_without_submitting=True) addprov = pe.Node(niu.Function(function=_add_provenance), name='provenance', run_without_submitting=True) addprov.inputs.settings = { 'fd_thres': settings.get('fd_thres', 0.2), 'hmc_fsl': settings.get('hmc_fsl', True), } # Save to JSON file datasink = pe.Node(IQMFileSink(modality='bold', out_dir=str(settings['output_dir']), dataset=settings.get( 'dataset_name', 'unknown')), name='datasink', run_without_submitting=True) workflow.connect([ (inputnode, datasink, [('exclude_index', 'dummy_trs')]), (inputnode, meta, [('in_file', 'in_file')]), (inputnode, addprov, [('in_file', 'in_file')]), (meta, datasink, [('relative_path', 'in_file'), ('subject_id', 'subject_id'), ('session_id', 'session_id'), ('task_id', 'task_id'), ('acq_id', 'acq_id'), ('rec_id', 'rec_id'), ('run_id', 'run_id'), ('out_dict', 'metadata')]), (addprov, datasink, [('out', 'provenance')]), (outliers, datasink, [(('out_file', _parse_tout), 'aor')]), (gcor, datasink, [(('out', _tofloat), 'gcor')]), (quality, datasink, [(('out_file', _parse_tqual), 'aqi')]), (measures, datasink, [('out_qc', 'root')]), (datasink, outputnode, [('out_file', 'out_file')]) ]) # FFT spikes finder if settings.get('fft_spikes_detector', False): from .utils import slice_wise_fft spikes_fft = pe.Node(niu.Function( input_names=['in_file'], output_names=['n_spikes', 'out_spikes', 'out_fft'], function=slice_wise_fft), name='SpikesFinderFFT') workflow.connect([ (inputnode, spikes_fft, [('in_ras', 'in_file')]), (spikes_fft, outputnode, [('out_spikes', 'out_spikes'), ('out_fft', 'out_fft')]), (spikes_fft, datasink, [('n_spikes', 'spikes_num')]) ]) return workflow
def init_bold_std_trans_wf( freesurfer, mem_gb, omp_nthreads, spaces, name='bold_std_trans_wf', use_compression=True, use_fieldwarp=False, ): """ Sample fMRI into standard space with a single-step resampling of the original BOLD series. .. important:: This workflow provides two outputnodes. One output node (with name ``poutputnode``) will be parameterized in a Nipype sense (see `Nipype iterables <https://miykael.github.io/nipype_tutorial/notebooks/basic_iteration.html>`__), and a second node (``outputnode``) will collapse the parameterized outputs into synchronous lists of the output fields listed below. Workflow Graph .. workflow:: :graph2use: colored :simple_form: yes from niworkflows.utils.spaces import SpatialReferences from fmriprep.workflows.bold import init_bold_std_trans_wf wf = init_bold_std_trans_wf( freesurfer=True, mem_gb=3, omp_nthreads=1, spaces=SpatialReferences( spaces=['MNI152Lin', ('MNIPediatricAsym', {'cohort': '6'})], checkpoint=True), ) Parameters ---------- freesurfer : bool Whether to generate FreeSurfer's aseg/aparc segmentations on BOLD space. mem_gb : float Size of BOLD file in GB omp_nthreads : int Maximum number of threads an individual process may use spaces : :py:class:`~niworkflows.utils.spaces.SpatialReferences` A container for storing, organizing, and parsing spatial normalizations. Composed of :py:class:`~niworkflows.utils.spaces.Reference` objects representing spatial references. Each ``Reference`` contains a space, which is a string of either TemplateFlow template IDs (e.g., ``MNI152Lin``, ``MNI152NLin6Asym``, ``MNIPediatricAsym``), nonstandard references (e.g., ``T1w`` or ``anat``, ``sbref``, ``run``, etc.), or a custom template located in the TemplateFlow root directory. Each ``Reference`` may also contain a spec, which is a dictionary with template specifications (e.g., a specification of ``{'resolution': 2}`` would lead to resampling on a 2mm resolution of the space). name : str Name of workflow (default: ``bold_std_trans_wf``) use_compression : bool Save registered BOLD series as ``.nii.gz`` use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to MNI Inputs ------ anat2std_xfm List of anatomical-to-standard space transforms generated during spatial normalization. bold_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_mask Skull-stripping mask of reference image bold_split Individual 3D volumes, not motion corrected fieldwarp a :abbr:`DFM (displacements field map)` in ITK format hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) name_source BOLD series NIfTI file Used to recover original information lost during processing templates List of templates that were applied as targets during spatial normalization. Outputs ------- bold_std BOLD series, resampled to template space bold_std_ref Reference, contrast-enhanced summary of the BOLD series, resampled to template space bold_mask_std BOLD series mask in template space bold_aseg_std FreeSurfer's ``aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) bold_aparc_std FreeSurfer's ``aparc+aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) template Template identifiers synchronized correspondingly to previously described outputs. """ workflow = Workflow(name=name) output_references = spaces.cached.get_spaces(nonstandard=False, dim=(3, )) std_vol_references = [(s.fullname, s.spec) for s in spaces.references if s.standard and s.dim == 3] if len(output_references) == 1: workflow.__desc__ = """\ The BOLD time-series were resampled into standard space, generating a *preprocessed BOLD run in {tpl} space*. """.format(tpl=output_references[0]) elif len(output_references) > 1: workflow.__desc__ = """\ The BOLD time-series were resampled into several standard spaces, correspondingly generating the following *spatially-normalized, preprocessed BOLD runs*: {tpl}. """.format(tpl=', '.join(output_references)) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'anat2std_xfm', 'bold_aparc', 'bold_aseg', 'bold_mask', 'bold_split', 'fieldwarp', 'hmc_xforms', 'itk_bold_to_t1', 'name_source', 'templates', ]), name='inputnode') iterablesource = pe.Node(niu.IdentityInterface(fields=['std_target']), name='iterablesource') # Generate conversions for every template+spec at the input iterablesource.iterables = [('std_target', std_vol_references)] split_target = pe.Node(niu.Function( function=_split_spec, input_names=['in_target'], output_names=['space', 'template', 'spec']), run_without_submitting=True, name='split_target') select_std = pe.Node(KeySelect(fields=['anat2std_xfm']), name='select_std', run_without_submitting=True) select_tpl = pe.Node(niu.Function(function=_select_template), name='select_tpl', run_without_submitting=True) gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB) mask_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='mask_std_tfm', mem_gb=1) # Write corrected file in the designated output dir mask_merge_tfms = pe.Node(niu.Merge(2), name='mask_merge_tfms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) nxforms = 3 + use_fieldwarp merge_xforms = pe.Node(niu.Merge(nxforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([(inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nxforms)])]) if use_fieldwarp: workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in3')])]) bold_to_std_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_std_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb * 3) # Generate a reference on the target standard space gen_final_ref = init_bold_reference_wf(omp_nthreads=omp_nthreads, pre_mask=True) workflow.connect([ (iterablesource, split_target, [('std_target', 'in_target')]), (iterablesource, select_tpl, [('std_target', 'template')]), (inputnode, select_std, [('anat2std_xfm', 'anat2std_xfm'), ('templates', 'keys')]), (inputnode, mask_std_tfm, [('bold_mask', 'input_image')]), (inputnode, gen_ref, [(('bold_split', _first), 'moving_image')]), (inputnode, merge_xforms, [(('itk_bold_to_t1', _aslist), 'in2')]), (inputnode, merge, [('name_source', 'header_source')]), (inputnode, mask_merge_tfms, [(('itk_bold_to_t1', _aslist), 'in2')]), (inputnode, bold_to_std_transform, [('bold_split', 'input_image')]), (split_target, select_std, [('space', 'key')]), (select_std, merge_xforms, [('anat2std_xfm', 'in1')]), (select_std, mask_merge_tfms, [('anat2std_xfm', 'in1')]), (split_target, gen_ref, [(('spec', _is_native), 'keep_native')]), (select_tpl, gen_ref, [('out', 'fixed_image')]), (merge_xforms, bold_to_std_transform, [('out', 'transforms')]), (gen_ref, bold_to_std_transform, [('out_file', 'reference_image')]), (gen_ref, mask_std_tfm, [('out_file', 'reference_image')]), (mask_merge_tfms, mask_std_tfm, [('out', 'transforms')]), (mask_std_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask') ]), (bold_to_std_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), ]) output_names = [ 'bold_mask_std', 'bold_std', 'bold_std_ref', 'spatial_reference', 'template', ] + freesurfer * ['bold_aseg_std', 'bold_aparc_std'] poutputnode = pe.Node(niu.IdentityInterface(fields=output_names), name='poutputnode') workflow.connect([ # Connecting outputnode (iterablesource, poutputnode, [(('std_target', format_reference), 'spatial_reference')]), (merge, poutputnode, [('out_file', 'bold_std')]), (gen_final_ref, poutputnode, [('outputnode.ref_image', 'bold_std_ref') ]), (mask_std_tfm, poutputnode, [('output_image', 'bold_mask_std')]), (select_std, poutputnode, [('key', 'template')]), ]) if freesurfer: # Sample the parcellation files to functional space aseg_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='aseg_std_tfm', mem_gb=1) aparc_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='aparc_std_tfm', mem_gb=1) workflow.connect([ (inputnode, aseg_std_tfm, [('bold_aseg', 'input_image')]), (inputnode, aparc_std_tfm, [('bold_aparc', 'input_image')]), (select_std, aseg_std_tfm, [('anat2std_xfm', 'transforms')]), (select_std, aparc_std_tfm, [('anat2std_xfm', 'transforms')]), (gen_ref, aseg_std_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_std_tfm, [('out_file', 'reference_image')]), (aseg_std_tfm, poutputnode, [('output_image', 'bold_aseg_std')]), (aparc_std_tfm, poutputnode, [('output_image', 'bold_aparc_std')]), ]) # Connect parametric outputs to a Join outputnode outputnode = pe.JoinNode(niu.IdentityInterface(fields=output_names), name='outputnode', joinsource='iterablesource') workflow.connect([ (poutputnode, outputnode, [(f, f) for f in output_names]), ]) return workflow
def init_bold_surf_wf(mem_gb, surface_spaces, medial_surface_nan, name='bold_surf_wf'): """ Sample functional images to FreeSurfer surfaces. For each vertex, the cortical ribbon is sampled at six points (spaced 20% of thickness apart) and averaged. Outputs are in GIFTI format. Workflow Graph .. workflow:: :graph2use: colored :simple_form: yes from fmriprep.workflows.bold import init_bold_surf_wf wf = init_bold_surf_wf(mem_gb=0.1, surface_spaces=['fsnative', 'fsaverage5'], medial_surface_nan=False) Parameters ---------- surface_spaces : list List of FreeSurfer surface-spaces (either ``fsaverage{3,4,5,6,}`` or ``fsnative``) the functional images are to be resampled to. For ``fsnative``, images will be resampled to the individual subject's native surface. medial_surface_nan : bool Replace medial wall values with NaNs on functional GIFTI files Inputs ------ source_file Motion-corrected BOLD series in T1 space t1w_preproc Bias-corrected structural template image subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID t1w2fsnative_xfm LTA-style affine matrix translating from T1w to FreeSurfer-conformed subject space Outputs ------- surfaces BOLD series, resampled to FreeSurfer surfaces """ workflow = Workflow(name=name) workflow.__desc__ = """\ The BOLD time-series were resampled onto the following surfaces (FreeSurfer reconstruction nomenclature): {out_spaces}. """.format(out_spaces=', '.join(['*%s*' % s for s in surface_spaces])) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'source_file', 't1w_preproc', 'subject_id', 'subjects_dir', 't1w2fsnative_xfm' ]), name='inputnode') itersource = pe.Node(niu.IdentityInterface(fields=['target']), name='itersource') itersource.iterables = [('target', surface_spaces)] def select_target(subject_id, space): """Get the target subject ID, given a source subject ID and a target space.""" return subject_id if space == 'fsnative' else space targets = pe.Node(niu.Function(function=select_target), name='targets', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Rename the source file to the output space to simplify naming later rename_src = pe.Node(niu.Rename(format_string='%(subject)s', keep_ext=True), name='rename_src', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) resampling_xfm = pe.Node(LTAConvert(in_lta='identity.nofile', out_lta=True), name='resampling_xfm') set_xfm_source = pe.Node(ConcatenateLTA(out_type='RAS2RAS'), name='set_xfm_source') sampler = pe.MapNode(fs.SampleToSurface( cortex_mask=True, interp_method='trilinear', out_type='gii', override_reg_subj=True, sampling_method='average', sampling_range=(0, 1, 0.2), sampling_units='frac', ), iterfield=['hemi'], name='sampler', mem_gb=mem_gb * 3) sampler.inputs.hemi = ['lh', 'rh'] update_metadata = pe.MapNode(GiftiSetAnatomicalStructure(), iterfield=['in_file'], name='update_metadata', mem_gb=DEFAULT_MEMORY_MIN_GB) outputnode = pe.JoinNode( niu.IdentityInterface(fields=['surfaces', 'target']), joinsource='itersource', name='outputnode') workflow.connect([ (inputnode, targets, [('subject_id', 'subject_id')]), (inputnode, rename_src, [('source_file', 'in_file')]), (inputnode, resampling_xfm, [('source_file', 'source_file'), ('t1w_preproc', 'target_file')]), (inputnode, set_xfm_source, [('t1w2fsnative_xfm', 'in_lta2')]), (inputnode, sampler, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (itersource, targets, [('target', 'space')]), (itersource, rename_src, [('target', 'subject')]), (resampling_xfm, set_xfm_source, [('out_lta', 'in_lta1')]), (set_xfm_source, sampler, [('out_file', 'reg_file')]), (targets, sampler, [('out', 'target_subject')]), (rename_src, sampler, [('out_file', 'source_file')]), (update_metadata, outputnode, [('out_file', 'surfaces')]), (itersource, outputnode, [('target', 'target')]), ]) if not medial_surface_nan: workflow.connect(sampler, 'out_file', update_metadata, 'in_file') return workflow # Refine if medial vertices should be NaNs medial_nans = pe.MapNode(MedialNaNs(), iterfield=['in_file'], name='medial_nans', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, medial_nans, [('subjects_dir', 'subjects_dir')]), (sampler, medial_nans, [('out_file', 'in_file')]), (medial_nans, update_metadata, [('out_file', 'in_file')]), ]) return workflow
def get_workflow(name, infosource, opts): ''' Create workflow to produce labeled images. 1. Invert T1 Native to MNI 152 transformation 2. Transform 4. Transform brain_mask from MNI 152 to T1 native 5. Create PVC labeled image 6. Create quantification labeled image 7. Create results labeled image :param name: Name for workflow :param infosource: Infosource for basic variables like subject id (sid) and condition id (cid) :param datasink: Node in which output data is sent :param opts: User options :returns: workflow ''' workflow = pe.Workflow(name=name) out_list = [ "pet_brain_mask", "brain_mask", "results_label_img_t1", "results_label_img_mni" ] in_list = [ "nativeT1", "mniT1", "brain_mask_stereo", "brain_mask_t1", "pet_header_json", "pet_volume", "results_labels", "results_label_template", "results_label_img", 'LinT1MNIXfm', 'LinMNIT1Xfm', "LinPETMNIXfm", "LinMNIPETXfm", 'LinT1MNIXfm', "LinT1PETXfm", "LinPETT1Xfm", "surf_left", 'surf_right' ] if not opts.pvc_method == None: out_list += ["pvc_label_img_t1", "pvc_label_img_mni"] in_list += [ "pvc_labels", "pvc_label_space", "pvc_label_img", "pvc_label_template" ] if not opts.tka_method == None: out_list += ["tka_label_img_t1", "tka_label_img_mni"] in_list += [ "tka_labels", "tka_label_space", "tka_label_template", "tka_label_img" ] #Define input node that will receive input from outside of workflow inputnode = pe.Node(niu.IdentityInterface(fields=in_list), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=out_list), name='outputnode') #Define empty node for output #Create Identity Transform identity_transform = pe.Node(param2xfmCommand(), name="identity_transform") identity_transform.inputs.translation = "0 0 0" identity_transform.inputs.rotation = "0 0 0" identity_transform.inputs.scales = "1 1 1" if not opts.pvc_method == None and not opts.pvc_method == None: pvc_tfm_node, pvc_tfm_file, pvc_target_file = get_transforms_for_stage( inputnode, opts.pvc_label_space, opts.analysis_space, identity_transform) if not opts.tka_method == None: tka_tfm_node, tka_tfm_file, tka_target_file = get_transforms_for_stage( inputnode, opts.tka_label_space, opts.analysis_space, identity_transform) results_tfm_node, results_tfm_file, results_target_file = get_transforms_for_stage( inputnode, opts.results_label_space, opts.analysis_space, identity_transform) ################### # Brain Mask Node # ################### if opts.analysis_space == "stereo": brain_mask_node = pe.Node( niu.IdentityInterface(fields=["output_file"]), "brain_mask") workflow.connect(inputnode, "brain_mask_stereo", brain_mask_node, "output_file") like_file = "mniT1" elif opts.analysis_space == "t1": brain_mask_node = pe.Node( niu.IdentityInterface(fields=["output_file"]), "brain_mask") workflow.connect(inputnode, "brain_mask_t1", brain_mask_node, "output_file") like_file = "nativeT1" elif opts.analysis_space == "pet": brain_mask_node = pe.Node(minc.Resample(), "brain_mask") brain_mask_node.inputs.nearest_neighbour_interpolation = True workflow.connect(inputnode, "brain_mask_stereo", brain_mask_node, "input_file") workflow.connect(inputnode, "LinMNIPETXfm", brain_mask_node, "transformation") workflow.connect(inputnode, "pet_volume", brain_mask_node, "like") like_file = "pet_volume" else: print("Error: Analysis space must be one of pet,stereo,t1 but is", opts.analysis_space) exit(1) ################# # Surface masks # ################# if opts.use_surfaces: if opts.analysis_space != "stereo": surface_left_node = pe.Node(transform_objectCommand(), name="surface_left_node") surface_right_node = pe.Node(transform_objectCommand(), name="surface_right_node") workflow.connect(inputnode, 'surf_left', surface_left_node, 'in_file') workflow.connect(inputnode, 'surf_right', surface_right_node, 'in_file') if opts.analysis_space == "t1": workflow.connect(inputnode, "LinMNIT1Xfm", surface_left_node, 'tfm_file') workflow.connect(inputnode, "LinMNIT1Xfm", surface_right_node, 'tfm_file') elif opts.analysis_space == "pet": workflow.connect(inputnode, 'LinMNIPETXfm', surface_left_node, 'tfm_file') workflow.connect(inputnode, 'LinMNIPETXfm', surface_right_node, 'tfm_file') else: surface_left_node = pe.Node( niu.IdentityInterface(fields=["output_file"]), "surf_left_node") surface_right_node = pe.Node( niu.IdentityInterface(fields=["output_file"]), "surf_right_node") workflow.connect(inputnode, "surf_left", surface_left_node, "output_file") workflow.connect(inputnode, "surf_right", surface_right_node, "output_file") resultsLabels = pe.Node(interface=Labels(), name="resultsLabels") resultsLabels.inputs.analysis_space = opts.analysis_space resultsLabels.inputs.label_type = opts.results_label_type resultsLabels.inputs.space = opts.results_label_space resultsLabels.inputs.erode_times = opts.results_erode_times resultsLabels.inputs.brain_only = opts.results_labels_brain_only resultsLabels.inputs.ones_only = opts.results_labels_ones_only workflow.connect(inputnode, 'results_labels', resultsLabels, 'labels') workflow.connect(inputnode, 'results_label_img', resultsLabels, 'label_img') workflow.connect(inputnode, 'results_label_template', resultsLabels, 'label_template') workflow.connect(inputnode, like_file, resultsLabels, 'like_file') workflow.connect(brain_mask_node, "output_file", resultsLabels, 'brain_mask') workflow.connect(results_tfm_node, results_tfm_file, resultsLabels, "LinXfm") #Setup node for nonlinear alignment of results template to default (icbm152) template if opts.results_label_template != None: results_template_norm = pe.Node(interface=reg.nLinRegRunning(), name="results_template_normalization") results_template_norm.inputs.in_target_file = opts.template results_template_norm.inputs.in_source_file = opts.results_label_template results_template_analysis_space = pe.Node( ConcatNLCommand(), name="results_template_analysis_space") workflow.connect(results_template_norm, 'out_file_xfm', results_template_analysis_space, 'in_file') workflow.connect(results_template_norm, 'out_file_warp', results_template_analysis_space, 'in_warp') workflow.connect(results_tfm_node, results_tfm_file, results_template_analysis_space, 'in_file_2') workflow.connect(results_template_analysis_space, 'out_file', resultsLabels, 'nLinAtlasMNIXfm') workflow.connect(results_template_analysis_space, 'out_warp', resultsLabels, 'warp') workflow.connect(results_template_norm, 'out_file_img', resultsLabels, 'template') if not opts.pvc_method == None and not opts.pvc_method == None: pvcLabels = pe.Node(interface=Labels(), name="pvcLabels") pvcLabels.inputs.analysis_space = opts.analysis_space pvcLabels.inputs.label_type = opts.pvc_label_type pvcLabels.inputs.space = opts.pvc_label_space pvcLabels.inputs.erode_times = opts.pvc_erode_times pvcLabels.inputs.brain_only = opts.pvc_labels_brain_only pvcLabels.inputs.ones_only = opts.pvc_labels_ones_only workflow.connect(inputnode, 'pvc_labels', pvcLabels, 'labels') workflow.connect(inputnode, 'pvc_label_img', pvcLabels, 'label_img') workflow.connect(inputnode, like_file, pvcLabels, 'like_file') workflow.connect(brain_mask_node, "output_file", pvcLabels, 'brain_mask') workflow.connect(pvc_tfm_node, pvc_tfm_file, pvcLabels, "LinXfm") if opts.pvc_label_template != None: pvc_template_norm = pe.Node(interface=reg.nLinRegRunning(), name="pvc_template_normalization") pvc_template_norm.inputs.in_target_file = opts.template pvc_template_norm.inputs.in_source_file = opts.pvc_label_template pvc_template_analysis_space = pe.Node( ConcatNLCommand(), name="pvc_template_analysis_space") workflow.connect(pvc_template_norm, 'out_file_xfm', pvc_template_analysis_space, 'in_file') workflow.connect(pvc_template_norm, 'out_file_warp', pvc_template_analysis_space, 'in_warp') workflow.connect(pvc_tfm_node, pvc_tfm_file, pvc_template_analysis_space, 'in_file_2') workflow.connect(pvc_template_analysis_space, 'out_file', pvcLabels, 'nLinAtlasMNIXfm') workflow.connect(pvc_template_analysis_space, 'out_warp', pvcLabels, 'warp') workflow.connect(pvc_template_norm, 'out_file_img', pvcLabels, 'template') if not opts.tka_method == None: tkaLabels = pe.Node(interface=Labels(), name="tkaLabels") tkaLabels.inputs.analysis_space = opts.analysis_space tkaLabels.inputs.label_type = opts.tka_label_type tkaLabels.inputs.space = opts.tka_label_space tkaLabels.inputs.erode_times = opts.tka_erode_times tkaLabels.inputs.brain_only = opts.tka_labels_brain_only tkaLabels.inputs.ones_only = opts.tka_labels_ones_only workflow.connect(inputnode, 'tka_labels', tkaLabels, 'labels') workflow.connect(inputnode, 'tka_label_img', tkaLabels, 'label_img') workflow.connect(inputnode, like_file, tkaLabels, 'like_file') workflow.connect(brain_mask_node, "output_file", tkaLabels, 'brain_mask') workflow.connect(tka_tfm_node, tka_tfm_file, tkaLabels, "LinXfm") if opts.tka_label_template != None: tka_template_norm = pe.Node(interface=reg.nLinRegRunning(), name="tka_template_normalization") tka_template_norm.inputs.in_source_file = opts.template tka_template_norm.inputs.in_target_file = opts.tka_label_template tka_template_analysis_space = pe.Node( ConcatNLCommand(), name="tka_template_analysis_space") workflow.connect(tka_template_norm, 'out_file_xfm', tka_template_analysis_space, 'in_file') workflow.connect(tka_template_norm, 'out_file_warp', tka_template_analysis_space, 'in_warp') workflow.connect(tka_tfm_node, tka_tfm_file, tka_template_analysis_space, 'in_file_2') workflow.connect(tka_template_analysis_space, 'out_file', tkaLabels, 'nLinAtlasMNIXfm') workflow.connect(tka_template_analysis_space, 'out_warp', tkaLabels, 'warp') workflow.connect(tka_template_norm, 'out_file_img', tkaLabels, 'template') return (workflow)
dest='ref', metavar='ref', help='Reference Image', required=True) parser.add_argument('-f', '--floating', dest='flo', metavar='flo', help='Floating Image', required=True) args = parser.parse_args() workflow = pe.Workflow(name=name) workflow.base_output_dir = name workflow.base_dir = name directory = os.getcwd() node = pe.Node(interface=niftyreg.RegResample(), name='resample') output_node = pe.Node( interface=niu.IdentityInterface(fields=['res_file', 'blank_file']), name='output_node') workflow.connect(node, 'res_file', output_node, 'res_file') workflow.connect(node, 'blank_file', output_node, 'res_file') node.inputs.ref_file = os.path.absbath(args.ref) node.inputs.flo_file = os.path.absbath(args.flo) workflow.run()
def create_fsl_fs_preproc(name='preproc', highpass=True, whichvol='middle'): """Create a FEAT preprocessing workflow together with freesurfer Parameters ---------- :: name : name of workflow (default: preproc) highpass : boolean (default: True) whichvol : which volume of the first run to register to ('first', 'middle', 'mean') Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) inputspec.subject_id : freesurfer subject id inputspec.subjects_dir : freesurfer subjects dir Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask_file : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.highpassed_files : highpassed functional data (if highpass=True) outputspec.reg_file : bbregister registration files outputspec.reg_cost : bbregister registration cost files Example ------- >>> preproc = create_fsl_fs_preproc(whichvol='first') >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.subjects_dir = '.' >>> preproc.inputs.inputspec.subject_id = 's1' >>> preproc.inputs.inputspec.fwhm = 6 >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) """ Set up a node to define all inputs required for the preprocessing workflow """ if highpass: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'subject_id', 'subjects_dir', 'highpass']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask_file', 'smoothed_files', 'highpassed_files', 'reg_file', 'reg_cost' ]), name='outputspec') else: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'subject_id', 'subjects_dir' ]), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask_file', 'smoothed_files', 'reg_file', 'reg_cost' ]), name='outputspec') """ Set up a node to define outputs for the preprocessing workflow """ """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string = '', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', img2float, 'in_file') """ Extract the first volume of the first run as the reference """ if whichvol != 'mean': extract_ref = pe.Node(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file'], name = 'extractref') featpreproc.connect(img2float, ('out_file', pickfirst), extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') """ Realign the functional runs to the reference (1st volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True, save_plots = True, interpolation = 'sinc'), name='realign', iterfield = ['in_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') else: motion_correct.inputs.mean_vol = True featpreproc.connect(motion_correct, 'mean_img', outputnode, 'reference') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """Get the mask from subject for each run """ maskflow = create_getmask_flow() featpreproc.connect([(inputnode, maskflow, [('subject_id','inputspec.subject_id'), ('subjects_dir', 'inputspec.subjects_dir')])]) maskflow.inputs.inputspec.contrast_type = 't2' if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', maskflow, 'inputspec.source_file') else: featpreproc.connect(motion_correct, ('mean_img', pickfirst), maskflow, 'inputspec.source_file') """ Mask the functional runs with the extracted mask """ maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name = 'maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), maskfunc, 'in_file2') """ Smooth each run using SUSAN with the brightness threshold set to 75% of the median value for each run and a mask consituting the mean functional """ smooth = create_susan_smooth(separate_masks=False) featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), smooth, 'inputnode.mask_file') """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') featpreproc.connect(maskfunc, ('out_file', tolist), concatnode, 'in1') featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(),name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files') """ Scale the median value of the run is set to 10000 """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file','op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield = ['in_file'], name='medianval') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), medianval, 'mask_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') """ Perform temporal highpass filtering on the data """ if highpass: highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), outputnode, 'mask_file') featpreproc.connect(maskflow, 'outputspec.reg_file', outputnode, 'reg_file') featpreproc.connect(maskflow, 'outputspec.reg_cost', outputnode, 'reg_cost') return featpreproc
def create_susan_smooth(name="susan_smooth", separate_masks=True): """Create a SUSAN smoothing workflow Parameters ---------- :: name : name of workflow (default: susan_smooth) separate_masks : separate masks for each run Inputs:: inputnode.in_files : functional runs (filename or list of filenames) inputnode.fwhm : fwhm for smoothing with SUSAN inputnode.mask_file : mask used for estimating SUSAN thresholds (but not for smoothing) Outputs:: outputnode.smoothed_files : functional runs (filename or list of filenames) Example ------- >>> smooth = create_susan_smooth() >>> smooth.inputs.inputnode.in_files = 'f3.nii' >>> smooth.inputs.inputnode.fwhm = 5 >>> smooth.inputs.inputnode.mask_file = 'mask.nii' >>> smooth.run() # doctest: +SKIP """ susan_smooth = pe.Workflow(name=name) """ Set up a node to define all inputs required for the preprocessing workflow """ inputnode = pe.Node(interface=util.IdentityInterface(fields=['in_files', 'fwhm', 'mask_file']), name='inputnode') """ Smooth each run using SUSAN with the brightness threshold set to 75% of the median value for each run and a mask consituting the mean functional """ smooth = pe.MapNode(interface=fsl.SUSAN(), iterfield=['in_file', 'brightness_threshold','usans'], name='smooth') """ Determine the median value of the functional runs using the mask """ if separate_masks: median = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield = ['in_file', 'mask_file'], name='median') else: median = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield = ['in_file'], name='median') susan_smooth.connect(inputnode, 'in_files', median, 'in_file') susan_smooth.connect(inputnode, 'mask_file', median, 'mask_file') """ Mask the motion corrected functional runs with the dilated mask """ if separate_masks: mask = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='mask') else: mask = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file'], name='mask') susan_smooth.connect(inputnode, 'in_files', mask, 'in_file') susan_smooth.connect(inputnode, 'mask_file', mask, 'in_file2') """ Determine the mean image from each functional run """ meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc2') susan_smooth.connect(mask, 'out_file', meanfunc, 'in_file') """ Merge the median values with the mean functional images into a coupled list """ merge = pe.Node(interface=util.Merge(2, axis='hstack'), name='merge') susan_smooth.connect(meanfunc,'out_file', merge, 'in1') susan_smooth.connect(median,'out_stat', merge, 'in2') """ Define a function to get the brightness threshold for SUSAN """ susan_smooth.connect(inputnode, 'fwhm', smooth, 'fwhm') susan_smooth.connect(inputnode, 'in_files', smooth, 'in_file') susan_smooth.connect(median, ('out_stat', getbtthresh), smooth, 'brightness_threshold') susan_smooth.connect(merge, ('out', getusans), smooth, 'usans') outputnode = pe.Node(interface=util.IdentityInterface(fields=['smoothed_files']), name='outputnode') susan_smooth.connect(smooth, 'smoothed_file', outputnode, 'smoothed_files') return susan_smooth
def create_parallelfeat_preproc(name='featpreproc', highpass=True): """Preprocess each run with FSL independently of the others Parameters ---------- :: name : name of workflow (default: featpreproc) highpass : boolean (default: True) Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.highpassed_files : highpassed functional data (if highpass=True) outputspec.mean : mean file Example ------- >>> preproc = create_parallelfeat_preproc() >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP >>> preproc = create_parallelfeat_preproc(highpass=False) >>> preproc.inputs.inputspec.func = 'f3.nii' >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) """ Set up a node to define all inputs required for the preprocessing workflow """ if highpass: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'highpass']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask', 'smoothed_files', 'highpassed_files', 'mean']), name='outputspec') else: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask', 'smoothed_files', 'mean']), name='outputspec') """ Set up a node to define outputs for the preprocessing workflow """ """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string = '', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', img2float, 'in_file') """ Extract the first volume of the first run as the reference """ extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file', 't_min'], name = 'extractref') featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickmiddle), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') """ Realign the functional runs to the reference (1st volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True, save_plots = True), name='realign', iterfield = ['in_file', 'ref_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """ Extract the mean volume of the first functional run """ meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string = '-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc') featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file') """ Strip the skull from the mean functional to generate a mask """ meanfuncmask = pe.MapNode(interface=fsl.BET(mask = True, no_output=True, frac = 0.3), iterfield=['in_file'], name = 'meanfuncmask') featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file') """ Mask the functional runs with the extracted mask """ maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file', 'in_file2'], name = 'maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2') """ Determine the 2nd and 98th percentile intensities of each functional run """ getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'), iterfield = ['in_file'], name='getthreshold') featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file') """ Threshold the first run of the functional data at 10% of the 98th percentile """ threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char', suffix='_thresh'), iterfield=['in_file', 'op_string'], name='threshold') featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield = ['in_file', 'mask_file'], name='medianval') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') """ Dilate the mask """ dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask') """ Mask the motion corrected functional runs with the dilated mask """ maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc2') featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2') """ Smooth each run using SUSAN with the brightness threshold set to 75% of the median value for each run and a mask consituting the mean functional """ smooth = create_susan_smooth() featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') featpreproc.connect(maskfunc2,('out_file', tolist), concatnode, 'in1') featpreproc.connect(maskfunc3,('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(),name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files') """ Scale the median value of the run is set to 10000 """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file','op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') """ Perform temporal highpass filtering on the data """ if highpass: highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') """ Generate a mean functional image from the first run """ meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc3') if highpass: featpreproc.connect(highpass, 'out_file', meanfunc3, 'in_file') else: featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean') return featpreproc
def init_func_preproc_wf( aroma_melodic_dim, bold2t1w_dof, bold_file, cifti_output, debug, dummy_scans, err_on_aroma_warn, fmap_bspline, fmap_demean, force_syn, freesurfer, ignore, low_mem, medial_surface_nan, omp_nthreads, output_dir, output_spaces, regressors_all_comps, regressors_dvars_th, regressors_fd_th, reportlets_dir, t2s_coreg, use_aroma, use_bbr, use_syn, layout=None, num_bold=1, ): """ This workflow controls the functional preprocessing stages of *fMRIPrep*. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold import init_func_preproc_wf from collections import namedtuple, OrderedDict BIDSLayout = namedtuple('BIDSLayout', ['root']) wf = init_func_preproc_wf( aroma_melodic_dim=-200, bold2t1w_dof=9, bold_file='/completely/made/up/path/sub-01_task-nback_bold.nii.gz', cifti_output=False, debug=False, dummy_scans=None, err_on_aroma_warn=False, fmap_bspline=True, fmap_demean=True, force_syn=True, freesurfer=True, ignore=[], low_mem=False, medial_surface_nan=False, omp_nthreads=1, output_dir='.', output_spaces=OrderedDict([ ('MNI152Lin', {}), ('fsaverage', {'density': '10k'}), ('T1w', {}), ('fsnative', {})]), regressors_all_comps=False, regressors_dvars_th=1.5, regressors_fd_th=0.5, reportlets_dir='.', t2s_coreg=False, use_aroma=False, use_bbr=True, use_syn=True, layout=BIDSLayout('.'), num_bold=1, ) Parameters ---------- aroma_melodic_dim : int Maximum number of components identified by MELODIC within ICA-AROMA (default is -200, ie. no limitation). bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration bold_file : str BOLD series NIfTI file cifti_output : bool Generate bold CIFTI file in output spaces debug : bool Enable debugging outputs dummy_scans : int or None Number of volumes to consider as non steady state err_on_aroma_warn : bool Do not crash on ICA-AROMA errors fmap_bspline : bool **Experimental**: Fit B-Spline field using least-squares fmap_demean : bool Demean voxel-shift map during unwarp force_syn : bool **Temporary**: Always run SyN-based SDC freesurfer : bool Enable FreeSurfer functional registration (bbregister) and resampling BOLD series to FreeSurfer surface meshes. ignore : list Preprocessing steps to skip (may include "slicetiming", "fieldmaps") low_mem : bool Write uncompressed .nii files in some cases to reduce memory usage medial_surface_nan : bool Replace medial wall values with NaNs on functional GIFTI files omp_nthreads : int Maximum number of threads an individual process may use output_dir : str Directory in which to save derivatives output_spaces : OrderedDict Ordered dictionary where keys are TemplateFlow ID strings (e.g. ``MNI152Lin``, ``MNI152NLin6Asym``, ``MNI152NLin2009cAsym``, or ``fsLR``) strings designating nonstandard references (e.g. ``T1w`` or ``anat``, ``sbref``, ``run``, etc.), or paths pointing to custom templates organized in a TemplateFlow-like structure. Values of the dictionary aggregate modifiers (e.g. the value for the key ``MNI152Lin`` could be ``{'resolution': 2}`` if one wants the resampling to be done on the 2mm resolution version of the selected template). regressors_all_comps Return all CompCor component time series instead of the top fraction regressors_dvars_th Criterion for flagging DVARS outliers regressors_fd_th Criterion for flagging framewise displacement outliers reportlets_dir : str Absolute path of a directory in which reportlets will be temporarily stored t2s_coreg : bool For multiecho EPI, use the calculated T2*-map for T2*-driven coregistration use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. When using ``t2s_coreg``, BBR will be enabled by default unless explicitly specified otherwise. use_syn : bool **Experimental**: Enable ANTs SyN-based susceptibility distortion correction (SDC). If fieldmaps are present and enabled, this is not run, by default. layout : BIDSLayout BIDSLayout structure to enable metadata retrieval num_bold : int Total number of BOLD files that have been set for preprocessing (default is 1) Inputs ------ bold_file BOLD series NIfTI file t1w_preproc Bias-corrected structural template image t1w_brain Skull-stripped ``t1w_preproc`` t1w_mask Mask of the skull-stripped template image t1w_dseg Segmentation of preprocessed structural image, including gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF) t1w_asec Segmentation of structural image, done with FreeSurfer. t1w_aparc Parcellation of structural image, done with FreeSurfer. t1w_tpms List of tissue probability maps in T1w space anat2std_xfm ANTs-compatible affine-and-warp transform file std2anat_xfm ANTs-compatible affine-and-warp transform file (inverse) 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 Outputs ------- bold_t1 BOLD series, resampled to T1w space bold_mask_t1 BOLD series mask in T1w space bold_std BOLD series, resampled to template space bold_mask_std BOLD series mask in template space confounds TSV of confounds surfaces BOLD series, resampled to FreeSurfer surfaces aroma_noise_ics Noise components identified by ICA-AROMA melodic_mix FSL MELODIC mixing matrix bold_cifti BOLD CIFTI image cifti_variant combination of target spaces for `bold_cifti` See also -------- * :py:func:`~fmriprep.workflows.bold.util.init_bold_reference_wf` * :py:func:`~fmriprep.workflows.bold.stc.init_bold_stc_wf` * :py:func:`~fmriprep.workflows.bold.hmc.init_bold_hmc_wf` * :py:func:`~fmriprep.workflows.bold.t2s.init_bold_t2s_wf` * :py:func:`~fmriprep.workflows.bold.registration.init_bold_t1_trans_wf` * :py:func:`~fmriprep.workflows.bold.registration.init_bold_reg_wf` * :py:func:`~fmriprep.workflows.bold.confounds.init_bold_confounds_wf` * :py:func:`~fmriprep.workflows.bold.confounds.init_ica_aroma_wf` * :py:func:`~fmriprep.workflows.bold.resampling.init_bold_std_trans_wf` * :py:func:`~fmriprep.workflows.bold.resampling.init_bold_preproc_trans_wf` * :py:func:`~fmriprep.workflows.bold.resampling.init_bold_surf_wf` * :py:func:`~fmriprep.workflows.fieldmap.pepolar.init_pepolar_unwarp_wf` * :py:func:`~fmriprep.workflows.fieldmap.init_fmap_estimator_wf` * :py:func:`~fmriprep.workflows.fieldmap.init_sdc_unwarp_wf` * :py:func:`~fmriprep.workflows.fieldmap.init_nonlinear_sdc_wf` """ from ...config import NONSTANDARD_REFERENCES from sdcflows.workflows.base import init_sdc_estimate_wf, fieldmap_wrangler # Filter out standard spaces to a separate dict std_spaces = OrderedDict([(key, modifiers) for key, modifiers in output_spaces.items() if key not in NONSTANDARD_REFERENCES]) volume_std_spaces = OrderedDict([(key, modifiers) for key, modifiers in std_spaces.items() if not key.startswith('fs')]) ref_file = bold_file mem_gb = {'filesize': 1, 'resampled': 1, 'largemem': 1} bold_tlen = 10 multiecho = isinstance(bold_file, list) if multiecho: tes = [layout.get_metadata(echo)['EchoTime'] for echo in bold_file] ref_file = dict(zip(tes, bold_file))[min(tes)] if os.path.isfile(ref_file): bold_tlen, mem_gb = _create_mem_gb(ref_file) wf_name = _get_wf_name(ref_file) LOGGER.log( 25, ('Creating bold processing workflow for "%s" (%.2f GB / %d TRs). ' 'Memory resampled/largemem=%.2f/%.2f GB.'), ref_file, mem_gb['filesize'], bold_tlen, mem_gb['resampled'], mem_gb['largemem']) sbref_file = None # For doc building purposes if not hasattr(layout, 'parse_file_entities'): LOGGER.log(25, 'No valid layout: building empty workflow.') metadata = { 'RepetitionTime': 2.0, 'SliceTiming': [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], 'PhaseEncodingDirection': 'j', } fmaps = { 'phasediff': [{ 'phases': [('sub-03/ses-2/fmap/sub-03_ses-2_run-1_phasediff.nii.gz', { 'EchoTime1': 0.0, 'EchoTime2': 0.00246 })], 'magnitude': [('sub-03/ses-2/fmap/sub-03_ses-2_run-1_magnitude1.nii.gz', {}), ('sub-03/ses-2/fmap/sub-03_ses-2_run-1_magnitude2.nii.gz', {}) ] }] } run_stc = True multiecho = False else: # Find associated sbref, if possible entities = layout.parse_file_entities(ref_file) entities['suffix'] = 'sbref' entities['extension'] = ['nii', 'nii.gz'] # Overwrite extensions files = layout.get(return_type='file', **entities) refbase = os.path.basename(ref_file) if 'sbref' in ignore: LOGGER.info("Single-band reference files ignored.") elif files and multiecho: LOGGER.warning("Single-band reference found, but not supported in " "multi-echo workflows at this time. Ignoring.") elif files: sbref_file = files[0] sbbase = os.path.basename(sbref_file) if len(files) > 1: LOGGER.warning( "Multiple single-band reference files found for {}; using " "{}".format(refbase, sbbase)) else: LOGGER.log( 25, "Using single-band reference file {}".format(sbbase)) else: LOGGER.log(25, "No single-band-reference found for {}".format(refbase)) metadata = layout.get_metadata(ref_file) # Find fieldmaps. Options: (phase1|phase2|phasediff|epi|fieldmap|syn) fmaps = None if 'fieldmaps' not in ignore: fmaps = fieldmap_wrangler(layout, ref_file, use_syn=use_syn, force_syn=force_syn) # Short circuits: (True and True and (False or 'TooShort')) == 'TooShort' run_stc = ("SliceTiming" in metadata and 'slicetiming' not in ignore and (_get_series_len(ref_file) > 4 or "TooShort")) # Check if MEEPI for T2* coregistration target if t2s_coreg and not multiecho: LOGGER.warning( "No multiecho BOLD images found for T2* coregistration. " "Using standard EPI-T1 coregistration.") t2s_coreg = False # By default, force-bbr for t2s_coreg unless user specifies otherwise if t2s_coreg and use_bbr is None: use_bbr = True # Build workflow workflow = Workflow(name=wf_name) workflow.__desc__ = """ Functional data preprocessing : For each of the {num_bold} BOLD runs found per subject (across all tasks and sessions), the following preprocessing was performed. """.format(num_bold=num_bold) workflow.__postdesc__ = """\ All resamplings can be performed with *a single interpolation step* by composing all the pertinent transformations (i.e. head-motion transform matrices, susceptibility distortion correction when available, and co-registrations to anatomical and output spaces). Gridded (volumetric) resamplings were performed using `antsApplyTransforms` (ANTs), configured with Lanczos interpolation to minimize the smoothing effects of other kernels [@lanczos]. Non-gridded (surface) resamplings were performed using `mri_vol2surf` (FreeSurfer). """ inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_file', 'subjects_dir', 'subject_id', 't1w_preproc', 't1w_brain', 't1w_mask', 't1w_dseg', 't1w_tpms', 't1w_aseg', 't1w_aparc', 'anat2std_xfm', 'std2anat_xfm', 'template', 'joint_anat2std_xfm', 'joint_std2anat_xfm', 'joint_template', 't1w2fsnative_xfm', 'fsnative2t1w_xfm' ]), name='inputnode') inputnode.inputs.bold_file = bold_file if sbref_file is not None: from niworkflows.interfaces.images import ValidateImage val_sbref = pe.Node(ValidateImage(in_file=sbref_file), name='val_sbref') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_t1', 'bold_t1_ref', 'bold_mask_t1', 'bold_aseg_t1', 'bold_aparc_t1', 'bold_std', 'bold_std_ref', 'bold_mask_std', 'bold_aseg_std', 'bold_aparc_std', 'bold_native', 'bold_cifti', 'cifti_variant', 'cifti_variant_key', 'surfaces', 'confounds', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file', 'confounds_metadata' ]), name='outputnode') # BOLD buffer: an identity used as a pointer to either the original BOLD # or the STC'ed one for further use. boldbuffer = pe.Node(niu.IdentityInterface(fields=['bold_file']), name='boldbuffer') summary = pe.Node(FunctionalSummary( slice_timing=run_stc, registration=('FSL', 'FreeSurfer')[freesurfer], registration_dof=bold2t1w_dof, pe_direction=metadata.get("PhaseEncodingDirection"), tr=metadata.get("RepetitionTime")), name='summary', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True) summary.inputs.dummy_scans = dummy_scans # CIfTI output: currently, we only support fsaverage{5,6} cifti_spaces = set(s for s in output_spaces.keys() if s in ('fsaverage5', 'fsaverage6')) fsaverage_den = output_spaces.get('fsaverage', {}).get('den') if fsaverage_den: cifti_spaces.add(FSAVERAGE_DENSITY[fsaverage_den]) cifti_output = cifti_output and cifti_spaces func_derivatives_wf = init_func_derivatives_wf( bids_root=layout.root, cifti_output=cifti_output, freesurfer=freesurfer, metadata=metadata, output_dir=output_dir, output_spaces=output_spaces, standard_spaces=list(std_spaces.keys()), use_aroma=use_aroma, ) workflow.connect([ (outputnode, func_derivatives_wf, [ ('bold_t1', 'inputnode.bold_t1'), ('bold_t1_ref', 'inputnode.bold_t1_ref'), ('bold_aseg_t1', 'inputnode.bold_aseg_t1'), ('bold_aparc_t1', 'inputnode.bold_aparc_t1'), ('bold_mask_t1', 'inputnode.bold_mask_t1'), ('bold_native', 'inputnode.bold_native'), ('confounds', 'inputnode.confounds'), ('surfaces', 'inputnode.surfaces'), ('aroma_noise_ics', 'inputnode.aroma_noise_ics'), ('melodic_mix', 'inputnode.melodic_mix'), ('nonaggr_denoised_file', 'inputnode.nonaggr_denoised_file'), ('bold_cifti', 'inputnode.bold_cifti'), ('cifti_variant', 'inputnode.cifti_variant'), ('cifti_variant_key', 'inputnode.cifti_variant_key'), ('confounds_metadata', 'inputnode.confounds_metadata'), ]), ]) # Generate a tentative boldref bold_reference_wf = init_bold_reference_wf(omp_nthreads=omp_nthreads) bold_reference_wf.inputs.inputnode.dummy_scans = dummy_scans if sbref_file is not None: workflow.connect([ (val_sbref, bold_reference_wf, [('out_file', 'inputnode.sbref_file')]), ]) # Top-level BOLD splitter bold_split = pe.Node(FSLSplit(dimension='t'), name='bold_split', mem_gb=mem_gb['filesize'] * 3) # HMC on the BOLD bold_hmc_wf = init_bold_hmc_wf(name='bold_hmc_wf', mem_gb=mem_gb['filesize'], omp_nthreads=omp_nthreads) # calculate BOLD registration to T1w bold_reg_wf = init_bold_reg_wf(name='bold_reg_wf', freesurfer=freesurfer, use_bbr=use_bbr, bold2t1w_dof=bold2t1w_dof, mem_gb=mem_gb['resampled'], omp_nthreads=omp_nthreads, use_compression=False) # apply BOLD registration to T1w bold_t1_trans_wf = init_bold_t1_trans_wf(name='bold_t1_trans_wf', freesurfer=freesurfer, use_fieldwarp=bool(fmaps), multiecho=multiecho, mem_gb=mem_gb['resampled'], omp_nthreads=omp_nthreads, use_compression=False) # get confounds bold_confounds_wf = init_bold_confs_wf( mem_gb=mem_gb['largemem'], metadata=metadata, regressors_all_comps=regressors_all_comps, regressors_fd_th=regressors_fd_th, regressors_dvars_th=regressors_dvars_th, name='bold_confounds_wf') bold_confounds_wf.get_node('inputnode').inputs.t1_transform_flags = [False] # Apply transforms in 1 shot # Only use uncompressed output if AROMA is to be run bold_bold_trans_wf = init_bold_preproc_trans_wf( mem_gb=mem_gb['resampled'], omp_nthreads=omp_nthreads, use_compression=not low_mem, use_fieldwarp=bool(fmaps), name='bold_bold_trans_wf') bold_bold_trans_wf.inputs.inputnode.name_source = ref_file # SLICE-TIME CORRECTION (or bypass) ############################################# if run_stc is True: # bool('TooShort') == True, so check True explicitly bold_stc_wf = init_bold_stc_wf(name='bold_stc_wf', metadata=metadata) workflow.connect([ (bold_reference_wf, bold_stc_wf, [('outputnode.skip_vols', 'inputnode.skip_vols')]), (bold_stc_wf, boldbuffer, [('outputnode.stc_file', 'bold_file')]), ]) if not multiecho: workflow.connect([(bold_reference_wf, bold_stc_wf, [ ('outputnode.bold_file', 'inputnode.bold_file') ])]) else: # for meepi, iterate through stc_wf for all workflows meepi_echos = boldbuffer.clone(name='meepi_echos') meepi_echos.iterables = ('bold_file', bold_file) workflow.connect([(meepi_echos, bold_stc_wf, [('bold_file', 'inputnode.bold_file')])]) elif not multiecho: # STC is too short or False # bypass STC from original BOLD to the splitter through boldbuffer workflow.connect([(bold_reference_wf, boldbuffer, [('outputnode.bold_file', 'bold_file')])]) else: # for meepi, iterate over all meepi echos to boldbuffer boldbuffer.iterables = ('bold_file', bold_file) # SDC (SUSCEPTIBILITY DISTORTION CORRECTION) or bypass ########################## bold_sdc_wf = init_sdc_estimate_wf(fmaps, metadata, omp_nthreads=omp_nthreads, debug=debug) # MULTI-ECHO EPI DATA ############################################# if multiecho: from .util import init_skullstrip_bold_wf skullstrip_bold_wf = init_skullstrip_bold_wf(name='skullstrip_bold_wf') inputnode.inputs.bold_file = ref_file # Replace reference w first echo join_echos = pe.JoinNode( niu.IdentityInterface(fields=['bold_files']), joinsource=('meepi_echos' if run_stc is True else 'boldbuffer'), joinfield=['bold_files'], name='join_echos') # create optimal combination, adaptive T2* map bold_t2s_wf = init_bold_t2s_wf(echo_times=tes, mem_gb=mem_gb['resampled'], omp_nthreads=omp_nthreads, t2s_coreg=t2s_coreg, name='bold_t2smap_wf') workflow.connect([ (skullstrip_bold_wf, join_echos, [('outputnode.skull_stripped_file', 'bold_files')]), (join_echos, bold_t2s_wf, [('bold_files', 'inputnode.bold_file')]), ]) # MAIN WORKFLOW STRUCTURE ####################################################### workflow.connect([ # Generate early reference (inputnode, bold_reference_wf, [('bold_file', 'inputnode.bold_file')]), # BOLD buffer has slice-time corrected if it was run, original otherwise (boldbuffer, bold_split, [('bold_file', 'in_file')]), # HMC (bold_reference_wf, bold_hmc_wf, [('outputnode.raw_ref_image', 'inputnode.raw_ref_image'), ('outputnode.bold_file', 'inputnode.bold_file')]), (bold_reference_wf, summary, [('outputnode.algo_dummy_scans', 'algo_dummy_scans')]), # EPI-T1 registration workflow ( inputnode, bold_reg_wf, [ ('t1w_brain', 'inputnode.t1w_brain'), ('t1w_dseg', 'inputnode.t1w_dseg'), # Undefined if --no-freesurfer, but this is safe ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('fsnative2t1w_xfm', 'inputnode.fsnative2t1w_xfm') ]), (inputnode, bold_t1_trans_wf, [('bold_file', 'inputnode.name_source'), ('t1w_brain', 'inputnode.t1w_brain'), ('t1w_mask', 'inputnode.t1w_mask'), ('t1w_aseg', 'inputnode.t1w_aseg'), ('t1w_aparc', 'inputnode.t1w_aparc')]), # unused if multiecho, but this is safe (bold_hmc_wf, bold_t1_trans_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]), (bold_reg_wf, bold_t1_trans_wf, [('outputnode.itk_bold_to_t1', 'inputnode.itk_bold_to_t1')]), (bold_t1_trans_wf, outputnode, [('outputnode.bold_t1', 'bold_t1'), ('outputnode.bold_t1_ref', 'bold_t1_ref'), ('outputnode.bold_aseg_t1', 'bold_aseg_t1'), ('outputnode.bold_aparc_t1', 'bold_aparc_t1')]), (bold_reg_wf, summary, [('outputnode.fallback', 'fallback')]), # SDC (or pass-through workflow) (inputnode, bold_sdc_wf, [('t1w_brain', 'inputnode.t1w_brain')]), (bold_reference_wf, bold_sdc_wf, [('outputnode.ref_image', 'inputnode.epi_file'), ('outputnode.ref_image_brain', 'inputnode.epi_brain'), ('outputnode.bold_mask', 'inputnode.epi_mask')]), (bold_sdc_wf, bold_t1_trans_wf, [('outputnode.out_warp', 'inputnode.fieldwarp')]), (bold_sdc_wf, bold_bold_trans_wf, [('outputnode.out_warp', 'inputnode.fieldwarp'), ('outputnode.epi_mask', 'inputnode.bold_mask')]), (bold_sdc_wf, summary, [('outputnode.method', 'distortion_correction') ]), # Connect bold_confounds_wf (inputnode, bold_confounds_wf, [('t1w_tpms', 'inputnode.t1w_tpms'), ('t1w_mask', 'inputnode.t1w_mask')]), (bold_hmc_wf, bold_confounds_wf, [('outputnode.movpar_file', 'inputnode.movpar_file')]), (bold_reg_wf, bold_confounds_wf, [('outputnode.itk_t1_to_bold', 'inputnode.t1_bold_xform')]), (bold_reference_wf, bold_confounds_wf, [('outputnode.skip_vols', 'inputnode.skip_vols')]), (bold_confounds_wf, outputnode, [ ('outputnode.confounds_file', 'confounds'), ]), (bold_confounds_wf, outputnode, [ ('outputnode.confounds_metadata', 'confounds_metadata'), ]), # Connect bold_bold_trans_wf (bold_split, bold_bold_trans_wf, [('out_files', 'inputnode.bold_file')] ), (bold_hmc_wf, bold_bold_trans_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]), # Summary (outputnode, summary, [('confounds', 'confounds_file')]), ]) if not t2s_coreg: workflow.connect([ (bold_sdc_wf, bold_reg_wf, [('outputnode.epi_brain', 'inputnode.ref_bold_brain')]), (bold_sdc_wf, bold_t1_trans_wf, [('outputnode.epi_brain', 'inputnode.ref_bold_brain'), ('outputnode.epi_mask', 'inputnode.ref_bold_mask')]), ]) else: workflow.connect([ # For t2s_coreg, replace EPI-to-T1w registration inputs (bold_t2s_wf, bold_reg_wf, [('outputnode.bold_ref_brain', 'inputnode.ref_bold_brain')]), (bold_t2s_wf, bold_t1_trans_wf, [('outputnode.bold_ref_brain', 'inputnode.ref_bold_brain'), ('outputnode.bold_mask', 'inputnode.ref_bold_mask')]), ]) # for standard EPI data, pass along correct file if not multiecho: workflow.connect([ (inputnode, func_derivatives_wf, [('bold_file', 'inputnode.source_file')]), (bold_bold_trans_wf, bold_confounds_wf, [('outputnode.bold', 'inputnode.bold'), ('outputnode.bold_mask', 'inputnode.bold_mask')]), (bold_split, bold_t1_trans_wf, [('out_files', 'inputnode.bold_split')]), ]) else: # for meepi, create and use optimal combination workflow.connect([ # update name source for optimal combination (inputnode, func_derivatives_wf, [(('bold_file', combine_meepi_source), 'inputnode.source_file')]), (bold_bold_trans_wf, skullstrip_bold_wf, [('outputnode.bold', 'inputnode.in_file')]), (bold_t2s_wf, bold_confounds_wf, [('outputnode.bold', 'inputnode.bold'), ('outputnode.bold_mask', 'inputnode.bold_mask')]), (bold_t2s_wf, bold_t1_trans_wf, [('outputnode.bold', 'inputnode.bold_split')]), ]) if fmaps: from sdcflows.workflows.outputs import init_sdc_unwarp_report_wf # Report on BOLD correction fmap_unwarp_report_wf = init_sdc_unwarp_report_wf() workflow.connect([ (inputnode, fmap_unwarp_report_wf, [('t1w_dseg', 'inputnode.in_seg')]), (bold_reference_wf, fmap_unwarp_report_wf, [('outputnode.ref_image', 'inputnode.in_pre')]), (bold_reg_wf, fmap_unwarp_report_wf, [('outputnode.itk_t1_to_bold', 'inputnode.in_xfm')]), (bold_sdc_wf, fmap_unwarp_report_wf, [('outputnode.epi_corrected', 'inputnode.in_post')]), ]) # Overwrite ``out_path_base`` of unwarping DataSinks for node in fmap_unwarp_report_wf.list_node_names(): if node.split('.')[-1].startswith('ds_'): fmap_unwarp_report_wf.get_node( node).interface.out_path_base = 'fmriprep' for node in bold_sdc_wf.list_node_names(): if node.split('.')[-1].startswith('ds_'): bold_sdc_wf.get_node(node).interface.out_path_base = 'fmriprep' if 'syn' in fmaps: sdc_select_std = pe.Node(KeySelect(fields=['std2anat_xfm']), name='sdc_select_std', run_without_submitting=True) sdc_select_std.inputs.key = 'MNI152NLin2009cAsym' workflow.connect([ (inputnode, sdc_select_std, [('joint_std2anat_xfm', 'std2anat_xfm'), ('joint_template', 'keys')]), (sdc_select_std, bold_sdc_wf, [('std2anat_xfm', 'inputnode.std2anat_xfm')]), ]) if fmaps.get('syn') is True: # SyN forced syn_unwarp_report_wf = init_sdc_unwarp_report_wf( name='syn_unwarp_report_wf', forcedsyn=True) workflow.connect([ (inputnode, syn_unwarp_report_wf, [('t1w_dseg', 'inputnode.in_seg')]), (bold_reference_wf, syn_unwarp_report_wf, [('outputnode.ref_image', 'inputnode.in_pre')]), (bold_reg_wf, syn_unwarp_report_wf, [('outputnode.itk_t1_to_bold', 'inputnode.in_xfm')]), (bold_sdc_wf, syn_unwarp_report_wf, [('outputnode.syn_ref', 'inputnode.in_post')]), ]) # Overwrite ``out_path_base`` of unwarping DataSinks for node in syn_unwarp_report_wf.list_node_names(): if node.split('.')[-1].startswith('ds_'): syn_unwarp_report_wf.get_node( node).interface.out_path_base = 'fmriprep' # Map final BOLD mask into T1w space (if required) if 'T1w' in output_spaces or 'anat' in output_spaces: from niworkflows.interfaces.fixes import (FixHeaderApplyTransforms as ApplyTransforms) boldmask_to_t1w = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='boldmask_to_t1w', mem_gb=0.1) workflow.connect([ (bold_reg_wf, boldmask_to_t1w, [('outputnode.itk_bold_to_t1', 'transforms')]), (bold_t1_trans_wf, boldmask_to_t1w, [('outputnode.bold_mask_t1', 'reference_image')]), (bold_bold_trans_wf if not multiecho else bold_t2s_wf, boldmask_to_t1w, [('outputnode.bold_mask', 'input_image')]), (boldmask_to_t1w, outputnode, [('output_image', 'bold_mask_t1')]), ]) if set(['func', 'run', 'bold', 'boldref', 'sbref']).intersection(output_spaces): workflow.connect([ (bold_bold_trans_wf, outputnode, [('outputnode.bold', 'bold_native')]), (bold_bold_trans_wf, func_derivatives_wf, [('outputnode.bold_ref', 'inputnode.bold_native_ref'), ('outputnode.bold_mask', 'inputnode.bold_mask_native')]), ]) if volume_std_spaces: # Apply transforms in 1 shot # Only use uncompressed output if AROMA is to be run bold_std_trans_wf = init_bold_std_trans_wf( freesurfer=freesurfer, mem_gb=mem_gb['resampled'], omp_nthreads=omp_nthreads, standard_spaces=volume_std_spaces, name='bold_std_trans_wf', use_compression=not low_mem, use_fieldwarp=bool(fmaps), ) workflow.connect([ (inputnode, bold_std_trans_wf, [('joint_template', 'inputnode.templates'), ('joint_anat2std_xfm', 'inputnode.anat2std_xfm'), ('bold_file', 'inputnode.name_source'), ('t1w_aseg', 'inputnode.bold_aseg'), ('t1w_aparc', 'inputnode.bold_aparc')]), (bold_hmc_wf, bold_std_trans_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]), (bold_reg_wf, bold_std_trans_wf, [('outputnode.itk_bold_to_t1', 'inputnode.itk_bold_to_t1')]), (bold_bold_trans_wf if not multiecho else bold_t2s_wf, bold_std_trans_wf, [('outputnode.bold_mask', 'inputnode.bold_mask')]), (bold_sdc_wf, bold_std_trans_wf, [('outputnode.out_warp', 'inputnode.fieldwarp')]), (bold_std_trans_wf, outputnode, [('outputnode.bold_std', 'bold_std'), ('outputnode.bold_std_ref', 'bold_std_ref'), ('outputnode.bold_mask_std', 'bold_mask_std')]), ]) if freesurfer: workflow.connect([ (bold_std_trans_wf, func_derivatives_wf, [ ('poutputnode.bold_aseg_std', 'inputnode.bold_aseg_std'), ('poutputnode.bold_aparc_std', 'inputnode.bold_aparc_std'), ]), (bold_std_trans_wf, outputnode, [('outputnode.bold_aseg_std', 'bold_aseg_std'), ('outputnode.bold_aparc_std', 'bold_aparc_std')]), ]) if 'MNI152NLin2009cAsym' in std_spaces: carpetplot_wf = init_carpetplot_wf(standard_spaces=std_spaces, mem_gb=mem_gb['resampled'], metadata=metadata, name='carpetplot_wf') workflow.connect([ (inputnode, carpetplot_wf, [('joint_std2anat_xfm', 'inputnode.std2anat_xfm')]), (bold_bold_trans_wf if not multiecho else bold_t2s_wf, carpetplot_wf, [('outputnode.bold', 'inputnode.bold'), ('outputnode.bold_mask', 'inputnode.bold_mask')]), (bold_reg_wf, carpetplot_wf, [('outputnode.itk_t1_to_bold', 'inputnode.t1_bold_xform')]), (bold_confounds_wf, carpetplot_wf, [('outputnode.confounds_file', 'inputnode.confounds_file')]), ]) if not multiecho: workflow.connect([(bold_split, bold_std_trans_wf, [('out_files', 'inputnode.bold_split')])]) else: split_opt_comb = bold_split.clone(name='split_opt_comb') workflow.connect([(bold_t2s_wf, split_opt_comb, [('outputnode.bold', 'in_file')]), (split_opt_comb, bold_std_trans_wf, [('out_files', 'inputnode.bold_split')])]) # Artifacts resampled in MNI space can only be sinked if they # were actually generated. See #1348. # Uses the parameterized outputnode to generate all outputs workflow.connect([ (bold_std_trans_wf, func_derivatives_wf, [ ('poutputnode.templates', 'inputnode.template'), ('poutputnode.bold_std_ref', 'inputnode.bold_std_ref'), ('poutputnode.bold_std', 'inputnode.bold_std'), ('poutputnode.bold_mask_std', 'inputnode.bold_mask_std'), ]), ]) if use_aroma and 'MNI152NLin6Asym' in std_spaces: # ICA-AROMA workflow from .confounds import init_ica_aroma_wf ica_aroma_wf = init_ica_aroma_wf( metadata=metadata, mem_gb=mem_gb['resampled'], omp_nthreads=omp_nthreads, use_fieldwarp=bool(fmaps), err_on_aroma_warn=err_on_aroma_warn, aroma_melodic_dim=aroma_melodic_dim, name='ica_aroma_wf') join = pe.Node(niu.Function(output_names=["out_file"], function=_to_join), name='aroma_confounds') mrg_conf_metadata = pe.Node(niu.Merge(2), name='merge_confound_metadata', run_without_submitting=True) mrg_conf_metadata2 = pe.Node(DictMerge(), name='merge_confound_metadata2', run_without_submitting=True) workflow.disconnect([ (bold_confounds_wf, outputnode, [ ('outputnode.confounds_file', 'confounds'), ]), (bold_confounds_wf, outputnode, [ ('outputnode.confounds_metadata', 'confounds_metadata'), ]), ]) workflow.connect([ (bold_std_trans_wf, ica_aroma_wf, [('outputnode.bold_std', 'inputnode.bold_std'), ('outputnode.bold_mask_std', 'inputnode.bold_mask_std'), ('outputnode.templates', 'inputnode.templates')]), (inputnode, ica_aroma_wf, [('bold_file', 'inputnode.name_source')]), (bold_hmc_wf, ica_aroma_wf, [('outputnode.movpar_file', 'inputnode.movpar_file')]), (bold_reference_wf, ica_aroma_wf, [('outputnode.skip_vols', 'inputnode.skip_vols')]), (bold_confounds_wf, join, [('outputnode.confounds_file', 'in_file')]), (bold_confounds_wf, mrg_conf_metadata, [('outputnode.confounds_metadata', 'in1')]), (ica_aroma_wf, join, [('outputnode.aroma_confounds', 'join_file')]), (ica_aroma_wf, mrg_conf_metadata, [('outputnode.aroma_metadata', 'in2')]), (mrg_conf_metadata, mrg_conf_metadata2, [('out', 'in_dicts')]), (ica_aroma_wf, outputnode, [('outputnode.aroma_noise_ics', 'aroma_noise_ics'), ('outputnode.melodic_mix', 'melodic_mix'), ('outputnode.nonaggr_denoised_file', 'nonaggr_denoised_file') ]), (join, outputnode, [('out_file', 'confounds')]), (mrg_conf_metadata2, outputnode, [('out_dict', 'confounds_metadata')]), ]) # SURFACES ################################################################################## surface_spaces = [ space for space in output_spaces.keys() if space.startswith('fs') ] if freesurfer and surface_spaces: LOGGER.log(25, 'Creating BOLD surface-sampling workflow.') bold_surf_wf = init_bold_surf_wf(mem_gb=mem_gb['resampled'], output_spaces=surface_spaces, medial_surface_nan=medial_surface_nan, name='bold_surf_wf') workflow.connect([ (inputnode, bold_surf_wf, [('t1w_preproc', 'inputnode.t1w_preproc'), ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('t1w2fsnative_xfm', 'inputnode.t1w2fsnative_xfm')]), (bold_t1_trans_wf, bold_surf_wf, [('outputnode.bold_t1', 'inputnode.source_file')]), (bold_surf_wf, outputnode, [('outputnode.surfaces', 'surfaces')]), ]) if cifti_output: from niworkflows.interfaces.cifti import GenerateCifti bold_surf_wf.__desc__ += """\ *Grayordinates* files [@hcppipelines], which combine surface-sampled data and volume-sampled data, were also generated. """ select_std = pe.Node(KeySelect(fields=['bold_std']), name='select_std', run_without_submitting=True) select_std.inputs.key = 'MNI152NLin2009cAsym' gen_cifti = pe.MapNode(GenerateCifti(), iterfield=["surface_target", "gifti_files"], name="gen_cifti") gen_cifti.inputs.TR = metadata.get("RepetitionTime") gen_cifti.inputs.surface_target = list(cifti_spaces) workflow.connect([ (bold_std_trans_wf, select_std, [('outputnode.templates', 'keys'), ('outputnode.bold_std', 'bold_std')]), (bold_surf_wf, gen_cifti, [('outputnode.surfaces', 'gifti_files')]), (inputnode, gen_cifti, [('subjects_dir', 'subjects_dir')]), (select_std, gen_cifti, [('bold_std', 'bold_file')]), (gen_cifti, outputnode, [('out_file', 'bold_cifti'), ('variant', 'cifti_variant'), ('variant_key', 'cifti_variant_key') ]), ]) # REPORTING ############################################################ ds_report_summary = pe.Node(DerivativesDataSink(desc='summary', keep_dtype=True), name='ds_report_summary', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_report_validation = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, desc='validation', keep_dtype=True), name='ds_report_validation', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (summary, ds_report_summary, [('out_report', 'in_file')]), (bold_reference_wf, ds_report_validation, [('outputnode.validation_report', 'in_file')]), ]) # Fill-in datasinks of reportlets seen so far for node in workflow.list_node_names(): if node.split('.')[-1].startswith('ds_report'): workflow.get_node(node).inputs.base_directory = reportlets_dir workflow.get_node(node).inputs.source_file = ref_file return workflow
def init_bold_preproc_trans_wf(mem_gb, omp_nthreads, name='bold_preproc_trans_wf', use_compression=True, use_fieldwarp=False, split_file=False, interpolation='LanczosWindowedSinc'): """ Resample in native (original) space. This workflow resamples the input fMRI in its native (original) space in a "single shot" from the original BOLD series. Workflow Graph .. workflow:: :graph2use: colored :simple_form: yes from fmriprep.workflows.bold import init_bold_preproc_trans_wf wf = init_bold_preproc_trans_wf(mem_gb=3, omp_nthreads=1) Parameters ---------- mem_gb : float Size of BOLD file in GB omp_nthreads : int Maximum number of threads an individual process may use name : str Name of workflow (default: ``bold_std_trans_wf``) use_compression : bool Save registered BOLD series as ``.nii.gz`` use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to MNI split_file : bool Whether the input file should be splitted (it is a 4D file) or it is a list of 3D files (default ``False``, do not split) interpolation : str Interpolation type to be used by ANTs' ``applyTransforms`` (default ``'LanczosWindowedSinc'``) Inputs ------ bold_file Individual 3D volumes, not motion corrected bold_mask Skull-stripping mask of reference image name_source BOLD series NIfTI file Used to recover original information lost during processing hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format fieldwarp a :abbr:`DFM (displacements field map)` in ITK format Outputs ------- bold BOLD series, resampled in native space, including all preprocessing bold_mask BOLD series mask calculated with the new time-series bold_ref BOLD reference image: an average-like 3D image of the time-series bold_ref_brain Same as ``bold_ref``, but once the brain mask has been applied """ workflow = Workflow(name=name) workflow.__desc__ = """\ The BOLD time-series (including slice-timing correction when applied) were resampled onto their original, native space by applying {transforms}. These resampled BOLD time-series will be referred to as *preprocessed BOLD in original space*, or just *preprocessed BOLD*. """.format(transforms="""\ a single, composite transform to correct for head-motion and susceptibility distortions""" if use_fieldwarp else """\ the transforms to correct for head-motion""") inputnode = pe.Node(niu.IdentityInterface(fields=[ 'name_source', 'bold_file', 'bold_mask', 'hmc_xforms', 'fieldwarp' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['bold', 'bold_mask', 'bold_ref', 'bold_ref_brain']), name='outputnode') bold_transform = pe.Node(MultiApplyTransforms(interpolation=interpolation, float=True, copy_dtype=True), name='bold_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb * 3) # Generate a new BOLD reference bold_reference_wf = init_bold_reference_wf(omp_nthreads=omp_nthreads) bold_reference_wf.__desc__ = None # Unset description to avoid second appearance workflow.connect([ (inputnode, merge, [('name_source', 'header_source')]), (bold_transform, merge, [('out_files', 'in_files')]), (merge, bold_reference_wf, [('out_file', 'inputnode.bold_file')]), (merge, outputnode, [('out_file', 'bold')]), (bold_reference_wf, outputnode, [('outputnode.ref_image', 'bold_ref'), ('outputnode.ref_image_brain', 'bold_ref_brain'), ('outputnode.bold_mask', 'bold_mask')]), ]) # Input file is not splitted if split_file: bold_split = pe.Node(FSLSplit(dimension='t'), name='bold_split', mem_gb=mem_gb * 3) workflow.connect([(inputnode, bold_split, [('bold_file', 'in_file')]), (bold_split, bold_transform, [ ('out_files', 'input_image'), (('out_files', _first), 'reference_image'), ])]) else: workflow.connect([ (inputnode, bold_transform, [('bold_file', 'input_image'), (('bold_file', _first), 'reference_image')]), ]) if use_fieldwarp: merge_xforms = pe.Node(niu.Merge(2), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, merge_xforms, [('fieldwarp', 'in1'), ('hmc_xforms', 'in2')]), (merge_xforms, bold_transform, [('out', 'transforms')]), ]) else: def _aslist(val): return [val] workflow.connect([ (inputnode, bold_transform, [(('hmc_xforms', _aslist), 'transforms')]), ]) return workflow
def init_t2w_template_wf(longitudinal, omp_nthreads, num_t2w, name="anat_t2w_template_wf"): """ Adapts :py:func:`~smriprep.workflows.anatomical.init_anat_template_wf` for T2w image reference """ from pkg_resources import resource_filename as pkgr from nipype.interfaces import freesurfer as fs, image, ants from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.interfaces.freesurfer import ( StructuralReference, PatchedLTAConvert as LTAConvert, ) from niworkflows.interfaces.images import TemplateDimensions, Conform, ValidateImage from niworkflows.interfaces.nitransforms import ConcatenateXFMs from niworkflows.utils.misc import add_suffix wf = Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=["t2w"]), name="inputnode") outputnode = pe.Node( niu.IdentityInterface(fields=[ "t2w_ref", "t2w_valid_list", "t2_realign_xfm", "out_report" ]), name="outputnode", ) # 0. Reorient T2w image(s) to RAS and resample to common voxel space t2w_ref_dimensions = pe.Node(TemplateDimensions(), name='t2w_ref_dimensions') t2w_conform = pe.MapNode(Conform(), iterfield='in_file', name='t2w_conform') wf.connect([ (inputnode, t2w_ref_dimensions, [('t2w', 't1w_list')]), (t2w_ref_dimensions, t2w_conform, [('t1w_valid_list', 'in_file'), ('target_zooms', 'target_zooms'), ('target_shape', 'target_shape')]), (t2w_ref_dimensions, outputnode, [('out_report', 'out_report'), ('t1w_valid_list', 't2w_valid_list') ]), ]) if num_t2w == 1: get1st = pe.Node(niu.Select(index=[0]), name='get1st') outputnode.inputs.t2w_realign_xfm = [ pkgr('smriprep', 'data/itkIdentityTransform.txt') ] wf.connect([ (t2w_conform, get1st, [('out_file', 'inlist')]), (get1st, outputnode, [('out', 't2w_ref')]), ]) return wf wf.__desc__ = f"""\ A T2w-reference map was computed after registration of {num_t2w} T2w images (after INU-correction) using `mri_robust_template` [FreeSurfer {fs.Info().looseversion() or "<ver>"}, @fs_template]. """ t2w_conform_xfm = pe.MapNode(LTAConvert(in_lta='identity.nofile', out_lta=True), iterfield=['source_file', 'target_file'], name='t2w_conform_xfm') # 1a. Correct for bias field: the bias field is an additive factor # in log-transformed intensity units. Therefore, it is not a linear # combination of fields and N4 fails with merged images. # 1b. Align and merge if several T1w images are provided n4_correct = pe.MapNode(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), iterfield='input_image', name='n4_correct', n_procs=1) # n_procs=1 for reproducibility # StructuralReference is fs.RobustTemplate if > 1 volume, copying otherwise t2w_merge = pe.Node( StructuralReference( auto_detect_sensitivity=True, initial_timepoint=1, # For deterministic behavior intensity_scaling=True, # 7-DOF (rigid + intensity) subsample_threshold=200, fixed_timepoint=not longitudinal, no_iteration=not longitudinal, transform_outputs=True, ), mem_gb=2 * num_t2w - 1, name='t2w_merge') # 2. Reorient template to RAS, if needed (mri_robust_template may set to LIA) t2w_reorient = pe.Node(image.Reorient(), name='t2w_reorient') merge_xfm = pe.MapNode(niu.Merge(2), name='merge_xfm', iterfield=['in1', 'in2'], run_without_submitting=True) concat_xfms = pe.MapNode(ConcatenateXFMs(inverse=True), name="concat_xfms", iterfield=['in_xfms'], run_without_submitting=True) def _set_threads(in_list, maximum): return min(len(in_list), maximum) wf.connect([ (t2w_ref_dimensions, t2w_conform_xfm, [('t1w_valid_list', 'source_file')]), (t2w_conform, t2w_conform_xfm, [('out_file', 'target_file')]), (t2w_conform, n4_correct, [('out_file', 'input_image')]), (t2w_conform, t2w_merge, [(('out_file', _set_threads, omp_nthreads), 'num_threads'), (('out_file', add_suffix, '_template'), 'out_file')]), (n4_correct, t2w_merge, [('output_image', 'in_files')]), (t2w_merge, t2w_reorient, [('out_file', 'in_file')]), # Combine orientation and template transforms (t2w_conform_xfm, merge_xfm, [('out_lta', 'in1')]), (t2w_merge, merge_xfm, [('transform_outputs', 'in2')]), (merge_xfm, concat_xfms, [('out', 'in_xfms')]), # Output (t2w_reorient, outputnode, [('out_file', 't2w_ref')]), (concat_xfms, outputnode, [('out_xfm', 't2w_realign_xfm')]), ]) return wf
def init_bold_preproc_report_wf(mem_gb, reportlets_dir, name='bold_preproc_report_wf'): """ Generate a visual report. This workflow generates and saves a reportlet showing the effect of resampling the BOLD signal using the standard deviation maps. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.resampling import init_bold_preproc_report_wf wf = init_bold_preproc_report_wf(mem_gb=1, reportlets_dir='.') Parameters ---------- mem_gb : float Size of BOLD file in GB reportlets_dir : str Directory in which to save reportlets name : str, optional Workflow name (default: bold_preproc_report_wf) Inputs ------ in_pre BOLD time-series, before resampling in_post BOLD time-series, after resampling name_source BOLD series NIfTI file Used to recover original information lost during processing """ from nipype.algorithms.confounds import TSNR from niworkflows.interfaces import SimpleBeforeAfter workflow = Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface(fields=['in_pre', 'in_post', 'name_source']), name='inputnode') pre_tsnr = pe.Node(TSNR(), name='pre_tsnr', mem_gb=mem_gb * 4.5) pos_tsnr = pe.Node(TSNR(), name='pos_tsnr', mem_gb=mem_gb * 4.5) bold_rpt = pe.Node(SimpleBeforeAfter(), name='bold_rpt', mem_gb=0.1) ds_report_bold = pe.Node(DerivativesDataSink(base_directory=reportlets_dir, desc='preproc', keep_dtype=True), name='ds_report_bold', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True) workflow.connect([ (inputnode, ds_report_bold, [('name_source', 'source_file')]), (inputnode, pre_tsnr, [('in_pre', 'in_file')]), (inputnode, pos_tsnr, [('in_post', 'in_file')]), (pre_tsnr, bold_rpt, [('stddev_file', 'before')]), (pos_tsnr, bold_rpt, [('stddev_file', 'after')]), (bold_rpt, ds_report_bold, [('out_report', 'in_file')]), ]) return workflow
def init_single_subject_wf( anat_only, aroma_melodic_dim, bold2t1w_dof, cifti_output, debug, dummy_scans, echo_idx, err_on_aroma_warn, fmap_bspline, fmap_demean, force_syn, freesurfer, hires, ignore, layout, longitudinal, low_mem, medial_surface_nan, name, omp_nthreads, output_dir, output_spaces, reportlets_dir, regressors_all_comps, regressors_dvars_th, regressors_fd_th, skull_strip_fixed_seed, skull_strip_template, subject_id, t2s_coreg, task_id, use_aroma, use_bbr, use_syn, ): """ This workflow organizes the preprocessing pipeline for a single subject. It collects and reports information about the subject, and prepares sub-workflows to perform anatomical and functional preprocessing. Anatomical preprocessing is performed in a single workflow, regardless of the number of sessions. Functional preprocessing is performed using a separate workflow for each individual BOLD series. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.base import init_single_subject_wf from collections import namedtuple, OrderedDict BIDSLayout = namedtuple('BIDSLayout', ['root']) wf = init_single_subject_wf( anat_only=False, aroma_melodic_dim=-200, bold2t1w_dof=9, cifti_output=False, debug=False, dummy_scans=None, echo_idx=None, err_on_aroma_warn=False, fmap_bspline=False, fmap_demean=True, force_syn=True, freesurfer=True, hires=True, ignore=[], layout=BIDSLayout('.'), longitudinal=False, low_mem=False, medial_surface_nan=False, name='single_subject_wf', omp_nthreads=1, output_dir='.', output_spaces=OrderedDict([ ('MNI152Lin', {}), ('fsaverage', {'density': '10k'}), ('T1w', {}), ('fsnative', {})]), reportlets_dir='.', regressors_all_comps=False, regressors_dvars_th=1.5, regressors_fd_th=0.5, skull_strip_fixed_seed=False, skull_strip_template=('OASIS30ANTs', {}), subject_id='test', t2s_coreg=False, task_id='', use_aroma=False, use_bbr=True, use_syn=True, ) Parameters anat_only : bool Disable functional workflows aroma_melodic_dim : int Maximum number of components identified by MELODIC within ICA-AROMA (default is -200, i.e., no limitation). bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration cifti_output : bool Generate bold CIFTI file in output spaces debug : bool Enable debugging outputs dummy_scans : int or None Number of volumes to consider as non steady state echo_idx : int or None Index of echo to preprocess in multiecho BOLD series, or ``None`` to preprocess all err_on_aroma_warn : bool Do not fail on ICA-AROMA errors fmap_bspline : bool **Experimental**: Fit B-Spline field using least-squares fmap_demean : bool Demean voxel-shift map during unwarp force_syn : bool **Temporary**: Always run SyN-based SDC freesurfer : bool Enable FreeSurfer surface reconstruction (may increase runtime) hires : bool Enable sub-millimeter preprocessing in FreeSurfer ignore : list Preprocessing steps to skip (may include "slicetiming", "fieldmaps") layout : BIDSLayout object BIDS dataset layout longitudinal : bool Treat multiple sessions as longitudinal (may increase runtime) See sub-workflows for specific differences low_mem : bool Write uncompressed .nii files in some cases to reduce memory usage medial_surface_nan : bool Replace medial wall values with NaNs on functional GIFTI files name : str Name of workflow omp_nthreads : int Maximum number of threads an individual process may use output_dir : str Directory in which to save derivatives output_spaces : OrderedDict Ordered dictionary where keys are TemplateFlow ID strings (e.g., ``MNI152Lin``, ``MNI152NLin6Asym``, ``MNI152NLin2009cAsym``, or ``fsLR``) strings designating nonstandard references (e.g., ``T1w`` or ``anat``, ``sbref``, ``run``, etc.), or paths pointing to custom templates organized in a TemplateFlow-like structure. Values of the dictionary aggregate modifiers (e.g., the value for the key ``MNI152Lin`` could be ``{'resolution': 2}`` if one wants the resampling to be done on the 2mm resolution version of the selected template). reportlets_dir : str Directory in which to save reportlets regressors_all_comps Return all CompCor component time series instead of the top fraction regressors_fd_th Criterion for flagging framewise displacement outliers regressors_dvars_th Criterion for flagging DVARS outliers skull_strip_fixed_seed : bool Do not use a random seed for skull-stripping - will ensure run-to-run replicability when used with --omp-nthreads 1 skull_strip_template : tuple Name of target template for brain extraction with ANTs' ``antsBrainExtraction``, and corresponding dictionary of output-space modifiers. subject_id : str List of subject labels t2s_coreg : bool For multi-echo EPI, use the calculated T2*-map for T2*-driven coregistration task_id : str or None Task ID of BOLD series to preprocess, or ``None`` to preprocess all use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. use_syn : bool **Experimental**: Enable ANTs SyN-based susceptibility distortion correction (SDC). If fieldmaps are present and enabled, this is not run, by default. Inputs subjects_dir FreeSurfer SUBJECTS_DIR """ from .bold.resampling import NONSTANDARD_REFERENCES if name in ('single_subject_wf', 'single_subject_fmripreptest_wf'): # for documentation purposes subject_data = { 't1w': ['/completely/made/up/path/sub-01_T1w.nii.gz'], 'bold': ['/completely/made/up/path/sub-01_task-nback_bold.nii.gz'] } else: subject_data = collect_data(layout, subject_id, task_id, echo_idx)[0] # Make sure we always go through these two checks if not anat_only and subject_data['bold'] == []: raise Exception("No BOLD images found for participant {} and task {}. " "All workflows require BOLD images.".format( subject_id, task_id if task_id else '<all>')) if not subject_data['t1w']: raise Exception("No T1w images found for participant {}. " "All workflows require T1w images.".format(subject_id)) workflow = Workflow(name=name) workflow.__desc__ = """ Results included in this manuscript come from preprocessing performed using *fMRIPrep* {fmriprep_ver} (@fmriprep1; @fmriprep2; RRID:SCR_016216), which is based on *Nipype* {nipype_ver} (@nipype1; @nipype2; RRID:SCR_002502). """.format(fmriprep_ver=__version__, nipype_ver=nipype_ver) workflow.__postdesc__ = """ Many internal operations of *fMRIPrep* use *Nilearn* {nilearn_ver} [@nilearn, RRID:SCR_001362], mostly within the functional processing workflow. For more details of the pipeline, see [the section corresponding to workflows in *fMRIPrep*'s documentation]\ (https://fmriprep.readthedocs.io/en/latest/workflows.html \ "FMRIPrep's documentation"). ### Copyright Waiver The above boilerplate text was automatically generated by fMRIPrep with the express intention that users should copy and paste this text into their manuscripts *unchanged*. It is released under the [CC0]\ (https://creativecommons.org/publicdomain/zero/1.0/) license. ### References """.format(nilearn_ver=nilearn_ver) # Filter out standard spaces to a separate dict std_spaces = OrderedDict([ (key, modifiers) for key, modifiers in output_spaces.items() if key not in NONSTANDARD_REFERENCES]) inputnode = pe.Node(niu.IdentityInterface(fields=['subjects_dir']), name='inputnode') bidssrc = pe.Node(BIDSDataGrabber(subject_data=subject_data, anat_only=anat_only), name='bidssrc') bids_info = pe.Node(BIDSInfo( bids_dir=layout.root, bids_validate=False), name='bids_info') summary = pe.Node(SubjectSummary( std_spaces=list(std_spaces.keys()), nstd_spaces=list(set(NONSTANDARD_REFERENCES).intersection(output_spaces.keys()))), name='summary', run_without_submitting=True) about = pe.Node(AboutSummary(version=__version__, command=' '.join(sys.argv)), name='about', run_without_submitting=True) ds_report_summary = pe.Node( DerivativesDataSink(base_directory=reportlets_dir, desc='summary', keep_dtype=True), name='ds_report_summary', run_without_submitting=True) ds_report_about = pe.Node( DerivativesDataSink(base_directory=reportlets_dir, desc='about', keep_dtype=True), name='ds_report_about', run_without_submitting=True) # Preprocessing of T1w (includes registration to MNI) anat_preproc_wf = init_anat_preproc_wf( bids_root=layout.root, debug=debug, freesurfer=freesurfer, hires=hires, longitudinal=longitudinal, name="anat_preproc_wf", num_t1w=len(subject_data['t1w']), omp_nthreads=omp_nthreads, output_dir=output_dir, output_spaces=std_spaces, reportlets_dir=reportlets_dir, skull_strip_fixed_seed=skull_strip_fixed_seed, skull_strip_template=skull_strip_template, ) workflow.connect([ (inputnode, anat_preproc_wf, [('subjects_dir', 'inputnode.subjects_dir')]), (bidssrc, bids_info, [(('t1w', fix_multi_T1w_source_name), 'in_file')]), (inputnode, summary, [('subjects_dir', 'subjects_dir')]), (bidssrc, summary, [('t1w', 't1w'), ('t2w', 't2w'), ('bold', 'bold')]), (bids_info, summary, [('subject', 'subject_id')]), (bids_info, anat_preproc_wf, [(('subject', _prefix), 'inputnode.subject_id')]), (bidssrc, anat_preproc_wf, [('t1w', 'inputnode.t1w'), ('t2w', 'inputnode.t2w'), ('roi', 'inputnode.roi'), ('flair', 'inputnode.flair')]), (bidssrc, ds_report_summary, [(('t1w', fix_multi_T1w_source_name), 'source_file')]), (summary, ds_report_summary, [('out_report', 'in_file')]), (bidssrc, ds_report_about, [(('t1w', fix_multi_T1w_source_name), 'source_file')]), (about, ds_report_about, [('out_report', 'in_file')]), ]) # Overwrite ``out_path_base`` of smriprep's DataSinks for node in workflow.list_node_names(): if node.split('.')[-1].startswith('ds_'): workflow.get_node(node).interface.out_path_base = 'fmriprep' if anat_only: return workflow for bold_file in subject_data['bold']: func_preproc_wf = init_func_preproc_wf( aroma_melodic_dim=aroma_melodic_dim, bold2t1w_dof=bold2t1w_dof, bold_file=bold_file, cifti_output=cifti_output, debug=debug, dummy_scans=dummy_scans, err_on_aroma_warn=err_on_aroma_warn, fmap_bspline=fmap_bspline, fmap_demean=fmap_demean, force_syn=force_syn, freesurfer=freesurfer, ignore=ignore, layout=layout, low_mem=low_mem, medial_surface_nan=medial_surface_nan, num_bold=len(subject_data['bold']), omp_nthreads=omp_nthreads, output_dir=output_dir, output_spaces=output_spaces, reportlets_dir=reportlets_dir, regressors_all_comps=regressors_all_comps, regressors_fd_th=regressors_fd_th, regressors_dvars_th=regressors_dvars_th, t2s_coreg=t2s_coreg, use_aroma=use_aroma, use_bbr=use_bbr, use_syn=use_syn, ) workflow.connect([ (anat_preproc_wf, func_preproc_wf, [(('outputnode.t1_preproc', _pop), 'inputnode.t1_preproc'), ('outputnode.t1_brain', 'inputnode.t1_brain'), ('outputnode.t1_mask', 'inputnode.t1_mask'), ('outputnode.t1_seg', 'inputnode.t1_seg'), ('outputnode.t1_aseg', 'inputnode.t1_aseg'), ('outputnode.t1_aparc', 'inputnode.t1_aparc'), ('outputnode.t1_tpms', 'inputnode.t1_tpms'), ('outputnode.template', 'inputnode.template'), ('outputnode.forward_transform', 'inputnode.anat2std_xfm'), ('outputnode.reverse_transform', 'inputnode.std2anat_xfm'), ('outputnode.joint_template', 'inputnode.joint_template'), ('outputnode.joint_forward_transform', 'inputnode.joint_anat2std_xfm'), ('outputnode.joint_reverse_transform', 'inputnode.joint_std2anat_xfm'), # Undefined if --no-freesurfer, but this is safe ('outputnode.subjects_dir', 'inputnode.subjects_dir'), ('outputnode.subject_id', 'inputnode.subject_id'), ('outputnode.t1_2_fsnative_forward_transform', 'inputnode.t1_2_fsnative_forward_transform'), ('outputnode.t1_2_fsnative_reverse_transform', 'inputnode.t1_2_fsnative_reverse_transform')]), ]) return workflow
def init_bold_grayords_wf(grayord_density, mem_gb, repetition_time, name='bold_grayords_wf'): """ Sample Grayordinates files onto the fsLR atlas. Outputs are in CIFTI2 format. Workflow Graph .. workflow:: :graph2use: colored :simple_form: yes from fmriprep.workflows.bold import init_bold_grayords_wf wf = init_bold_grayords_wf(mem_gb=0.1, grayord_density='91k') Parameters ---------- grayord_density : :obj:`str` Either `91k` or `170k`, representing the total of vertices or *grayordinates*. mem_gb : :obj:`float` Size of BOLD file in GB name : :obj:`str` Unique name for the subworkflow (default: ``'bold_grayords_wf'``) Inputs ------ bold_std : :obj:`str` List of BOLD conversions to standard spaces. spatial_reference :obj:`str` List of unique identifiers corresponding to the BOLD standard-conversions. subjects_dir : :obj:`str` FreeSurfer's subjects directory. surf_files : :obj:`str` List of BOLD files resampled on the fsaverage (ico7) surfaces. surf_refs : List of unique identifiers corresponding to the BOLD surface-conversions. Outputs ------- cifti_bold : :obj:`str` List of BOLD grayordinates files - (L)eft and (R)ight. cifti_variant : :obj:`str` Only ``'HCP Grayordinates'`` is currently supported. cifti_metadata : :obj:`str` Path of metadata files corresponding to ``cifti_bold``. cifti_density : :obj:`str` Density (i.e., either `91k` or `170k`) of ``cifti_bold``. """ import templateflow.api as tf from niworkflows.interfaces.cifti import GenerateCifti workflow = Workflow(name=name) workflow.__desc__ = """\ *Grayordinates* files [@hcppipelines] containing {density} samples were also generated using the highest-resolution ``fsaverage`` as intermediate standardized surface space. """.format(density=grayord_density) fslr_density, mni_density = ('32k', '2') if grayord_density == '91k' else ('59k', '1') inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_std', 'spatial_reference', 'subjects_dir', 'surf_files', 'surf_refs', ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'cifti_bold', 'cifti_variant', 'cifti_metadata', 'cifti_density', ]), name='outputnode') # extract out to BOLD base select_std = pe.Node(KeySelect(fields=['bold_std']), name='select_std', run_without_submitting=True, nohash=True) select_std.inputs.key = 'MNI152NLin6Asym_res-%s' % mni_density select_fs_surf = pe.Node(KeySelect(fields=['surf_files']), name='select_fs_surf', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) select_fs_surf.inputs.key = 'fsaverage' # Setup Workbench command. LR ordering for hemi can be assumed, as it is imposed # by the iterfield of the MapNode in the surface sampling workflow above. resample = pe.MapNode(wb.MetricResample(method='ADAP_BARY_AREA', area_metrics=True), name='resample', iterfield=[ 'in_file', 'out_file', 'new_sphere', 'new_area', 'current_sphere', 'current_area' ]) resample.inputs.current_sphere = [ str( tf.get('fsLR', space='fsaverage', suffix='sphere', hemi=hemi, density='164k')) for hemi in 'LR' ] resample.inputs.current_area = [ str( tf.get('fsLR', space='fsaverage', suffix='midthickness', hemi=hemi, density='164k')) for hemi in 'LR' ] resample.inputs.new_sphere = [ str( tf.get('fsLR', space=None, suffix='sphere', hemi=hemi, density=fslr_density)) for hemi in 'LR' ] resample.inputs.new_area = [ str( tf.get('fsLR', space=None, suffix='midthickness', hemi=hemi, density=fslr_density)) for hemi in 'LR' ] resample.inputs.out_file = [ 'space-fsLR_hemi-%s_den-%s_bold.gii' % (h, grayord_density) for h in 'LR' ] gen_cifti = pe.Node(GenerateCifti( volume_target='MNI152NLin6Asym', surface_target='fsLR', TR=repetition_time, surface_density=fslr_density, ), name="gen_cifti") workflow.connect([ (inputnode, gen_cifti, [('subjects_dir', 'subjects_dir')]), (inputnode, select_std, [('bold_std', 'bold_std'), ('spatial_reference', 'keys')]), (inputnode, select_fs_surf, [('surf_files', 'surf_files'), ('surf_refs', 'keys')]), (select_fs_surf, resample, [('surf_files', 'in_file')]), (select_std, gen_cifti, [('bold_std', 'bold_file')]), (resample, gen_cifti, [('out_file', 'surface_bolds')]), (gen_cifti, outputnode, [('out_file', 'cifti_bold'), ('variant', 'cifti_variant'), ('out_metadata', 'cifti_metadata'), ('density', 'cifti_density')]), ]) return workflow
def individual_reports(settings, name='ReportsWorkflow'): """ Encapsulates nodes writing plots .. workflow:: from mriqc.workflows.functional import individual_reports wf = individual_reports(settings={'output_dir': 'out'}) """ from ..interfaces import PlotMosaic, PlotSpikes from ..reports import individual_html verbose = settings.get('verbose_reports', False) biggest_file_gb = settings.get("biggest_file_size_gb", 1) pages = 5 extra_pages = int(verbose) * 4 workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_iqms', 'in_ras', 'hmc_epi', 'epi_mean', 'brainmask', 'hmc_fd', 'fd_thres', 'epi_parc', 'in_dvars', 'in_stddev', 'outliers', 'in_spikes', 'in_fft', 'mni_report', 'ica_report' ]), name='inputnode') # Set FD threshold inputnode.inputs.fd_thres = settings.get('fd_thres', 0.2) spmask = pe.Node(niu.Function(input_names=['in_file', 'in_mask'], output_names=['out_file', 'out_plot'], function=spikes_mask), name='SpikesMask', mem_gb=biggest_file_gb * 3.5) spikes_bg = pe.Node(Spikes(no_zscore=True, detrend=False), name='SpikesFinderBgMask', mem_gb=biggest_file_gb * 2.5) bigplot = pe.Node(FMRISummary(), name='BigPlot', mem_gb=biggest_file_gb * 3.5) workflow.connect([ (inputnode, spikes_bg, [('in_ras', 'in_file')]), (inputnode, spmask, [('in_ras', 'in_file')]), (inputnode, bigplot, [('hmc_epi', 'in_func'), ('brainmask', 'in_mask'), ('hmc_fd', 'fd'), ('fd_thres', 'fd_thres'), ('in_dvars', 'dvars'), ('epi_parc', 'in_segm'), ('outliers', 'outliers')]), (spikes_bg, bigplot, [('out_tsz', 'in_spikes_bg')]), (spmask, spikes_bg, [('out_file', 'in_mask')]), ]) mosaic_mean = pe.Node(PlotMosaic(out_file='plot_func_mean_mosaic1.svg', cmap='Greys_r'), name='PlotMosaicMean') mosaic_stddev = pe.Node(PlotMosaic( out_file='plot_func_stddev_mosaic2_stddev.svg', cmap='viridis'), name='PlotMosaicSD') mplots = pe.Node( niu.Merge(pages + extra_pages + int(settings.get('fft_spikes_detector', False)) + int(settings.get('ica', False))), name='MergePlots') rnode = pe.Node(niu.Function(input_names=['in_iqms', 'in_plots'], output_names=['out_file'], function=individual_html), name='GenerateReport') # Link images that should be reported dsplots = pe.Node(nio.DataSink(base_directory=str(settings['output_dir']), parameterization=False), name='dsplots', run_without_submitting=True) workflow.connect([ (inputnode, rnode, [('in_iqms', 'in_iqms')]), (inputnode, mosaic_mean, [('epi_mean', 'in_file')]), (inputnode, mosaic_stddev, [('in_stddev', 'in_file')]), (mosaic_mean, mplots, [('out_file', 'in1')]), (mosaic_stddev, mplots, [('out_file', 'in2')]), (bigplot, mplots, [('out_file', 'in3')]), (mplots, rnode, [('out', 'in_plots')]), (rnode, dsplots, [('out_file', '@html_report')]), ]) if settings.get('fft_spikes_detector', False): mosaic_spikes = pe.Node(PlotSpikes(out_file='plot_spikes.svg', cmap='viridis', title='High-Frequency spikes'), name='PlotSpikes') workflow.connect([(inputnode, mosaic_spikes, [('in_ras', 'in_file'), ('in_spikes', 'in_spikes'), ('in_fft', 'in_fft')]), (mosaic_spikes, mplots, [('out_file', 'in4')])]) if settings.get('ica', False): page_number = 4 if settings.get('fft_spikes_detector', False): page_number += 1 workflow.connect([(inputnode, mplots, [('ica_report', 'in%d' % page_number)])]) if not verbose: return workflow mosaic_zoom = pe.Node(PlotMosaic(out_file='plot_anat_mosaic1_zoomed.svg', cmap='Greys_r'), name='PlotMosaicZoomed') mosaic_noise = pe.Node(PlotMosaic(out_file='plot_anat_mosaic2_noise.svg', only_noise=True, cmap='viridis_r'), name='PlotMosaicNoise') # Verbose-reporting goes here from ..interfaces.viz import PlotContours plot_bmask = pe.Node(PlotContours(display_mode='z', levels=[.5], colors=['r'], cut_coords=10, out_file='bmask'), name='PlotBrainmask') workflow.connect([ (inputnode, plot_bmask, [('epi_mean', 'in_file'), ('brainmask', 'in_contours')]), (inputnode, mosaic_zoom, [('epi_mean', 'in_file'), ('brainmask', 'bbox_mask_file')]), (inputnode, mosaic_noise, [('epi_mean', 'in_file')]), (mosaic_zoom, mplots, [('out_file', 'in%d' % (pages + 1))]), (mosaic_noise, mplots, [('out_file', 'in%d' % (pages + 2))]), (plot_bmask, mplots, [('out_file', 'in%d' % (pages + 3))]), (inputnode, mplots, [('mni_report', 'in%d' % (pages + 4))]), ]) return workflow
def init_bold_stc_wf(metadata, name="bold_stc_wf"): """ Create a workflow for :abbr:`STC (slice-timing correction)`. This workflow performs :abbr:`STC (slice-timing correction)` over the input :abbr:`BOLD (blood-oxygen-level dependent)` image. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from fprodents.workflows.bold.stc import init_bold_stc_wf wf = init_bold_stc_wf( metadata={"RepetitionTime": 2.0, "SliceTiming": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]}, ) Parameters ---------- metadata : :obj:`dict` BIDS metadata for BOLD file name : :obj:`str` Name of workflow (default: ``bold_stc_wf``) Inputs ------ bold_file BOLD series NIfTI file skip_vols Number of non-steady-state volumes detected at beginning of ``bold_file`` Outputs ------- stc_file Slice-timing corrected BOLD series NIfTI file """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.interfaces.header import CopyXForm workflow = Workflow(name=name) workflow.__desc__ = """\ BOLD runs were slice-time corrected using `3dTshift` from AFNI {afni_ver} [@afni, RRID:SCR_005927]. """.format( afni_ver="".join(["%02d" % v for v in afni.Info().version() or []]) ) inputnode = pe.Node( niu.IdentityInterface(fields=["bold_file", "skip_vols"]), name="inputnode" ) outputnode = pe.Node(niu.IdentityInterface(fields=["stc_file"]), name="outputnode") LOGGER.log(25, "Slice-timing correction will be included.") # It would be good to fingerprint memory use of afni.TShift slice_timing_correction = pe.Node( afni.TShift( outputtype="NIFTI_GZ", tr="{}s".format(metadata["RepetitionTime"]), slice_timing=metadata["SliceTiming"], slice_encoding_direction=metadata.get("SliceEncodingDirection", "k"), ), name="slice_timing_correction", ) copy_xform = pe.Node(CopyXForm(), name="copy_xform", mem_gb=0.1) # fmt:off workflow.connect([ (inputnode, slice_timing_correction, [('bold_file', 'in_file'), ('skip_vols', 'ignore')]), (slice_timing_correction, copy_xform, [('out_file', 'in_file')]), (inputnode, copy_xform, [('bold_file', 'hdr_file')]), (copy_xform, outputnode, [('out_file', 'stc_file')]), ]) # fmt:on return workflow
def hmc_afni(settings, name='fMRI_HMC_afni', st_correct=False, despike=False, deoblique=False, start_idx=None, stop_idx=None): """ A :abbr:`HMC (head motion correction)` workflow for functional scans .. workflow:: from mriqc.workflows.functional import hmc_afni wf = hmc_afni({'biggest_file_size_gb': 1}) """ biggest_file_gb = settings.get("biggest_file_size_gb", 1) workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_file', 'fd_radius', 'start_idx', 'stop_idx']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_fd']), name='outputnode') if (start_idx is not None) or (stop_idx is not None): drop_trs = pe.Node(afni.Calc(expr='a', outputtype='NIFTI_GZ'), name='drop_trs') workflow.connect([ (inputnode, drop_trs, [('in_file', 'in_file_a'), ('start_idx', 'start_idx'), ('stop_idx', 'stop_idx')]), ]) else: drop_trs = pe.Node(niu.IdentityInterface(fields=['out_file']), name='drop_trs') workflow.connect([ (inputnode, drop_trs, [('in_file', 'out_file')]), ]) gen_ref = pe.Node(nwr.EstimateReferenceImage(mc_method="AFNI"), name="gen_ref") # calculate hmc parameters hmc = pe.Node(afni.Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'), name='motion_correct', mem_gb=biggest_file_gb * 2.5) # Compute the frame-wise displacement fdnode = pe.Node(nac.FramewiseDisplacement(normalize=False, parameter_source="AFNI"), name='ComputeFD') workflow.connect([ (inputnode, fdnode, [('fd_radius', 'radius')]), (gen_ref, hmc, [('ref_image', 'basefile')]), (hmc, outputnode, [('out_file', 'out_file')]), (hmc, fdnode, [('oned_file', 'in_file')]), (fdnode, outputnode, [('out_file', 'out_fd')]), ]) # Slice timing correction, despiking, and deoblique st_corr = pe.Node(afni.TShift(outputtype='NIFTI_GZ'), name='TimeShifts') deoblique_node = pe.Node(afni.Refit(deoblique=True), name='deoblique') despike_node = pe.Node(afni.Despike(outputtype='NIFTI_GZ'), name='despike') if st_correct and despike and deoblique: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, despike_node, [('out_file', 'in_file')]), (despike_node, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) elif st_correct and despike: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, despike_node, [('out_file', 'in_file')]), (despike_node, gen_ref, [('out_file', 'in_file')]), (despike_node, hmc, [('out_file', 'in_file')]), ]) elif st_correct and deoblique: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) elif st_correct: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, gen_ref, [('out_file', 'in_file')]), (st_corr, hmc, [('out_file', 'in_file')]), ]) elif despike and deoblique: workflow.connect([ (drop_trs, despike_node, [('out_file', 'in_file')]), (despike_node, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) elif despike: workflow.connect([ (drop_trs, despike_node, [('out_file', 'in_file')]), (despike_node, gen_ref, [('out_file', 'in_file')]), (despike_node, hmc, [('out_file', 'in_file')]), ]) elif deoblique: workflow.connect([ (drop_trs, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) else: workflow.connect([ (drop_trs, gen_ref, [('out_file', 'in_file')]), (drop_trs, hmc, [('out_file', 'in_file')]), ]) return workflow
def create_getmask_flow(name='getmask', dilate_mask=True): """Registers a source file to freesurfer space and create a brain mask in source space Requires fsl tools for initializing registration Parameters ---------- name : string name of workflow dilate_mask : boolean indicates whether to dilate mask or not Example ------- >>> getmask = create_getmask_flow() >>> getmask.inputs.inputspec.source_file = 'mean.nii' >>> getmask.inputs.inputspec.subject_id = 's1' >>> getmask.inputs.inputspec.subjects_dir = '.' >>> getmask.inputs.inputspec.contrast_type = 't2' Inputs:: inputspec.source_file : reference image for mask generation inputspec.subject_id : freesurfer subject id inputspec.subjects_dir : freesurfer subjects directory inputspec.contrast_type : MR contrast of reference image Outputs:: outputspec.mask_file : binary mask file in reference image space outputspec.reg_file : registration file that maps reference image to freesurfer space outputspec.reg_cost : cost of registration (useful for detecting misalignment) """ """ Initialize the workflow """ getmask = pe.Workflow(name=name) """ Define the inputs to the workflow. """ inputnode = pe.Node(niu.IdentityInterface(fields=['source_file', 'subject_id', 'subjects_dir', 'contrast_type']), name='inputspec') """ Define all the nodes of the workflow: fssource: used to retrieve aseg.mgz threshold : binarize aseg register : coregister source file to freesurfer space voltransform: convert binarized aparc+aseg to source file space """ fssource = pe.Node(nio.FreeSurferSource(), name = 'fssource') threshold = pe.Node(fs.Binarize(min=0.5, out_type='nii'), name='threshold') register = pe.MapNode(fs.BBRegister(init='fsl'), iterfield=['source_file'], name='register') voltransform = pe.MapNode(fs.ApplyVolTransform(inverse=True), iterfield=['source_file', 'reg_file'], name='transform') """ Connect the nodes """ def get_aparc_aseg(files): for name in files: if 'aparc+aseg' in name: return name raise ValueError('aparc+aseg.mgz not found') getmask.connect([ (inputnode, fssource, [('subject_id','subject_id'), ('subjects_dir','subjects_dir')]), (inputnode, register, [('source_file', 'source_file'), ('subject_id', 'subject_id'), ('subjects_dir', 'subjects_dir'), ('contrast_type', 'contrast_type')]), (inputnode, voltransform, [('subjects_dir', 'subjects_dir'), ('source_file', 'source_file')]), (fssource, threshold, [(('aparc_aseg', get_aparc_aseg), 'in_file')]), (register, voltransform, [('out_reg_file','reg_file')]), (threshold, voltransform, [('binary_file','target_file')]) ]) """ Add remaining nodes and connections dilate : dilate the transformed file in source space threshold2 : binarize transformed file """ threshold2 = pe.MapNode(fs.Binarize(min=0.5, out_type='nii'), iterfield=['in_file'], name='threshold2') if dilate_mask: dilate = pe.MapNode(fsl.maths.DilateImage(operation='max'), iterfield=['in_file'], name='dilate') getmask.connect([ (voltransform, dilate, [('transformed_file', 'in_file')]), (dilate, threshold2, [('out_file', 'in_file')]), ]) else: getmask.connect([ (voltransform, threshold2, [('transformed_file', 'in_file')]) ]) """ Setup an outputnode that defines relevant inputs of the workflow. """ outputnode = pe.Node(niu.IdentityInterface(fields=["mask_file", "reg_file", "reg_cost" ]), name="outputspec") getmask.connect([ (register, outputnode, [("out_reg_file", "reg_file")]), (register, outputnode, [("min_cost_file", "reg_cost")]), (threshold2, outputnode, [("binary_file", "mask_file")]), ]) return getmask
def create_wf_collect_transforms(map_node, name='create_wf_collect_transforms'): """ DOCSTRINGS Parameters ---------- name : string, optional Name of the workflow. Returns ------- collect_transforms_wf : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: inputspec.transform_file : string (nifti file) Output matrix of FSL-based functional to anatomical registration inputspec.reference_file : string (nifti file) File of skull-stripped anatomical brain to be used in affine conversion inputspec.source_file : string (nifti file) Should match the input of the apply warp (in_file) unless you are applying the warp to a 4-d file, in which case this file should be a mean_functional file Workflow Outputs:: outputspec.itk_transform : string (nifti file) Converted affine transform in ITK format usable with ANTS """ collect_transforms_wf = pe.Workflow(name=name) inputspec = pe.Node(util.IdentityInterface(fields=['warp_file', 'linear_initial', 'linear_affine', 'linear_rigid', \ 'fsl_to_itk_affine']), name='inputspec') # converts FSL-format .mat affine xfm into ANTS-format .txt # .mat affine comes from Func->Anat registration if map_node == 0: collect_transforms = pe.Node(util.Merge(5), name='collect_transforms') elif map_node == 1: collect_transforms = pe.MapNode(util.Merge(5), name='collect_transforms_mapnode', iterfield=['in5']) outputspec = pe.Node( util.IdentityInterface(fields=['transformation_series']), name='outputspec') # Field file from anatomical nonlinear registration collect_transforms_wf.connect(inputspec, 'warp_file', collect_transforms, 'in1') # affine transformation from anatomical registration collect_transforms_wf.connect(inputspec, 'linear_affine', collect_transforms, 'in2') # rigid transformation from anatomical registration collect_transforms_wf.connect(inputspec, 'linear_rigid', collect_transforms, 'in3') # initial transformation from anatomical registration collect_transforms_wf.connect(inputspec, 'linear_initial', collect_transforms, 'in4') # Premat from Func->Anat linear reg and bbreg (if bbreg is enabled) collect_transforms_wf.connect(inputspec, 'fsl_to_itk_affine', collect_transforms, 'in5') collect_transforms_wf.connect(collect_transforms, 'out', outputspec, 'transformation_series') return collect_transforms_wf
def create_get_stats_flow(name='getstats', withreg=False): """Retrieves stats from labels Parameters ---------- name : string name of workflow withreg : boolean indicates whether to register source to label Example ------- Inputs:: inputspec.source_file : reference image for mask generation inputspec.label_file : label file from which to get ROIs (optionally with registration) inputspec.reg_file : bbreg file (assumes reg from source to label inputspec.inverse : boolean whether to invert the registration inputspec.subjects_dir : freesurfer subjects directory Outputs:: outputspec.stats_file : stats file """ """ Initialize the workflow """ getstats = pe.Workflow(name=name) """ Define the inputs to the workflow. """ if withreg: inputnode = pe.Node(niu.IdentityInterface(fields=['source_file', 'label_file', 'reg_file', 'subjects_dir']), name='inputspec') else: inputnode = pe.Node(niu.IdentityInterface(fields=['source_file', 'label_file']), name='inputspec') statnode = pe.MapNode(fs.SegStats(), iterfield=['segmentation_file','in_file'], name='segstats') """ Convert between source and label spaces if registration info is provided """ if withreg: voltransform = pe.MapNode(fs.ApplyVolTransform(inverse=True), iterfield=['source_file', 'reg_file'], name='transform') getstats.connect(inputnode, 'reg_file', voltransform, 'reg_file') getstats.connect(inputnode, 'source_file', voltransform, 'source_file') getstats.connect(inputnode, 'label_file', voltransform, 'target_file') getstats.connect(inputnode, 'subjects_dir', voltransform, 'subjects_dir') def switch_labels(inverse, transform_output, source_file, label_file): if inverse: return transform_output, source_file else: return label_file, transform_output chooser = pe.MapNode(niu.Function(input_names = ['inverse', 'transform_output', 'source_file', 'label_file'], output_names = ['label_file', 'source_file'], function=switch_labels), iterfield=['transform_output','source_file'], name='chooser') getstats.connect(inputnode,'source_file', chooser, 'source_file') getstats.connect(inputnode,'label_file', chooser, 'label_file') getstats.connect(inputnode,'inverse', chooser, 'inverse') getstats.connect(voltransform, 'transformed_file', chooser, 'transform_output') getstats.connect(chooser, 'label_file', statnode, 'segmentation_file') getstats.connect(chooser, 'source_file', statnode, 'in_file') else: getstats.connect(inputnode, 'label_file', statnode, 'segmentation_file') getstats.connect(inputnode, 'source_file', statnode, 'in_file') """ Setup an outputnode that defines relevant inputs of the workflow. """ outputnode = pe.Node(niu.IdentityInterface(fields=["stats_file" ]), name="outputspec") getstats.connect([ (statnode, outputnode, [("summary_file", "stats_file")]), ]) return getstats
def create_register_func_to_anat(name='register_func_to_anat'): """ Registers a functional scan in native space to anatomical space using a linear transform and does not include bbregister. Parameters ---------- name : string, optional Name of the workflow. Returns ------- create_register_func_to_anat : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: inputspec.func : string (nifti file) Input functional scan to be registered to anatomical space inputspec.anat : string (nifti file) Corresponding anatomical scan of subject inputspec.interp : string Type of interpolation to use ('trilinear' or 'nearestneighbour' or 'sinc') Workflow Outputs:: outputspec.func_to_anat_linear_xfm_nobbreg : string (mat file) Affine transformation from functional to anatomical native space outputspec.anat_func_nobbreg : string (nifti file) Functional scan registered to anatomical space """ register_func_to_anat = pe.Workflow(name=name) inputspec = pe.Node( util.IdentityInterface(fields=['func', 'anat', 'interp']), name='inputspec') outputspec = pe.Node( util.IdentityInterface(fields=[ 'func_to_anat_linear_xfm_nobbreg', #'func_to_mni_linear_xfm', #'mni_to_func_linear_xfm', #'anat_wm_edge', 'anat_func_nobbreg' ]), name='outputspec') linear_reg = pe.Node(interface=fsl.FLIRT(), name='linear_func_to_anat') linear_reg.inputs.cost = 'corratio' linear_reg.inputs.dof = 6 register_func_to_anat.connect(inputspec, 'func', linear_reg, 'in_file') register_func_to_anat.connect(inputspec, 'anat', linear_reg, 'reference') register_func_to_anat.connect(inputspec, 'interp', linear_reg, 'interp') register_func_to_anat.connect(linear_reg, 'out_matrix_file', outputspec, 'func_to_anat_linear_xfm_nobbreg') register_func_to_anat.connect(linear_reg, 'out_file', outputspec, 'anat_func_nobbreg') return register_func_to_anat
def create_tessellation_flow(name='tessellate', out_format='stl'): """Tessellates the input subject's aseg.mgz volume and returns the surfaces for each region in stereolithic (.stl) format Example ------- >>> from nipype.workflows.smri.freesurfer import create_tessellation_flow >>> tessflow = create_tessellation_flow() >>> tessflow.inputs.inputspec.subject_id = 'subj1' >>> tessflow.inputs.inputspec.subjects_dir = '.' >>> tessflow.inputs.inputspec.lookup_file = 'FreeSurferColorLUT.txt' # doctest: +SKIP >>> tessflow.run() # doctest: +SKIP Inputs:: inputspec.subject_id : freesurfer subject id inputspec.subjects_dir : freesurfer subjects directory inputspec.lookup_file : lookup file from freesurfer directory Outputs:: outputspec.meshes : output region meshes in (by default) stereolithographic (.stl) format """ """ Initialize the workflow """ tessflow = pe.Workflow(name=name) """ Define the inputs to the workflow. """ inputnode = pe.Node(niu.IdentityInterface(fields=['subject_id', 'subjects_dir', 'lookup_file']), name='inputspec') """ Define all the nodes of the workflow: fssource: used to retrieve aseg.mgz mri_convert : converts aseg.mgz to aseg.nii tessellate : tessellates regions in aseg.mgz surfconvert : converts regions to stereolithographic (.stl) format smoother: smooths the tessellated regions """ fssource = pe.Node(nio.FreeSurferSource(), name = 'fssource') volconvert = pe.Node(fs.MRIConvert(out_type='nii'), name = 'volconvert') tessellate = pe.MapNode(fs.MRIMarchingCubes(), iterfield=['label_value','out_file'], name='tessellate') surfconvert = pe.MapNode(fs.MRIsConvert(out_datatype=out_format), iterfield=['in_file'], name='surfconvert') smoother = pe.MapNode(fs.SmoothTessellation(), iterfield=['in_file'], name='smoother') smoother.inputs.curvature_averaging_iterations = 1 smoother.inputs.smoothing_iterations = 1 region_list_from_volume_interface = Function(input_names=["in_file"], output_names=["region_list"], function=region_list_from_volume) id_list_from_lookup_table_interface = Function(input_names=["lookup_file", "region_list"], output_names=["id_list"], function=id_list_from_lookup_table) region_list_from_volume_node = pe.Node(interface=region_list_from_volume_interface, name='region_list_from_volume_node') id_list_from_lookup_table_node = pe.Node(interface=id_list_from_lookup_table_interface, name='id_list_from_lookup_table_node') """ Connect the nodes """ tessflow.connect([ (inputnode, fssource, [('subject_id','subject_id'), ('subjects_dir','subjects_dir')]), (fssource, volconvert, [('aseg', 'in_file')]), (volconvert, region_list_from_volume_node, [('out_file', 'in_file')]), (region_list_from_volume_node, tessellate, [('region_list', 'label_value')]), (region_list_from_volume_node, id_list_from_lookup_table_node, [('region_list', 'region_list')]), (inputnode, id_list_from_lookup_table_node, [('lookup_file', 'lookup_file')]), (id_list_from_lookup_table_node, tessellate, [('id_list', 'out_file')]), (fssource, tessellate, [('aseg', 'in_file')]), (tessellate, surfconvert, [('surface','in_file')]), (surfconvert, smoother, [('converted','in_file')]), ]) """ Setup an outputnode that defines relevant inputs of the workflow. """ outputnode = pe.Node(niu.IdentityInterface(fields=["meshes"]), name="outputspec") tessflow.connect([ (smoother, outputnode, [("surface", "meshes")]), ]) return tessflow
def create_wf_calculate_ants_warp(name='create_wf_calculate_ants_warp', mult_input=0): ''' Calculates the nonlinear ANTS registration transform. This workflow employs the antsRegistration tool: http://stnava.github.io/ANTs/ Parameters ---------- name : string, optional Name of the workflow. Returns ------- calc_ants_warp_wf : nipype.pipeline.engine.Workflow Notes ----- Some of the inputs listed below are lists or lists of lists. This is because antsRegistration can perform multiple stages of calculations depending on how the user configures their registration. For example, if one wants to employ a different metric (with different parameters) at each stage, the lists would be configured like this: warp_wf.inputs.inputspec.transforms = ['Rigid','Affine','SyN'] warp_wf.inputs.inputspec.transform_parameters = [[0.1],[0.1],[0.1,3,0]] ..where each element in the first list is a metric to be used at each stage, 'Rigid' being for stage 1, 'Affine' for stage 2, etc. The lists within the list for transform_parameters would then correspond to each stage's metric, with [0.1] applying to 'Rigid' and 'Affine' (stages 1 and 2), and [0.1,3,0] applying to 'SyN' of stage 3. In some cases, when a parameter is not needed for a stage, 'None' must be entered in its place if there are other parameters for other stages. Workflow Inputs:: inputspec.anatomical_brain : string (nifti file) File of brain to be normalized (registered) inputspec.reference_brain : string (nifti file) Target brain file to normalize to inputspec.dimension : integer Dimension of the image (default: 3) inputspec.use_histogram_matching : boolean Histogram match the images before registration inputspec.winsorize_lower_quantile : float Winsorize data based on quantiles (lower range) inputspec.winsorize_higher_quantile : float Winsorize data based on quantiles (higher range) inputspec.metric : list of strings Image metric(s) to be used at each stage inputspec.metric_weight : list of floats Modulate the per-stage weighting of the corresponding metric inputspec.radius_or_number_of_bins : list of integers Number of bins in each stage for the MI and Mattes metric, the radius for other metrics inputspec.sampling_strategy : list of strings Sampling strategy (or strategies) to use for the metrics {None, Regular, or Random} inputspec.sampling_percentage : list of floats Defines the sampling strategy {float value, or None} inputspec.number_of_iterations : list of lists of integers Determines the convergence inputspec.convergence_threshold : list of floats Threshold compared to the slope of the line fitted in convergence inputspec.convergence_window_size : list of integers Window size of convergence calculations inputspec.transforms : list of strings Selection of transform options. See antsRegistration documentation for a full list of options and their descriptions inputspec.transform_parameters : list of lists of floats Fine-tuning for the different transform options inputspec.shrink_factors : list of lists of integers Specify the shrink factor for the virtual domain (typically the fixed image) at each level inputspec.smoothing_sigmas : list of lists of floats Specify the sigma of gaussian smoothing at each level Workflow Outputs:: outputspec.warp_field : string (nifti file) Output warp field of registration outputspec.inverse_warp_field : string (nifti file) Inverse of the warp field of the registration outputspec.ants_affine_xfm : string (.mat file) The affine matrix of the registration outputspec.ants_inverse_affine_xfm : string (.mat file) The affine matrix of the reverse registration outputspec.composite_transform : string (nifti file) The combined transform including the warp field and rigid & affine linear warps outputspec.normalized_output_brain : string (nifti file) Template-registered version of input brain Registration Procedure: 1. Calculates a nonlinear anatomical-to-template registration. Workflow Graph: .. image:: :width: 500 Detailed Workflow Graph: .. image:: :width: 500 ''' import nipype.interfaces.ants as ants from nipype.interfaces.utility import Function from CPAC.registration.utils import seperate_warps_list, \ combine_inputs_into_list, \ hardcoded_reg calc_ants_warp_wf = pe.Workflow(name=name) inputspec = pe.Node(util.IdentityInterface(fields=[ 'anatomical_brain', 'reference_brain', 'dimension', 'use_histogram_matching', 'winsorize_lower_quantile', 'winsorize_upper_quantile', 'metric', 'metric_weight', 'radius_or_number_of_bins', 'sampling_strategy', 'sampling_percentage', 'number_of_iterations', 'convergence_threshold', 'convergence_window_size', 'transforms', 'transform_parameters', 'shrink_factors', 'smoothing_sigmas', 'write_composite_transform', 'anatomical_skull', 'reference_skull' ]), name='inputspec') #,'wait']),name='inputspec') # use ANTS to warp the masked anatomical image to a template image ''' calculate_ants_warp = pe.Node(interface=ants.Registration(), name='calculate_ants_warp') calculate_ants_warp.inputs.output_warped_image = True calculate_ants_warp.inputs.initial_moving_transform_com = 0 ''' calculate_ants_warp = pe.Node(interface=util.Function( input_names=[ 'anatomical_brain', 'reference_brain', 'anatomical_skull', 'reference_skull', 'wait' ], output_names=['warp_list', 'warped_image'], function=hardcoded_reg), name='calc_ants_warp') select_forward_initial = pe.Node(util.Function( input_names=['warp_list', 'selection'], output_names=['selected_warp'], function=seperate_warps_list), name='select_forward_initial') select_forward_initial.inputs.selection = "Initial" select_forward_rigid = pe.Node(util.Function( input_names=['warp_list', 'selection'], output_names=['selected_warp'], function=seperate_warps_list), name='select_forward_rigid') select_forward_rigid.inputs.selection = "Rigid" select_forward_affine = pe.Node(util.Function( input_names=['warp_list', 'selection'], output_names=['selected_warp'], function=seperate_warps_list), name='select_forward_affine') select_forward_affine.inputs.selection = "Affine" select_forward_warp = pe.Node(util.Function( input_names=['warp_list', 'selection'], output_names=['selected_warp'], function=seperate_warps_list), name='select_forward_warp') select_forward_warp.inputs.selection = "3Warp" select_inverse_warp = pe.Node(util.Function( input_names=['warp_list', 'selection'], output_names=['selected_warp'], function=seperate_warps_list), name='select_inverse_warp') select_inverse_warp.inputs.selection = "Inverse" outputspec = pe.Node(util.IdentityInterface(fields=[ 'ants_initial_xfm', 'ants_rigid_xfm', 'ants_affine_xfm', 'warp_field', 'inverse_warp_field', 'composite_transform', 'wait', 'normalized_output_brain' ]), name='outputspec') # connections from inputspec if mult_input == 1: ''' combine_inputs = pe.Node(util.Function(input_names=['input1', 'input2', 'input3'], output_names=['inputs_list'], function=combine_inputs_into_list), name='ants_reg_combine_inputs') combine_refs = pe.Node(util.Function(input_names=['input1', 'input2', 'input3'], output_names=['inputs_list'], function=combine_inputs_into_list), name='ants_reg_combine_refs') ''' calc_ants_warp_wf.connect(inputspec, 'anatomical_brain', calculate_ants_warp, 'anatomical_brain') calc_ants_warp_wf.connect(inputspec, 'anatomical_skull', calculate_ants_warp, 'anatomical_skull') calc_ants_warp_wf.connect(inputspec, 'reference_brain', calculate_ants_warp, 'reference_brain') calc_ants_warp_wf.connect(inputspec, 'reference_skull', calculate_ants_warp, 'reference_skull') #calc_ants_warp_wf.connect(inputspec, 'wait', # calculate_ants_warp, 'wait') ''' calc_ants_warp_wf.connect(inputspec, 'anatomical_brain', combine_inputs, 'input1') calc_ants_warp_wf.connect(inputspec, 'anatomical_brain', combine_inputs, 'input2') calc_ants_warp_wf.connect(inputspec, 'anatomical_skull', combine_inputs, 'input3') calc_ants_warp_wf.connect(combine_inputs, 'inputs_list', calculate_ants_warp, 'moving_image') calc_ants_warp_wf.connect(inputspec, 'reference_brain', combine_refs, 'input1') calc_ants_warp_wf.connect(inputspec, 'reference_brain', combine_refs, 'input2') calc_ants_warp_wf.connect(inputspec, 'reference_skull', combine_refs, 'input3') calc_ants_warp_wf.connect(combine_refs, 'inputs_list', calculate_ants_warp, 'fixed_image') ''' else: ''' calc_ants_warp_wf.connect(inputspec, 'anatomical_brain', calculate_ants_warp, 'moving_image') calc_ants_warp_wf.connect(inputspec, 'reference_brain', calculate_ants_warp, 'fixed_image') ''' calc_ants_warp_wf.connect(inputspec, 'anatomical_brain', calculate_ants_warp, 'anatomical_brain') calc_ants_warp_wf.connect(inputspec, 'anatomical_brain', calculate_ants_warp, 'anatomical_skull') calc_ants_warp_wf.connect(inputspec, 'reference_brain', calculate_ants_warp, 'reference_brain') calc_ants_warp_wf.connect(inputspec, 'reference_brain', calculate_ants_warp, 'reference_skull') #calc_ants_warp_wf.connect(inputspec, 'wait', # calculate_ants_warp, 'wait') calc_ants_warp_wf.connect(inputspec, 'dimension', calculate_ants_warp, 'dimension') calc_ants_warp_wf.connect(inputspec, 'use_histogram_matching', calculate_ants_warp, 'use_histogram_matching') calc_ants_warp_wf.connect(inputspec, 'winsorize_lower_quantile', calculate_ants_warp, 'winsorize_lower_quantile') calc_ants_warp_wf.connect(inputspec, 'winsorize_upper_quantile', calculate_ants_warp, 'winsorize_upper_quantile') calc_ants_warp_wf.connect(inputspec, 'metric', calculate_ants_warp, 'metric') calc_ants_warp_wf.connect(inputspec, 'metric_weight', calculate_ants_warp, 'metric_weight') calc_ants_warp_wf.connect(inputspec, 'radius_or_number_of_bins', calculate_ants_warp, 'radius_or_number_of_bins') calc_ants_warp_wf.connect(inputspec, 'sampling_strategy', calculate_ants_warp, 'sampling_strategy') calc_ants_warp_wf.connect(inputspec, 'sampling_percentage', calculate_ants_warp, 'sampling_percentage') calc_ants_warp_wf.connect(inputspec, 'number_of_iterations', calculate_ants_warp, 'number_of_iterations') calc_ants_warp_wf.connect(inputspec, 'convergence_threshold', calculate_ants_warp, 'convergence_threshold') calc_ants_warp_wf.connect(inputspec, 'convergence_window_size', calculate_ants_warp, 'convergence_window_size') calc_ants_warp_wf.connect(inputspec, 'transforms', calculate_ants_warp, 'transforms') calc_ants_warp_wf.connect(inputspec, 'transform_parameters', calculate_ants_warp, 'transform_parameters') calc_ants_warp_wf.connect(inputspec, 'shrink_factors', calculate_ants_warp, 'shrink_factors') calc_ants_warp_wf.connect(inputspec, 'smoothing_sigmas', calculate_ants_warp, 'smoothing_sigmas') calc_ants_warp_wf.connect(inputspec, 'write_composite_transform', calculate_ants_warp, 'write_composite_transform') # inter-workflow connections calc_ants_warp_wf.connect(calculate_ants_warp, 'warp_list', select_forward_initial, 'warp_list') calc_ants_warp_wf.connect(calculate_ants_warp, 'warp_list', select_forward_rigid, 'warp_list') calc_ants_warp_wf.connect(calculate_ants_warp, 'warp_list', select_forward_affine, 'warp_list') calc_ants_warp_wf.connect(calculate_ants_warp, 'warp_list', select_forward_warp, 'warp_list') calc_ants_warp_wf.connect(calculate_ants_warp, 'warp_list', select_inverse_warp, 'warp_list') # connections to outputspec calc_ants_warp_wf.connect(select_forward_initial, 'selected_warp', outputspec, 'ants_initial_xfm') calc_ants_warp_wf.connect(select_forward_rigid, 'selected_warp', outputspec, 'ants_rigid_xfm') calc_ants_warp_wf.connect(select_forward_affine, 'selected_warp', outputspec, 'ants_affine_xfm') calc_ants_warp_wf.connect(select_forward_warp, 'selected_warp', outputspec, 'warp_field') calc_ants_warp_wf.connect(select_inverse_warp, 'selected_warp', outputspec, 'inverse_warp_field') calc_ants_warp_wf.connect(calculate_ants_warp, 'warped_image', outputspec, 'normalized_output_brain') # calc_ants_warp_wf.connect(inputspec, 'wait', # outputspec, 'wait') return calc_ants_warp_wf
def anat_qc_workflow(dataset, settings, mod='T1w', name='anatMRIQC'): """ One-subject-one-session-one-run pipeline to extract the NR-IQMs from anatomical images .. workflow:: import os.path as op from mriqc.workflows.anatomical import anat_qc_workflow datadir = op.abspath('data') wf = anat_qc_workflow([op.join(datadir, 'sub-001/anat/sub-001_T1w.nii.gz')], settings={'bids_dir': datadir, 'output_dir': op.abspath('out'), 'ants_nthreads': 1}) """ workflow = pe.Workflow(name=name + mod) WFLOGGER.info( 'Building anatomical MRI QC workflow, datasets list: %s', sorted([d.replace(settings['bids_dir'] + '/', '') for d in dataset])) # Define workflow, inputs and outputs # 0. Get data inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') inputnode.iterables = [('in_file', dataset)] outputnode = pe.Node(niu.IdentityInterface(fields=['out_json']), name='outputnode') meta = pe.Node(ReadSidecarJSON(), name='metadata') # 1. Reorient anatomical image to_ras = pe.Node(ConformImage(), name='conform') # 2. Skull-stripping (afni) asw = skullstrip_wf(n4_nthreads=settings.get('ants_nthreads', 1)) # 3. Head mask hmsk = headmsk_wf() # 4. Spatial Normalization, using ANTs norm = spatial_normalization(settings) # 5. Air mask (with and without artifacts) amw = airmsk_wf() # 6. Brain tissue segmentation segment = pe.Node(fsl.FAST(segments=True, out_basename='segment', img_type=int(mod[1])), name='segmentation', estimated_memory_gb=3) # 7. Compute IQMs iqmswf = compute_iqms(settings, modality=mod) # Reports repwf = individual_reports(settings) # Upload metrics upldwf = upload_wf(settings) # Connect all nodes workflow.connect([ (inputnode, to_ras, [('in_file', 'in_file')]), (inputnode, meta, [('in_file', 'in_file')]), (meta, iqmswf, [('subject_id', 'inputnode.subject_id'), ('session_id', 'inputnode.session_id'), ('acq_id', 'inputnode.acq_id'), ('rec_id', 'inputnode.rec_id'), ('run_id', 'inputnode.run_id')]), (to_ras, asw, [('out_file', 'inputnode.in_file')]), (asw, segment, [('outputnode.out_file', 'in_files')]), (asw, hmsk, [('outputnode.bias_corrected', 'inputnode.in_file')]), (segment, hmsk, [('tissue_class_map', 'inputnode.in_segm')]), (asw, norm, [('outputnode.bias_corrected', 'inputnode.moving_image'), ('outputnode.out_mask', 'inputnode.moving_mask')]), (norm, amw, [('outputnode.inverse_composite_transform', 'inputnode.inverse_composite_transform')]), (norm, iqmswf, [('outputnode.inverse_composite_transform', 'inputnode.inverse_composite_transform')]), (norm, repwf, ([('outputnode.out_report', 'inputnode.mni_report')])), (to_ras, amw, [('out_file', 'inputnode.in_file')]), (asw, amw, [('outputnode.out_mask', 'inputnode.in_mask')]), (hmsk, amw, [('outputnode.out_file', 'inputnode.head_mask')]), (to_ras, iqmswf, [('out_file', 'inputnode.orig')]), (asw, iqmswf, [('outputnode.bias_corrected', 'inputnode.inu_corrected'), ('outputnode.bias_image', 'inputnode.in_inu'), ('outputnode.out_mask', 'inputnode.brainmask')]), (amw, iqmswf, [('outputnode.out_file', 'inputnode.airmask'), ('outputnode.artifact_msk', 'inputnode.artmask')]), (segment, iqmswf, [('tissue_class_map', 'inputnode.segmentation'), ('partial_volume_files', 'inputnode.pvms')]), (meta, iqmswf, [('out_dict', 'inputnode.metadata')]), (hmsk, iqmswf, [('outputnode.out_file', 'inputnode.headmask')]), (to_ras, repwf, [('out_file', 'inputnode.orig')]), (asw, repwf, [('outputnode.bias_corrected', 'inputnode.inu_corrected'), ('outputnode.out_mask', 'inputnode.brainmask')]), (hmsk, repwf, [('outputnode.out_file', 'inputnode.headmask')]), (amw, repwf, [('outputnode.out_file', 'inputnode.airmask'), ('outputnode.artifact_msk', 'inputnode.artmask')]), (segment, repwf, [('tissue_class_map', 'inputnode.segmentation')]), (iqmswf, repwf, [('outputnode.out_noisefit', 'inputnode.noisefit')]), (iqmswf, repwf, [('outputnode.out_file', 'inputnode.in_iqms')]), (iqmswf, upldwf, [('outputnode.out_file', 'inputnode.in_iqms')]), (iqmswf, outputnode, [('outputnode.out_file', 'out_json')]) ]) return workflow