def init_anat_preproc_wf(skull_strip_ants, output_spaces, template, debug, freesurfer, longitudinal, omp_nthreads, hires, reportlets_dir, output_dir, name='anat_preproc_wf'): """T1w images preprocessing pipeline""" workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface(fields=['t1w', 't2w', 'subjects_dir', 'subject_id']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['t1_preproc', 't1_brain', 't1_mask', 't1_seg', 't1_tpms', 't1_2_mni', 't1_2_mni_forward_transform', 't1_2_mni_reverse_transform', 'mni_mask', 'mni_seg', 'mni_tpms', 'subjects_dir', 'subject_id', 'fs_2_t1_transform', 'surfaces']), name='outputnode') # 0. Reorient T1w image(s) to RAS and resample to common voxel space t1_conform = pe.Node(ConformSeries(), name='t1_conform') # 1. Align and merge if several T1w images are provided t1_merge = pe.Node( # StructuralReference is fs.RobustTemplate if > 1 volume, copying otherwise StructuralReference(auto_detect_sensitivity=True, initial_timepoint=1, # For deterministic behavior intensity_scaling=True, # 7-DOF (rigid + intensity) subsample_threshold=200, ), name='t1_merge') # 1.5 Reorient template to RAS, if needed (mri_robust_template sets LIA) t1_reorient = pe.Node(ConformSeries(), name='t1_reorient') # 2. T1 Bias Field Correction # Bias field correction is handled in skull strip workflows. # 3. Skull-stripping skullstrip_wf = init_skullstrip_afni_wf(name='skullstrip_afni_wf') if skull_strip_ants: skullstrip_wf = init_skullstrip_ants_wf(name='skullstrip_ants_wf', debug=debug, omp_nthreads=omp_nthreads) # 4. Segmentation t1_seg = pe.Node(FASTRPT(generate_report=True, segments=True, no_bias=True, probability_maps=True), name='t1_seg', mem_gb=3) # 5. Spatial normalization (T1w to MNI registration) t1_2_mni = pe.Node( RobustMNINormalizationRPT( float=True, generate_report=True, num_threads=omp_nthreads, flavor='testing' if debug else 'precise', ), name='t1_2_mni', n_procs=omp_nthreads ) # Resample the brain mask and the tissue probability maps into mni space mni_mask = pe.Node( ants.ApplyTransforms(dimension=3, default_value=0, float=True, interpolation='NearestNeighbor'), name='mni_mask' ) mni_seg = pe.Node( ants.ApplyTransforms(dimension=3, default_value=0, float=True, interpolation='NearestNeighbor'), name='mni_seg' ) mni_tpms = pe.MapNode( ants.ApplyTransforms(dimension=3, default_value=0, float=True, interpolation='Linear'), iterfield=['input_image'], name='mni_tpms' ) def set_threads(in_list, maximum): return min(len(in_list), maximum) def len_above_thresh(in_list, threshold, longitudinal): if longitudinal: return False return len(in_list) > threshold workflow.connect([ (inputnode, t1_conform, [('t1w', 't1w_list')]), (t1_conform, t1_merge, [ ('t1w_list', 'in_files'), (('t1w_list', set_threads, omp_nthreads), 'num_threads'), (('t1w_list', len_above_thresh, 2, longitudinal), 'fixed_timepoint'), (('t1w_list', len_above_thresh, 2, longitudinal), 'no_iteration'), (('t1w_list', add_suffix, '_template'), 'out_file')]), (t1_merge, t1_reorient, [('out_file', 't1w_list')]), (t1_reorient, skullstrip_wf, [('t1w_list', 'inputnode.in_file')]), (skullstrip_wf, t1_seg, [('outputnode.out_file', 'in_files')]), (skullstrip_wf, outputnode, [('outputnode.bias_corrected', 't1_preproc'), ('outputnode.out_file', 't1_brain'), ('outputnode.out_mask', 't1_mask')]), (t1_seg, outputnode, [('tissue_class_map', 't1_seg'), ('probability_maps', 't1_tpms')]), ]) if 'template' in output_spaces: template_str = nid.TEMPLATE_MAP[template] ref_img = op.join(nid.get_dataset(template_str), '1mm_T1.nii.gz') t1_2_mni.inputs.template = template_str mni_mask.inputs.reference_image = ref_img mni_seg.inputs.reference_image = ref_img mni_tpms.inputs.reference_image = ref_img workflow.connect([ (skullstrip_wf, t1_2_mni, [('outputnode.bias_corrected', 'moving_image')]), (skullstrip_wf, t1_2_mni, [('outputnode.out_mask', 'moving_mask')]), (skullstrip_wf, mni_mask, [('outputnode.out_mask', 'input_image')]), (t1_2_mni, mni_mask, [('composite_transform', 'transforms')]), (t1_seg, mni_seg, [('tissue_class_map', 'input_image')]), (t1_2_mni, mni_seg, [('composite_transform', 'transforms')]), (t1_seg, mni_tpms, [('probability_maps', 'input_image')]), (t1_2_mni, mni_tpms, [('composite_transform', 'transforms')]), (t1_2_mni, outputnode, [ ('warped_image', 't1_2_mni'), ('composite_transform', 't1_2_mni_forward_transform'), ('inverse_composite_transform', 't1_2_mni_reverse_transform')]), (mni_mask, outputnode, [('output_image', 'mni_mask')]), (mni_seg, outputnode, [('output_image', 'mni_seg')]), (mni_tpms, outputnode, [('output_image', 'mni_tpms')]), ]) # 6. FreeSurfer reconstruction if freesurfer: surface_recon_wf = init_surface_recon_wf(name='surface_recon_wf', omp_nthreads=omp_nthreads, hires=hires) workflow.connect([ (inputnode, surface_recon_wf, [ ('t2w', 'inputnode.t2w'), ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id')]), (t1_reorient, surface_recon_wf, [('t1w_list', 'inputnode.t1w')]), (skullstrip_wf, surface_recon_wf, [ ('outputnode.out_file', 'inputnode.skullstripped_t1')]), (surface_recon_wf, outputnode, [ ('outputnode.subjects_dir', 'subjects_dir'), ('outputnode.subject_id', 'subject_id'), ('outputnode.fs_2_t1_transform', 'fs_2_t1_transform'), ('outputnode.surfaces', 'surfaces')]), ]) anat_reports_wf = init_anat_reports_wf( reportlets_dir=reportlets_dir, skull_strip_ants=skull_strip_ants, output_spaces=output_spaces, template=template, freesurfer=freesurfer) workflow.connect([ (inputnode, anat_reports_wf, [ (('t1w', fix_multi_T1w_source_name), 'inputnode.source_file')]), (t1_conform, anat_reports_wf, [('out_report', 'inputnode.t1_conform_report')]), (t1_seg, anat_reports_wf, [('out_report', 'inputnode.t1_seg_report')]), ]) if skull_strip_ants: workflow.connect([ (skullstrip_wf, anat_reports_wf, [ ('outputnode.out_report', 'inputnode.t1_skull_strip_report')]) ]) if freesurfer: workflow.connect([ (surface_recon_wf, anat_reports_wf, [ ('outputnode.out_report', 'inputnode.recon_report')]) ]) if 'template' in output_spaces: workflow.connect([ (t1_2_mni, anat_reports_wf, [('out_report', 'inputnode.t1_2_mni_report')]), ]) anat_derivatives_wf = init_anat_derivatives_wf(output_dir=output_dir, output_spaces=output_spaces, template=template, freesurfer=freesurfer) workflow.connect([ (inputnode, anat_derivatives_wf, [ (('t1w', fix_multi_T1w_source_name), 'inputnode.source_file')]), (outputnode, anat_derivatives_wf, [ ('t1_preproc', 'inputnode.t1_preproc'), ('t1_mask', 'inputnode.t1_mask'), ('t1_seg', 'inputnode.t1_seg'), ('t1_tpms', 'inputnode.t1_tpms'), ('t1_2_mni_forward_transform', 'inputnode.t1_2_mni_forward_transform'), ('t1_2_mni', 'inputnode.t1_2_mni'), ('mni_mask', 'inputnode.mni_mask'), ('mni_seg', 'inputnode.mni_seg'), ('mni_tpms', 'inputnode.mni_tpms'), ('surfaces', 'inputnode.surfaces'), ]), ]) return workflow
def init_single_subject_wf(subject_id, task_id, name, ignore, debug, anat_only, omp_nthreads, skull_strip_ants, reportlets_dir, output_dir, bids_dir, freesurfer, output_spaces, template, hires, bold2t1w_dof, fmap_bspline, fmap_demean, use_syn, force_syn, output_grid_ref, use_aroma, ignore_aroma_err): """ The adaptable fMRI preprocessing workflow """ if name == 'single_subject_wf': # for documentation purposes subject_data = { 'func': ['/completely/made/up/path/sub-01_task-nback_bold.nii.gz'] } layout = None else: layout = BIDSLayout(bids_dir) subject_data = collect_bids_data(bids_dir, subject_id, task_id) if not anat_only and subject_data['func'] == []: 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 = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['subjects_dir']), name='inputnode') bidssrc = pe.Node(BIDSDataGrabber(subject_data=subject_data, anat_only=anat_only), name='bidssrc') # Preprocessing of T1w (includes registration to MNI) anat_preproc_wf = init_anat_preproc_wf(name="anat_preproc_wf", skull_strip_ants=skull_strip_ants, output_spaces=output_spaces, template=template, debug=debug, omp_nthreads=omp_nthreads, freesurfer=freesurfer, hires=hires, reportlets_dir=reportlets_dir, output_dir=output_dir) workflow.connect([ (inputnode, anat_preproc_wf, [('subjects_dir', 'inputnode.subjects_dir')]), (bidssrc, anat_preproc_wf, [('t1w', 'inputnode.t1w'), ('t2w', 'inputnode.t2w')]), ]) if anat_only: return workflow for bold_file in subject_data['func']: func_preproc_wf = init_func_preproc_wf( bold_file=bold_file, layout=layout, ignore=ignore, freesurfer=freesurfer, bold2t1w_dof=bold2t1w_dof, reportlets_dir=reportlets_dir, output_spaces=output_spaces, template=template, output_dir=output_dir, omp_nthreads=omp_nthreads, fmap_bspline=fmap_bspline, fmap_demean=fmap_demean, use_syn=use_syn, force_syn=force_syn, debug=debug, output_grid_ref=output_grid_ref, use_aroma=use_aroma, ignore_aroma_err=ignore_aroma_err) workflow.connect([(anat_preproc_wf, func_preproc_wf, [('outputnode.t1_preproc', 'inputnode.t1_preproc'), ('outputnode.t1_brain', 'inputnode.t1_brain'), ('outputnode.t1_mask', 'inputnode.t1_mask'), ('outputnode.t1_seg', 'inputnode.t1_seg'), ('outputnode.t1_tpms', 'inputnode.t1_tpms'), ('outputnode.t1_2_mni_forward_transform', 'inputnode.t1_2_mni_forward_transform'), ('outputnode.t1_2_mni_reverse_transform', 'inputnode.t1_2_mni_reverse_transform')])]) if freesurfer: workflow.connect([ (anat_preproc_wf, func_preproc_wf, [('outputnode.subjects_dir', 'inputnode.subjects_dir'), ('outputnode.subject_id', 'inputnode.subject_id'), ('outputnode.fs_2_t1_transform', 'inputnode.fs_2_t1_transform')]), ]) return workflow
def init_bold_stc_wf(metadata, name='bold_stc_wf'): """ This workflow performs :abbr:`STC (slice-timing correction)` over the input :abbr:`BOLD (blood-oxygen-level dependent)` image. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold 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 : dict BIDS metadata for BOLD file name : 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 """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface(fields=['bold_file', 'skip_vols']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['stc_file']), name='outputnode') LOGGER.info('Slice-timing correction will be included.') def create_custom_slice_timing_file_func(metadata): import os slice_timings = metadata["SliceTiming"] slice_timings_ms = [str(t) for t in slice_timings] out_file = "timings.1D" with open("timings.1D", "w") as fp: fp.write("\t".join(slice_timings_ms)) return os.path.abspath(out_file) create_custom_slice_timing_file = pe.Node( niu.Function(function=create_custom_slice_timing_file_func), name="create_custom_slice_timing_file", mem_gb=DEFAULT_MEMORY_MIN_GB) create_custom_slice_timing_file.inputs.metadata = metadata # 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"])), name='slice_timing_correction') def _prefix_at(x): return "@" + x workflow.connect([ (inputnode, slice_timing_correction, [('bold_file', 'in_file'), ('skip_vols', 'ignore')]), (create_custom_slice_timing_file, slice_timing_correction, [(('out', _prefix_at), 'tpattern')]), (slice_timing_correction, outputnode, [('out_file', 'stc_file')]), ]) return workflow
def init_bold_reg_wf(freesurfer, use_bbr, bold2t1w_dof, mem_gb, omp_nthreads, name='bold_reg_wf', use_compression=True, use_fieldwarp=False): """ This workflow registers the reference BOLD image to T1-space, using a boundary-based registration (BBR) cost function. If FreeSurfer-based preprocessing is enabled, the ``bbregister`` utility is used to align the BOLD images to the reconstructed subject, and the resulting transform is adjusted to target the T1 space. If FreeSurfer-based preprocessing is disabled, FSL FLIRT is used with the BBR cost function to directly target the T1 space. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_bold_reg_wf wf = init_bold_reg_wf(freesurfer=True, mem_gb=3, omp_nthreads=1, use_bbr=True, bold2t1w_dof=9) **Parameters** freesurfer : bool Enable FreeSurfer functional registration (bbregister) use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration 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_reg_wf``) use_compression : bool Save registered BOLD series as ``.nii.gz`` use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to T1 **Inputs** name_source BOLD series NIfTI file Used to recover original information lost during processing ref_bold_brain Reference image to which BOLD series is aligned If ``fieldwarp == True``, ``ref_bold_brain`` should be unwarped ref_bold_mask Skull-stripping mask of reference image t1_preproc Bias-corrected structural template image t1_brain Skull-stripped ``t1_preproc`` t1_mask Mask of the skull-stripped template image t1_seg Segmentation of preprocessed structural image, including gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF) t1_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). t1_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_split Individual 3D BOLD volumes, not motion corrected hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID t1_2_fsnative_reverse_transform LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w fieldwarp a :abbr:`DFM (displacements field map)` in ITK format **Outputs** itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) itk_t1_to_bold Affine transform from T1 space to BOLD space (ITK format) bold_t1 Motion-corrected BOLD series in T1 space bold_mask_t1 BOLD mask in T1 space bold_aseg_t1 FreeSurfer's ``aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). bold_aparc_t1 FreeSurfer's ``aparc+aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). out_report Reportlet visualizing quality of registration fallback Boolean indicating whether BBR was rejected (mri_coreg registration returned) **Subworkflows** * :py:func:`~fmriprep.workflows.util.init_bbreg_wf` * :py:func:`~fmriprep.workflows.util.init_fsl_bbr_wf` """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface( fields=['name_source', 'ref_bold_brain', 'ref_bold_mask', 't1_preproc', 't1_brain', 't1_mask', 't1_seg', 't1_aseg', 't1_aparc', 'bold_split', 'hmc_xforms', 'subjects_dir', 'subject_id', 't1_2_fsnative_reverse_transform', 'fieldwarp']), name='inputnode' ) outputnode = pe.Node( niu.IdentityInterface(fields=[ 'itk_bold_to_t1', 'itk_t1_to_bold', 'out_report', 'fallback', 'bold_t1', 'bold_mask_t1', 'bold_aseg_t1', 'bold_aparc_t1']), name='outputnode' ) if freesurfer: bbr_wf = init_bbreg_wf(use_bbr=use_bbr, bold2t1w_dof=bold2t1w_dof, omp_nthreads=omp_nthreads) else: bbr_wf = init_fsl_bbr_wf(use_bbr=use_bbr, bold2t1w_dof=bold2t1w_dof) workflow.connect([ (inputnode, bbr_wf, [ ('ref_bold_brain', 'inputnode.in_file'), ('t1_2_fsnative_reverse_transform', 'inputnode.t1_2_fsnative_reverse_transform'), ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('t1_seg', 'inputnode.t1_seg'), ('t1_brain', 'inputnode.t1_brain')]), (bbr_wf, outputnode, [('outputnode.itk_bold_to_t1', 'itk_bold_to_t1'), ('outputnode.itk_t1_to_bold', 'itk_t1_to_bold'), ('outputnode.out_report', 'out_report'), ('outputnode.fallback', 'fallback')]), ]) gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB mask_t1w_tfm = pe.Node( ApplyTransforms(interpolation='MultiLabel', float=True), name='mask_t1w_tfm', mem_gb=0.1 ) workflow.connect([ (inputnode, gen_ref, [('ref_bold_brain', 'moving_image'), ('t1_brain', 'fixed_image'), ('t1_mask', 'fov_mask')]), (inputnode, mask_t1w_tfm, [('ref_bold_mask', 'input_image')]), (gen_ref, mask_t1w_tfm, [('out_file', 'reference_image')]), (bbr_wf, mask_t1w_tfm, [('outputnode.itk_bold_to_t1', 'transforms')]), (mask_t1w_tfm, outputnode, [('output_image', 'bold_mask_t1')]), ]) if freesurfer: # Resample aseg and aparc in T1w space (no transforms needed) aseg_t1w_tfm = pe.Node( ApplyTransforms(interpolation='MultiLabel', transforms='identity', float=True), name='aseg_t1w_tfm', mem_gb=0.1) aparc_t1w_tfm = pe.Node( ApplyTransforms(interpolation='MultiLabel', transforms='identity', float=True), name='aparc_t1w_tfm', mem_gb=0.1) workflow.connect([ (inputnode, aseg_t1w_tfm, [('t1_aseg', 'input_image')]), (inputnode, aparc_t1w_tfm, [('t1_aparc', 'input_image')]), (gen_ref, aseg_t1w_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_t1w_tfm, [('out_file', 'reference_image')]), (aseg_t1w_tfm, outputnode, [('output_image', 'bold_aseg_t1')]), (aparc_t1w_tfm, outputnode, [('output_image', 'bold_aparc_t1')]), ]) # Merge transforms placing the head motion correction last nforms = 2 + int(use_fieldwarp) merge_xforms = pe.Node(niu.Merge(nforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nforms)]) ]) if use_fieldwarp: workflow.connect([ (inputnode, merge_xforms, [('fieldwarp', 'in2')]) ]) bold_to_t1w_transform = pe.Node( MultiApplyTransforms(interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_t1w_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) workflow.connect([ (bbr_wf, merge_xforms, [('outputnode.itk_bold_to_t1', 'in1')]), (merge_xforms, bold_to_t1w_transform, [('out', 'transforms')]), (inputnode, merge, [('name_source', 'header_source')]), (merge, outputnode, [('out_file', 'bold_t1')]), (inputnode, bold_to_t1w_transform, [('bold_split', 'input_image')]), (gen_ref, bold_to_t1w_transform, [('out_file', 'reference_image')]), (bold_to_t1w_transform, merge, [('out_files', 'in_files')]), ]) return workflow
def init_bold_confs_wf(bold_file_size_gb, use_aroma, ignore_aroma_err, metadata, name="bold_confs_wf"): """ This workflow calculates confounds for a BOLD series, and aggregates them into a :abbr:`TSV (tab-separated value)` file, for use as nuisance regressors in a :abbr:`GLM (general linear model)`. The following confounds are calculated, with column headings in parentheses: #. White matter / global signals (WhiteMatter, GlobalSignal) #. DVARS - standard, nonstandard, and voxel-wise standard variants (stdDVARS, non-stdDVARS, vx-wisestdDVARS) #. Framewise displacement, based on MCFLIRT motion parameters (FramewiseDisplacement) #. tCompCor #. aCompCor #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (CosineXX) #. Non-steady-state volumes (NonSteadyStateXX) #. MCFLIRT motion parameters, in mm and rad (X, Y, Z, RotX, RotY, RotZ) #. ICA-AROMA-identified noise components, if enabled (AROMAAggrCompXX) Prior to estimating aCompCor and tCompCor, non-steady-state volumes are censored and high-pass filtered using a :abbr:`DCT (discrete cosine transform)` basis. The cosine basis, as well as one regressor per censored volume, are included for convenience. .. workflow:: :graph2use: orig :simpleform: yes from fmriprep.workflows.confounds import init_bold_confs_wf wf = init_bold_confs_wf(bold_file_size_gb=1, use_aroma=True, ignore_aroma_err=True, metadata={}) Parameters bold_file_size_gb : float Size of BOLD file in GB use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series ignore_aroma_err : bool Do not fail on ICA-AROMA errors metadata : dict BIDS metadata for BOLD file Inputs bold_t1 BOLD image, resampled in T1w space movpar_file SPM-formatted motion parameters file t1_mask Mask of the skull-stripped template image t1_tpms List of tissue probability maps in T1w space bold_mask_t1 BOLD series mask in T1w space bold_mni BOLD series, resampled to template space bold_mask_mni BOLD series mask in template space Outputs confounds_file TSV of all aggregated confounds confounds_list List of calculated confounds for reporting acompcor_report Reportlet visualizing white-matter/CSF mask used for aCompCor tcompcor_report Reportlet visualizing ROI identified in tCompCor ica_aroma_report Reportlet visualizing MELODIC ICs, with ICA-AROMA signal/noise labels aroma_noise_ics CSV of noise components identified by ICA-AROMA melodic_mix FSL MELODIC mixing matrix nonaggr_denoised_file BOLD series with non-aggressive ICA-AROMA denoising applied """ inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_t1', 'movpar_file', 't1_mask', 't1_tpms', 'bold_mask_t1', 'bold_mni', 'bold_mask_mni' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'confounds_file', 'confounds_list', 'acompcor_report', 'tcompcor_report', 'ica_aroma_report', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file' ]), name='outputnode') # ICA-AROMA if use_aroma: ica_aroma_wf = init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=ignore_aroma_err) # DVARS dvars = pe.Node(nac.ComputeDVARS(save_all=True, remove_zerovariance=True), name="dvars", mem_gb=bold_file_size_gb * 3) # Frame displacement fdisp = pe.Node(nac.FramewiseDisplacement(parameter_source="SPM"), name="fdisp", mem_gb=bold_file_size_gb * 3) # CompCor non_steady_state = pe.Node(nac.NonSteadyStateDetector(), name='non_steady_state') tcompcor = pe.Node(TCompCorRPT(components_file='tcompcor.tsv', generate_report=True, pre_filter='cosine', save_pre_filter=True, percentile_threshold=.05), name="tcompcor", mem_gb=bold_file_size_gb * 3) acompcor = pe.Node(ACompCorRPT(components_file='acompcor.tsv', pre_filter='cosine', save_pre_filter=True, generate_report=True), name="acompcor", mem_gb=bold_file_size_gb * 3) csf_roi = pe.Node(TPM2ROI(erode_mm=0, mask_erode_mm=30), name='csf_roi') wm_roi = pe.Node(TPM2ROI(erode_mm=6, mask_erode_mm=10), name='wm_roi') merge_rois = pe.Node(niu.Merge(2), name='merge_rois', run_without_submitting=True, mem_gb=0.01) combine_rois = pe.Node(CombineROIs(), name='combine_rois') concat_rois = pe.Node(ConcatROIs(), name='concat_rois') # Global and segment regressors signals = pe.Node(SignalExtraction( detrend=True, class_labels=["WhiteMatter", "GlobalSignal"]), name="signals", mem_gb=bold_file_size_gb * 3) # Arrange confounds add_header = pe.Node( AddTSVHeader(columns=["X", "Y", "Z", "RotX", "RotY", "RotZ"]), name="add_header", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) # Set TR if present if 'RepetitionTime' in metadata: tcompcor.inputs.repetition_time = metadata['RepetitionTime'] acompcor.inputs.repetition_time = metadata['RepetitionTime'] def _pick_csf(files): return files[0] def _pick_wm(files): return files[2] workflow = pe.Workflow(name=name) workflow.connect([ # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('bold_t1', 'in_file'), ('bold_mask_t1', 'in_mask')]), (inputnode, fdisp, [('movpar_file', 'in_file')]), (inputnode, non_steady_state, [('bold_t1', 'in_file')]), (inputnode, tcompcor, [('bold_t1', 'realigned_file')]), (non_steady_state, tcompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (non_steady_state, acompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (inputnode, csf_roi, [(('t1_tpms', _pick_csf), 't1_tpm'), ('t1_mask', 't1_mask'), ('bold_mask_t1', 'bold_mask')]), (csf_roi, tcompcor, [('eroded_mask', 'mask_files')]), (inputnode, wm_roi, [(('t1_tpms', _pick_wm), 't1_tpm'), ('t1_mask', 't1_mask'), ('bold_mask_t1', 'bold_mask')]), (csf_roi, merge_rois, [('roi_file', 'in1')]), (wm_roi, merge_rois, [('roi_file', 'in2')]), (merge_rois, combine_rois, [('out', 'in_files')]), (inputnode, combine_rois, [('bold_t1', 'ref_header')]), # anatomical confound: aCompCor. (inputnode, acompcor, [('bold_t1', 'realigned_file')]), (combine_rois, acompcor, [('out_file', 'mask_files')]), (wm_roi, concat_rois, [('roi_file', 'in_file')]), (inputnode, concat_rois, [('bold_mask_t1', 'in_mask')]), (inputnode, concat_rois, [('bold_t1', 'ref_header')]), # anatomical confound: signal extraction (concat_rois, signals, [('out_file', 'label_files')]), (inputnode, signals, [('bold_t1', 'in_file')]), # connect the confound nodes to the concatenate node (inputnode, add_header, [('movpar_file', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (dvars, concat, [('out_all', 'dvars')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_header, concat, [('out_file', 'motion')]), (concat, outputnode, [('confounds_file', 'confounds_file'), ('confounds_list', 'confounds_list')]), (acompcor, outputnode, [('out_report', 'acompcor_report')]), (tcompcor, outputnode, [('out_report', 'tcompcor_report')]), ]) if use_aroma: workflow.connect([ (inputnode, ica_aroma_wf, [('bold_mni', 'inputnode.bold_mni'), ('bold_mask_mni', 'inputnode.bold_mask_mni'), ('movpar_file', 'inputnode.movpar_file')]), (ica_aroma_wf, concat, [('outputnode.aroma_confounds', 'aroma')]), (ica_aroma_wf, outputnode, [('outputnode.out_report', 'ica_aroma_report'), ('outputnode.aroma_noise_ics', 'aroma_noise_ics'), ('outputnode.melodic_mix', 'melodic_mix'), ('outputnode.nonaggr_denoised_file', 'nonaggr_denoised_file')]) ]) return workflow
def init_func_preproc_wf(bold_file, ignore, freesurfer, use_bbr, t2s_coreg, bold2t1w_dof, reportlets_dir, output_spaces, template, output_dir, omp_nthreads, fmap_bspline, fmap_demean, use_syn, force_syn, use_aroma, ignore_aroma_err, medial_surface_nan, debug, low_mem, output_grid_ref, layout=None): """ This workflow controls the functional preprocessing stages of FMRIPREP. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold import init_func_preproc_wf wf = init_func_preproc_wf('/completely/made/up/path/sub-01_task-nback_bold.nii.gz', omp_nthreads=1, ignore=[], freesurfer=True, reportlets_dir='.', output_dir='.', template='MNI152NLin2009cAsym', output_spaces=['T1w', 'fsnative', 'template', 'fsaverage5'], debug=False, use_bbr=True, t2s_coreg=False, bold2t1w_dof=9, fmap_bspline=True, fmap_demean=True, use_syn=True, force_syn=True, low_mem=False, output_grid_ref=None, medial_surface_nan=False, use_aroma=False, ignore_aroma_err=False) **Parameters** bold_file : str BOLD series NIfTI file ignore : list Preprocessing steps to skip (may include "slicetiming", "fieldmaps") freesurfer : bool Enable FreeSurfer functional registration (bbregister) and resampling BOLD series to FreeSurfer surface meshes. use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. t2s_coreg : bool Use multiple BOLD echos to create T2*-map for T2*-driven coregistration bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration reportlets_dir : str Directory in which to save reportlets output_spaces : list List of output spaces functional images are to be resampled to. Some parts of pipeline will only be instantiated for some output spaces. Valid spaces: - T1w - template - fsnative - fsaverage (or other pre-existing FreeSurfer templates) template : str Name of template targeted by `'template'` output space output_dir : str Directory in which to save derivatives omp_nthreads : int Maximum number of threads an individual process may use fmap_bspline : bool **Experimental**: Fit B-Spline field using least-squares fmap_demean : bool Demean voxel-shift map during unwarp use_syn : bool **Experimental**: Enable ANTs SyN-based susceptibility distortion correction (SDC). If fieldmaps are present and enabled, this is not run, by default. force_syn : bool **Temporary**: Always run SyN-based SDC use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series ignore_aroma_err : bool Do not fail on ICA-AROMA errors medial_surface_nan : bool Replace medial wall values with NaNs on functional GIFTI files debug : bool Enable debugging outputs low_mem : bool Write uncompressed .nii files in some cases to reduce memory usage output_grid_ref : str or None Path of custom reference image for normalization layout : BIDSLayout BIDSLayout structure to enable metadata retrieval **Inputs** bold_file BOLD series NIfTI file t1_preproc Bias-corrected structural template image t1_brain Skull-stripped ``t1_preproc`` t1_mask Mask of the skull-stripped template image t1_seg Segmentation of preprocessed structural image, including gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF) t1_tpms List of tissue probability maps in T1w space t1_2_mni_forward_transform ANTs-compatible affine-and-warp transform file t1_2_mni_reverse_transform ANTs-compatible affine-and-warp transform file (inverse) subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID t1_2_fsnative_forward_transform LTA-style affine matrix translating from T1w to FreeSurfer-conformed subject space t1_2_fsnative_reverse_transform 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_mni BOLD series, resampled to template space bold_mask_mni 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 **Subworkflows** * :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_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_mni_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` """ 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']) # For doc building purposes if layout is None or bold_file == 'bold_preprocesing': 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 = [{ 'type': 'phasediff', 'phasediff': 'sub-03/ses-2/fmap/sub-03_ses-2_run-1_phasediff.nii.gz', 'magnitude1': 'sub-03/ses-2/fmap/sub-03_ses-2_run-1_magnitude1.nii.gz', 'magnitude2': 'sub-03/ses-2/fmap/sub-03_ses-2_run-1_magnitude2.nii.gz', }] run_stc = True multiecho = False else: metadata = layout.get_metadata(ref_file) # Find fieldmaps. Options: (phase1|phase2|phasediff|epi|fieldmap|syn) fmaps = [] if 'fieldmaps' not in ignore: fmaps = layout.get_fieldmap(ref_file, return_list=True) for fmap in fmaps: fmap['metadata'] = layout.get_metadata(fmap[fmap['type']]) # Run SyN if forced or in the absence of fieldmap correction if force_syn or (use_syn and not fmaps): fmaps.append({'type': '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(bold_file) > 4 or "TooShort")) # Use T2* as target for ME-EPI in co-registration if t2s_coreg and not multiecho: LOGGER.warning( "No multiecho BOLD images found for T2* coregistration. " "Using standard EPI-T1 coregistration.") t2s_coreg = False # Build workflow workflow = pe.Workflow(name=wf_name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_file', 'subjects_dir', 'subject_id', 't1_preproc', 't1_brain', 't1_mask', 't1_seg', 't1_tpms', 't1_aseg', 't1_aparc', 't1_2_mni_forward_transform', 't1_2_mni_reverse_transform', 't1_2_fsnative_forward_transform', 't1_2_fsnative_reverse_transform' ]), name='inputnode') inputnode.inputs.bold_file = bold_file outputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_t1', 'bold_mask_t1', 'bold_aseg_t1', 'bold_aparc_t1', 'bold_mni', 'bold_mask_mni', 'confounds', 'surfaces', 't2s_map', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file' ]), 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( output_spaces=output_spaces, slice_timing=run_stc, registration='FreeSurfer' if freesurfer else 'FSL', registration_dof=bold2t1w_dof, pe_direction=metadata.get("PhaseEncodingDirection")), name='summary', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True) func_reports_wf = init_func_reports_wf(reportlets_dir=reportlets_dir, freesurfer=freesurfer, use_aroma=use_aroma, use_syn=use_syn, t2s_coreg=t2s_coreg) func_derivatives_wf = init_func_derivatives_wf(output_dir=output_dir, output_spaces=output_spaces, template=template, freesurfer=freesurfer, use_aroma=use_aroma) workflow.connect([ (inputnode, func_reports_wf, [('bold_file', 'inputnode.source_file')]), (inputnode, func_derivatives_wf, [('bold_file', 'inputnode.source_file')]), (outputnode, func_derivatives_wf, [ ('bold_t1', 'inputnode.bold_t1'), ('bold_aseg_t1', 'inputnode.bold_aseg_t1'), ('bold_aparc_t1', 'inputnode.bold_aparc_t1'), ('bold_mask_t1', 'inputnode.bold_mask_t1'), ('bold_mni', 'inputnode.bold_mni'), ('bold_mask_mni', 'inputnode.bold_mask_mni'), ('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'), ]), ]) # The first reference uses T2 contrast enhancement bold_reference_wf = init_bold_reference_wf(omp_nthreads=omp_nthreads, enhance_t2=True) # 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) # mean 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, use_fieldwarp=(fmaps is not None or use_syn)) # get confounds bold_confounds_wf = init_bold_confs_wf(mem_gb=mem_gb['largemem'], metadata=metadata, 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=(fmaps is not None or use_syn), name='bold_bold_trans_wf') # 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_stc_wf, boldbuffer, [('outputnode.stc_file', 'bold_file')]), (bold_reference_wf, bold_stc_wf, [('outputnode.bold_file', 'inputnode.bold_file'), ('outputnode.skip_vols', 'inputnode.skip_vols')]), ]) else: # bypass STC from original BOLD to the splitter through boldbuffer workflow.connect([ (bold_reference_wf, boldbuffer, [('outputnode.bold_file', 'bold_file')]), ]) # SDC (SUSCEPTIBILITY DISTORTION CORRECTION) or bypass ########################## bold_sdc_wf = init_sdc_wf(fmaps, metadata, omp_nthreads=omp_nthreads, debug=debug, fmap_demean=fmap_demean, fmap_bspline=fmap_bspline) bold_sdc_wf.inputs.inputnode.template = template if not fmaps: LOGGER.warning('SDC: no fieldmaps found or they were ignored (%s).', ref_file) elif fmaps[0]['type'] == 'syn': LOGGER.warning( 'SDC: no fieldmaps found or they were ignored. ' 'Using EXPERIMENTAL "fieldmap-less SyN" correction ' 'for dataset %s.', ref_file) else: LOGGER.log( 25, 'SDC: fieldmap estimation of type "%s" intended for %s found.', fmaps[0]['type'], ref_file) # MAIN WORKFLOW STRUCTURE ####################################################### workflow.connect([ # BOLD buffer has slice-time corrected if it was run, original otherwise (boldbuffer, bold_split, [('bold_file', 'in_file')]), # Generate early reference (inputnode, bold_reference_wf, [('bold_file', 'inputnode.bold_file')]), (bold_reference_wf, func_reports_wf, [('outputnode.validation_report', 'inputnode.validation_report')]), # EPI-T1 registration workflow ( inputnode, bold_reg_wf, [ ('bold_file', 'inputnode.name_source'), ('t1_preproc', 'inputnode.t1_preproc'), ('t1_brain', 'inputnode.t1_brain'), ('t1_mask', 'inputnode.t1_mask'), ('t1_seg', 'inputnode.t1_seg'), ('t1_aseg', 'inputnode.t1_aseg'), ('t1_aparc', 'inputnode.t1_aparc'), # Undefined if --no-freesurfer, but this is safe ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('t1_2_fsnative_reverse_transform', 'inputnode.t1_2_fsnative_reverse_transform') ]), (bold_split, bold_reg_wf, [('out_files', 'inputnode.bold_split')]), (bold_hmc_wf, bold_reg_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]), (bold_reg_wf, func_reports_wf, [ ('outputnode.out_report', 'inputnode.bold_reg_report'), ('outputnode.fallback', 'inputnode.bold_reg_fallback'), ]), (bold_reg_wf, outputnode, [('outputnode.bold_t1', 'bold_t1'), ('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, [('t1_brain', 'inputnode.t1_brain'), ('t1_2_mni_reverse_transform', 'inputnode.t1_2_mni_reverse_transform')]), (bold_reference_wf, bold_sdc_wf, [('outputnode.ref_image', 'inputnode.bold_ref'), ('outputnode.ref_image_brain', 'inputnode.bold_ref_brain'), ('outputnode.bold_mask', 'inputnode.bold_mask')]), (bold_sdc_wf, bold_reg_wf, [('outputnode.bold_ref_brain', 'inputnode.ref_bold_brain'), ('outputnode.bold_mask', 'inputnode.ref_bold_mask'), ('outputnode.out_warp', 'inputnode.fieldwarp')]), (bold_sdc_wf, bold_bold_trans_wf, [('outputnode.out_warp', 'inputnode.fieldwarp'), ('outputnode.bold_mask', 'inputnode.bold_mask')]), (bold_sdc_wf, summary, [('outputnode.method', 'distortion_correction') ]), # Connect bold_confounds_wf (inputnode, bold_confounds_wf, [('t1_tpms', 'inputnode.t1_tpms'), ('t1_mask', 'inputnode.t1_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_confounds_wf, func_reports_wf, [('outputnode.rois_report', 'inputnode.bold_rois_report')]), (bold_confounds_wf, outputnode, [ ('outputnode.confounds_file', 'confounds'), ]), # Connect bold_bold_trans_wf (inputnode, bold_bold_trans_wf, [('bold_file', 'inputnode.name_source') ]), (bold_split, bold_bold_trans_wf, [('out_files', 'inputnode.bold_split') ]), (bold_hmc_wf, bold_bold_trans_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]), (bold_bold_trans_wf, bold_confounds_wf, [('outputnode.bold', 'inputnode.bold'), ('outputnode.bold_mask', 'inputnode.bold_mask')]), # Summary (outputnode, summary, [('confounds', 'confounds_file')]), (summary, func_reports_wf, [('out_report', 'inputnode.summary_report') ]), ]) if fmaps: from ..fieldmap.unwarp import init_fmap_unwarp_report_wf sdc_type = fmaps[0]['type'] # Report on BOLD correction fmap_unwarp_report_wf = init_fmap_unwarp_report_wf(suffix='sdc_%s' % sdc_type) workflow.connect([ (inputnode, fmap_unwarp_report_wf, [('t1_seg', '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.bold_ref', 'inputnode.in_post')]), ]) if force_syn and sdc_type != 'syn': syn_unwarp_report_wf = init_fmap_unwarp_report_wf( suffix='forcedsyn', name='syn_unwarp_report_wf') workflow.connect([ (inputnode, syn_unwarp_report_wf, [('t1_seg', '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_bold_ref', 'inputnode.in_post')]), ]) # 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 = bold_file # if multiecho data, select first echo for hmc correction if multiecho: inputnode.iterables = ('bold_file', bold_file) me_first_echo = pe.JoinNode(interface=FirstEcho(te_list=tes), joinfield=['in_files', 'ref_imgs'], joinsource='inputnode', name='me_first_echo') workflow.connect([(bold_reference_wf, me_first_echo, [('outputnode.bold_file', 'in_files'), ('outputnode.raw_ref_image', 'ref_imgs')]), (me_first_echo, bold_hmc_wf, [('first_image', 'inputnode.bold_file'), ('first_ref_image', 'inputnode.raw_ref_image')])]) if t2s_coreg: # use a joinNode to gather all preprocessed echos join_split_echos = pe.JoinNode( niu.IdentityInterface(fields=['echo_files']), joinsource='inputnode', joinfield='echo_files', name='join_split_echos') # create a T2* map bold_t2s_wf = init_bold_t2s_wf(echo_times=tes, name='bold_t2s_wf', mem_gb=mem_gb['filesize'], omp_nthreads=omp_nthreads) subset_reg_reports = pe.JoinNode(niu.Select(index=0), name='subset_reg_reports', joinsource=inputnode, joinfield=['inlist']) subset_reg_fallbacks = pe.JoinNode(niu.Select(index=0), name='subset_reg_fallbacks', joinsource=inputnode, joinfield=['inlist']) first_echo = pe.Node(niu.IdentityInterface(fields=['first_echo']), name='first_echo') first_echo.inputs.first_echo = ref_file # remove duplicate registration reports workflow.disconnect([ (bold_reg_wf, func_reports_wf, [('outputnode.out_report', 'inputnode.bold_reg_report'), ('outputnode.fallback', 'inputnode.bold_reg_fallback')]), (bold_sdc_wf, bold_reg_wf, [('outputnode.out_warp', 'inputnode.fieldwarp'), ('outputnode.bold_ref_brain', 'inputnode.ref_bold_brain'), ('outputnode.bold_mask', 'inputnode.ref_bold_mask')]), ]) workflow.connect([ (first_echo, func_reports_wf, [('first_echo', 'inputnode.first_echo')]), (bold_split, join_split_echos, [('out_files', 'echo_files')]), (join_split_echos, bold_t2s_wf, [('echo_files', 'inputnode.echo_split')]), (bold_hmc_wf, bold_t2s_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]), (bold_t2s_wf, bold_reg_wf, [('outputnode.t2s_map', 'inputnode.ref_bold_brain'), ('outputnode.oc_mask', 'inputnode.ref_bold_mask')]), (bold_reg_wf, subset_reg_reports, [('outputnode.out_report', 'inlist')]), (bold_reg_wf, subset_reg_fallbacks, [('outputnode.fallback', 'inlist')]), (subset_reg_reports, func_reports_wf, [('out', 'inputnode.bold_reg_report')]), (subset_reg_fallbacks, func_reports_wf, [('out', 'inputnode.bold_reg_fallback')]), ]) else: workflow.connect([(bold_reference_wf, bold_hmc_wf, [ ('outputnode.raw_ref_image', 'inputnode.raw_ref_image'), ('outputnode.bold_file', 'inputnode.bold_file') ])]) # Map final BOLD mask into T1w space (if required) if 'T1w' 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_bold_trans_wf, boldmask_to_t1w, [('outputnode.bold_mask', 'input_image')]), (bold_reg_wf, boldmask_to_t1w, [('outputnode.bold_mask_t1', 'reference_image'), ('outputnode.itk_bold_to_t1', 'transforms')]), (boldmask_to_t1w, outputnode, [('output_image', 'bold_mask_t1')]), ]) if 'template' in output_spaces: # Apply transforms in 1 shot # Only use uncompressed output if AROMA is to be run bold_mni_trans_wf = init_bold_mni_trans_wf( template=template, mem_gb=mem_gb['resampled'], omp_nthreads=omp_nthreads, output_grid_ref=output_grid_ref, use_compression=not (low_mem and use_aroma), use_fieldwarp=fmaps is not None, name='bold_mni_trans_wf') workflow.connect([ (inputnode, bold_mni_trans_wf, [('bold_file', 'inputnode.name_source'), ('t1_2_mni_forward_transform', 'inputnode.t1_2_mni_forward_transform')]), (bold_split, bold_mni_trans_wf, [('out_files', 'inputnode.bold_split')]), (bold_hmc_wf, bold_mni_trans_wf, [('outputnode.xforms', 'inputnode.hmc_xforms')]), (bold_reg_wf, bold_mni_trans_wf, [('outputnode.itk_bold_to_t1', 'inputnode.itk_bold_to_t1')]), (bold_bold_trans_wf, bold_mni_trans_wf, [('outputnode.bold_mask', 'inputnode.bold_mask')]), (bold_sdc_wf, bold_mni_trans_wf, [('outputnode.out_warp', 'inputnode.fieldwarp')]), (bold_mni_trans_wf, outputnode, [('outputnode.bold_mni', 'bold_mni'), ('outputnode.bold_mask_mni', 'bold_mask_mni')]), ]) if use_aroma: # ICA-AROMA workflow """ ica_aroma_report Reportlet visualizing MELODIC ICs, with ICA-AROMA signal/noise labels aroma_noise_ics CSV of noise components identified by ICA-AROMA melodic_mix FSL MELODIC mixing matrix nonaggr_denoised_file BOLD series with non-aggressive ICA-AROMA denoising applied """ from .confounds import init_ica_aroma_wf from ...interfaces import JoinTSVColumns ica_aroma_wf = init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=ignore_aroma_err) join = pe.Node(JoinTSVColumns(), name='aroma_confounds') workflow.disconnect([ (bold_confounds_wf, outputnode, [ ('outputnode.confounds_file', 'confounds'), ]), ]) workflow.connect([ (bold_hmc_wf, ica_aroma_wf, [('outputnode.movpar_file', 'inputnode.movpar_file')]), (bold_mni_trans_wf, ica_aroma_wf, [('outputnode.bold_mask_mni', 'inputnode.bold_mask_mni'), ('outputnode.bold_mni', 'inputnode.bold_mni')]), (bold_confounds_wf, join, [('outputnode.confounds_file', 'in_file')]), (ica_aroma_wf, join, [('outputnode.aroma_confounds', 'join_file')]), (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')]), (ica_aroma_wf, func_reports_wf, [('outputnode.out_report', 'inputnode.ica_aroma_report')]), ]) # SURFACES ################################################################################## if freesurfer and any(space.startswith('fs') for space in output_spaces): LOGGER.log(25, 'Creating BOLD surface-sampling workflow.') bold_surf_wf = init_bold_surf_wf(mem_gb=mem_gb['resampled'], output_spaces=output_spaces, medial_surface_nan=medial_surface_nan, name='bold_surf_wf') workflow.connect([ (inputnode, bold_surf_wf, [('t1_preproc', 'inputnode.t1_preproc'), ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('t1_2_fsnative_forward_transform', 'inputnode.t1_2_fsnative_forward_transform')]), (bold_reg_wf, bold_surf_wf, [('outputnode.bold_t1', 'inputnode.source_file')]), (bold_surf_wf, outputnode, [('outputnode.surfaces', 'surfaces')]), ]) # REPORTING ############################################################ bold_bold_report_wf = init_bold_preproc_report_wf( mem_gb=mem_gb['resampled'], reportlets_dir=reportlets_dir) workflow.connect([ (inputnode, bold_bold_report_wf, [('bold_file', 'inputnode.name_source'), ('bold_file', 'inputnode.in_pre')]), # This should be after STC (bold_bold_trans_wf, bold_bold_report_wf, [('outputnode.bold', 'inputnode.in_post')]), ]) return workflow
def init_func_derivatives_wf(output_dir, output_spaces, template, freesurfer, use_aroma, name='func_derivatives_wf'): """ Set up a battery of datasinks to store derivatives in the right location """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'source_file', 'bold_t1', 'bold_mask_t1', 'bold_mni', 'bold_mask_mni', 'bold_aseg_t1', 'bold_aparc_t1', 'confounds', 'surfaces', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file' ]), name='inputnode') suffix_fmt = 'space-{}_{}'.format variant_suffix_fmt = 'space-{}_variant-{}_{}'.format ds_confounds = pe.Node(DerivativesDataSink(base_directory=output_dir, suffix='confounds'), name="ds_confounds", run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, ds_confounds, [('source_file', 'source_file'), ('confounds', 'in_file')]), ]) # Resample to T1w space if 'T1w' in output_spaces: ds_bold_t1 = pe.Node(DerivativesDataSink(base_directory=output_dir, suffix=suffix_fmt( 'T1w', 'preproc')), name='ds_bold_t1', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_bold_mask_t1 = pe.Node(DerivativesDataSink( base_directory=output_dir, suffix=suffix_fmt('T1w', 'brainmask')), name='ds_bold_mask_t1', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, ds_bold_t1, [('source_file', 'source_file'), ('bold_t1', 'in_file')]), (inputnode, ds_bold_mask_t1, [('source_file', 'source_file'), ('bold_mask_t1', 'in_file')]), ]) # Resample to template (default: MNI) if 'template' in output_spaces: ds_bold_mni = pe.Node(DerivativesDataSink(base_directory=output_dir, suffix=suffix_fmt( template, 'preproc')), name='ds_bold_mni', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_bold_mask_mni = pe.Node(DerivativesDataSink( base_directory=output_dir, suffix=suffix_fmt(template, 'brainmask')), name='ds_bold_mask_mni', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, ds_bold_mni, [('source_file', 'source_file'), ('bold_mni', 'in_file')]), (inputnode, ds_bold_mask_mni, [('source_file', 'source_file'), ('bold_mask_mni', 'in_file')]), ]) if freesurfer: ds_bold_aseg_t1 = pe.Node(DerivativesDataSink( base_directory=output_dir, suffix='space-T1w_label-aseg_roi'), name='ds_bold_aseg_t1', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_bold_aparc_t1 = pe.Node(DerivativesDataSink( base_directory=output_dir, suffix='space-T1w_label-aparcaseg_roi'), name='ds_bold_aparc_t1', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, ds_bold_aseg_t1, [('source_file', 'source_file'), ('bold_aseg_t1', 'in_file')]), (inputnode, ds_bold_aparc_t1, [('source_file', 'source_file'), ('bold_aparc_t1', 'in_file')]), ]) # fsaverage space if freesurfer and any(space.startswith('fs') for space in output_spaces): name_surfs = pe.MapNode(GiftiNameSource( pattern=r'(?P<LR>[lr])h.(?P<space>\w+).gii', template='space-{space}.{LR}.func'), iterfield='in_file', name='name_surfs', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True) ds_bold_surfs = pe.MapNode( DerivativesDataSink(base_directory=output_dir), iterfield=['in_file', 'suffix'], name='ds_bold_surfs', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, name_surfs, [('surfaces', 'in_file')]), (inputnode, ds_bold_surfs, [('source_file', 'source_file'), ('surfaces', 'in_file')]), (name_surfs, ds_bold_surfs, [('out_name', 'suffix')]), ]) if use_aroma: ds_aroma_noise_ics = pe.Node(DerivativesDataSink( base_directory=output_dir, suffix='AROMAnoiseICs'), name="ds_aroma_noise_ics", run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_melodic_mix = pe.Node(DerivativesDataSink(base_directory=output_dir, suffix='MELODICmix'), name="ds_melodic_mix", run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_aroma_mni = pe.Node(DerivativesDataSink( base_directory=output_dir, suffix=variant_suffix_fmt(template, 'smoothAROMAnonaggr', 'preproc')), name='ds_aroma_mni', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, ds_aroma_noise_ics, [('source_file', 'source_file'), ('aroma_noise_ics', 'in_file')]), (inputnode, ds_melodic_mix, [('source_file', 'source_file'), ('melodic_mix', 'in_file')]), (inputnode, ds_aroma_mni, [('source_file', 'source_file'), ('nonaggr_denoised_file', 'in_file')]), ]) return workflow
def init_bold_hmc_wf(mem_gb, omp_nthreads, name='bold_hmc_wf'): """ This workflow estimates the motion parameters to perform :abbr:`HMC (head motion correction)` over the input :abbr:`BOLD (blood-oxygen-level dependent)` image. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold import init_bold_hmc_wf wf = init_bold_hmc_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_hmc_wf``) **Inputs** bold_file BOLD series NIfTI file raw_ref_image Reference image to which BOLD series is motion corrected **Outputs** xforms ITKTransform file aligning each volume to ``ref_image`` movpar_file MCFLIRT motion parameters, normalized to SPM format (X, Y, Z, Rx, Ry, Rz) """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface(fields=['bold_file', 'raw_ref_image']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['xforms', 'movpar_file']), name='outputnode') # Head motion correction (hmc) mcflirt = pe.Node(fsl.MCFLIRT(save_mats=True, save_plots=True), name='mcflirt', mem_gb=mem_gb * 3) fsl2itk = pe.Node(MCFLIRT2ITK(), name='fsl2itk', mem_gb=0.05, n_procs=omp_nthreads) normalize_motion = pe.Node(NormalizeMotionParams(format='FSL'), name="normalize_motion", mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, mcflirt, [('raw_ref_image', 'ref_file'), ('bold_file', 'in_file')]), (inputnode, fsl2itk, [('raw_ref_image', 'in_source'), ('raw_ref_image', 'in_reference')]), (mcflirt, fsl2itk, [('mat_file', 'in_files')]), (mcflirt, normalize_motion, [('par_file', 'in_file')]), (fsl2itk, outputnode, [('out_file', 'xforms')]), (normalize_motion, outputnode, [('out_file', 'movpar_file')]), ]) return workflow
def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', omp_nthreads=1, enhance_t2=False): """ This workflow takes in a :abbr:`BOLD (blood-oxygen level-dependant)` :abbr:`fMRI (functional MRI)` average/summary (e.g. a reference image averaging non-steady-state timepoints), and sharpens the histogram with the application of the N4 algorithm for removing the :abbr:`INU (intensity non-uniformity)` bias field and calculates a signal mask. Steps of this workflow are: 1. Calculate a conservative mask using Nilearn's ``create_epi_mask``. 2. Run ANTs' ``N4BiasFieldCorrection`` on the input :abbr:`BOLD (blood-oxygen level-dependant)` average, using the mask generated in 1) instead of the internal Otsu thresholding. 3. Calculate a loose mask using FSL's ``bet``, with one mathematical morphology dilation of one iteration and a sphere of 6mm as structuring element. 4. Mask the :abbr:`INU (intensity non-uniformity)`-corrected image with the latest mask calculated in 3), then use AFNI's ``3dUnifize`` to *standardize* the T2* contrast distribution. 5. Calculate a mask using AFNI's ``3dAutomask`` after the contrast enhancement of 4). 6. Calculate a final mask as the intersection of 3) and 5). 7. Apply final mask on the enhanced reference. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.util import init_enhance_and_skullstrip_bold_wf wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=1) **Parameters** name : str Name of workflow (default: ``enhance_and_skullstrip_bold_wf``) omp_nthreads : int number of threads available to parallel nodes enhance_t2 : bool perform logarithmic transform of input BOLD image to improve contrast before calculating the preliminary mask **Inputs** in_file BOLD image (single volume) **Outputs** bias_corrected_file the ``in_file`` after `N4BiasFieldCorrection`_ skull_stripped_file the ``bias_corrected_file`` after skull-stripping mask_file mask of the skull-stripped input file out_report reportlet for the skull-stripping .. _N4BiasFieldCorrection: https://hdl.handle.net/10380/3053 """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['mask_file', 'skull_stripped_file', 'bias_corrected_file']), name='outputnode') # Create a loose mask to avoid N4 internal's Otsu mask n4_mask = pe.Node(MaskEPI(upper_cutoff=0.75, enhance_t2=enhance_t2, opening=1, no_sanitize=True), name='n4_mask') # Run N4 normally, force num_threads=1 for stability (images are small, no need for >1) n4_correct = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), name='n4_correct', n_procs=1) # Create a generous BET mask out of the bias-corrected EPI skullstrip_first_pass = pe.Node(fsl.BET(frac=0.2, mask=True), name='skullstrip_first_pass') bet_dilate = pe.Node(fsl.DilateImage(operation='max', kernel_shape='sphere', kernel_size=6.0, internal_datatype='char'), name='skullstrip_first_dilate') bet_mask = pe.Node(fsl.ApplyMask(), name='skullstrip_first_mask') # Use AFNI's unifize for T2 constrast & fix header unifize = pe.Node( afni.Unifize( t2=True, outputtype='NIFTI_GZ', # Default -clfrac is 0.1, 0.4 was too conservative # -rbt because I'm a Jedi AFNI Master (see 3dUnifize's documentation) args='-clfrac 0.2 -rbt 18.3 65.0 90.0', out_file="uni.nii.gz"), name='unifize') fixhdr_unifize = pe.Node(CopyXForm(), name='fixhdr_unifize', mem_gb=0.1) # Run ANFI's 3dAutomask to extract a refined brain mask skullstrip_second_pass = pe.Node(afni.Automask(dilate=1, outputtype='NIFTI_GZ'), name='skullstrip_second_pass') fixhdr_skullstrip2 = pe.Node(CopyXForm(), name='fixhdr_skullstrip2', mem_gb=0.1) # Take intersection of both masks combine_masks = pe.Node(fsl.BinaryMaths(operation='mul'), name='combine_masks') # Compute masked brain apply_mask = pe.Node(fsl.ApplyMask(), name='apply_mask') workflow.connect([ (inputnode, n4_mask, [('in_file', 'in_files')]), (inputnode, n4_correct, [('in_file', 'input_image')]), (inputnode, fixhdr_unifize, [('in_file', 'hdr_file')]), (inputnode, fixhdr_skullstrip2, [('in_file', 'hdr_file')]), (n4_mask, n4_correct, [('out_mask', 'mask_image')]), (n4_correct, skullstrip_first_pass, [('output_image', 'in_file')]), (skullstrip_first_pass, bet_dilate, [('mask_file', 'in_file')]), (bet_dilate, bet_mask, [('out_file', 'mask_file')]), (skullstrip_first_pass, bet_mask, [('out_file', 'in_file')]), (bet_mask, unifize, [('out_file', 'in_file')]), (unifize, fixhdr_unifize, [('out_file', 'in_file')]), (fixhdr_unifize, skullstrip_second_pass, [('out_file', 'in_file')]), (skullstrip_first_pass, combine_masks, [('mask_file', 'in_file')]), (skullstrip_second_pass, fixhdr_skullstrip2, [('out_file', 'in_file') ]), (fixhdr_skullstrip2, combine_masks, [('out_file', 'operand_file')]), (fixhdr_unifize, apply_mask, [('out_file', 'in_file')]), (combine_masks, apply_mask, [('out_file', 'mask_file')]), (combine_masks, outputnode, [('out_file', 'mask_file')]), (apply_mask, outputnode, [('out_file', 'skull_stripped_file')]), (n4_correct, outputnode, [('output_image', 'bias_corrected_file')]), ]) return workflow
def init_fmap_unwarp_report_wf(reportlets_dir, name='fmap_unwarp_report_wf'): """ This workflow generates and saves a reportlet showing the effect of fieldmap unwarping a BOLD image. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.unwarp import init_fmap_unwarp_report_wf wf = init_fmap_unwarp_report_wf(reportlets_dir='.') **Parameters** reportlets_dir : str Directory in which to save reportlets name : str, optional Workflow name (default: fmap_unwarp_report_wf) **Inputs** in_pre Reference image, before unwarping in_post Reference image, after unwarping in_seg Segmentation of preprocessed structural image, including gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF) in_xfm Affine transform from T1 space to BOLD space (ITK format) name_source BOLD series NIfTI file Used to recover original information lost during processing """ from niworkflows.nipype.pipeline import engine as pe from niworkflows.nipype.interfaces import utility as niu from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.interfaces import SimpleBeforeAfter from ...interfaces.images import extract_wm from ...interfaces import DerivativesDataSink DEFAULT_MEMORY_MIN_GB = 0.01 workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_pre', 'in_post', 'in_seg', 'in_xfm', 'name_source']), name='inputnode') map_seg = pe.Node(ApplyTransforms(dimension=3, float=True, interpolation='NearestNeighbor'), name='map_seg', mem_gb=0.3) sel_wm = pe.Node(niu.Function(function=extract_wm), name='sel_wm', mem_gb=DEFAULT_MEMORY_MIN_GB) bold_rpt = pe.Node(SimpleBeforeAfter(), name='bold_rpt', mem_gb=0.1) bold_rpt_ds = pe.Node(DerivativesDataSink(base_directory=reportlets_dir, suffix='variant-hmcsdc_preproc'), name='bold_rpt_ds', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True) workflow.connect([ (inputnode, bold_rpt, [('in_post', 'after'), ('in_pre', 'before')]), (inputnode, bold_rpt_ds, [('name_source', 'source_file')]), (bold_rpt, bold_rpt_ds, [('out_report', 'in_file')]), (inputnode, map_seg, [('in_post', 'reference_image'), ('in_seg', 'input_image'), ('in_xfm', 'transforms')]), (map_seg, sel_wm, [('output_image', 'in_seg')]), (sel_wm, bold_rpt, [('out', 'wm_seg')]), ]) return workflow
def init_sdc_unwarp_wf(reportlets_dir, omp_nthreads, fmap_bspline, fmap_demean, debug, name='sdc_unwarp_wf'): """ This workflow takes in a displacements fieldmap and calculates the corresponding displacements field (in other words, an ANTs-compatible warp file). It also calculates a new mask for the input dataset that takes into account the distortions. The mask is restricted to the field of view of the fieldmap since outside of it corrections could not be performed. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.unwarp import init_sdc_unwarp_wf wf = init_sdc_unwarp_wf(reportlets_dir='.', omp_nthreads=8, fmap_bspline=False, fmap_demean=True, debug=False) Inputs in_reference the reference image in_mask a brain mask corresponding to ``in_reference`` name_source path to the original _bold file being unwarped fmap the fieldmap in Hz fmap_ref the reference (anatomical) image corresponding to ``fmap`` fmap_mask a brain mask corresponding to ``fmap`` Outputs out_reference the ``in_reference`` after unwarping out_reference_brain the ``in_reference`` after unwarping and skullstripping out_warp the corresponding :abbr:`DFM (displacements field map)` compatible with ANTs out_jacobian the jacobian of the field (for drop-out alleviation) out_mask mask of the unwarped input file """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_reference', 'in_reference_brain', 'in_mask', 'name_source', 'fmap_ref', 'fmap_mask', 'fmap' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_reference', 'out_reference_brain', 'out_warp', 'out_mask', 'out_jacobian' ]), name='outputnode') meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01, run_without_submitting=True) # Register the reference of the fieldmap to the reference # of the target image (the one that shall be corrected) ants_settings = pkgr.resource_filename('fmriprep', 'data/fmap-any_registration.json') if debug: ants_settings = pkgr.resource_filename( 'fmriprep', 'data/fmap-any_registration_testing.json') fmap2ref_reg = pe.Node(ANTSRegistrationRPT( generate_report=True, from_file=ants_settings, output_inverse_warped_image=True, output_warped_image=True), name='fmap2ref_reg', n_procs=omp_nthreads) ds_reg = pe.Node(DerivativesDataSink(base_directory=reportlets_dir, suffix='fmap_reg'), name='ds_reg', mem_gb=0.01, run_without_submitting=True) # Map the VSM into the EPI space fmap2ref_apply = pe.Node(ANTSApplyTransformsRPT(generate_report=True, dimension=3, interpolation='BSpline', float=True), name='fmap2ref_apply') fmap_mask2ref_apply = pe.Node(ANTSApplyTransformsRPT( generate_report=False, dimension=3, interpolation='NearestNeighbor', float=True), name='fmap_mask2ref_apply') ds_reg_vsm = pe.Node(DerivativesDataSink(base_directory=reportlets_dir, suffix='fmap_reg_vsm'), name='ds_reg_vsm', mem_gb=0.01, run_without_submitting=True) # Fieldmap to rads and then to voxels (VSM - voxel shift map) torads = pe.Node(niu.Function(function=_hz2rads), name='torads') get_ees = pe.Node(niu.Function(function=_get_ees, output_names=['ees']), name='get_ees') gen_vsm = pe.Node(fsl.FUGUE(save_unmasked_shift=True), name='gen_vsm') # Convert the VSM into a DFM (displacements field map) # or: FUGUE shift to ANTS warping. vsm2dfm = pe.Node(itk.FUGUEvsm2ANTSwarp(), name='vsm2dfm') jac_dfm = pe.Node(ants.CreateJacobianDeterminantImage( imageDimension=3, outputImage='jacobian.nii.gz'), name='jac_dfm') unwarp_reference = pe.Node(ANTSApplyTransformsRPT( dimension=3, generate_report=False, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_reference') fieldmap_fov_mask = pe.Node(niu.Function(function=_fill_with_ones), name='fieldmap_fov_mask') fmap_fov2ref_apply = pe.Node(ANTSApplyTransformsRPT( generate_report=False, dimension=3, interpolation='NearestNeighbor', float=True), name='fmap_fov2ref_apply') apply_fov_mask = pe.Node(fsl.ApplyMask(), name="apply_fov_mask") enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf( omp_nthreads=omp_nthreads) workflow.connect([ (inputnode, meta, [('name_source', 'in_file')]), (inputnode, fmap2ref_reg, [('fmap_ref', 'moving_image')]), (inputnode, fmap2ref_apply, [('in_reference', 'reference_image')]), (fmap2ref_reg, fmap2ref_apply, [('composite_transform', 'transforms') ]), (inputnode, fmap_mask2ref_apply, [('in_reference', 'reference_image') ]), (fmap2ref_reg, fmap_mask2ref_apply, [('composite_transform', 'transforms')]), (inputnode, ds_reg_vsm, [('name_source', 'source_file')]), (fmap2ref_apply, ds_reg_vsm, [('out_report', 'in_file')]), (inputnode, fmap2ref_reg, [('in_reference_brain', 'fixed_image')]), (inputnode, ds_reg, [('name_source', 'source_file')]), (fmap2ref_reg, ds_reg, [('out_report', 'in_file')]), (inputnode, fmap2ref_apply, [('fmap', 'input_image')]), (inputnode, fmap_mask2ref_apply, [('fmap_mask', 'input_image')]), (fmap2ref_apply, torads, [('output_image', 'in_file')]), (meta, get_ees, [('out_dict', 'in_meta')]), (inputnode, get_ees, [('name_source', 'in_file')]), (get_ees, gen_vsm, [('ees', 'dwell_time')]), (meta, gen_vsm, [(('out_dict', _get_pedir_fugue), 'unwarp_direction') ]), (meta, vsm2dfm, [(('out_dict', _get_pedir_bids), 'pe_dir')]), (torads, gen_vsm, [('out', 'fmap_in_file')]), (vsm2dfm, unwarp_reference, [('out_file', 'transforms')]), (inputnode, unwarp_reference, [('in_reference', 'reference_image')]), (inputnode, unwarp_reference, [('in_reference', 'input_image')]), (vsm2dfm, outputnode, [('out_file', 'out_warp')]), (vsm2dfm, jac_dfm, [('out_file', 'deformationField')]), (inputnode, fieldmap_fov_mask, [('fmap_ref', 'in_file')]), (fieldmap_fov_mask, fmap_fov2ref_apply, [('out', 'input_image')]), (inputnode, fmap_fov2ref_apply, [('in_reference', 'reference_image')]), (fmap2ref_reg, fmap_fov2ref_apply, [('composite_transform', 'transforms')]), (fmap_fov2ref_apply, apply_fov_mask, [('output_image', 'mask_file')]), (unwarp_reference, apply_fov_mask, [('output_image', 'in_file')]), (apply_fov_mask, enhance_and_skullstrip_bold_wf, [('out_file', 'inputnode.in_file')]), (apply_fov_mask, outputnode, [('out_file', 'out_reference')]), (enhance_and_skullstrip_bold_wf, outputnode, [('outputnode.mask_file', 'out_mask'), ('outputnode.skull_stripped_file', 'out_reference_brain')]), (jac_dfm, outputnode, [('jacobian_image', 'out_jacobian')]), ]) if not fmap_bspline: workflow.connect([(fmap_mask2ref_apply, gen_vsm, [('output_image', 'mask_file')])]) if fmap_demean: # Demean within mask demean = pe.Node(niu.Function(function=_demean), name='demean') workflow.connect([ (gen_vsm, demean, [('shift_out_file', 'in_file')]), (fmap_mask2ref_apply, demean, [('output_image', 'in_mask')]), (demean, vsm2dfm, [('out', 'in_file')]), ]) else: workflow.connect([ (gen_vsm, vsm2dfm, [('shift_out_file', 'in_file')]), ]) return workflow
def init_fmap_wf(reportlets_dir, omp_nthreads, fmap_bspline, name='fmap_wf'): """ Fieldmap workflow - when we have a sequence that directly measures the fieldmap we just need to mask it (using the corresponding magnitude image) to remove the noise in the surrounding air region, and ensure that units are Hz. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.fmap import init_fmap_wf wf = init_fmap_wf(reportlets_dir='.', omp_nthreads=6, fmap_bspline=False) """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['magnitude', 'fieldmap']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') # Merge input magnitude images magmrg = pe.Node(IntraModalMerge(), name='magmrg') # Merge input fieldmap images fmapmrg = pe.Node(IntraModalMerge(zero_based_avg=False, hmc=False), name='fmapmrg') # de-gradient the fields ("bias/illumination artifact") n4_correct = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), name='n4_correct') bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') ds_fmap_mask = pe.Node( DerivativesDataSink(base_directory=reportlets_dir, suffix='fmap_mask'), name='ds_fmap_mask') workflow.connect([ (inputnode, magmrg, [('magnitude', 'in_files')]), (inputnode, fmapmrg, [('fieldmap', 'in_files')]), (magmrg, n4_correct, [('out_file', 'input_image')]), (n4_correct, bet, [('output_image', 'in_file')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), (inputnode, ds_fmap_mask, [('fieldmap', 'source_file')]), (bet, ds_fmap_mask, [('out_report', 'in_file')]), ]) if fmap_bspline: # despike_threshold=1.0, mask_erode=1), fmapenh = pe.Node(FieldEnhance( unwrap=False, despike=False, njobs=omp_nthreads), name='fmapenh') fmapenh.interface.num_threads = omp_nthreads fmapenh.interface.estimated_memory_gb = 4 workflow.connect([ (bet, fmapenh, [('mask_file', 'in_mask'), ('out_file', 'in_magnitude')]), (fmapmrg, fmapenh, [('out_file', 'in_file')]), (fmapenh, outputnode, [('out_file', 'fmap')]), ]) else: torads = pe.Node(niu.Function(output_names=['out_file', 'cutoff_hz'], function=_torads), name='torads') prelude = pe.Node(fsl.PRELUDE(), name='prelude') tohz = pe.Node(niu.Function(function=_tohz), name='tohz') denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', kernel_size=3), name='denoise') demean = pe.Node(niu.Function(function=demean_image), name='demean') cleanup_wf = cleanup_edge_pipeline(name='cleanup_wf') applymsk = pe.Node(ApplyMask(), name='applymsk') workflow.connect([ (bet, prelude, [('mask_file', 'mask_file'), ('out_file', 'magnitude_file')]), (fmapmrg, torads, [('out_file', 'in_file')]), (torads, tohz, [('cutoff_hz', 'cutoff_hz')]), (torads, prelude, [('out_file', 'phase_file')]), (prelude, tohz, [('unwrapped_phase_file', 'in_file')]), (tohz, denoise, [('out', 'in_file')]), (denoise, demean, [('out_file', 'in_file')]), (demean, cleanup_wf, [('out', 'inputnode.in_file')]), (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), (cleanup_wf, applymsk, [('outputnode.out_file', 'in_file')]), (bet, applymsk, [('mask_file', 'in_mask')]), (applymsk, outputnode, [('out_file', 'fmap')]), ]) return workflow
def init_anat_derivatives_wf(output_dir, output_spaces, template, freesurfer, name='anat_derivatives_wf'): """ Set up a battery of datasinks to store derivatives in the right location """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface( fields=['source_file', 't1_preproc', 't1_mask', 't1_seg', 't1_tpms', 't1_2_mni_forward_transform', 't1_2_mni', 'mni_mask', 'mni_seg', 'mni_tpms', 'surfaces']), name='inputnode') ds_t1_preproc = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='preproc'), name='ds_t1_preproc', run_without_submitting=True) ds_t1_mask = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='brainmask'), name='ds_t1_mask', run_without_submitting=True) ds_t1_seg = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='dtissue'), name='ds_t1_seg', run_without_submitting=True) ds_t1_tpms = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='class-{extra_value}_probtissue'), name='ds_t1_tpms', run_without_submitting=True) ds_t1_tpms.inputs.extra_values = ['CSF', 'GM', 'WM'] suffix_fmt = 'space-{}_{}'.format ds_t1_mni = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix=suffix_fmt(template, 'preproc')), name='ds_t1_mni', run_without_submitting=True) ds_mni_mask = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix=suffix_fmt(template, 'brainmask')), name='ds_mni_mask', run_without_submitting=True) ds_mni_seg = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix=suffix_fmt(template, 'dtissue')), name='ds_mni_seg', run_without_submitting=True) ds_mni_tpms = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix=suffix_fmt(template, 'class-{extra_value}_probtissue')), name='ds_mni_tpms', run_without_submitting=True) ds_mni_tpms.inputs.extra_values = ['CSF', 'GM', 'WM'] ds_t1_mni_warp = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix=suffix_fmt(template, 'warp')), name='ds_t1_mni_warp', run_without_submitting=True) name_surfs = pe.MapNode(GiftiNameSource(pattern=r'(?P<LR>[lr])h.(?P<surf>.+)_converted.gii', template='{surf}.{LR}.surf'), iterfield='in_file', name='name_surfs', run_without_submitting=True) ds_surfs = pe.MapNode( DerivativesDataSink(base_directory=output_dir), iterfield=['in_file', 'suffix'], name='ds_surfs', run_without_submitting=True) workflow.connect([ (inputnode, ds_t1_preproc, [('source_file', 'source_file'), ('t1_preproc', 'in_file')]), (inputnode, ds_t1_mask, [('source_file', 'source_file'), ('t1_mask', 'in_file')]), (inputnode, ds_t1_seg, [('source_file', 'source_file'), ('t1_seg', 'in_file')]), (inputnode, ds_t1_tpms, [('source_file', 'source_file'), ('t1_tpms', 'in_file')]), ]) if freesurfer: workflow.connect([ (inputnode, name_surfs, [('surfaces', 'in_file')]), (inputnode, ds_surfs, [('source_file', 'source_file'), ('surfaces', 'in_file')]), (name_surfs, ds_surfs, [('out_name', 'suffix')]), ]) if 'template' in output_spaces: workflow.connect([ (inputnode, ds_t1_mni_warp, [('source_file', 'source_file'), ('t1_2_mni_forward_transform', 'in_file')]), (inputnode, ds_t1_mni, [('source_file', 'source_file'), ('t1_2_mni', 'in_file')]), (inputnode, ds_mni_mask, [('source_file', 'source_file'), ('mni_mask', 'in_file')]), (inputnode, ds_mni_seg, [('source_file', 'source_file'), ('mni_seg', 'in_file')]), (inputnode, ds_mni_tpms, [('source_file', 'source_file'), ('mni_tpms', 'in_file')]), ]) return workflow
def init_autorecon_resume_wf(omp_nthreads, name='autorecon_resume_wf'): """ Resume broken recon-all execution """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface( fields=['subjects_dir', 'subject_id', 'use_T2']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface( fields=['subjects_dir', 'subject_id', 'out_report']), name='outputnode') autorecon2_vol = pe.Node( fs.ReconAll(directive='autorecon2-volonly', openmp=omp_nthreads), n_procs=omp_nthreads, name='autorecon2_vol') autorecon_surfs = pe.MapNode( fs.ReconAll( directive='autorecon-hemi', flags=['-noparcstats', '-nocortparc2', '-noparcstats2', '-nocortparc3', '-noparcstats3', '-nopctsurfcon', '-nohyporelabel', '-noaparc2aseg', '-noapas2aseg', '-nosegstats', '-nowmparc', '-nobalabels'], openmp=omp_nthreads), iterfield='hemi', n_procs=omp_nthreads, name='autorecon_surfs') autorecon_surfs.inputs.hemi = ['lh', 'rh'] autorecon3 = pe.MapNode( fs.ReconAll(directive='autorecon3', openmp=omp_nthreads), iterfield='hemi', n_procs=omp_nthreads, name='autorecon3') autorecon3.inputs.hemi = ['lh', 'rh'] # Only generate the report once; should be nothing to do recon_report = pe.Node( ReconAllRPT(directive='autorecon3', generate_report=True), name='recon_report') def _dedup(in_list): vals = set(in_list) if len(vals) > 1: raise ValueError( "Non-identical values can't be deduplicated:\n{!r}".format(in_list)) return vals.pop() workflow.connect([ (inputnode, autorecon3, [('use_T2', 'use_T2')]), (inputnode, autorecon2_vol, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (autorecon2_vol, autorecon_surfs, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (autorecon_surfs, autorecon3, [(('subjects_dir', _dedup), 'subjects_dir'), (('subject_id', _dedup), 'subject_id')]), (autorecon3, outputnode, [(('subjects_dir', _dedup), 'subjects_dir'), (('subject_id', _dedup), 'subject_id')]), (autorecon3, recon_report, [(('subjects_dir', _dedup), 'subjects_dir'), (('subject_id', _dedup), 'subject_id')]), (recon_report, outputnode, [('out_report', 'out_report')]), ]) return workflow
def headmsk_wf(name='HeadMaskWorkflow', use_bet=True): """ Computes a head mask as in [Mortamet2009]_. .. workflow:: from mriqc.workflows.anatomical import headmsk_wf wf = headmsk_wf() """ has_dipy = False try: from dipy.denoise import nlmeans # noqa has_dipy = True except ImportError: pass workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['in_file', 'in_segm']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_file']), name='outputnode') if use_bet or not has_dipy: # Alternative for when dipy is not installed bet = pe.Node(fsl.BET(surfaces=True), name='fsl_bet') workflow.connect([(inputnode, bet, [('in_file', 'in_file')]), (bet, outputnode, [('outskin_mask_file', 'out_file') ])]) else: from niworkflows.nipype.interfaces.dipy import Denoise enhance = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=_enhance), name='Enhance') estsnr = pe.Node(niu.Function(input_names=['in_file', 'seg_file'], output_names=['out_snr'], function=_estimate_snr), name='EstimateSNR') denoise = pe.Node(Denoise(), name='Denoise') gradient = pe.Node(niu.Function(input_names=['in_file', 'snr'], output_names=['out_file'], function=image_gradient), name='Grad') thresh = pe.Node(niu.Function(input_names=['in_file', 'in_segm'], output_names=['out_file'], function=gradient_threshold), name='GradientThreshold') workflow.connect([(inputnode, estsnr, [('in_file', 'in_file'), ('in_segm', 'seg_file')]), (estsnr, denoise, [('out_snr', 'snr')]), (inputnode, enhance, [('in_file', 'in_file')]), (enhance, denoise, [('out_file', 'in_file')]), (estsnr, gradient, [('out_snr', 'snr')]), (denoise, gradient, [('out_file', 'in_file')]), (inputnode, thresh, [('in_segm', 'in_segm')]), (gradient, thresh, [('out_file', 'in_file')]), (thresh, outputnode, [('out_file', 'out_file')])]) return workflow
def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf', gen_report=False, enhance_t2=False): """ This workflow generates reference BOLD images for a series The raw reference image is the target of :abbr:`HMC (head motion correction)`, and a contrast-enhanced reference is the subject of distortion correction, as well as boundary-based registration to T1w and template spaces. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold import init_bold_reference_wf wf = init_bold_reference_wf(omp_nthreads=1) **Parameters** bold_file : str BOLD series NIfTI file omp_nthreads : int Maximum number of threads an individual process may use name : str Name of workflow (default: ``bold_reference_wf``) gen_report : bool Whether a mask report node should be appended in the end enhance_t2 : bool Perform logarithmic transform of input BOLD image to improve contrast before calculating the preliminary mask **Inputs** bold_file BOLD series NIfTI file **Outputs** bold_file Validated BOLD series NIfTI file raw_ref_image Reference image to which BOLD series is motion corrected skip_vols Number of non-steady-state volumes detected at beginning of ``bold_file`` ref_image Contrast-enhanced reference image ref_image_brain Skull-stripped reference image bold_mask Skull-stripping mask of reference image validation_report HTML reportlet indicating whether ``bold_file`` had a valid affine **Subworkflows** * :py:func:`~fmriprep.workflows.bold.util.init_enhance_and_skullstrip_wf` """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['bold_file']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_file', 'raw_ref_image', 'skip_vols', 'ref_image', 'ref_image_brain', 'bold_mask', 'validation_report', 'mask_report' ]), name='outputnode') # Simplify manually setting input image if bold_file is not None: inputnode.inputs.bold_file = bold_file validate = pe.Node(ValidateImage(), name='validate', mem_gb=DEFAULT_MEMORY_MIN_GB) gen_ref = pe.Node(EstimateReferenceImage(), name="gen_ref", mem_gb=1) # OE: 128x128x128x50 * 64 / 8 ~ 900MB. enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf( omp_nthreads=omp_nthreads) workflow.connect([ (inputnode, validate, [('bold_file', 'in_file')]), (validate, gen_ref, [('out_file', 'in_file')]), (gen_ref, enhance_and_skullstrip_bold_wf, [('ref_image', 'inputnode.in_file')]), (validate, outputnode, [('out_file', 'bold_file'), ('out_report', 'validation_report')]), (gen_ref, outputnode, [('ref_image', 'raw_ref_image'), ('n_volumes_to_discard', 'skip_vols')]), (enhance_and_skullstrip_bold_wf, outputnode, [('outputnode.bias_corrected_file', 'ref_image'), ('outputnode.mask_file', 'bold_mask'), ('outputnode.skull_stripped_file', 'ref_image_brain')]), ]) if gen_report: mask_reportlet = pe.Node(SimpleShowMaskRPT(), name='mask_reportlet') workflow.connect([ (enhance_and_skullstrip_bold_wf, mask_reportlet, [ ('outputnode.bias_corrected_file', 'background_file'), ('outputnode.mask_file', 'mask_file'), ]), ]) return workflow
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, 'no_sub': True}) """ workflow = pe.Workflow(name="%s%s" % (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') # 1. Reorient anatomical image to_ras = pe.Node(ConformImage(check_dtype=False), name='conform') # 2. Skull-stripping (afni) asw = skullstrip_wf(n4_nthreads=settings.get('ants_nthreads', 1), unifize=False) # 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', mem_gb=5) # 7. Compute IQMs iqmswf = compute_iqms(settings, modality=mod) # Reports repwf = individual_reports(settings) # Connect all nodes workflow.connect([ (inputnode, to_ras, [('in_file', 'in_file')]), (inputnode, iqmswf, [('in_file', 'inputnode.in_file')]), (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.in_ras')]), (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'), ('outputnode.rot_mask', 'inputnode.rotmask')]), (segment, iqmswf, [('tissue_class_map', 'inputnode.segmentation'), ('partial_volume_files', 'inputnode.pvms')]), (hmsk, iqmswf, [('outputnode.out_file', 'inputnode.headmask')]), (to_ras, repwf, [('out_file', 'inputnode.in_ras')]), (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'), ('outputnode.rot_mask', 'inputnode.rotmask')]), (segment, repwf, [('tissue_class_map', 'inputnode.segmentation')]), (iqmswf, repwf, [('outputnode.out_noisefit', 'inputnode.noisefit')]), (iqmswf, repwf, [('outputnode.out_file', 'inputnode.in_iqms')]), (iqmswf, outputnode, [('outputnode.out_file', 'out_json')]) ]) # Upload metrics if not settings.get('no_sub', False): from ..interfaces.webapi import UploadIQMs upldwf = pe.Node(UploadIQMs(), name='UploadMetrics') upldwf.inputs.email = settings.get('email', '') upldwf.inputs.url = settings.get('webapi_url') if settings.get('webapi_port'): upldwf.inputs.port = settings.get('webapi_port') upldwf.inputs.strict = settings.get('upload_strict', False) workflow.connect([ (iqmswf, upldwf, [('outputnode.out_file', 'in_iqms')]), ]) return workflow
def init_skullstrip_bold_wf(name='skullstrip_bold_wf'): """ This workflow applies skull-stripping to a BOLD image. It is intended to be used on an image that has previously been bias-corrected with :py:func:`~fmriprep.workflows.bold.util.init_enhance_and_skullstrip_bold_wf` .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.util import init_skullstrip_bold_wf wf = init_skullstrip_bold_wf() Inputs in_file BOLD image (single volume) Outputs skull_stripped_file the ``in_file`` after skull-stripping mask_file mask of the skull-stripped input file out_report reportlet for the skull-stripping """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['mask_file', 'skull_stripped_file', 'out_report']), name='outputnode') skullstrip_first_pass = pe.Node(fsl.BET(frac=0.2, mask=True), name='skullstrip_first_pass') skullstrip_second_pass = pe.Node(afni.Automask(dilate=1, outputtype='NIFTI_GZ'), name='skullstrip_second_pass') combine_masks = pe.Node(fsl.BinaryMaths(operation='mul'), name='combine_masks') apply_mask = pe.Node(fsl.ApplyMask(), name='apply_mask') mask_reportlet = pe.Node(SimpleShowMaskRPT(), name='mask_reportlet') workflow.connect([ (inputnode, skullstrip_first_pass, [('in_file', 'in_file')]), (skullstrip_first_pass, skullstrip_second_pass, [('out_file', 'in_file')]), (skullstrip_first_pass, combine_masks, [('mask_file', 'in_file')]), (skullstrip_second_pass, combine_masks, [('out_file', 'operand_file') ]), (combine_masks, outputnode, [('out_file', 'mask_file')]), # Masked file (inputnode, apply_mask, [('in_file', 'in_file')]), (combine_masks, apply_mask, [('out_file', 'mask_file')]), (apply_mask, outputnode, [('out_file', 'skull_stripped_file')]), # Reportlet (inputnode, mask_reportlet, [('in_file', 'background_file')]), (combine_masks, mask_reportlet, [('out_file', 'mask_file')]), (mask_reportlet, outputnode, [('out_report', 'out_report')]), ]) return workflow
def init_func_reports_wf(reportlets_dir, freesurfer, use_aroma, use_syn, t2s_coreg, name='func_reports_wf'): """ Set up a battery of datasinks to store reports in the right location """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'source_file', 'summary_report', 'validation_report', 'bold_reg_report', 'bold_reg_fallback', 'bold_rois_report', 'syn_sdc_report', 'ica_aroma_report', 'first_echo' ]), name='inputnode') ds_summary_report = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, suffix='summary'), name='ds_summary_report', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_validation_report = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, suffix='validation'), name='ds_validation_report', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_bold_rois_report = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, suffix='rois'), name='ds_bold_rois_report', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) def _bold_reg_suffix(fallback, freesurfer): if fallback: return 'coreg' if freesurfer else 'flirt' return 'bbr' if freesurfer else 'flt_bbr' ds_bold_reg_report = pe.Node( DerivativesDataSink(base_directory=reportlets_dir), name='ds_bold_reg_report', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_ica_aroma_report = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, suffix='ica_aroma'), name='ds_ica_aroma_report', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, ds_bold_reg_report, [('first_echo' if t2s_coreg else 'source_file', 'source_file')]), (inputnode, ds_summary_report, [('source_file', 'source_file'), ('summary_report', 'in_file')]), (inputnode, ds_validation_report, [('source_file', 'source_file'), ('validation_report', 'in_file')]), (inputnode, ds_bold_rois_report, [('source_file', 'source_file'), ('bold_rois_report', 'in_file')]), (inputnode, ds_bold_reg_report, [('bold_reg_report', 'in_file'), (('bold_reg_fallback', _bold_reg_suffix, freesurfer), 'suffix')]), ]) if use_aroma: workflow.connect([ (inputnode, ds_ica_aroma_report, [('source_file', 'source_file'), ('ica_aroma_report', 'in_file') ]), ]) return workflow
def init_fmriprep_wf(subject_list, task_id, run_uuid, ignore, debug, low_mem, anat_only, longitudinal, t2s_coreg, omp_nthreads, skull_strip_template, work_dir, output_dir, bids_dir, freesurfer, output_spaces, template, medial_surface_nan, cifti_output, hires, use_bbr, bold2t1w_dof, fmap_bspline, fmap_demean, use_syn, force_syn, use_aroma, ignore_aroma_err, aroma_melodic_dim, template_out_grid): """ This workflow organizes the execution of FMRIPREP, with a sub-workflow for each subject. If FreeSurfer's recon-all is to be run, a FreeSurfer derivatives folder is created and populated with any needed template subjects. .. workflow:: :graph2use: orig :simple_form: yes import os os.environ['FREESURFER_HOME'] = os.getcwd() from fmriprep.workflows.base import init_fmriprep_wf wf = init_fmriprep_wf(subject_list=['fmripreptest'], task_id='', run_uuid='X', ignore=[], debug=False, low_mem=False, anat_only=False, longitudinal=False, t2s_coreg=False, omp_nthreads=1, skull_strip_template='OASIS', work_dir='.', output_dir='.', bids_dir='.', freesurfer=True, output_spaces=['T1w', 'fsnative', 'template', 'fsaverage5'], template='MNI152NLin2009cAsym', medial_surface_nan=False, cifti_output=False, hires=True, use_bbr=True, bold2t1w_dof=9, fmap_bspline=False, fmap_demean=True, use_syn=True, force_syn=True, use_aroma=False, ignore_aroma_err=False, aroma_melodic_dim=None, template_out_grid='native') Parameters subject_list : list List of subject labels task_id : str or None Task ID of BOLD series to preprocess, or ``None`` to preprocess all run_uuid : str Unique identifier for execution instance ignore : list Preprocessing steps to skip (may include "slicetiming", "fieldmaps") debug : bool Enable debugging outputs low_mem : bool Write uncompressed .nii files in some cases to reduce memory usage anat_only : bool Disable functional workflows longitudinal : bool Treat multiple sessions as longitudinal (may increase runtime) See sub-workflows for specific differences t2s_coreg : bool Use multiple BOLD echos to create T2*-map for T2*-driven coregistration omp_nthreads : int Maximum number of threads an individual process may use skull_strip_template : str Name of ANTs skull-stripping template ('OASIS' or 'NKI') work_dir : str Directory in which to store workflow execution state and temporary files output_dir : str Directory in which to save derivatives bids_dir : str Root directory of BIDS dataset freesurfer : bool Enable FreeSurfer surface reconstruction (may increase runtime) output_spaces : list List of output spaces functional images are to be resampled to. Some parts of pipeline will only be instantiated for some output spaces. Valid spaces: - T1w - template - fsnative - fsaverage (or other pre-existing FreeSurfer templates) template : str Name of template targeted by ``template`` output space medial_surface_nan : bool Replace medial wall values with NaNs on functional GIFTI files cifti_output : bool Generate bold CIFTI file in output spaces hires : bool Enable sub-millimeter preprocessing in FreeSurfer use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration fmap_bspline : bool **Experimental**: Fit B-Spline field using least-squares fmap_demean : bool Demean voxel-shift map during unwarp use_syn : bool **Experimental**: Enable ANTs SyN-based susceptibility distortion correction (SDC). If fieldmaps are present and enabled, this is not run, by default. force_syn : bool **Temporary**: Always run SyN-based SDC use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series ignore_aroma_err : bool Do not fail on ICA-AROMA errors template_out_grid : str Keyword ('native', '1mm' or '2mm') or path of custom reference image for normalization """ fmriprep_wf = pe.Workflow(name='fmriprep_wf') fmriprep_wf.base_dir = work_dir if freesurfer: fsdir = pe.Node(BIDSFreeSurferDir( derivatives=output_dir, freesurfer_home=os.getenv('FREESURFER_HOME'), spaces=output_spaces), name='fsdir', run_without_submitting=True) reportlets_dir = os.path.join(work_dir, 'reportlets') for subject_id in subject_list: single_subject_wf = init_single_subject_wf( subject_id=subject_id, task_id=task_id, name="single_subject_" + subject_id + "_wf", ignore=ignore, debug=debug, low_mem=low_mem, anat_only=anat_only, longitudinal=longitudinal, t2s_coreg=t2s_coreg, omp_nthreads=omp_nthreads, skull_strip_template=skull_strip_template, reportlets_dir=reportlets_dir, output_dir=output_dir, bids_dir=bids_dir, freesurfer=freesurfer, output_spaces=output_spaces, template=template, medial_surface_nan=medial_surface_nan, cifti_output=cifti_output, hires=hires, use_bbr=use_bbr, bold2t1w_dof=bold2t1w_dof, fmap_bspline=fmap_bspline, fmap_demean=fmap_demean, use_syn=use_syn, force_syn=force_syn, template_out_grid=template_out_grid, use_aroma=use_aroma, aroma_melodic_dim=aroma_melodic_dim, ignore_aroma_err=ignore_aroma_err) single_subject_wf.config['execution']['crashdump_dir'] = (os.path.join( output_dir, "fmriprep", "sub-" + subject_id, 'log', run_uuid)) for node in single_subject_wf._get_all_nodes(): node.config = deepcopy(single_subject_wf.config) if freesurfer: fmriprep_wf.connect(fsdir, 'subjects_dir', single_subject_wf, 'inputnode.subjects_dir') else: fmriprep_wf.add_nodes([single_subject_wf]) return fmriprep_wf
def init_bbreg_wf(use_bbr, bold2t1w_dof, omp_nthreads, name='bbreg_wf'): """ This workflow uses FreeSurfer's ``bbregister`` to register a BOLD image to a T1-weighted structural image. It is a counterpart to :py:func:`~fmriprep.workflows.util.init_fsl_bbr_wf`, which performs the same task using FSL's FLIRT with a BBR cost function. The ``use_bbr`` option permits a high degree of control over registration. If ``False``, standard, affine coregistration will be performed using FreeSurfer's ``mri_coreg`` tool. If ``True``, ``bbregister`` will be seeded with the initial transform found by ``mri_coreg`` (equivalent to running ``bbregister --init-coreg``). If ``None``, after ``bbregister`` is run, the resulting affine transform will be compared to the initial transform found by ``mri_coreg``. Excessive deviation will result in rejecting the BBR refinement and accepting the original, affine registration. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_bbreg_wf wf = init_bbreg_wf(use_bbr=True, bold2t1w_dof=9, omp_nthreads=1) Parameters use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration name : str, optional Workflow name (default: bbreg_wf) Inputs in_file Reference BOLD image to be registered t1_2_fsnative_reverse_transform FSL-style affine matrix translating from FreeSurfer T1.mgz to T1w subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID (must have folder in SUBJECTS_DIR) t1_brain Unused (see :py:func:`~fmriprep.workflows.util.init_fsl_bbr_wf`) t1_seg Unused (see :py:func:`~fmriprep.workflows.util.init_fsl_bbr_wf`) Outputs itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) itk_t1_to_bold Affine transform from T1 space to BOLD space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (mri_coreg registration returned) """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface([ 'in_file', 't1_2_fsnative_reverse_transform', 'subjects_dir', 'subject_id', # BBRegister 't1_seg', 't1_brain']), # FLIRT BBR name='inputnode') outputnode = pe.Node( niu.IdentityInterface(['itk_bold_to_t1', 'itk_t1_to_bold', 'out_report', 'fallback']), name='outputnode') mri_coreg = pe.Node( MRICoregRPT(dof=bold2t1w_dof, sep=[4], ftol=0.0001, linmintol=0.01, generate_report=not use_bbr), name='mri_coreg', n_procs=omp_nthreads, mem_gb=5) lta_concat = pe.Node(ConcatenateLTA(out_file='out.lta'), name='lta_concat') # XXX LTA-FSL-ITK may ultimately be able to be replaced with a straightforward # LTA-ITK transform, but right now the translation parameters are off. lta2fsl_fwd = pe.Node(fs.utils.LTAConvert(out_fsl=True), name='lta2fsl_fwd') lta2fsl_inv = pe.Node(fs.utils.LTAConvert(out_fsl=True, invert=True), name='lta2fsl_inv') fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd', mem_gb=DEFAULT_MEMORY_MIN_GB) fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, mri_coreg, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id'), ('in_file', 'source_file')]), # Output ITK transforms (inputnode, lta_concat, [('t1_2_fsnative_reverse_transform', 'in_lta2')]), (lta_concat, lta2fsl_fwd, [('out_file', 'in_lta')]), (lta_concat, lta2fsl_inv, [('out_file', 'in_lta')]), (inputnode, fsl2itk_fwd, [('t1_brain', 'reference_file'), ('in_file', 'source_file')]), (inputnode, fsl2itk_inv, [('in_file', 'reference_file'), ('t1_brain', 'source_file')]), (lta2fsl_fwd, fsl2itk_fwd, [('out_fsl', 'transform_file')]), (lta2fsl_inv, fsl2itk_inv, [('out_fsl', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_bold_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_bold')]), ]) # Short-circuit workflow building, use initial registration if use_bbr is False: workflow.connect([ (mri_coreg, outputnode, [('out_report', 'out_report')]), (mri_coreg, lta_concat, [('out_lta_file', 'in_lta1')])]) outputnode.inputs.fallback = True return workflow bbregister = pe.Node( BBRegisterRPT(dof=bold2t1w_dof, contrast_type='t2', registered_file=True, out_lta_file=True, generate_report=True), name='bbregister', mem_gb=12) workflow.connect([ (inputnode, bbregister, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id'), ('in_file', 'source_file')]), (mri_coreg, bbregister, [('out_lta_file', 'init_reg_file')]), ]) # Short-circuit workflow building, use boundary-based registration if use_bbr is True: workflow.connect([ (bbregister, outputnode, [('out_report', 'out_report')]), (bbregister, lta_concat, [('out_lta_file', 'in_lta1')])]) outputnode.inputs.fallback = False return workflow transforms = pe.Node(niu.Merge(2), run_without_submitting=True, name='transforms') reports = pe.Node(niu.Merge(2), run_without_submitting=True, name='reports') lta_ras2ras = pe.MapNode(fs.utils.LTAConvert(out_lta=True), iterfield=['in_lta'], name='lta_ras2ras', mem_gb=2) compare_transforms = pe.Node(niu.Function(function=compare_xforms), name='compare_transforms') select_transform = pe.Node(niu.Select(), run_without_submitting=True, name='select_transform') select_report = pe.Node(niu.Select(), run_without_submitting=True, name='select_report') workflow.connect([ (bbregister, transforms, [('out_lta_file', 'in1')]), (mri_coreg, transforms, [('out_lta_file', 'in2')]), # Normalize LTA transforms to RAS2RAS (inputs are VOX2VOX) and compare (transforms, lta_ras2ras, [('out', 'in_lta')]), (lta_ras2ras, compare_transforms, [('out_lta', 'lta_list')]), (compare_transforms, outputnode, [('out', 'fallback')]), # Select output transform (transforms, select_transform, [('out', 'inlist')]), (compare_transforms, select_transform, [('out', 'index')]), (select_transform, lta_concat, [('out', 'in_lta1')]), # Select output report (bbregister, reports, [('out_report', 'in1')]), (mri_coreg, reports, [('out_report', 'in2')]), (reports, select_report, [('out', 'inlist')]), (compare_transforms, select_report, [('out', 'index')]), (select_report, outputnode, [('out', 'out_report')]), ]) return workflow
def init_nonlinear_sdc_wf(bold_file, freesurfer, bold2t1w_dof, template, omp_nthreads, bold_pe='j', atlas_threshold=3, name='nonlinear_sdc_wf'): """ This workflow takes a skull-stripped T1w image and reference BOLD image and estimates a susceptibility distortion correction warp, using ANTs symmetric normalization (SyN) and the average fieldmap atlas described in [Treiber2016]_. SyN deformation is restricted to the phase-encoding (PE) direction. If no PE direction is specified, anterior-posterior PE is assumed. SyN deformation is also restricted to regions that are expected to have a >3mm (approximately 1 voxel) warp, based on the fieldmap atlas. This technique is a variation on those developed in [Huntenburg2014]_ and [Wang2017]_. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.syn import init_nonlinear_sdc_wf wf = init_nonlinear_sdc_wf( bold_file='/dataset/sub-01/func/sub-01_task-rest_bold.nii.gz', bold_pe='j', freesurfer=True, bold2t1w_dof=9, template='MNI152NLin2009cAsym', omp_nthreads=8) **Inputs** t1_brain skull-stripped, bias-corrected structural image bold_ref skull-stripped reference image t1_seg FAST segmentation white and gray matter, in native T1w space t1_2_mni_reverse_transform inverse registration transform of T1w image to MNI template **Outputs** out_reference_brain the ``bold_ref`` image after unwarping out_warp the corresponding :abbr:`DFM (displacements field map)` compatible with ANTs out_mask mask of the unwarped input file out_mask_report reportlet for the skullstripping .. [Huntenburg2014] Huntenburg, J. M. (2014) Evaluating Nonlinear Coregistration of BOLD EPI and T1w Images. Berlin: Master Thesis, Freie Universität. `PDF <http://pubman.mpdl.mpg.de/pubman/item/escidoc:2327525:5/component/escidoc:2327523/master_thesis_huntenburg_4686947.pdf>`_. .. [Treiber2016] Treiber, J. M. et al. (2016) Characterization and Correction of Geometric Distortions in 814 Diffusion Weighted Images, PLoS ONE 11(3): e0152472. doi:`10.1371/journal.pone.0152472 <https://doi.org/10.1371/journal.pone.0152472>`_. .. [Wang2017] Wang S, et al. (2017) Evaluation of Field Map and Nonlinear Registration Methods for Correction of Susceptibility Artifacts in Diffusion MRI. Front. Neuroinform. 11:17. doi:`10.3389/fninf.2017.00017 <https://doi.org/10.3389/fninf.2017.00017>`_. """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( ['t1_brain', 'bold_ref', 't1_2_mni_reverse_transform', 't1_seg']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface([ 'out_reference_brain', 'out_mask', 'out_warp', 'out_warp_report', 'out_mask_report' ]), name='outputnode') if bold_pe is None or bold_pe[0] not in ['i', 'j']: LOGGER.warning( 'Incorrect phase-encoding direction, assuming PA (posterior-to-anterior' ) bold_pe = 'j' # Collect predefined data # Atlas image and registration affine atlas_img = pkgr.resource_filename('fmriprep', 'data/fmap_atlas.nii.gz') atlas_2_template_affine = pkgr.resource_filename( 'fmriprep', 'data/fmap_atlas_2_{}_affine.mat'.format(template)) # Registration specifications affine_transform = pkgr.resource_filename('fmriprep', 'data/affine.json') syn_transform = pkgr.resource_filename('fmriprep', 'data/susceptibility_syn.json') invert_t1w = pe.Node(InvertT1w(), name='invert_t1w', mem_gb=0.3) ref_2_t1 = pe.Node(Registration(from_file=affine_transform), name='ref_2_t1', n_procs=omp_nthreads) t1_2_ref = pe.Node(ApplyTransforms(invert_transform_flags=[True]), name='t1_2_ref', n_procs=omp_nthreads) # 1) BOLD -> T1; 2) MNI -> T1; 3) ATLAS -> MNI transform_list = pe.Node(niu.Merge(3), name='transform_list', mem_gb=DEFAULT_MEMORY_MIN_GB) transform_list.inputs.in3 = atlas_2_template_affine # Inverting (1), then applying in reverse order: # # ATLAS -> MNI -> T1 -> BOLD atlas_2_ref = pe.Node( ApplyTransforms(invert_transform_flags=[True, False, False]), name='atlas_2_ref', n_procs=omp_nthreads, mem_gb=0.3) atlas_2_ref.inputs.input_image = atlas_img threshold_atlas = pe.Node(fsl.maths.MathsCommand( args='-thr {:.8g} -bin'.format(atlas_threshold), output_datatype='char'), name='threshold_atlas', mem_gb=0.3) fixed_image_masks = pe.Node(niu.Merge(2), name='fixed_image_masks', mem_gb=DEFAULT_MEMORY_MIN_GB) fixed_image_masks.inputs.in1 = 'NULL' restrict = [[int(bold_pe[0] == 'i'), int(bold_pe[0] == 'j'), 0]] * 2 syn = pe.Node(Registration(from_file=syn_transform, restrict_deformation=restrict), name='syn', n_procs=omp_nthreads) seg_2_ref = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True, invert_transform_flags=[True]), name='seg_2_ref', n_procs=omp_nthreads, mem_gb=0.3) sel_wm = pe.Node(niu.Function(function=extract_wm), name='sel_wm', mem_gb=DEFAULT_MEMORY_MIN_GB) syn_rpt = pe.Node(SimpleBeforeAfter(), name='syn_rpt', mem_gb=0.1) skullstrip_bold_wf = init_skullstrip_bold_wf() workflow.connect([ (inputnode, invert_t1w, [('t1_brain', 'in_file'), ('bold_ref', 'ref_file')]), (inputnode, ref_2_t1, [('bold_ref', 'moving_image')]), (invert_t1w, ref_2_t1, [('out_file', 'fixed_image')]), (inputnode, t1_2_ref, [('bold_ref', 'reference_image')]), (invert_t1w, t1_2_ref, [('out_file', 'input_image')]), (ref_2_t1, t1_2_ref, [('forward_transforms', 'transforms')]), (ref_2_t1, transform_list, [('forward_transforms', 'in1')]), (inputnode, transform_list, [('t1_2_mni_reverse_transform', 'in2')]), (inputnode, atlas_2_ref, [('bold_ref', 'reference_image')]), (transform_list, atlas_2_ref, [('out', 'transforms')]), (atlas_2_ref, threshold_atlas, [('output_image', 'in_file')]), (threshold_atlas, fixed_image_masks, [('out_file', 'in2')]), (inputnode, syn, [('bold_ref', 'moving_image')]), (t1_2_ref, syn, [('output_image', 'fixed_image')]), (fixed_image_masks, syn, [('out', 'fixed_image_masks')]), (inputnode, seg_2_ref, [('t1_seg', 'input_image')]), (ref_2_t1, seg_2_ref, [('forward_transforms', 'transforms')]), (syn, seg_2_ref, [('warped_image', 'reference_image')]), (seg_2_ref, sel_wm, [('output_image', 'in_seg')]), (inputnode, syn_rpt, [('bold_ref', 'before')]), (syn, syn_rpt, [('warped_image', 'after')]), (sel_wm, syn_rpt, [('out', 'wm_seg')]), (syn, skullstrip_bold_wf, [('warped_image', 'inputnode.in_file')]), (syn, outputnode, [('forward_transforms', 'out_warp')]), (skullstrip_bold_wf, outputnode, [('outputnode.skull_stripped_file', 'out_reference_brain'), ('outputnode.mask_file', 'out_mask'), ('outputnode.out_report', 'out_mask_report')]), (syn_rpt, outputnode, [('out_report', 'out_warp_report')]) ]) return workflow
def init_fsl_bbr_wf(use_bbr, bold2t1w_dof, name='fsl_bbr_wf'): """ This workflow uses FSL FLIRT to register a BOLD image to a T1-weighted structural image, using a boundary-based registration (BBR) cost function. It is a counterpart to :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf`, which performs the same task using FreeSurfer's ``bbregister``. The ``use_bbr`` option permits a high degree of control over registration. If ``False``, standard, rigid coregistration will be performed by FLIRT. If ``True``, FLIRT-BBR will be seeded with the initial transform found by the rigid coregistration. If ``None``, after FLIRT-BBR is run, the resulting affine transform will be compared to the initial transform found by FLIRT. Excessive deviation will result in rejecting the BBR refinement and accepting the original, affine registration. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_fsl_bbr_wf wf = init_fsl_bbr_wf(use_bbr=True, bold2t1w_dof=9) Parameters use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration name : str, optional Workflow name (default: fsl_bbr_wf) Inputs in_file Reference BOLD image to be registered t1_brain Skull-stripped T1-weighted structural image t1_seg FAST segmentation of ``t1_brain`` t1_2_fsnative_reverse_transform Unused (see :py:func:`~fmriprep.workflows.util.init_bbreg_wf`) subjects_dir Unused (see :py:func:`~fmriprep.workflows.util.init_bbreg_wf`) subject_id Unused (see :py:func:`~fmriprep.workflows.util.init_bbreg_wf`) Outputs itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) itk_t1_to_bold Affine transform from T1 space to BOLD space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (rigid FLIRT registration returned) """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface([ 'in_file', 't1_2_fsnative_reverse_transform', 'subjects_dir', 'subject_id', # BBRegister 't1_seg', 't1_brain']), # FLIRT BBR name='inputnode') outputnode = pe.Node( niu.IdentityInterface(['itk_bold_to_t1', 'itk_t1_to_bold', 'out_report', 'fallback']), name='outputnode') wm_mask = pe.Node(niu.Function(function=extract_wm), name='wm_mask') flt_bbr_init = pe.Node(FLIRTRPT(dof=6, generate_report=not use_bbr), name='flt_bbr_init') invt_bbr = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='invt_bbr', mem_gb=DEFAULT_MEMORY_MIN_GB) # BOLD to T1 transform matrix is from fsl, using c3 tools to convert to # something ANTs will like. fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd', mem_gb=DEFAULT_MEMORY_MIN_GB) fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, flt_bbr_init, [('in_file', 'in_file'), ('t1_brain', 'reference')]), (inputnode, fsl2itk_fwd, [('t1_brain', 'reference_file'), ('in_file', 'source_file')]), (inputnode, fsl2itk_inv, [('in_file', 'reference_file'), ('t1_brain', 'source_file')]), (invt_bbr, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_bold_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_bold')]), ]) # Short-circuit workflow building, use rigid registration if use_bbr is False: workflow.connect([ (flt_bbr_init, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr_init, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (flt_bbr_init, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = True return workflow flt_bbr = pe.Node( FLIRTRPT(cost_func='bbr', dof=bold2t1w_dof, generate_report=True, schedule=op.join(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch')), name='flt_bbr') workflow.connect([ (inputnode, wm_mask, [('t1_seg', 'in_seg')]), (inputnode, flt_bbr, [('in_file', 'in_file'), ('t1_brain', 'reference')]), (flt_bbr_init, flt_bbr, [('out_matrix_file', 'in_matrix_file')]), (wm_mask, flt_bbr, [('out', 'wm_seg')]), ]) # Short-circuit workflow building, use boundary-based registration if use_bbr is True: workflow.connect([ (flt_bbr, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (flt_bbr, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = False return workflow transforms = pe.Node(niu.Merge(2), run_without_submitting=True, name='transforms') reports = pe.Node(niu.Merge(2), run_without_submitting=True, name='reports') compare_transforms = pe.Node(niu.Function(function=compare_xforms), name='compare_transforms') select_transform = pe.Node(niu.Select(), run_without_submitting=True, name='select_transform') select_report = pe.Node(niu.Select(), run_without_submitting=True, name='select_report') fsl_to_lta = pe.MapNode(fs.utils.LTAConvert(out_lta=True), iterfield=['in_fsl'], name='fsl_to_lta') workflow.connect([ (flt_bbr, transforms, [('out_matrix_file', 'in1')]), (flt_bbr_init, transforms, [('out_matrix_file', 'in2')]), # Convert FSL transforms to LTA (RAS2RAS) transforms and compare (inputnode, fsl_to_lta, [('in_file', 'source_file'), ('t1_brain', 'target_file')]), (transforms, fsl_to_lta, [('out', 'in_fsl')]), (fsl_to_lta, compare_transforms, [('out_lta', 'lta_list')]), (compare_transforms, outputnode, [('out', 'fallback')]), # Select output transform (transforms, select_transform, [('out', 'inlist')]), (compare_transforms, select_transform, [('out', 'index')]), (select_transform, invt_bbr, [('out', 'in_file')]), (select_transform, fsl2itk_fwd, [('out', 'transform_file')]), (flt_bbr, reports, [('out_report', 'in1')]), (flt_bbr_init, reports, [('out_report', 'in2')]), (reports, select_report, [('out', 'inlist')]), (compare_transforms, select_report, [('out', 'index')]), (select_report, outputnode, [('out', 'out_report')]), ]) return workflow
def init_phdiff_wf(reportlets_dir, omp_nthreads, name='phdiff_wf'): """ Estimates the fieldmap using a phase-difference image and one or more magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` acquisitions. The `original code was taken from nipype <https://github.com/nipy/nipype/blob/master/nipype/workflows/dmri/fsl/artifacts.py#L514>`_. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.phdiff import init_phdiff_wf wf = init_phdiff_wf(reportlets_dir='.', omp_nthreads=1) Outputs:: outputnode.fmap_ref - The average magnitude image, skull-stripped outputnode.fmap_mask - The brain mask applied to the fieldmap outputnode.fmap - The estimated fieldmap in Hz """ inputnode = pe.Node( niu.IdentityInterface(fields=['magnitude', 'phasediff']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') def _pick1st(inlist): return inlist[0] # Read phasediff echo times meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01, run_without_submitting=True) dte = pe.Node(niu.Function(function=_delta_te), name='dte', mem_gb=0.01) # Merge input magnitude images magmrg = pe.Node(IntraModalMerge(), name='magmrg') # de-gradient the fields ("bias/illumination artifact") n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True, num_threads=omp_nthreads), name='n4', n_procs=omp_nthreads) bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') ds_fmap_mask = pe.Node(DerivativesDataSink(base_directory=reportlets_dir, suffix='fmap_mask'), name='ds_fmap_mask', mem_gb=0.01, run_without_submitting=True) # uses mask from bet; outputs a mask # dilate = pe.Node(fsl.maths.MathsCommand( # nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') # phase diff -> radians pha2rads = pe.Node(niu.Function(function=siemens2rads), name='pha2rads') # FSL PRELUDE will perform phase-unwrapping prelude = pe.Node(fsl.PRELUDE(), name='prelude') denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', kernel_size=3), name='denoise') demean = pe.Node(niu.Function(function=demean_image), name='demean') cleanup_wf = cleanup_edge_pipeline(name="cleanup_wf") compfmap = pe.Node(niu.Function(function=phdiff2fmap), name='compfmap') # The phdiff2fmap interface is equivalent to: # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') # rsec2hz (divide by 2pi) workflow = pe.Workflow(name=name) workflow.connect([ (inputnode, meta, [('phasediff', 'in_file')]), (inputnode, magmrg, [('magnitude', 'in_files')]), (magmrg, n4, [('out_avg', 'input_image')]), (n4, prelude, [('output_image', 'magnitude_file')]), (n4, bet, [('output_image', 'in_file')]), (bet, prelude, [('mask_file', 'mask_file')]), (inputnode, pha2rads, [('phasediff', 'in_file')]), (pha2rads, prelude, [('out', 'phase_file')]), (meta, dte, [('out_dict', 'in_values')]), (dte, compfmap, [('out', 'delta_te')]), (prelude, denoise, [('unwrapped_phase_file', 'in_file')]), (denoise, demean, [('out_file', 'in_file')]), (demean, cleanup_wf, [('out', 'inputnode.in_file')]), (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), (cleanup_wf, compfmap, [('outputnode.out_file', 'in_file')]), (compfmap, outputnode, [('out', 'fmap')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), (inputnode, ds_fmap_mask, [('phasediff', 'source_file')]), (bet, ds_fmap_mask, [('out_report', 'in_file')]), ]) return workflow
def init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=False): ''' This workflow wraps `ICA-AROMA`_ to identify and remove motion-related independent components from a BOLD time series. The following steps are performed: #. Smooth data using SUSAN #. Run MELODIC outside of ICA-AROMA to generate the report #. Run ICA-AROMA #. Aggregate identified motion components (aggressive) to TSV #. Return classified_motion_ICs and melodic_mix for user to complete non-aggressive denoising in T1w space Additionally, non-aggressive denoising is performed on the BOLD series resampled into MNI space. .. workflow:: :graph2use: orig :simpleform: yes from fmriprep.workflows.confounds import init_ica_aroma_wf wf = init_ica_aroma_wf() Parameters ignore_aroma_err : bool Do not fail on ICA-AROMA errors Inputs bold_mni BOLD series, resampled to template space movpar_file SPM-formatted motion parameters file bold_mask_mni BOLD series mask in template space Outputs aroma_confounds TSV of confounds identified as noise by ICA-AROMA aroma_noise_ics CSV of noise components identified by ICA-AROMA melodic_mix FSL MELODIC mixing matrix nonaggr_denoised_file BOLD series with non-aggressive ICA-AROMA denoising applied out_report Reportlet visualizing MELODIC ICs, with ICA-AROMA signal/noise labels .. _ICA-AROMA: https://github.com/rhr-pruim/ICA-AROMA ''' workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['bold_mni', 'movpar_file', 'bold_mask_mni']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'aroma_confounds', 'out_report', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file' ]), name='outputnode') calc_median_val = pe.Node(fsl.ImageStats(op_string='-k %s -p 50'), name='calc_median_val') calc_bold_mean = pe.Node(fsl.MeanImage(), name='calc_bold_mean') def getusans_func(image, thresh): return [tuple([image, thresh])] getusans = pe.Node(niu.Function(function=getusans_func, output_names=['usans']), name='getusans', mem_gb=0.01) smooth = pe.Node(fsl.SUSAN(fwhm=6.0), name='smooth') # melodic node melodic = pe.Node(fsl.MELODIC(no_bet=True, no_mm=True), name="melodic") # ica_aroma node ica_aroma = pe.Node(nws.ICA_AROMARPT(denoise_type='nonaggr', generate_report=True), name='ica_aroma') # extract the confound ICs from the results ica_aroma_confound_extraction = pe.Node( ICAConfounds(ignore_aroma_err=ignore_aroma_err), name='ica_aroma_confound_extraction') def _getbtthresh(medianval): return 0.75 * medianval # connect the nodes workflow.connect([ # Connect input nodes to complete smoothing (inputnode, calc_median_val, [('bold_mni', 'in_file'), ('bold_mask_mni', 'mask_file')]), (inputnode, calc_bold_mean, [('bold_mni', 'in_file')]), (calc_bold_mean, getusans, [('out_file', 'image')]), (calc_median_val, getusans, [('out_stat', 'thresh')]), (inputnode, smooth, [('bold_mni', 'in_file')]), (getusans, smooth, [('usans', 'usans')]), (calc_median_val, smooth, [(('out_stat', _getbtthresh), 'brightness_threshold')]), # connect smooth to melodic (smooth, melodic, [('smoothed_file', 'in_files')]), (inputnode, melodic, [('bold_mask_mni', 'mask')]), # connect nodes to ICA-AROMA (smooth, ica_aroma, [('smoothed_file', 'in_file')]), (inputnode, ica_aroma, [('bold_mask_mni', 'report_mask'), ('movpar_file', 'motion_parameters')]), (melodic, ica_aroma, [('out_dir', 'melodic_dir')]), # generate tsvs from ICA-AROMA (ica_aroma, ica_aroma_confound_extraction, [('out_dir', 'in_directory') ]), # output for processing and reporting (ica_aroma_confound_extraction, outputnode, [('aroma_confounds', 'aroma_confounds'), ('aroma_noise_ics', 'aroma_noise_ics'), ('melodic_mix', 'melodic_mix')]), # TODO change melodic report to reflect noise and non-noise components (ica_aroma, outputnode, [('out_report', 'out_report'), ('nonaggr_denoised_file', 'nonaggr_denoised_file')]), ]) return workflow
def compute_iqms(settings, modality='T1w', name='ComputeIQMs'): """ Workflow that actually computes the IQMs .. workflow:: from mriqc.workflows.anatomical import compute_iqms wf = compute_iqms(settings={'output_dir': 'out'}) """ from .utils import _tofloat from ..interfaces.anatomical import Harmonize workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_file', 'in_ras', 'brainmask', 'airmask', 'artmask', 'headmask', 'rotmask', 'segmentation', 'inu_corrected', 'in_inu', 'pvms', 'metadata', 'inverse_composite_transform' ]), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['out_file', 'out_noisefit']), name='outputnode') deriv_dir = check_folder( op.abspath(op.join(settings['output_dir'], 'derivatives'))) # Extract metadata meta = pe.Node(ReadSidecarJSON(), name='metadata') # Add provenance addprov = pe.Node(niu.Function(function=_add_provenance), name='provenance') addprov.inputs.settings = {'testing': settings.get('testing', False)} # AFNI check smoothing fwhm_interface = get_fwhmx() fwhm = pe.Node(fwhm_interface, name='smoothness') # Harmonize homog = pe.Node(Harmonize(), name='harmonize') # Mortamet's QI2 getqi2 = pe.Node(ComputeQI2(erodemsk=settings.get('testing', False)), name='ComputeQI2') # Compute python-coded measures measures = pe.Node(StructuralQC(), 'measures') # Project MNI segmentation to T1 space invt = pe.MapNode(ants.ApplyTransforms(dimension=3, default_value=0, interpolation='Linear', float=True), iterfield=['input_image'], name='MNItpms2t1') invt.inputs.input_image = [ op.join(get_mni_icbm152_nlin_asym_09c(), fname + '.nii.gz') for fname in ['1mm_tpm_csf', '1mm_tpm_gm', '1mm_tpm_wm'] ] datasink = pe.Node(IQMFileSink(modality=modality, out_dir=deriv_dir), name='datasink') datasink.inputs.modality = modality def _getwm(inlist): return inlist[-1] workflow.connect([ (inputnode, meta, [('in_file', 'in_file')]), (meta, datasink, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('acq_id', 'acq_id'), ('rec_id', 'rec_id'), ('run_id', 'run_id'), ('out_dict', 'metadata')]), (inputnode, addprov, [('in_file', 'in_file'), ('airmask', 'air_msk'), ('rotmask', 'rot_msk')]), (inputnode, getqi2, [('in_ras', 'in_file'), ('airmask', 'air_msk')]), (inputnode, homog, [('inu_corrected', 'in_file'), (('pvms', _getwm), 'wm_mask')]), (inputnode, measures, [('in_inu', 'in_bias'), ('in_ras', 'in_file'), ('airmask', 'air_msk'), ('headmask', 'head_msk'), ('artmask', 'artifact_msk'), ('rotmask', 'rot_msk'), ('segmentation', 'in_segm'), ('pvms', 'in_pvms')]), (inputnode, fwhm, [('in_ras', 'in_file'), ('brainmask', 'mask')]), (inputnode, invt, [('in_ras', 'reference_image'), ('inverse_composite_transform', 'transforms')]), (homog, measures, [('out_file', 'in_noinu')]), (invt, measures, [('output_image', 'mni_tpms')]), (fwhm, measures, [(('fwhm', _tofloat), 'in_fwhm')]), (measures, datasink, [('out_qc', 'root')]), (addprov, datasink, [('out', 'provenance')]), (getqi2, datasink, [('qi2', 'qi_2')]), (getqi2, outputnode, [('out_file', 'out_noisefit')]), (datasink, outputnode, [('out_file', 'out_file')]), ]) return workflow
def init_fmriprep_wf( subject_list, task_id, run_uuid, ignore, debug, anat_only, omp_nthreads, skull_strip_ants, reportlets_dir, output_dir, bids_dir, freesurfer, output_spaces, template, hires, bold2t1w_dof, fmap_bspline, fmap_demean, use_syn, force_syn, use_aroma, ignore_aroma_err, output_grid_ref, ): fmriprep_wf = pe.Workflow(name='fmriprep_wf') if freesurfer: fsdir = pe.Node(BIDSFreeSurferDir( derivatives=output_dir, freesurfer_home=os.getenv('FREESURFER_HOME'), spaces=output_spaces), name='fsdir') for subject_id in subject_list: single_subject_wf = init_single_subject_wf( subject_id=subject_id, task_id=task_id, name="single_subject_" + subject_id + "_wf", ignore=ignore, debug=debug, anat_only=anat_only, omp_nthreads=omp_nthreads, skull_strip_ants=skull_strip_ants, reportlets_dir=reportlets_dir, output_dir=output_dir, bids_dir=bids_dir, freesurfer=freesurfer, output_spaces=output_spaces, template=template, hires=hires, bold2t1w_dof=bold2t1w_dof, fmap_bspline=fmap_bspline, fmap_demean=fmap_demean, use_syn=use_syn, force_syn=force_syn, output_grid_ref=output_grid_ref, use_aroma=use_aroma, ignore_aroma_err=ignore_aroma_err) single_subject_wf.config['execution']['crashdump_dir'] = (os.path.join( output_dir, "fmriprep", "sub-" + subject_id, 'log', run_uuid)) for node in single_subject_wf._get_all_nodes(): node.config = deepcopy(single_subject_wf.config) if freesurfer: fmriprep_wf.connect(fsdir, 'subjects_dir', single_subject_wf, 'inputnode.subjects_dir') else: fmriprep_wf.add_nodes([single_subject_wf]) return fmriprep_wf
def individual_reports(settings, name='ReportsWorkflow'): """ Encapsulates nodes writing plots .. workflow:: from mriqc.workflows.anatomical import individual_reports wf = individual_reports(settings={'output_dir': 'out'}) """ from ..interfaces import PlotMosaic from ..reports import individual_html verbose = settings.get('verbose_reports', False) pages = 2 extra_pages = 0 if verbose: extra_pages = 7 workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_ras', 'brainmask', 'headmask', 'airmask', 'artmask', 'rotmask', 'segmentation', 'inu_corrected', 'noisefit', 'in_iqms', 'mni_report' ]), name='inputnode') 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') mplots = pe.Node(niu.Merge(pages + extra_pages), 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=settings['output_dir'], parameterization=False), name='dsplots') dsplots.inputs.container = 'reports' workflow.connect([ (inputnode, rnode, [('in_iqms', 'in_iqms')]), (inputnode, mosaic_zoom, [('in_ras', 'in_file'), ('brainmask', 'bbox_mask_file')]), (inputnode, mosaic_noise, [('in_ras', 'in_file')]), (mosaic_zoom, mplots, [('out_file', "in1")]), (mosaic_noise, mplots, [('out_file', "in2")]), (mplots, rnode, [('out', 'in_plots')]), (rnode, dsplots, [('out_file', "@html_report")]), ]) if not verbose: return workflow from ..interfaces.viz import PlotContours from ..viz.utils import plot_bg_dist plot_bgdist = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=plot_bg_dist), name='PlotBackground') plot_segm = pe.Node(PlotContours(display_mode='z', levels=[.5, 1.5, 2.5], cut_coords=10, colors=['r', 'g', 'b']), name='PlotSegmentation') plot_bmask = pe.Node(PlotContours(display_mode='z', levels=[.5], colors=['r'], cut_coords=10, out_file='bmask'), name='PlotBrainmask') plot_airmask = pe.Node(PlotContours(display_mode='x', levels=[.5], colors=['r'], cut_coords=6, out_file='airmask'), name='PlotAirmask') plot_headmask = pe.Node(PlotContours(display_mode='x', levels=[.5], colors=['r'], cut_coords=6, out_file='headmask'), name='PlotHeadmask') plot_artmask = pe.Node(PlotContours(display_mode='z', levels=[.5], colors=['r'], cut_coords=10, out_file='artmask', saturate=True), name='PlotArtmask') workflow.connect([ (inputnode, plot_segm, [('in_ras', 'in_file'), ('segmentation', 'in_contours')]), (inputnode, plot_bmask, [('in_ras', 'in_file'), ('brainmask', 'in_contours')]), (inputnode, plot_headmask, [('in_ras', 'in_file'), ('headmask', 'in_contours')]), (inputnode, plot_airmask, [('in_ras', 'in_file'), ('airmask', 'in_contours')]), (inputnode, plot_artmask, [('in_ras', 'in_file'), ('artmask', 'in_contours')]), (inputnode, plot_bgdist, [('noisefit', 'in_file')]), (inputnode, mplots, [('mni_report', "in%d" % (pages + 1))]), (plot_bmask, mplots, [('out_file', 'in%d' % (pages + 2))]), (plot_segm, mplots, [('out_file', 'in%d' % (pages + 3))]), (plot_artmask, mplots, [('out_file', 'in%d' % (pages + 4))]), (plot_headmask, mplots, [('out_file', 'in%d' % (pages + 5))]), (plot_airmask, mplots, [('out_file', 'in%d' % (pages + 6))]), (plot_bgdist, mplots, [('out_file', 'in%d' % (pages + 7))]) ]) return workflow
def init_single_subject_wf(subject_id, task_id, name, ignore, debug, low_mem, anat_only, longitudinal, omp_nthreads, skull_strip_ants, reportlets_dir, output_dir, bids_dir, freesurfer, output_spaces, template, medial_surface_nan, hires, bold2t1w_dof, fmap_bspline, fmap_demean, use_syn, force_syn, output_grid_ref, use_aroma, ignore_aroma_err): """ 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 wf = init_single_subject_wf(subject_id='test', name='single_subject_wf', task_id='', longitudinal=False, omp_nthreads=1, freesurfer=True, reportlets_dir='.', output_dir='.', bids_dir='.', skull_strip_ants=True, template='MNI152NLin2009cAsym', output_spaces=['T1w', 'fsnative', 'template', 'fsaverage5'], medial_surface_nan=False, ignore=[], debug=False, low_mem=False, anat_only=False, hires=True, bold2t1w_dof=9, fmap_bspline=False, fmap_demean=True, use_syn=True, force_syn=True, output_grid_ref=None, use_aroma=False, ignore_aroma_err=False) Parameters subject_id : str List of subject labels task_id : str or None Task ID of BOLD series to preprocess, or ``None`` to preprocess all name : str Name of workflow ignore : list Preprocessing steps to skip (may include "slicetiming", "fieldmaps") debug : bool Enable debugging outputs low_mem : bool Write uncompressed .nii files in some cases to reduce memory usage anat_only : bool Disable functional workflows longitudinal : bool Treat multiple sessions as longitudinal (may increase runtime) See sub-workflows for specific differences omp_nthreads : int Maximum number of threads an individual process may use skull_strip_ants : bool Use ANTs BrainExtraction.sh-based skull-stripping workflow If ``False``, uses a faster AFNI-based workflow reportlets_dir : str Directory in which to save reportlets output_dir : str Directory in which to save derivatives bids_dir : str Root directory of BIDS dataset freesurfer : bool Enable FreeSurfer surface reconstruction (may increase runtime) output_spaces : list List of output spaces functional images are to be resampled to. Some parts of pipeline will only be instantiated for some output spaces. Valid spaces: - T1w - template - fsnative - fsaverage (or other pre-existing FreeSurfer templates) template : str Name of template targeted by `'template'` output space medial_surface_nan : bool Replace medial wall values with NaNs on functional GIFTI files hires : bool Enable sub-millimeter preprocessing in FreeSurfer bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration fmap_bspline : bool **Experimental**: Fit B-Spline field using least-squares fmap_demean : bool Demean voxel-shift map during unwarp use_syn : bool **Experimental**: Enable ANTs SyN-based susceptibility distortion correction (SDC). If fieldmaps are present and enabled, this is not run, by default. force_syn : bool **Temporary**: Always run SyN-based SDC output_grid_ref : str or None Path of custom reference image for normalization use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series ignore_aroma_err : bool Do not fail on ICA-AROMA errors Inputs subjects_dir FreeSurfer SUBJECTS_DIR """ 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'] } layout = None else: subject_data, layout = collect_data(bids_dir, subject_id, task_id) # 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 = pe.Workflow(name=name) 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(), name='bids_info', run_without_submitting=True) summary = pe.Node(SubjectSummary(output_spaces=output_spaces, template=template), name='summary', run_without_submitting=True) about = pe.Node(AboutSummary(version=__version__, command=' '.join(sys.argv)), name='about', run_without_submitting=True) ds_summary_report = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, suffix='summary'), name='ds_summary_report', run_without_submitting=True) ds_about_report = pe.Node(DerivativesDataSink( base_directory=reportlets_dir, suffix='about'), name='ds_about_report', run_without_submitting=True) # Preprocessing of T1w (includes registration to MNI) anat_preproc_wf = init_anat_preproc_wf(name="anat_preproc_wf", skull_strip_ants=skull_strip_ants, output_spaces=output_spaces, template=template, debug=debug, longitudinal=longitudinal, omp_nthreads=omp_nthreads, freesurfer=freesurfer, hires=hires, reportlets_dir=reportlets_dir, output_dir=output_dir) 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_id', 'subject_id')]), (bidssrc, anat_preproc_wf, [('t1w', 'inputnode.t1w'), ('t2w', 'inputnode.t2w')]), (summary, anat_preproc_wf, [('subject_id', 'inputnode.subject_id')]), (bidssrc, ds_summary_report, [(('t1w', fix_multi_T1w_source_name), 'source_file')]), (summary, ds_summary_report, [('out_report', 'in_file')]), (bidssrc, ds_about_report, [(('t1w', fix_multi_T1w_source_name), 'source_file')]), (about, ds_about_report, [('out_report', 'in_file')]), ]) if anat_only: return workflow for bold_file in subject_data['bold']: func_preproc_wf = init_func_preproc_wf( bold_file=bold_file, layout=layout, ignore=ignore, freesurfer=freesurfer, bold2t1w_dof=bold2t1w_dof, reportlets_dir=reportlets_dir, output_spaces=output_spaces, template=template, medial_surface_nan=medial_surface_nan, output_dir=output_dir, omp_nthreads=omp_nthreads, low_mem=low_mem, fmap_bspline=fmap_bspline, fmap_demean=fmap_demean, use_syn=use_syn, force_syn=force_syn, debug=debug, output_grid_ref=output_grid_ref, use_aroma=use_aroma, ignore_aroma_err=ignore_aroma_err) workflow.connect([(anat_preproc_wf, func_preproc_wf, [('outputnode.t1_preproc', 'inputnode.t1_preproc'), ('outputnode.t1_brain', 'inputnode.t1_brain'), ('outputnode.t1_mask', 'inputnode.t1_mask'), ('outputnode.t1_seg', 'inputnode.t1_seg'), ('outputnode.t1_tpms', 'inputnode.t1_tpms'), ('outputnode.t1_2_mni_forward_transform', 'inputnode.t1_2_mni_forward_transform'), ('outputnode.t1_2_mni_reverse_transform', 'inputnode.t1_2_mni_reverse_transform')])]) if freesurfer: workflow.connect([ (anat_preproc_wf, func_preproc_wf, [('outputnode.subjects_dir', 'inputnode.subjects_dir'), ('outputnode.subject_id', 'inputnode.subject_id'), ('outputnode.fs_2_t1_transform', 'inputnode.fs_2_t1_transform')]), ]) return workflow
def init_surface_recon_wf(omp_nthreads, hires, name='surface_recon_wf'): workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface( fields=['t1w', 't2w', 'skullstripped_t1', 'subjects_dir', 'subject_id']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface( fields=['subjects_dir', 'subject_id', 'fs_2_t1_transform', 'surfaces', 'out_report']), name='outputnode') recon_config = pe.Node(FSDetectInputs(hires_enabled=hires), name='recon_config', run_without_submitting=True) autorecon1 = pe.Node( fs.ReconAll( directive='autorecon1', flags='-noskullstrip', openmp=omp_nthreads), name='autorecon1', n_procs=omp_nthreads) autorecon1.interface._can_resume = False skull_strip_extern = pe.Node(FSInjectBrainExtracted(), name='skull_strip_extern', run_without_submitting=True) fs_transform = pe.Node( fs.Tkregister2(fsl_out='freesurfer2subT1.mat', reg_header=True), name='fs_transform') autorecon_resume_wf = init_autorecon_resume_wf(omp_nthreads=omp_nthreads) gifti_surface_wf = init_gifti_surface_wf() workflow.connect([ # Configuration (inputnode, recon_config, [('t1w', 't1w_list'), ('t2w', 't2w_list')]), # Passing subjects_dir / subject_id enforces serial order (inputnode, autorecon1, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (autorecon1, skull_strip_extern, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (skull_strip_extern, autorecon_resume_wf, [('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id')]), (autorecon_resume_wf, gifti_surface_wf, [ ('outputnode.subjects_dir', 'inputnode.subjects_dir'), ('outputnode.subject_id', 'inputnode.subject_id')]), # Reconstruction phases (inputnode, autorecon1, [('t1w', 'T1_files')]), (recon_config, autorecon1, [('t2w', 'T2_file'), ('hires', 'hires'), # First run only (recon-all saves expert options) ('mris_inflate', 'mris_inflate')]), (inputnode, skull_strip_extern, [('skullstripped_t1', 'in_brain')]), (recon_config, autorecon_resume_wf, [('use_t2w', 'inputnode.use_T2')]), # Construct transform from FreeSurfer conformed image to FMRIPREP # reoriented image (inputnode, fs_transform, [('t1w', 'target_image')]), (autorecon1, fs_transform, [('T1', 'moving_image')]), # Output (autorecon_resume_wf, outputnode, [('outputnode.subjects_dir', 'subjects_dir'), ('outputnode.subject_id', 'subject_id'), ('outputnode.out_report', 'out_report')]), (gifti_surface_wf, outputnode, [('outputnode.surfaces', 'surfaces')]), (fs_transform, outputnode, [('fsl_file', 'fs_2_t1_transform')]), ]) return workflow