def init_fitlins_wf(database_path, out_dir, analysis_level, space, desc=None, model=None, participants=None, smoothing=None, drop_missing=False, base_dir=None, name='fitlins_wf'): from nipype.pipeline import engine as pe from nipype.interfaces import utility as niu from ..interfaces.bids import (ModelSpecLoader, LoadBIDSModel, BIDSSelect, BIDSDataSink) from ..interfaces.nistats import DesignMatrix, FirstLevelModel, SecondLevelModel from ..interfaces.visualizations import (DesignPlot, DesignCorrelationPlot, ContrastMatrixPlot, GlassBrainPlot) from ..interfaces.utils import MergeAll, CollateWithMetadata wf = pe.Workflow(name=name, base_dir=base_dir) # Find the appropriate model file(s) specs = ModelSpecLoader(database_path=database_path) if model is not None: specs.inputs.model = model model_dict = specs.run().outputs.model_spec if not model_dict: raise RuntimeError("Unable to find or construct models") if isinstance(model_dict, list): raise RuntimeError( "Currently unable to run multiple models in parallel - " "please specify model") # # Load and run the model # loader = pe.Node(LoadBIDSModel(database_path=database_path, model=model_dict, selectors={ 'desc': desc, 'space': space }), name='loader') if participants is not None: loader.inputs.selectors['subject'] = participants if database_path is not None: loader.inputs.database_path = database_path # Select preprocessed BOLD series to analyze getter = pe.Node(BIDSSelect(database_path=database_path, selectors={ 'suffix': 'bold', 'extension': ['nii.gz', 'nii', 'gii'] }), name='getter') if smoothing: smoothing_params = smoothing.split(':', 2) # Convert old style and warn; this should turn into an (informative) error around 0.5.0 if smoothing_params[0] == 'iso': smoothing_params = (smoothing_params[1], 'l1', smoothing_params[0]) warnings.warn( "The format for smoothing arguments has changed. Please use " f"{':'.join(smoothing_params)} instead of {smoothing}.", FutureWarning) # Add defaults to simplify later logic if len(smoothing_params) == 1: smoothing_params.extend(('l1', 'iso')) elif len(smoothing_params) == 2: smoothing_params.append('iso') smoothing_fwhm, smoothing_level, smoothing_type = smoothing_params smoothing_fwhm = float(smoothing_fwhm) if smoothing_type not in ('iso'): raise ValueError(f"Unknown smoothing type {smoothing_type}") # Check that smmoothing level exists in model if smoothing_level.lower().startswith("l"): if int(smoothing_level[1:]) > len(model_dict['Steps']): raise ValueError(f"Invalid smoothing level {smoothing_level}") elif smoothing_level.lower() not in (step['Level'].lower() for step in model_dict['Steps']): raise ValueError(f"Invalid smoothing level {smoothing_level}") design_matrix = pe.MapNode(DesignMatrix(drop_missing=drop_missing), iterfield=['session_info', 'bold_file'], name='design_matrix') l1_model = pe.MapNode( FirstLevelModel(), iterfield=['design_matrix', 'contrast_info', 'bold_file', 'mask_file'], mem_gb=3, name='l1_model') def _deindex(tsv): from pathlib import Path import pandas as pd out_tsv = str(Path.cwd() / Path(tsv).name) pd.read_csv(tsv, sep='\t', index_col=0).to_csv(out_tsv, sep='\t', index=False) return out_tsv deindex_tsv = pe.MapNode(niu.Function(function=_deindex), iterfield=['tsv'], name='deindex_tsv') # Set up common patterns image_pattern = 'reports/[sub-{subject}/][ses-{session}/]figures/[run-{run}/]' \ '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \ '[_rec-{reconstruction}][_run-{run}][_echo-{echo}]_' \ '{suffix<design|corr|contrasts>}.svg' contrast_plot_pattern = 'reports/[sub-{subject}/][ses-{session}/]figures/[run-{run}/]' \ '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \ '[_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}]_' \ 'contrast-{contrast}_stat-{stat<effect|variance|z|p|t|F|FEMA>}_ortho.png' design_matrix_pattern = '[sub-{subject}/][ses-{session}/]' \ '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \ '[_rec-{reconstruction}][_run-{run}][_echo-{echo}]_{suffix<design>}.tsv' contrast_pattern = '[sub-{subject}/][ses-{session}/]' \ '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \ '[_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}]_' \ 'contrast-{contrast}_stat-{stat<effect|variance|z|p|t|F|FEMA>}_statmap.nii.gz' model_map_pattern = '[sub-{subject}/][ses-{session}/]' \ '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \ '[_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}]_' \ 'stat-{stat<rSquare|logLikelihood>}_statmap.nii.gz' # Set up general interfaces # # HTML snippets to be included directly in report, not # saved as individual derivative files # reportlet_dir = Path(base_dir) / 'reportlets' / 'fitlins' reportlet_dir.mkdir(parents=True, exist_ok=True) snippet_pattern = '[sub-{subject}/][ses-{session}/][sub-{subject}_]' \ '[ses-{session}_]task-{task}_[run-{run}_]snippet.html' ds_model_warnings = pe.MapNode(BIDSDataSink( base_directory=str(reportlet_dir), path_patterns=snippet_pattern), iterfield=['entities', 'in_file'], run_without_submitting=True, name='ds_model_warning') plot_design = pe.MapNode(DesignPlot(image_type='svg'), iterfield='data', name='plot_design') plot_corr = pe.MapNode(DesignCorrelationPlot(image_type='svg'), iterfield=['data', 'contrast_info'], name='plot_corr') plot_l1_contrast_matrix = pe.MapNode(ContrastMatrixPlot(image_type='svg'), iterfield=['data', 'contrast_info'], name='plot_l1_contrast_matrix') ds_design = pe.MapNode(BIDSDataSink(base_directory=out_dir, fixed_entities={'suffix': 'design'}, path_patterns=image_pattern), iterfield=['entities', 'in_file'], run_without_submitting=True, name='ds_design') ds_design_matrix = pe.MapNode(BIDSDataSink( base_directory=out_dir, fixed_entities={'suffix': 'design'}, path_patterns=design_matrix_pattern), iterfield=['entities', 'in_file'], run_without_submitting=True, name='ds_design_matrix') ds_corr = pe.MapNode(BIDSDataSink(base_directory=out_dir, fixed_entities={'suffix': 'corr'}, path_patterns=image_pattern), iterfield=['entities', 'in_file'], run_without_submitting=True, name='ds_corr') ds_l1_contrasts = pe.MapNode(BIDSDataSink( base_directory=out_dir, fixed_entities={'suffix': 'contrasts'}, path_patterns=image_pattern), iterfield=['entities', 'in_file'], run_without_submitting=True, name='ds_l1_contrasts') # # General Connections # wf.connect([ (loader, ds_model_warnings, [('warnings', 'in_file')]), (loader, design_matrix, [('design_info', 'session_info')]), (getter, design_matrix, [('bold_files', 'bold_file')]), (getter, l1_model, [('bold_files', 'bold_file'), ('mask_files', 'mask_file')]), (design_matrix, l1_model, [('design_matrix', 'design_matrix')]), (design_matrix, plot_design, [('design_matrix', 'data')]), (design_matrix, plot_l1_contrast_matrix, [('design_matrix', 'data')]), (design_matrix, plot_corr, [('design_matrix', 'data')]), (design_matrix, deindex_tsv, [('design_matrix', 'tsv')]), (deindex_tsv, ds_design_matrix, [('out', 'in_file')]), ]) stage = None model = l1_model for ix, step in enumerate(step['Level'] for step in model_dict['Steps']): # Set up elements common across levels # # Because pybids generates the entire model in one go, we will need # various helper nodes to select the correct portions of the model # level = 'l{:d}'.format(ix + 1) # TODO: No longer used at higher level, suggesting we can simply return # entities from loader as a single list select_entities = pe.Node(niu.Select(index=ix), name='select_{}_entities'.format(level), run_without_submitting=True) select_contrasts = pe.Node(niu.Select(index=ix), name='select_{}_contrasts'.format(level), run_without_submitting=True) # Squash the results of MapNodes that may have generated multiple maps # into single lists. # Do the same with corresponding metadata - interface will complain if shapes mismatch collate = pe.Node(MergeAll([ 'effect_maps', 'variance_maps', 'stat_maps', 'zscore_maps', 'pvalue_maps', 'contrast_metadata' ], check_lengths=(not drop_missing)), name='collate_{}'.format(level), run_without_submitting=True) # # Plotting # plot_contrasts = pe.MapNode(GlassBrainPlot(image_type='png'), iterfield='data', name='plot_{}_contrasts'.format(level)) # # Derivatives # collate_outputs = pe.Node(CollateWithMetadata(fields=[ 'effect_maps', 'variance_maps', 'stat_maps', 'pvalue_maps', 'zscore_maps' ], field_to_metadata_map={ 'effect_maps': { 'stat': 'effect' }, 'variance_maps': { 'stat': 'variance' }, 'pvalue_maps': { 'stat': 'p' }, 'zscore_maps': { 'stat': 'z' }, }), name=f'collate_{level}_outputs') ds_contrast_maps = pe.Node(BIDSDataSink( base_directory=out_dir, path_patterns=contrast_pattern), run_without_submitting=True, name='ds_{}_contrast_maps'.format(level)) ds_contrast_plots = pe.Node(BIDSDataSink( base_directory=out_dir, path_patterns=contrast_plot_pattern), run_without_submitting=True, name='ds_{}_contrast_plots'.format(level)) if ix == 0: ds_model_maps = pe.Node(BIDSDataSink( base_directory=out_dir, path_patterns=model_map_pattern), run_without_submitting=True, name='ds_{}_model_maps'.format(level)) collate_mm = pe.Node(MergeAll(['model_maps', 'model_metadata'], check_lengths=(not drop_missing)), name='collate_mm_{}'.format(level), run_without_submitting=True) wf.connect([ (loader, select_entities, [('entities', 'inlist')]), (select_entities, getter, [('out', 'entities')]), (select_entities, ds_model_warnings, [('out', 'entities')]), (select_entities, ds_design, [('out', 'entities')]), (select_entities, ds_design_matrix, [('out', 'entities')]), (plot_design, ds_design, [('figure', 'in_file')]), (select_contrasts, plot_l1_contrast_matrix, [('out', 'contrast_info')]), (select_contrasts, plot_corr, [('out', 'contrast_info')]), (select_entities, ds_l1_contrasts, [('out', 'entities')]), (select_entities, ds_corr, [('out', 'entities')]), (plot_l1_contrast_matrix, ds_l1_contrasts, [('figure', 'in_file')]), (plot_corr, ds_corr, [('figure', 'in_file')]), (model, collate_mm, [('model_maps', 'model_maps'), ('model_metadata', 'model_metadata')]), (collate_mm, ds_model_maps, [('model_maps', 'in_file'), ('model_metadata', 'entities')]), ]) # Set up higher levels else: model = pe.MapNode(SecondLevelModel(), iterfield=['contrast_info'], name='{}_model'.format(level)) wf.connect([ (stage, model, [('effect_maps', 'effect_maps'), ('variance_maps', 'variance_maps'), ('contrast_metadata', 'stat_metadata')]), ]) if smoothing and smoothing_level in (step, level): model.inputs.smoothing_fwhm = smoothing_fwhm wf.connect([ (loader, select_contrasts, [('contrast_info', 'inlist')]), (select_contrasts, model, [('out', 'contrast_info')]), (model, collate, [('effect_maps', 'effect_maps'), ('variance_maps', 'variance_maps'), ('stat_maps', 'stat_maps'), ('zscore_maps', 'zscore_maps'), ('pvalue_maps', 'pvalue_maps'), ('contrast_metadata', 'contrast_metadata')]), (collate, collate_outputs, [ ('contrast_metadata', 'metadata'), ('effect_maps', 'effect_maps'), ('variance_maps', 'variance_maps'), ('stat_maps', 'stat_maps'), ('zscore_maps', 'zscore_maps'), ('pvalue_maps', 'pvalue_maps'), ]), (collate, plot_contrasts, [('stat_maps', 'data')]), (collate_outputs, ds_contrast_maps, [('out', 'in_file'), ('metadata', 'entities')]), (collate, ds_contrast_plots, [('contrast_metadata', 'entities')]), (plot_contrasts, ds_contrast_plots, [('figure', 'in_file')]), ]) stage = model if step == analysis_level: break return 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 = Workflow(name=name) workflow.__desc__ = """\ The BOLD reference was then co-registered to the T1w reference using `bbregister` (FreeSurfer) which implements boundary-based registration [@bbr]. Co-registration was configured with nine degrees of freedom to account for distortions remaining in the BOLD reference. """ 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 create_motion_correct_pipeline(name='motion_correct'): """Creates a pipeline that corrects for motion artifact in dMRI sequences. It takes a series of diffusion weighted images and rigidly co-registers them to one reference image. Finally, the b-matrix is rotated accordingly (Leemans et al. 2009 - http://www.ncbi.nlm.nih.gov/pubmed/19319973), making use of the rotation matrix obtained by FLIRT. .. warning:: IMPORTANT NOTICE: this workflow rotates the b-vectors, so please be adviced that not all the dicom converters ensure the consistency between the resulting nifti orientation and the b matrix table (e.g. dcm2nii checks it). Example ------- >>> nipype_motioncorrect = create_motion_correct_pipeline('nipype_motioncorrect') >>> nipype_motioncorrect.inputs.inputnode.in_file = 'diffusion.nii' >>> nipype_motioncorrect.inputs.inputnode.in_bvec = 'diffusion.bvec' >>> nipype_motioncorrect.inputs.inputnode.ref_num = 0 >>> nipype_motioncorrect.run() # doctest: +SKIP Inputs:: inputnode.in_file inputnode.ref_num inputnode.in_bvec Outputs:: outputnode.motion_corrected outputnode.out_bvec """ inputnode = pe.Node( niu.IdentityInterface( fields=['in_file', 'ref_num', 'in_bvec']), name='inputnode') pipeline = pe.Workflow(name=name) split = pe.Node(fsl.Split(dimension='t'), name='split') pick_ref = pe.Node(niu.Select(), name='pick_ref') coregistration = pe.MapNode(fsl.FLIRT(no_search=True, interp='spline', padding_size=1, dof=6), name='coregistration', iterfield=['in_file']) rotate_bvecs = pe.Node(niu.Function(input_names=['in_bvec', 'in_matrix'], output_names=[ 'out_file'], function=_rotate_bvecs), name='rotate_b_matrix') merge = pe.Node(fsl.Merge(dimension='t'), name='merge') outputnode = pe.Node( niu.IdentityInterface( fields=['motion_corrected', 'out_bvec']), name='outputnode') pipeline.connect([ (inputnode, split, [('in_file', 'in_file')]) ,(split, pick_ref, [('out_files', 'inlist')]) ,(inputnode, pick_ref, [('ref_num', 'index')]) ,(split, coregistration, [('out_files', 'in_file')]) ,(inputnode, rotate_bvecs, [('in_bvec', 'in_bvec')]) ,(coregistration, rotate_bvecs, [('out_matrix_file', 'in_matrix')]) ,(pick_ref, coregistration, [('out', 'reference')]) ,(coregistration, merge, [('out_file', 'in_files')]) ,(merge, outputnode, [('merged_file', 'motion_corrected')]) ,(rotate_bvecs, outputnode, [('out_file', 'out_bvec')]) ]) return pipeline
def create_workflow(): featpreproc = pe.Workflow(name="featpreproc") featpreproc.base_dir = os.path.join(ds_root, 'workingdirs') # =================================================================== # _____ _ # |_ _| | | # | | _ __ _ __ _ _| |_ # | | | '_ \| '_ \| | | | __| # _| |_| | | | |_) | |_| | |_ # |_____|_| |_| .__/ \__,_|\__| # | | # |_| # =================================================================== # ------------------ Specify variables inputnode = pe.Node( niu.IdentityInterface(fields=[ 'funcs', 'subject_id', 'session_id', 'fwhm', # smoothing 'highpass' ]), name="inputspec") # SelectFiles templates = { 'ref_manual_fmapmask': # was: manual_fmapmask 'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_magnitude1_res-1x1x1_manualmask.nii.gz', 'ref_fmap_magnitude': 'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_magnitude1_res-1x1x1_reference.nii.gz', 'ref_fmap_phasediff': 'derivatives/resampled-isotropic-1mm/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_phasediff_res-1x1x1_preproc' '.nii.gz', # 'manualweights': # 'manual-masks/sub-eddy/ses-20170511/func/' # 'sub-eddy_ses-20170511_task-curvetracing_run-01_frame-50_bold' # '_res-1x1x1_manualweights.nii.gz', 'ref_func': # was: manualmask_func_ref 'derivatives/manual-masks/sub-eddy/ses-20170607/func/' 'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_' 'res-1x1x1_fnirt_reference.nii.gz', 'ref_funcmask': # was: manualmask 'derivatives/manual-masks/sub-eddy/ses-20170607/func/' 'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_' 'res-1x1x1_fnirt_mask.nii.gz', 'ref_t1': 'derivatives/manual-masks/sub-eddy/ses-20170511/anat/' 'sub-eddy_ses-20170511_T1w_res-1x1x1_reference.nii.gz', 'ref_t1mask': 'derivatives/manual-masks/sub-eddy/ses-20170511/anat/' 'sub-eddy_ses-20170511_T1w_res-1x1x1_manualmask.nii.gz', # 'funcs': # 'resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/func/' # # 'sub-{subject_id}_ses-{session_id}*_bold_res-1x1x1_preproc' # 'sub-{subject_id}_ses-{session_id}*run-01_bold_res-1x1x1_preproc' # # '.nii.gz', # '_nvol10.nii.gz', 'fmap_phasediff': 'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/' 'sub-{subject_id}_ses-{session_id}_phasediff_res-1x1x1_preproc' '.nii.gz', 'fmap_magnitude': 'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/' 'sub-{subject_id}_ses-{session_id}_magnitude1_res-1x1x1_preproc' '.nii.gz', # 'fmap_mask': # 'transformed-manual-fmap-mask/sub-{subject_id}/ses-{session_id}/fmap/' # 'sub-{subject_id}_ses-{session_id}_' # 'magnitude1_res-1x1x1_preproc.nii.gz', } inputfiles = pe.Node(nio.SelectFiles(templates, base_directory=data_dir), name="input_files") featpreproc.connect([(inputnode, inputfiles, [ ('subject_id', 'subject_id'), ('session_id', 'session_id'), ])]) # =================================================================== # ____ _ _ # / __ \ | | | | # | | | |_ _| |_ _ __ _ _| |_ # | | | | | | | __| '_ \| | | | __| # | |__| | |_| | |_| |_) | |_| | |_ # \____/ \__,_|\__| .__/ \__,_|\__| # | | # |_| # =================================================================== # ------------------ Output Files # Datasink outputfiles = pe.Node(nio.DataSink(base_directory=ds_root, container='derivatives/featpreproc', parameterization=True), name="output_files") # Use the following DataSink output substitutions # each tuple is only matched once per file outputfiles.inputs.substitutions = [ ('/_mc_method_afni3dAllinSlices/', '/'), ('/_mc_method_afni3dAllinSlices/', '/'), # needs to appear twice ('/oned_file/', '/'), ('/out_file/', '/'), ('/oned_matrix_save/', '/'), ('subject_id_', 'sub-'), ('session_id_', 'ses-'), ] # Put result into a BIDS-like format outputfiles.inputs.regexp_substitutions = [ (r'_ses-([a-zA-Z0-9]+)_sub-([a-zA-Z0-9]+)', r'sub-\2/ses-\1'), (r'/_addmean[0-9]+/', r'/func/'), (r'/_funcbrains[0-9]+/', r'/func/'), (r'/_maskfunc[0-9]+/', r'/func/'), (r'/_mc[0-9]+/', r'/func/'), (r'/_meanfunc[0-9]+/', r'/func/'), (r'/_outliers[0-9]+/', r'/func/'), (r'_run_id_[0-9][0-9]', r''), ] outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'motion_parameters', 'motion_corrected', 'motion_plots', 'motion_outlier_files', 'mask', 'smoothed_files', 'highpassed_files', 'mean', 'func_unwarp', 'ref_func', 'ref_funcmask', 'ref_t1', 'ref_t1mask', ]), name='outputspec') # =================================================================== # _____ _ _ _ # | __ (_) | (_) # | |__) | _ __ ___| |_ _ __ ___ # | ___/ | '_ \ / _ \ | | '_ \ / _ \ # | | | | |_) | __/ | | | | | __/ # |_| |_| .__/ \___|_|_|_| |_|\___| # | | # |_| # =================================================================== # ~|~ _ _ _ _ |` _ _ _ _ _ _ _ _| _ # | | (_|| |_\~|~(_)| | | | | | |(_|_\|<_\ # # Transform manual skull-stripped masks to multiple images # -------------------------------------------------------- # should just be used as input to motion correction, # after mc, all functionals should be aligned to reference transmanmask_mc = transform_manualmask.create_workflow() # - - - - - - Connections - - - - - - - featpreproc.connect([(inputfiles, transmanmask_mc, [ ('subject_id', 'in.subject_id'), ('session_id', 'in.session_id'), ])]) featpreproc.connect(inputfiles, 'ref_funcmask', transmanmask_mc, 'in.manualmask') featpreproc.connect(inputnode, 'funcs', transmanmask_mc, 'in.funcs') featpreproc.connect(inputfiles, 'ref_func', transmanmask_mc, 'in.manualmask_func_ref') # fieldmaps not being used if False: trans_fmapmask = transmanmask_mc.clone('trans_fmapmask') featpreproc.connect(inputfiles, 'ref_manual_fmapmask', trans_fmapmask, 'in.manualmask') featpreproc.connect(inputfiles, 'fmap_magnitude', trans_fmapmask, 'in.funcs') featpreproc.connect(inputfiles, 'ref_func', trans_fmapmask, 'in.manualmask_func_ref') # |\/| _ _|_. _ _ _ _ _ _ _ __|_. _ _ # | |(_) | |(_)| | (_(_)| | (/_(_ | |(_)| | # # Perform motion correction, using some pipeline # -------------------------------------------------------- # mc = motioncorrection_workflow.create_workflow_afni() # Register an image from the functionals to the reference image median_func = pe.MapNode( interface=fsl.maths.MedianImage(dimension="T"), name='median_func', iterfield=('in_file'), ) pre_mc = motioncorrection_workflow.create_workflow_allin_slices( name='premotioncorrection') featpreproc.connect([ (inputnode, median_func, [ ('funcs', 'in_file'), ]), (median_func, pre_mc, [ ('out_file', 'in.funcs'), ]), ( inputfiles, pre_mc, [ # median func image will be used a reference / base ('ref_func', 'in.ref_func'), ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, pre_mc, [ ('funcreg.out_file', 'in.funcs_masks'), # use mask as weights ]), (pre_mc, outputnode, [ ('mc.out_file', 'pre_motion_corrected'), ('mc.oned_file', 'pre_motion_parameters.oned_file'), ('mc.oned_matrix_save', 'pre_motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('pre_motion_corrected', 'pre_motion_corrected.out_file'), ('pre_motion_parameters.oned_file', 'pre_motion_corrected.oned_file' ), # warp parameters in ASCII (.1D) ('pre_motion_parameters.oned_matrix_save', 'pre_motion_corrected.oned_matrix_save' ), # transformation matrices for each sub-brick ]), ]) mc = motioncorrection_workflow.create_workflow_allin_slices( name='motioncorrection', iterfield=('in_file', 'ref_file', 'in_weight_file')) # - - - - - - Connections - - - - - - - featpreproc.connect([ (inputnode, mc, [ ('funcs', 'in.funcs'), ]), ( pre_mc, mc, [ # the median image realigned to the reference functional will serve as reference # this way motion correction is done to an image more similar to the functionals ('mc.out_file', 'in.ref_func'), ]), ( inputfiles, mc, [ # Check and make sure the ref func mask is close enough to the registered median # image. ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, mc, [ ('funcreg.out_file', 'in.funcs_masks'), # use mask as weights ]), (mc, outputnode, [ ('mc.out_file', 'motion_corrected'), ('mc.oned_file', 'motion_parameters.oned_file'), ('mc.oned_matrix_save', 'motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('motion_corrected', 'motion_corrected.out_file'), ('motion_parameters.oned_file', 'motion_corrected.oned_file' ), # warp parameters in ASCII (.1D) ('motion_parameters.oned_matrix_save', 'motion_corrected.oned_matrix_save' ), # transformation matrices for each sub-brick ]), ]) # |~. _ | _| _ _ _ _ _ _ _ _ _ __|_. _ _ # |~|(/_|(_|| | |(_||_) (_(_)| | (/_(_ | |(_)| | # | # Unwarp EPI distortions # -------------------------------------------------------- # Performing motion correction to a reference that is undistorted, # so b0_unwarp is currently not needed if False: b0_unwarp = undistort_workflow.create_workflow() featpreproc.connect([ ( inputfiles, b0_unwarp, [ # ('subject_id', 'in.subject_id'), # ('session_id', 'in.session_id'), ('fmap_phasediff', 'in.fmap_phasediff'), ('fmap_magnitude', 'in.fmap_magnitude'), ]), (mc, b0_unwarp, [ ('mc.out_file', 'in.funcs'), ]), (transmanmask_mc, b0_unwarp, [ ('funcreg.out_file', 'in.funcmasks'), ]), (trans_fmapmask, b0_unwarp, [('funcreg.out_file', 'in.fmap_mask') ]), (b0_unwarp, outputfiles, [ ('out.funcs', 'func_unwarp.funcs'), ('out.funcmasks', 'func_unwarp.funcmasks'), ]), (b0_unwarp, outputnode, [ ('out.funcs', 'func_unwarp.funcs'), ('out.funcmasks', 'mask'), ]), ]) # undistort the reference images if False: b0_unwarp_ref = b0_unwarp.clone('b0_unwarp_ref') featpreproc.connect([ ( inputfiles, b0_unwarp_ref, [ # ('subject_id', 'in.subject_id'), # ('session_id', 'in.session_id'), ('ref_fmap_phasediff', 'in.fmap_phasediff'), ('ref_fmap_magnitude', 'in.fmap_magnitude'), ('ref_manual_fmapmask', 'in.fmap_mask'), ('ref_func', 'in.funcs'), ('ref_funcmask', 'in.funcmasks'), ]), (b0_unwarp_ref, outputfiles, [ ('out.funcs', 'func_unwarp_ref.func'), ('out.funcmasks', 'func_unwarp_ref.funcmask'), ]), (b0_unwarp_ref, outputnode, [ ('out.funcs', 'ref_func'), ('out.funcmasks', 'ref_mask'), ]), ]) else: featpreproc.connect([ (inputfiles, outputfiles, [ ('ref_func', 'reference/func'), ('ref_funcmask', 'reference/func_mask'), ]), (inputfiles, outputnode, [ ('ref_func', 'ref_func'), ('ref_funcmask', 'ref_funcmask'), ]), ]) # |~) _ _ . __|_ _ _ _|_ _ |~) _ |` _ _ _ _ _ _ # |~\(/_(_||_\ | (/_| | (_) |~\(/_~|~(/_| (/_| |(_(/_ # _| # Register all functionals to common reference # -------------------------------------------------------- if False: # this is now done during motion correction # FLIRT cost: intermodal: corratio, intramodal: least squares and normcorr reg_to_ref = pe.MapNode( # intra-modal # some runs need to be scaled along the anterior-posterior direction interface=fsl.FLIRT(dof=12, cost='normcorr'), name='reg_to_ref', iterfield=('in_file', 'in_weight'), ) refEPI_to_refT1 = pe.Node( # some runs need to be scaled along the anterior-posterior direction interface=fsl.FLIRT(dof=12, cost='corratio'), name='refEPI_to_refT1', ) # combine func -> ref_func and ref_func -> ref_T1 reg_to_refT1 = pe.MapNode( interface=fsl.ConvertXFM(concat_xfm=True), name='reg_to_refT1', iterfield=('in_file'), ) reg_funcs = pe.MapNode( interface=fsl.preprocess.ApplyXFM(), name='reg_funcs', iterfield=('in_file', 'in_matrix_file'), ) reg_funcmasks = pe.MapNode(interface=fsl.preprocess.ApplyXFM(), name='reg_funcmasks', iterfield=('in_file', 'in_matrix_file')) def deref_list(x): assert len(x) == 1 return x[0] featpreproc.connect([ ( b0_unwarp, reg_to_ref, # --> reg_to_ref, (A) [ ('out.funcs', 'in_file'), ('out.funcmasks', 'in_weight'), ]), (b0_unwarp_ref, reg_to_ref, [ (('out.funcs', deref_list), 'reference'), (('out.funcmasks', deref_list), 'ref_weight'), ]), ( b0_unwarp_ref, refEPI_to_refT1, # --> refEPI_to_refT1 (B) [ (('out.funcs', deref_list), 'in_file'), (('out.funcmasks', deref_list), 'in_weight'), ]), (inputfiles, refEPI_to_refT1, [ ('ref_t1', 'reference'), ('ref_t1mask', 'ref_weight'), ]), ( reg_to_ref, reg_to_refT1, # --> reg_to_refT1 (A*B) [ ('out_matrix_file', 'in_file'), ]), (refEPI_to_refT1, reg_to_refT1, [ ('out_matrix_file', 'in_file2'), ]), ( reg_to_refT1, reg_funcs, # --> reg_funcs [ # ('out_matrix_file', 'in_matrix_file'), ('out_file', 'in_matrix_file'), ]), (b0_unwarp, reg_funcs, [ ('out.funcs', 'in_file'), ]), (b0_unwarp_ref, reg_funcs, [ (('out.funcs', deref_list), 'reference'), ]), ( reg_to_refT1, reg_funcmasks, # --> reg_funcmasks [ # ('out_matrix_file', 'in_matrix_file'), ('out_file', 'in_matrix_file'), ]), (b0_unwarp, reg_funcmasks, [ ('out.funcmasks', 'in_file'), ]), (b0_unwarp_ref, reg_funcmasks, [ (('out.funcs', deref_list), 'reference'), ]), (reg_funcs, outputfiles, [ ('out_file', 'common_ref.func'), ]), (reg_funcmasks, outputfiles, [ ('out_file', 'common_ref.funcmask'), ]), ]) # |\/| _ _|_. _ _ _ _|_|. _ _ _ # | |(_) | |(_)| | (_)|_|| ||(/_| _\ # # -------------------------------------------------------- # Apply brain masks to functionals # -------------------------------------------------------- # Dilate mask """ Dilate the mask """ if False: dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(reg_funcmasks, 'out_file', dilatemask, 'in_file') else: dilatemask = pe.Node(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), name='dilatemask') featpreproc.connect(inputfiles, 'ref_funcmask', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputfiles, 'dilate_mask') funcbrains = pe.MapNode(fsl.BinaryMaths(operation='mul'), iterfield=('in_file', 'operand_file'), name='funcbrains') featpreproc.connect([ (mc, funcbrains, [ ('mc.out_file', 'in_file'), ]), (dilatemask, funcbrains, [ ('out_file', 'operand_file'), ]), (funcbrains, outputfiles, [ ('out_file', 'funcbrains'), ]), ]) # Detect motion outliers # -------------------------------------------------------- import nipype.algorithms.rapidart as ra outliers = pe.MapNode( ra.ArtifactDetect( mask_type='file', # trying to "disable" `norm_threshold`: use_norm=True, norm_threshold=10.0, # combines translations in mm and rotations # use_norm=Undefined, # translation_threshold=1.0, # translation in mm # rotation_threshold=0.02, # rotation in radians zintensity_threshold=3.0, # z-score parameter_source='AFNI', save_plot=True), iterfield=('realigned_files', 'realignment_parameters', 'mask_file'), name='outliers') featpreproc.connect([ ( mc, outliers, [ # ('mc.par_file', 'realignment_parameters'), ('mc.oned_file', 'realignment_parameters'), ]), (funcbrains, outliers, [ ('out_file', 'realigned_files'), ]), (dilatemask, outliers, [ ('out_file', 'mask_file'), ]), ( outliers, outputfiles, [ ('outlier_files', 'motion_outliers.@outlier_files'), ('plot_files', 'motion_outliers.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]), (mc, outputnode, [ ('mc.oned_file', 'motion_parameters'), ]), ( outliers, outputnode, [ ('outlier_files', 'motion_outlier_files'), ('plot_files', 'motion_plots.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]) ]) """ Determine the 2nd and 98th percentile intensities of each functional run """ getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'), iterfield=['in_file'], name='getthreshold') if False: featpreproc.connect(b0_unwarp, 'out.funcs', getthresh, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', getthresh, 'in_file') """ Threshold the first run of functional data at 10% of the 98th percentile """ threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char', suffix='_thresh'), iterfield=['in_file', 'op_string'], name='threshold') if False: featpreproc.connect(b0_unwarp, 'out.funcs', threshold, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ def getthreshop(thresh): return ['-thr %.10f -Tmin -bin' % (0.1 * val[1]) for val in thresh] featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield=['in_file', 'mask_file'], name='medianval') if False: featpreproc.connect(b0_unwarp, 'out.funcs', medianval, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') # (~ _ _ _|_. _ | (~ _ _ _ _ _|_|_ . _ _ # _)|_)(_| | |(_|| _)| | |(_)(_) | | ||| |(_| # | _| # Spatial smoothing (SUSAN) # -------------------------------------------------------- # create_susan_smooth takes care of calculating the mean and median # functional, applying mask to functional, and running the smoothing smooth = create_susan_smooth(separate_masks=False) featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') # featpreproc.connect(b0_unwarp, 'out.funcs', smooth, 'inputnode.in_files') if False: featpreproc.connect(reg_funcs, 'out_file', smooth, 'inputnode.in_files') else: featpreproc.connect(mc, 'mc.out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') # ------------------------------------------------------- # The below is from workflows/fmri/fsl/preprocess.py """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') tolist = lambda x: [x] def chooseindex(fwhm): if fwhm < 1: return [0] else: return [1] # maskfunc2 is the functional data before SUSAN if False: featpreproc.connect(b0_unwarp, ('out.funcs', tolist), concatnode, 'in1') else: featpreproc.connect(mc, ('mc.out_file', tolist), concatnode, 'in1') # maskfunc3 is the functional data after SUSAN featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(), name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputfiles, 'smoothed_files') """ Scale the median value of the run is set to 10000. """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file', 'op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') # |_|. _ |_ _ _ _ _ # | ||(_|| ||_)(_|_\_\ # _| | # Temporal filtering # -------------------------------------------------------- highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') highpass_operand = lambda x: '-bptf %.10f -1' % x featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') version = 0 if fsl.Info.version() and \ LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 if version < 507: featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') else: """ Add back the mean removed by the highpass filter operation as of FSL 5.0.7 """ meanfunc4 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc4') featpreproc.connect(meanscale, 'out_file', meanfunc4, 'in_file') addmean = pe.MapNode(interface=fsl.BinaryMaths(operation='add'), iterfield=['in_file', 'operand_file'], name='addmean') featpreproc.connect(highpass, 'out_file', addmean, 'in_file') featpreproc.connect(meanfunc4, 'out_file', addmean, 'operand_file') featpreproc.connect(addmean, 'out_file', outputnode, 'highpassed_files') """ Generate a mean functional image from the first run """ meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc3') featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputfiles, 'mean') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean_highpassed') featpreproc.connect(outputnode, 'highpassed_files', outputfiles, 'highpassed_files') return (featpreproc)
def init_fsl_bbr_wf(use_bbr, asl2t1w_dof, asl2t1w_init, sloppy=False, name='fsl_bbr_wf'): """ Build a workflow to run FSL's ``flirt``. This workflow uses FSL FLIRT to register a ASL image to a T1-weighted structural image, using a boundary-based registration (BBR) cost function. It is a counterpart to :py:func:`~aslprep.workflows.asl.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 Graph .. workflow :: :graph2use: orig :simple_form: yes from aslprep.workflows.asl.registration import init_fsl_bbr_wf wf = init_fsl_bbr_wf(use_bbr=True, asl2t1w_dof=9, asl2t1w_init='register') Parameters ---------- use_bbr : :obj:`bool` or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. asl2t1w_dof : 6, 9 or 12 Degrees-of-freedom for ASL-T1w registration asl2t1w_init : str, 'header' or 'register' If ``'header'``, use header information for initialization of ASL and T1 images. If ``'register'``, align volumes by their centers. name : :obj:`str`, optional Workflow name (default: fsl_bbr_wf) Inputs ------ in_file Reference ASL image to be registered t1w_brain Skull-stripped T1-weighted structural image t1w_dseg FAST segmentation of ``t1w_brain`` fsnative2t1w_xfm Unused (see :py:func:`~aslprep.workflows.asl.registration.init_bbreg_wf`) subjects_dir Unused (see :py:func:`~aslprep.workflows.asl.registration.init_bbreg_wf`) subject_id Unused (see :py:func:`~aslprep.workflows.asl.registration.init_bbreg_wf`) Outputs ------- itk_asl_to_t1 Affine transform from ``ref_asl_brain`` to T1w space (ITK format) itk_t1_to_asl Affine transform from T1 space to ASL space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (rigid FLIRT registration returned) """ from ...niworkflows.engine.workflows import LiterateWorkflow as Workflow from ...niworkflows.utils.images import dseg_label as _dseg_label from ...niworkflows.interfaces.freesurfer import PatchedLTAConvert as LTAConvert from ...niworkflows.interfaces.registration import FLIRTRPT workflow = Workflow(name=name) workflow.__desc__ = """\ The ASL reference was then co-registered to the T1w reference using `flirt` [FSL {fsl_ver}, @flirt] with the boundary-based registration [@bbr] cost-function. Co-registration was configured with nine degrees of freedom to account for distortions remaining in the ASL reference. """.format(fsl_ver=FLIRTRPT().version or '<ver>') inputnode = pe.Node( niu.IdentityInterface([ 'in_file', 'fsnative2t1w_xfm', 'subjects_dir', 'subject_id', # BBRegister 't1w_dseg', 't1w_brain' ]), # FLIRT BBR name='inputnode') outputnode = pe.Node(niu.IdentityInterface( ['itk_asl_to_t1', 'itk_t1_to_asl', 'out_report', 'fallback']), name='outputnode') wm_mask = pe.Node(niu.Function(function=_dseg_label), name='wm_mask') wm_mask.inputs.label = 2 # BIDS default is WM=2 flt_bbr_init = pe.Node(FLIRTRPT(dof=6, generate_report=not use_bbr, uses_qform=True), name='flt_bbr_init') if asl2t1w_init not in ("register", "header"): raise ValueError( f"Unknown ASL-T1w initialization option: {asl2t1w_init}") if asl2t1w_init == "header": raise NotImplementedError( "Header-based registration initialization not supported for FSL") invt_bbr = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='invt_bbr', mem_gb=DEFAULT_MEMORY_MIN_GB) # ASL 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'), ('t1w_brain', 'reference')]), (inputnode, fsl2itk_fwd, [('t1w_brain', 'reference_file'), ('in_file', 'source_file')]), (inputnode, fsl2itk_inv, [('in_file', 'reference_file'), ('t1w_brain', 'source_file')]), (invt_bbr, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_asl_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_asl')]), ]) # 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=asl2t1w_dof, generate_report=True), name='flt_bbr') FSLDIR = os.getenv('FSLDIR') if FSLDIR: flt_bbr.inputs.schedule = op.join(FSLDIR, 'etc/flirtsch/bbr.sch') else: # Should mostly be hit while building docs LOGGER.warning("FSLDIR unset - using packaged BBR schedule") flt_bbr.inputs.schedule = pkgr.resource_filename( 'aslprep', 'data/flirtsch/bbr.sch') workflow.connect([ (inputnode, wm_mask, [('t1w_dseg', 'in_seg')]), (inputnode, flt_bbr, [('in_file', 'in_file')]), (flt_bbr_init, flt_bbr, [('out_matrix_file', 'in_matrix_file')]), ]) if sloppy is True: downsample = pe.Node(niu.Function( function=_conditional_downsampling, output_names=["out_file", "out_mask"]), name='downsample') workflow.connect([ (inputnode, downsample, [("t1w_brain", "in_file")]), (wm_mask, downsample, [("out", "in_mask")]), (downsample, flt_bbr, [('out_file', 'reference'), ('out_mask', 'wm_seg')]), ]) else: workflow.connect([ (inputnode, flt_bbr, [('t1w_brain', 'reference')]), (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, output_names="out"), 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(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'), ('t1w_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
"""Use :class:`nipype.interfaces.spm.EstimateModel` to determine the parameters of the model. """ level1estimate = pe.Node(spm.EstimateModel(), name="level1estimate") level1estimate.inputs.estimation_method = {'Classical': 1} """Use :class:`nipype.interfaces.spm.EstimateContrast` to estimate the first level contrasts specified in a few steps above. """ contrastestimate = pe.Node(spm.EstimateContrast(), name="contrastestimate") """Use :class: `nipype.interfaces.utility.Select` to select each contrast for reporting. """ selectcontrast = pe.Node(niu.Select(), name="selectcontrast") """Use :class:`nipype.interfaces.fsl.Overlay` to combine the statistical output of the contrast estimate and a background image into one volume. """ overlaystats = pe.Node(fsl.Overlay(), name="overlaystats") overlaystats.inputs.stat_thresh = (3, 10) overlaystats.inputs.show_negative_stats = True overlaystats.inputs.auto_thresh_bg = True """Use :class:`nipype.interfaces.fsl.Slicer` to create images of the overlaid statistical volumes for a report of the first-level results. """ slicestats = pe.Node(fsl.Slicer(), name="slicestats") slicestats.inputs.all_axial = True slicestats.inputs.image_width = 750
preprocessing.connect(iter_fwhm, "fwhm", isotropic_surface_smooth, "surface_fwhm") preprocessing.connect(iter_fwhm, "fwhm", isotropic_surface_smooth, "vol_fwhm") preprocessing.connect(recon_all, 'subjects_dir', isotropic_surface_smooth, 'subjects_dir') merge_smoothed_files = pe.Node(interface=util.Merge(3), name='merge_smoothed_files') preprocessing.connect(isotropic_voxel_smooth, 'smoothed_files', merge_smoothed_files, 'in1') preprocessing.connect(anisotropic_voxel_smooth, 'outputnode.smoothed_files', merge_smoothed_files, 'in2') preprocessing.connect(isotropic_surface_smooth, 'smoothed_file', merge_smoothed_files, 'in3') select_smoothed_files = pe.Node(interface=util.Select(), name="select_smoothed_files") preprocessing.connect(merge_smoothed_files, 'out', select_smoothed_files, 'inlist') def chooseindex(roi): return { 'isotropic_voxel': range(0, 4), 'anisotropic_voxel': range(4, 8), 'isotropic_surface': range(8, 12) }[roi] preprocessing.connect(iter_smoothing_method, ("smoothing_method", chooseindex), select_smoothed_files, 'index')
def epi_pipeline(name="susceptibility_distortion_correction_using_t1"): """Perform EPI correction. This workflow allows to correct for echo-planar induced susceptibility artifacts without fieldmap (e.g. ADNI Database) by elastically register DWIs to their respective baseline T1-weighted structural scans using an inverse consistent registration algorithm with a mutual information cost function (SyN algorithm). This workflow allows also a coregistration of DWIs with their respective baseline T1-weighted structural scans in order to latter combine tracks and cortex parcellation. Warnings: This workflow rotates the b-vectors. Notes: Nir et al. (2015): Connectivity network measures predict volumetric atrophy in mild cognitive impairment Leow et al. (2007): Statistical Properties of Jacobian Maps and the Realization of Unbiased Large Deformation Nonlinear Image Registration """ import nipype.interfaces.ants as ants import nipype.interfaces.c3 as c3 import nipype.interfaces.fsl as fsl import nipype.interfaces.utility as niu import nipype.pipeline.engine as pe from .dwi_preprocessing_using_t1_utils import ( ants_combine_transform, change_itk_transform_type, create_jacobian_determinant_image, expend_matrix_list, rotate_bvecs) inputnode = pe.Node(niu.IdentityInterface(fields=["T1", "DWI", "bvec"]), name="inputnode") split = pe.Node(fsl.Split(dimension="t"), name="SplitDWIs") pick_ref = pe.Node(niu.Select(), name="Pick_b0") pick_ref.inputs.index = [0] flirt_b0_2_t1 = pe.Node(interface=fsl.FLIRT(dof=6), name="flirt_B0_2_T1") flirt_b0_2_t1.inputs.interp = "spline" flirt_b0_2_t1.inputs.cost = "normmi" flirt_b0_2_t1.inputs.cost_func = "normmi" apply_xfm = pe.Node(interface=fsl.preprocess.ApplyXFM(), name="apply_xfm") apply_xfm.inputs.apply_xfm = True expend_matrix = pe.Node( interface=niu.Function( input_names=["in_matrix", "in_bvec"], output_names=["out_matrix_list"], function=expend_matrix_list, ), name="expend_matrix", ) rot_bvec = pe.Node( niu.Function( input_names=["in_matrix", "in_bvec"], output_names=["out_file"], function=rotate_bvecs, ), name="Rotate_Bvec", ) ants_registration = pe.Node( interface=ants.registration.RegistrationSynQuick(transform_type="br", dimension=3), name="antsRegistrationSyNQuick", ) c3d_flirt2ants = pe.Node(c3.C3dAffineTool(), name="fsl_reg_2_itk") c3d_flirt2ants.inputs.itk_transform = True c3d_flirt2ants.inputs.fsl2ras = True change_transform = pe.Node( niu.Function( input_names=["input_affine_file"], output_names=["updated_affine_file"], function=change_itk_transform_type, ), name="change_transform_type", ) merge_transform = pe.Node(niu.Merge(3), name="MergeTransforms") apply_transform = pe.MapNode( interface=niu.Function( input_names=["fix_image", "moving_image", "ants_warp_affine"], output_names=["out_warp_field", "out_warped"], function=ants_combine_transform, ), iterfield=["moving_image"], name="warp_filed", ) jacobian = pe.MapNode( interface=niu.Function( input_names=["imageDimension", "deformationField", "outputImage"], output_names=["outputImage"], function=create_jacobian_determinant_image, ), iterfield=["deformationField"], name="jacobian", ) jacobian.inputs.imageDimension = 3 jacobian.inputs.outputImage = "Jacobian_image.nii.gz" jacmult = pe.MapNode( fsl.MultiImageMaths(op_string="-mul %s"), iterfield=["in_file", "operand_files"], name="ModulateDWIs", ) thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=["in_file"], name="RemoveNegative") merge = pe.Node(fsl.Merge(dimension="t"), name="MergeDWIs") outputnode = pe.Node( niu.IdentityInterface(fields=[ "DWI_2_T1_Coregistration_matrix", "epi_correction_deformation_field", "epi_correction_affine_transform", "epi_correction_image_warped", "DWIs_epicorrected", "warp_epi", "out_bvec", ]), name="outputnode", ) wf = pe.Workflow(name="epi_pipeline") # fmt: off wf.connect([ (inputnode, split, [("DWI", "in_file")]), (split, pick_ref, [("out_files", "inlist")]), (pick_ref, flirt_b0_2_t1, [("out", "in_file")]), (inputnode, flirt_b0_2_t1, [("T1", "reference")]), (inputnode, rot_bvec, [("bvec", "in_bvec")]), (flirt_b0_2_t1, expend_matrix, [("out_matrix_file", "in_matrix")]), (inputnode, expend_matrix, [("bvec", "in_bvec")]), (expend_matrix, rot_bvec, [("out_matrix_list", "in_matrix")]), (inputnode, ants_registration, [("T1", "fixed_image")]), (flirt_b0_2_t1, ants_registration, [("out_file", "moving_image")]), (inputnode, c3d_flirt2ants, [("T1", "reference_file")]), (pick_ref, c3d_flirt2ants, [("out", "source_file")]), (flirt_b0_2_t1, c3d_flirt2ants, [("out_matrix_file", "transform_file") ]), (c3d_flirt2ants, change_transform, [("itk_transform", "input_affine_file")]), (ants_registration, merge_transform, [("forward_warp_field", "in1")]), (ants_registration, merge_transform, [("out_matrix", "in2")]), (change_transform, merge_transform, [("updated_affine_file", "in3")]), (inputnode, apply_transform, [("T1", "fix_image")]), (split, apply_transform, [("out_files", "moving_image")]), (merge_transform, apply_transform, [("out", "ants_warp_affine")]), (apply_transform, jacobian, [("out_warp_field", "deformationField")]), (apply_transform, jacmult, [("out_warped", "operand_files")]), (jacobian, jacmult, [("outputImage", "in_file")]), (jacmult, thres, [("out_file", "in_file")]), (thres, merge, [("out_file", "in_files")]), (merge, outputnode, [("merged_file", "DWIs_epicorrected")]), (flirt_b0_2_t1, outputnode, [("out_matrix_file", "DWI_2_T1_Coregistration_matrix")]), (ants_registration, outputnode, [("forward_warp_field", "epi_correction_deformation_field"), ("out_matrix", "epi_correction_affine_transform"), ("warped_image", "epi_correction_image_warped")]), (merge_transform, outputnode, [("out", "warp_epi")]), (rot_bvec, outputnode, [("out_file", "out_bvec")]), ]) # fmt: on return wf
# Node 9 node_fsl_subtract = pe.Node(interface=fsl.BinaryMaths(), name='node_fsl_subtract') #node_fsl_subtract.inputs.in_file = root_dir + nipype_dir + nlin_displacement_field_4d_gz #node_fsl_subtract.inputs.operand_file = root_dir + nipype_dir + affine_displacement_field_4d_gz node_fsl_subtract.inputs.operation = 'sub' node_fsl_subtract.inputs.out_file = root_dir + nipype_dir + nlin_displacement_field_4d_only # Node 10 node_fsl_split = pe.Node(interface=fsl.Split(), name='node_fsl_split') node_fsl_split.inputs.dimension = 't' #node_fsl_split.inputs.in_file = root_dir + nipype_dir + nlin_displacement_field_4d_only node_fsl_split.inputs.out_base_name = root_dir + nipype_dir + nlin_displacement_field_4d_only_split # Node 11 node_select1 = pe.Node(interface=util.Select(), name='node_select1') node_select1.inputs.index = [0] node_select1.inputs.inlist = [ root_dir + nipype_dir + nlin_displacement_field_4d_only_split1, root_dir + nipype_dir + nlin_displacement_field_4d_only_split2, root_dir + nipype_dir + nlin_displacement_field_4d_only_split3 ] # Node 12 node_select2 = pe.Node(interface=util.Select(), name='node_select2') node_select2.inputs.index = [1] node_select2.inputs.inlist = [ root_dir + nipype_dir + nlin_displacement_field_4d_only_split1, root_dir + nipype_dir + nlin_displacement_field_4d_only_split2, root_dir + nipype_dir + nlin_displacement_field_4d_only_split3 ]
def init_atropos_wf( name="atropos_wf", use_random_seed=True, omp_nthreads=None, mem_gb=3.0, padding=10, in_segmentation_model=tuple(ATROPOS_MODELS["T1w"].values()), bspline_fitting_distance=200, wm_prior=False, ): """ Create an ANTs' ATROPOS workflow for brain tissue segmentation. Re-interprets supersteps 6 and 7 of ``antsBrainExtraction.sh``, which refine the mask previously computed with the spatial normalization to the template. The workflow also executes steps 8 and 9 of the brain extraction workflow. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from niworkflows.anat.ants import init_atropos_wf wf = init_atropos_wf() Parameters ---------- name : str, optional Workflow name (default: "atropos_wf"). use_random_seed : bool Whether ATROPOS should generate a random seed based on the system's clock omp_nthreads : int Maximum number of threads an individual process may use mem_gb : float Estimated peak memory consumption of the most hungry nodes in the workflow padding : int Pad images with zeros before processing in_segmentation_model : tuple A k-means segmentation is run to find gray or white matter around the edge of the initial brain mask warped from the template. This produces a segmentation image with :math:`$K$` classes, ordered by mean intensity in increasing order. With this option, you can control :math:`$K$` and tell the script which classes represent CSF, gray and white matter. Format (K, csfLabel, gmLabel, wmLabel). Examples: ``(3,1,2,3)`` for T1 with K=3, CSF=1, GM=2, WM=3 (default), ``(3,3,2,1)`` for T2 with K=3, CSF=3, GM=2, WM=1, ``(3,1,3,2)`` for FLAIR with K=3, CSF=1 GM=3, WM=2, ``(4,4,2,3)`` uses K=4, CSF=4, GM=2, WM=3. bspline_fitting_distance : float The size of the b-spline mesh grid elements, in mm (default: 200) wm_prior : :obj:`bool` Whether the WM posterior obtained with ATROPOS should be regularized with a prior map (typically, mapped from the template). When ``wm_prior`` is ``True`` the input field ``wm_prior`` of the input node must be connected. Inputs ------ in_files : list The original anatomical images passed in to the brain-extraction workflow. in_corrected : list :abbr:`INU (intensity non-uniformity)`-corrected files. in_mask : str Brain mask calculated previously. wm_prior : :obj:`str` Path to the WM prior probability map, aligned with the individual data. Outputs ------- out_file : :obj:`str` Path of the corrected and brain-extracted result, using the ATROPOS refinement. bias_corrected : :obj:`str` Path of the corrected and result, using the ATROPOS refinement. bias_image : :obj:`str` Path of the estimated INU bias field, using the ATROPOS refinement. out_mask : str Refined brain mask out_segm : str Output segmentation out_tpms : str Output :abbr:`TPMs (tissue probability maps)` """ wf = pe.Workflow(name) out_fields = [ "bias_corrected", "bias_image", "out_mask", "out_segm", "out_tpms" ] inputnode = pe.Node( niu.IdentityInterface( fields=["in_files", "in_corrected", "in_mask", "wm_prior"]), name="inputnode", ) outputnode = pe.Node(niu.IdentityInterface(fields=["out_file"] + out_fields), name="outputnode") copy_xform = pe.Node(CopyXForm(fields=out_fields), name="copy_xform", run_without_submitting=True) # Morphological dilation, radius=2 dil_brainmask = pe.Node(ImageMath(operation="MD", op2="2", copy_header=True), name="dil_brainmask") # Get largest connected component get_brainmask = pe.Node( ImageMath(operation="GetLargestComponent", copy_header=True), name="get_brainmask", ) # Run atropos (core node) atropos = pe.Node( Atropos( convergence_threshold=0.0, dimension=3, initialization="KMeans", likelihood_model="Gaussian", mrf_radius=[1, 1, 1], mrf_smoothing_factor=0.1, n_iterations=3, number_of_tissue_classes=in_segmentation_model[0], save_posteriors=True, use_random_seed=use_random_seed, ), name="01_atropos", n_procs=omp_nthreads, mem_gb=mem_gb, ) # massage outputs pad_segm = pe.Node( ImageMath(operation="PadImage", op2=f"{padding}", copy_header=False), name="02_pad_segm", ) pad_mask = pe.Node( ImageMath(operation="PadImage", op2=f"{padding}", copy_header=False), name="03_pad_mask", ) # Split segmentation in binary masks sel_labels = pe.Node( niu.Function(function=_select_labels, output_names=["out_wm", "out_gm", "out_csf"]), name="04_sel_labels", ) sel_labels.inputs.labels = list(reversed(in_segmentation_model[1:])) # Select largest components (GM, WM) # ImageMath ${DIMENSION} ${EXTRACTION_WM} GetLargestComponent ${EXTRACTION_WM} get_wm = pe.Node(ImageMath(operation="GetLargestComponent"), name="05_get_wm") get_gm = pe.Node(ImageMath(operation="GetLargestComponent"), name="06_get_gm") # Fill holes and calculate intersection # ImageMath ${DIMENSION} ${EXTRACTION_TMP} FillHoles ${EXTRACTION_GM} 2 # MultiplyImages ${DIMENSION} ${EXTRACTION_GM} ${EXTRACTION_TMP} ${EXTRACTION_GM} fill_gm = pe.Node(ImageMath(operation="FillHoles", op2="2"), name="07_fill_gm") mult_gm = pe.Node( MultiplyImages(dimension=3, output_product_image="08_mult_gm.nii.gz"), name="08_mult_gm", ) # MultiplyImages ${DIMENSION} ${EXTRACTION_WM} ${ATROPOS_WM_CLASS_LABEL} ${EXTRACTION_WM} # ImageMath ${DIMENSION} ${EXTRACTION_TMP} ME ${EXTRACTION_CSF} 10 relabel_wm = pe.Node( MultiplyImages( dimension=3, second_input=in_segmentation_model[-1], output_product_image="09_relabel_wm.nii.gz", ), name="09_relabel_wm", ) me_csf = pe.Node(ImageMath(operation="ME", op2="10"), name="10_me_csf") # ImageMath ${DIMENSION} ${EXTRACTION_GM} addtozero ${EXTRACTION_GM} ${EXTRACTION_TMP} # MultiplyImages ${DIMENSION} ${EXTRACTION_GM} ${ATROPOS_GM_CLASS_LABEL} ${EXTRACTION_GM} # ImageMath ${DIMENSION} ${EXTRACTION_SEGMENTATION} addtozero ${EXTRACTION_WM} ${EXTRACTION_GM} add_gm = pe.Node(ImageMath(operation="addtozero"), name="11_add_gm") relabel_gm = pe.Node( MultiplyImages( dimension=3, second_input=in_segmentation_model[-2], output_product_image="12_relabel_gm.nii.gz", ), name="12_relabel_gm", ) add_gm_wm = pe.Node(ImageMath(operation="addtozero"), name="13_add_gm_wm") # Superstep 7 # Split segmentation in binary masks sel_labels2 = pe.Node( niu.Function(function=_select_labels, output_names=["out_gm", "out_wm"]), name="14_sel_labels2", ) sel_labels2.inputs.labels = in_segmentation_model[2:] # ImageMath ${DIMENSION} ${EXTRACTION_MASK} addtozero ${EXTRACTION_MASK} ${EXTRACTION_TMP} add_7 = pe.Node(ImageMath(operation="addtozero"), name="15_add_7") # ImageMath ${DIMENSION} ${EXTRACTION_MASK} ME ${EXTRACTION_MASK} 2 me_7 = pe.Node(ImageMath(operation="ME", op2="2"), name="16_me_7") # ImageMath ${DIMENSION} ${EXTRACTION_MASK} GetLargestComponent ${EXTRACTION_MASK} comp_7 = pe.Node(ImageMath(operation="GetLargestComponent"), name="17_comp_7") # ImageMath ${DIMENSION} ${EXTRACTION_MASK} MD ${EXTRACTION_MASK} 4 md_7 = pe.Node(ImageMath(operation="MD", op2="4"), name="18_md_7") # ImageMath ${DIMENSION} ${EXTRACTION_MASK} FillHoles ${EXTRACTION_MASK} 2 fill_7 = pe.Node(ImageMath(operation="FillHoles", op2="2"), name="19_fill_7") # ImageMath ${DIMENSION} ${EXTRACTION_MASK} addtozero ${EXTRACTION_MASK} \ # ${EXTRACTION_MASK_PRIOR_WARPED} add_7_2 = pe.Node(ImageMath(operation="addtozero"), name="20_add_7_2") # ImageMath ${DIMENSION} ${EXTRACTION_MASK} MD ${EXTRACTION_MASK} 5 md_7_2 = pe.Node(ImageMath(operation="MD", op2="5"), name="21_md_7_2") # ImageMath ${DIMENSION} ${EXTRACTION_MASK} ME ${EXTRACTION_MASK} 5 me_7_2 = pe.Node(ImageMath(operation="ME", op2="5"), name="22_me_7_2") # De-pad depad_mask = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding), name="23_depad_mask") depad_segm = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding), name="24_depad_segm") depad_gm = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding), name="25_depad_gm") depad_wm = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding), name="26_depad_wm") depad_csf = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding), name="27_depad_csf") msk_conform = pe.Node(niu.Function(function=_conform_mask), name="msk_conform") merge_tpms = pe.Node(niu.Merge(in_segmentation_model[0]), name="merge_tpms") sel_wm = pe.Node(niu.Select(), name="sel_wm", run_without_submitting=True) if not wm_prior: sel_wm.inputs.index = in_segmentation_model[-1] - 1 copy_xform_wm = pe.Node(CopyXForm(fields=["wm_map"]), name="copy_xform_wm", run_without_submitting=True) # Refine INU correction inu_n4_final = pe.MapNode( N4BiasFieldCorrection( dimension=3, save_bias=True, copy_header=True, n_iterations=[50] * 5, convergence_threshold=1e-7, shrink_factor=4, bspline_fitting_distance=bspline_fitting_distance, ), n_procs=omp_nthreads, name="inu_n4_final", iterfield=["input_image"], ) try: inu_n4_final.inputs.rescale_intensities = True except ValueError: warn( "N4BiasFieldCorrection's --rescale-intensities option was added in ANTS 2.1.0 " f"({inu_n4_final.interface.version} found.) Please consider upgrading.", UserWarning, ) # Apply mask apply_mask = pe.MapNode(ApplyMask(), iterfield=["in_file"], name="apply_mask") # fmt: off wf.connect([ (inputnode, dil_brainmask, [("in_mask", "op1")]), (inputnode, copy_xform, [(("in_files", _pop), "hdr_file")]), (inputnode, copy_xform_wm, [(("in_files", _pop), "hdr_file")]), (inputnode, pad_mask, [("in_mask", "op1")]), (inputnode, atropos, [("in_corrected", "intensity_images")]), (inputnode, inu_n4_final, [("in_files", "input_image")]), (inputnode, msk_conform, [(("in_files", _pop), "in_reference")]), (dil_brainmask, get_brainmask, [("output_image", "op1")]), (get_brainmask, atropos, [("output_image", "mask_image")]), (atropos, pad_segm, [("classified_image", "op1")]), (pad_segm, sel_labels, [("output_image", "in_segm")]), (sel_labels, get_wm, [("out_wm", "op1")]), (sel_labels, get_gm, [("out_gm", "op1")]), (get_gm, fill_gm, [("output_image", "op1")]), (get_gm, mult_gm, [("output_image", "first_input")]), (fill_gm, mult_gm, [("output_image", "second_input")]), (get_wm, relabel_wm, [("output_image", "first_input")]), (sel_labels, me_csf, [("out_csf", "op1")]), (mult_gm, add_gm, [("output_product_image", "op1")]), (me_csf, add_gm, [("output_image", "op2")]), (add_gm, relabel_gm, [("output_image", "first_input")]), (relabel_wm, add_gm_wm, [("output_product_image", "op1")]), (relabel_gm, add_gm_wm, [("output_product_image", "op2")]), (add_gm_wm, sel_labels2, [("output_image", "in_segm")]), (sel_labels2, add_7, [("out_wm", "op1"), ("out_gm", "op2")]), (add_7, me_7, [("output_image", "op1")]), (me_7, comp_7, [("output_image", "op1")]), (comp_7, md_7, [("output_image", "op1")]), (md_7, fill_7, [("output_image", "op1")]), (fill_7, add_7_2, [("output_image", "op1")]), (pad_mask, add_7_2, [("output_image", "op2")]), (add_7_2, md_7_2, [("output_image", "op1")]), (md_7_2, me_7_2, [("output_image", "op1")]), (me_7_2, depad_mask, [("output_image", "op1")]), (add_gm_wm, depad_segm, [("output_image", "op1")]), (relabel_wm, depad_wm, [("output_product_image", "op1")]), (relabel_gm, depad_gm, [("output_product_image", "op1")]), (sel_labels, depad_csf, [("out_csf", "op1")]), (depad_csf, merge_tpms, [("output_image", "in1")]), (depad_gm, merge_tpms, [("output_image", "in2")]), (depad_wm, merge_tpms, [("output_image", "in3")]), (depad_mask, msk_conform, [("output_image", "in_mask")]), (msk_conform, copy_xform, [("out", "out_mask")]), (depad_segm, copy_xform, [("output_image", "out_segm")]), (merge_tpms, copy_xform, [("out", "out_tpms")]), (atropos, sel_wm, [("posteriors", "inlist")]), (sel_wm, copy_xform_wm, [("out", "wm_map")]), (copy_xform_wm, inu_n4_final, [("wm_map", "weight_image")]), (inu_n4_final, copy_xform, [("output_image", "bias_corrected"), ("bias_image", "bias_image")]), (copy_xform, apply_mask, [("bias_corrected", "in_file"), ("out_mask", "in_mask")]), (apply_mask, outputnode, [("out_file", "out_file")]), (copy_xform, outputnode, [ ("bias_corrected", "bias_corrected"), ("bias_image", "bias_image"), ("out_mask", "out_mask"), ("out_segm", "out_segm"), ("out_tpms", "out_tpms"), ]), ]) # fmt: on if wm_prior: from nipype.algorithms.metrics import FuzzyOverlap def _argmax(in_dice): import numpy as np return np.argmax(in_dice) match_wm = pe.Node( niu.Function(function=_matchlen), name="match_wm", run_without_submitting=True, ) overlap = pe.Node(FuzzyOverlap(), name="overlap", run_without_submitting=True) apply_wm_prior = pe.Node(niu.Function(function=_improd), name="apply_wm_prior") # fmt: off wf.disconnect([ (copy_xform_wm, inu_n4_final, [("wm_map", "weight_image")]), ]) wf.connect([ (inputnode, apply_wm_prior, [("in_mask", "in_mask"), ("wm_prior", "op2")]), (inputnode, match_wm, [("wm_prior", "value")]), (atropos, match_wm, [("posteriors", "reference")]), (atropos, overlap, [("posteriors", "in_ref")]), (match_wm, overlap, [("out", "in_tst")]), (overlap, sel_wm, [(("class_fdi", _argmax), "index")]), (copy_xform_wm, apply_wm_prior, [("wm_map", "op1")]), (apply_wm_prior, inu_n4_final, [("out", "weight_image")]), ]) # fmt: on return wf
def make_workflow(): default_conf = [ { str('step'): 32, str('blur_fwhm'): 16, str('iterations'): 20 }, { str('step'): 16, str('blur_fwhm'): 8, str('iterations'): 20 }, { str('step'): 12, str('blur_fwhm'): 6, str('iterations'): 20 }, { str('step'): 8, str('blur_fwhm'): 4, str('iterations'): 20 }, { str('step'): 6, str('blur_fwhm'): 3, str('iterations'): 20 }, { str('step'): 4, str('blur_fwhm'): 2, str('iterations'): 10 }, { str('step'): 2, str('blur_fwhm'): 1, str('iterations'): 10 }, ] #FAST_EXAMPLE_BASE_DIR='/data/lfs2/model-mie/pyScripts/python3volgenmodel-nipype/volgenmodel-fast-example/' FAST_EXAMPLE_BASE_DIR = str('/scratch/volgenmodel-fast-example/') # Top level workflow. workflow = pe.Workflow(name="workflow") # FIXME # Just for testing, tell Nipype to keep all outputs. # workflow.config['execution'] = {'remove_unnecessary_outputs': 'false'} workflow.base_dir = os.path.abspath(FAST_EXAMPLE_BASE_DIR) infiles = sorted( glob.glob(os.path.join(FAST_EXAMPLE_BASE_DIR, 'mouse*mnc'))) datasource = pe.Node(interface=nio.DataGrabber(sort_filelist=True), name='datasource_mouse') datasource.inputs.base_directory = os.path.abspath(FAST_EXAMPLE_BASE_DIR) datasource.inputs.template = 'mouse*.mnc' datasink = pe.Node(interface=nio.DataSink(), name="datasink") datasink.inputs.base_directory = os.path.abspath( os.path.join(FAST_EXAMPLE_BASE_DIR, str('volgenmodel_final_output'))) opt = { 'verbose': 0, 'clobber': 0, 'fake': 0, 'check': 1, 'clean': 0, 'keep_tmp': 0, # 'workdir': os.path.join(os.getcwd(), 'work'), # "./$me-work", 'batch': 0, 'symmetric': 0, 'symmetric_dir': 'x', 'normalise': 1, 'model_norm_thresh': 0.1, 'model_min_step': 0.5, 'pad': 10, 'iso': 1, 'config_file': None, 'linmethod': 'bestlinreg', 'init_model': None, 'output_model': None, 'output_stdev': None, 'fit_stages': 'lin,lin,lin,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3', } # from create-model.sh opt['symmetric'] = 1 opt['symmetric_dir'] = 'x' opt['check'] = 1 opt['normalise'] = 1 opt['model_norm_thresh'] = 0.1 opt['model_min_step'] = 0.5 opt['pad'] = 5 # opt['config_file'] = os.path.join(FAST_EXAMPLE_BASE_DIR, 'fit.10-genmodel.conf') opt['config_file'] = None opt['fit_stages'] = 'lin, 0, 0, 1, 1, 2, 2, 3, 3' opt['output_model'] = 'model.mnc' opt['output_stdev'] = 'stdev.mnc' # opt['workdir'] = '/scratch/volgenmodel-fast-example/work' opt['verbose'] = 1 opt['clobber'] = 1 def eval_to_int(x): try: return int(x) except: return x # setup the fit stages fit_stages = opt['fit_stages'].split(',') fit_stages = list(map(eval_to_int, fit_stages)) # check for infiles and create files array if opt['verbose']: print("+++ INFILES\n") dirs = [None] * len(infiles) files = [None] * len(infiles) fileh = {} sub_id = [] c = 0 for z in infiles: dir = None f = None c_txt = '%04d' % c # check assert os.path.exists(z) # set up arrays dirs[c] = os.path.split(z)[0] # &dirname($_); files[c] = c_txt + '-' + os.path.basename( z) # "$c_txt-" . &basename($_); files[c] = files[c].replace('.mnc', '') # =~ s/\.mnc$//; fileh[files[c]] = c sub_id.append(c) if opt['verbose']: print(" | [{c_txt}] {d} / {f}".format(c_txt=c_txt, d=dirs[c], f=files[c])) c += 1 conf = read_conf_array(opt) # sanity check for fit config if fit_stages[-1] > (len(conf) - 1): assert False, ( "Something is amiss with fit config, requested a " "fit step ($fit_stages[-1]) beyond what is defined in the " "fitting protocol (size: $#conf)\n\n") #rename #renameFiles = pe.MapNode(interface=Rename(format_string="importDcm2Mnc%(sd)04d_normStepSize_", keep_ext=True), #iterfield=['in_file', 'sd'], name='RenameFile') #renameFiles.inputs.sd = sub_id #workflow.connect(datasource, 'outfiles', renameFiles, 'in_file') # do pre-processing preprocess_volcentre = pe.MapNode(interface=Volcentre(zero_dircos=True), name='preprocess_volcentre', iterfield=['input_file']) #workflow.connect(renameFiles, 'out_file', preprocess_volcentre, 'input_file') workflow.connect(datasource, 'outfiles', preprocess_volcentre, 'input_file') # normalise if opt['normalise']: preprocess_threshold_blur = pe.MapNode( interface=deepcopy( calc_threshold_blur_preprocess ), # Beware! Need deepcopy since calc_threshold_blur_preprocess is not a constructor! name='preprocess_threshold_blur', iterfield=['input_file']) workflow.connect(preprocess_volcentre, 'output_file', preprocess_threshold_blur, 'input_file') preprocess_normalise = pe.MapNode( interface=Norm(cutoff=opt['model_norm_thresh'], threshold=True, threshold_perc=opt['model_norm_thresh']), # output_file=nrmfile), name='preprocess_normalise', iterfield=['input_file', 'threshold_blur']) workflow.connect(preprocess_threshold_blur, 'threshold_blur', preprocess_normalise, 'threshold_blur') # do_cmd('mv -f %s %s' % (nrmfile, resfiles[f],)) else: preprocess_normalise_id = utils.Function( input_names=['input_file'], output_names=['output_file'], function=IdentityFile, ) preprocess_normalise = pe.MapNode(interface=preprocess_normalise_id, name='preprocess_normalise', iterfield=['input_file']) workflow.connect(preprocess_volcentre, 'output_file', preprocess_normalise, 'input_file') # extend/pad if opt['pad'] > 0: #smoothPadValue = 2 preprocess_volpad = pe.MapNode( interface=Volpad(distance=opt['pad'], smooth=True, smooth_distance=5), # output_file=fitfiles[f]), name='preprocess_volpad', iterfield=['input_file']) else: preprocess_volpad_id = utils.Function( input_names=['input_file'], output_names=['output_file'], function=IdentityFile, ) preprocess_volpad = pe.MapNode(interface=preprocess_volpad_id, name='preprocess_volpad', iterfield=['input_file']) workflow.connect(preprocess_normalise, 'output_file', preprocess_volpad, 'input_file') # isotropic resampling if opt['iso']: preprocess_voliso = pe.MapNode( interface=Voliso(avgstep=True), # output_file=isofile), name='preprocess_voliso', iterfield=['input_file']) else: preprocess_voliso_id = utils.Function( input_names=['input_file'], output_names=['output_file'], function=IdentityFile, ) preprocess_voliso = pe.MapNode(interface=preprocess_voliso_id, name='preprocess_iso', iterfield=['input_file']) workflow.connect(preprocess_volpad, 'output_file', preprocess_voliso, 'input_file') # checkfile if opt['check']: preprocess_pik = pe.MapNode( interface=Pik(triplanar=True, sagittal_offset=10), # output_file=chkfile), name='preprocess_pik', iterfield=['input_file']) else: preprocess_pik_id = utils.Function( input_names=['input_file'], output_names=['output_file'], function=IdentityFile, ) preprocess_pik = pe.MapNode(interface=preprocess_pik_id, name='preprocess_pik', iterfield=['input_file']) workflow.connect(preprocess_volpad, 'output_file', preprocess_pik, 'input_file') # setup the initial model if opt['init_model'] is not None: # cmodel = opt['init_model'] raise NotImplemented # To do this, make a data grabber that sends the MNC file to # the identity_transformation node below. else: # Select the 'first' output file from volpad (fitfiles[] in the original volgenmodel). select_first_volpad = pe.Node(interface=utils.Select(index=[0]), name='select_first_volpad') workflow.connect(preprocess_volpad, 'output_file', select_first_volpad, 'inlist') # Select the 'first' input file to calculate the fhwm3d parameter (infiles[] in the original volgenmodel). select_first_datasource = pe.Node(interface=utils.Select(index=[0]), name='select_first_datasource') workflow.connect(datasource, 'outfiles', select_first_datasource, 'inlist') # Calculate the fhwm3d parameter using the first datasource. initial_model_fwhm3d = pe.Node( interface=deepcopy(calc_initial_model_fwhm3d), name='initial_model_fwhm3d' ) # Beware! Need deepcopy since calc_initial_model_fwhm3d is not a constructor! workflow.connect(select_first_datasource, 'out', initial_model_fwhm3d, 'input_file') initial_model = pe.Node( interface=Blur( ), # output_file_base=os.path.join(opt['workdir'], '00-init-model')), name='initial_model') workflow.connect(select_first_volpad, 'out', initial_model, 'input_file') workflow.connect(initial_model_fwhm3d, 'fwhm3d', initial_model, 'fwhm3d') # Current model starts off as the initial model. cmodel = initial_model identity_transformation = pe.Node( interface=Gennlxfm( step=conf[0]['step']), # output_file=initxfm, also output_grid! name='identity_transformation') workflow.connect(initial_model, 'output_file', identity_transformation, 'like') # get last linear stage from fit config s = None end_stage = None snum = 0 lastlin = 0 for snum in range(len( fit_stages)): # for($snum = 0; $snum <= $#fit_stages; $snum++){ if fit_stages[snum] == 'lin': lastlin = snum # "%02d" % snum print("+++ Last Linear stage:", lastlin) # Foreach end stage in the fitting profile print("+++ Fitting") last_linear_stage_xfm_node = None for snum in range(len(fit_stages)): snum_txt = None end_stage = None # f = None # cworkdir = None # conf_fname = None # modxfm = [None] * len(files) # rsmpl = [None] * len(files) end_stage = fit_stages[snum] snum_txt = "%02d_" % snum print(" + [Stage: {snum_txt}] End stage: {end_stage}".format( snum_txt=snum_txt, end_stage=end_stage)) # make subdir in working dir for files # cworkdir = os.path.join(opt['workdir'], snum_txt) # if not os.path.exists(cworkdir): # do_cmd('mkdir ' + cworkdir) # set up model and xfm names # avgxfm = os.path.join(cworkdir, "avgxfm.xfm") # iavgfile = os.path.join(cworkdir, "model.iavg.mnc") # istdfile = os.path.join(cworkdir, "model.istd.mnc") # stage_model = os.path.join(cworkdir, "model.avg.mnc") # iavgfilechk = os.path.join(cworkdir, "model.iavg.jpg") # istdfilechk = os.path.join(cworkdir, "model.istd.jpg") # stage_modelchk = os.path.join(cworkdir, "model.avg.jpg") # create the ISO model # isomodel_base = os.path.join(cworkdir, "fit-model-iso") if end_stage == 'lin': _idx = 0 else: _idx = end_stage modelmaxstep = conf[_idx]['step'] / 4 # check that the resulting model won't be too large # this seems confusing but it actually makes sense... if float(modelmaxstep) < float(opt['model_min_step']): modelmaxstep = opt['model_min_step'] print(" -- Model Max step:", modelmaxstep) norm = pe.Node( interface=Norm(cutoff=opt['model_norm_thresh'], threshold=True, threshold_perc=opt['model_norm_thresh'], threshold_blur=3), # output_threshold_mask=isomodel_base + ".msk.mnc"), # input_file=cmodel, # output_file=isomodel_base + ".nrm.mnc"), name='norm_' + snum_txt) workflow.connect(cmodel, 'output_file', norm, 'input_file') voliso = pe.Node( interface=Voliso(maxstep=modelmaxstep), # input_file=isomodel_base + ".nrm.mnc", # output_file=isomodel_base + ".mnc"), name='voliso_' + snum_txt) workflow.connect(norm, 'output_file', voliso, 'input_file') if opt['check']: pik = pe.Node( interface=Pik(triplanar=True, horizontal_triplanar_view=True, scale=4, tile_size=400, sagittal_offset=10), # input_file=isomodel_base + ".mnc", # output_file=isomodel_base + ".jpg"), name='pik_check_voliso' + snum_txt) workflow.connect(voliso, 'output_file', pik, 'input_file') # create the isomodel fit mask #chomp($step_x = `mincinfo -attvalue xspace:step $isomodel_base.msk.mnc`); step_x = 1 blur = pe.Node( interface=Blur(fwhm=step_x * 15), # input_file=isomodel_base + ".msk.mnc", # output_file_base=isomodel_base + ".msk"), name='blur_' + snum_txt) workflow.connect(norm, 'output_threshold_mask', blur, 'input_file') mincmath = pe.Node( interface=Math(test_gt=0.1), # input_files=[isomodel_base + ".msk_blur.mnc"], # output_file=isomodel_base + ".fit-msk.mnc"), name='mincmath_' + snum_txt) workflow.connect(blur, 'output_file', mincmath, 'input_files') # linear or nonlinear fit if end_stage == 'lin': print("---Linear fit---") else: print("---Non Linear fit---") # create nlin fit config if end_stage != 'lin': write_conf = pe.Node( interface=deepcopy( write_stage_conf_file ), # Beware! Need deepcopy since write_stage_conf_file is not a constructor! name='write_conf_' + snum_txt) write_conf.inputs.snum = snum write_conf.inputs.snum_txt = snum_txt write_conf.inputs.conf = conf write_conf.inputs.end_stage = end_stage # register each file in the input series if end_stage == 'lin': assert opt['linmethod'] == 'bestlinreg' bestlinreg = pe.MapNode( interface=BestLinReg(), # source=isomodel_base + ".mnc", # target=fitfiles[f], # output_xfm=modxfm[f]), name='register_' + snum_txt, iterfield=['target']) workflow.connect(voliso, 'output_file', bestlinreg, 'source') workflow.connect(preprocess_voliso, 'output_file', bestlinreg, 'target') if snum == lastlin: last_linear_stage_xfm_node = bestlinreg modxfm = bestlinreg else: xfmconcat = pe.MapNode( interface=XfmConcat(), # input_files=[os.path.join(opt['workdir'], lastlin, files[f] + ".xfm"), initxfm], # output_file=initcnctxfm), name='xfmconcat_for_nlpfit_' + snum_txt, iterfield=['input_files']) merge_lastlin_initxfm = pe.MapNode(interface=utils.Merge(2), name='merge_lastlin_initxfm_' + snum_txt, iterfield=['in1']) workflow.connect(last_linear_stage_xfm_node, 'output_xfm', merge_lastlin_initxfm, 'in1') workflow.connect(identity_transformation, 'output_file', merge_lastlin_initxfm, 'in2') workflow.connect(merge_lastlin_initxfm, 'out', xfmconcat, 'input_files') workflow.connect(identity_transformation, 'output_grid', xfmconcat, 'input_grid_files') nlpfit = pe.MapNode( interface=NlpFit(), # init_xfm=initcnctxfm, # config_file=conf_fname), # source_mask=isomodel_base + ".fit-msk.mnc", # source=isomodel_base + ".mnc", # target=fitfiles[f], # output_xfm=modxfm[f]), name='nlpfit_' + snum_txt, iterfield=['target', 'init_xfm']) workflow.connect(write_conf, 'conf_fname', nlpfit, 'config_file') workflow.connect(xfmconcat, 'output_file', nlpfit, 'init_xfm') workflow.connect(mincmath, 'output_file', nlpfit, 'source_mask') workflow.connect(voliso, 'output_file', nlpfit, 'source') workflow.connect( preprocess_voliso, 'output_file', nlpfit, 'target' ) # Make sure that fitfiles[f] is preprocess_voliso at this point in the program. workflow.connect(xfmconcat, 'output_grids', nlpfit, 'input_grid_files') modxfm = nlpfit # average xfms xfmavg = pe.Node( interface=XfmAvg(), # input_files=modxfm, # output_file=avgxfm), name='xfmavg_' + snum_txt) if end_stage != 'lin': workflow.connect(nlpfit, 'output_grid', xfmavg, 'input_grid_files') workflow.connect( modxfm, 'output_xfm', xfmavg, 'input_files' ) # check that this works - multiple outputs of MapNode going into single list of xfmavg. if end_stage == 'lin': xfmavg.interface.inputs.ignore_nonlinear = True else: xfmavg.interface.inputs.ignore_linear = True # invert model xfm xfminvert = pe.MapNode( interface=XfmInvert(), # input_file=modxfm[f], # output_file=invxfm), name='xfminvert_' + snum_txt, iterfield=['input_file']) workflow.connect(modxfm, 'output_xfm', xfminvert, 'input_file') # concat: invxfm, avgxfm merge_xfm = pe.MapNode(interface=utils.Merge(2), name='merge_xfm_' + snum_txt, iterfield=['in1']) workflow.connect(xfminvert, 'output_file', merge_xfm, 'in1') workflow.connect(xfmavg, 'output_file', merge_xfm, 'in2') # Collect grid f iles of xfminvert and xvmavg. This is in two steps. # # 1. Merge MapNode results. merge_xfm_mapnode_result = pe.Node(interface=utils.Merge(1), name='merge_xfm_mapnode_result_' + snum_txt) workflow.connect(xfminvert, 'output_grid', merge_xfm_mapnode_result, 'in1') # 2. Merge xfmavg's single output with the result from step 1. merge_xfmavg_and_step1 = pe.Node(interface=utils.Merge(2), name='merge_xfmavg_and_step1' + snum_txt) workflow.connect(merge_xfm_mapnode_result, 'out', merge_xfmavg_and_step1, 'in1') workflow.connect(xfmavg, 'output_grid', merge_xfmavg_and_step1, 'in2') xfmconcat = pe.MapNode( interface=XfmConcat(), # input_files=[invxfm, avgxfm], # output_file=resxfm), name='xfmconcat_' + snum_txt, iterfield=['input_files']) workflow.connect(merge_xfm, 'out', xfmconcat, 'input_files') workflow.connect(merge_xfmavg_and_step1, 'out', xfmconcat, 'input_grid_files') # resample resample = pe.MapNode( interface=Resample(sinc_interpolation=True), # transformation=resxfm, # like=isomodel_base + ".mnc", # input_file=resfiles[f], # output_file=rsmpl[f]), name='resample_' + snum_txt, iterfield=['input_file', 'transformation']) workflow.connect(preprocess_normalise, 'output_file', resample, 'input_file') workflow.connect(xfmconcat, 'output_file', resample, 'transformation') workflow.connect(voliso, 'output_file', resample, 'like') if opt['check']: pik_check_resample = pe.MapNode( interface=Pik(triplanar=True, sagittal_offset=10), # input_file=rsmpl[f], # output_file=chkfile), name='pik_check_resample_' + snum_txt, iterfield=['input_file']) workflow.connect(resample, 'output_file', pik_check_resample, 'input_file') # create model bigaverage = pe.Node( interface=BigAverage(output_float=True, robust=True), # tmpdir=os.path.join(opt['workdir'], 'tmp'), # sd_file=istdfile, # input_files=rsmpl, # output_file=iavgfile), name='bigaverage_' + snum_txt, iterfield=['input_file']) workflow.connect(resample, 'output_file', bigaverage, 'input_files') if opt['check']: pik_check_iavg = pe.Node( interface=Pik(triplanar=True, horizontal_triplanar_view=True, scale=4, tile_size=400, sagittal_offset=10), # input_file=iavgfile, # output_file=iavgfilechk), name='pik_check_iavg_' + snum_txt) workflow.connect(bigaverage, 'output_file', pik_check_iavg, 'input_file') # do symmetric averaging if required if opt['symmetric']: # symxfm = os.path.join(cworkdir, 'model.sym.xfm') # symfile = os.path.join(cworkdir, 'model.iavg-short.mnc') # convert double model to short resample_to_short = pe.Node( interface=Reshape(write_short=True), # input_file=iavgfile, # output_file=symfile), name='resample_to_short_' + snum_txt) workflow.connect(bigaverage, 'output_file', resample_to_short, 'input_file') assert opt['symmetric_dir'] == 'x' # handle other cases volsymm_on_short = pe.Node( interface=VolSymm(x=True), # input_file=symfile, # trans_file=symxfm, # This is an output! # output_file=stage_model), name='volsymm_on_short_' + snum_txt) workflow.connect(resample_to_short, 'output_file', volsymm_on_short, 'input_file') # set up fit args if end_stage == 'lin': volsymm_on_short.interface.inputs.fit_linear = True else: volsymm_on_short.interface.inputs.fit_nonlinear = True workflow.connect(write_conf, 'conf_fname', volsymm_on_short, 'config_file') else: # do_cmd('ln -s -f %s %s' % (os.path.basename(iavgfile), stage_model,)) volsymm_on_short_id = utils.Function( input_names=['input_file'], output_names=['output_file'], function=IdentityFile, ) volsymm_on_short = pe.Node(interface=volsymm_on_short_id, name='volsymm_on_short_' + snum_txt) workflow.connect(bigaverage, 'output_file', volsymm_on_short, 'input_file') # We finally have the stage model. stage_model = volsymm_on_short if opt['check']: pik_on_stage_model = pe.Node( interface=Pik(triplanar=True, horizontal_triplanar_view=True, scale=4, tile_size=400, sagittal_offset=10), # input_file=stage_model, # output_file=stage_modelchk), name='pik_on_stage_model_' + snum_txt) workflow.connect(stage_model, 'output_file', pik_on_stage_model, 'input_file') # if on last step, copy model to $opt{'output_model'} if snum == len(fit_stages) - 1: workflow.connect(stage_model, 'output_file', datasink, 'model') # create and output standard deviation file if requested if opt['output_stdev'] is not None: if opt['symmetric']: assert opt['symmetric_dir'] == 'x' # handle other cases volsymm_final_model = pe.Node( interface=VolSymm(x=True, nofit=True), # input_file=istdfile, # trans_file=symxfm, # This is an output! # output_file=opt['output_stdev']), name='volsymm_final_model_' + snum_txt) workflow.connect(bigaverage, 'sd_file', volsymm_final_model, 'input_file') workflow.connect(volsymm_on_short, 'trans_file', volsymm_final_model, 'trans_file') workflow.connect(volsymm_on_short, 'output_grid', volsymm_final_model, 'input_grid_files') workflow.connect(volsymm_final_model, 'output_file', datasink, 'stdev') # we ignore opt['output_stdev'] else: # do_cmd('cp -f %s %s' % (istdfile, opt['output_stdev'],)) workflow.connect(bigaverage, 'sd_file', datasink, 'stdev') # we ignore opt['output_stdev'] cmodel = stage_model return workflow
""" level1estimate = pe.Node(interface=spm.EstimateModel(), name="level1estimate") level1estimate.inputs.estimation_method = {'Classical': 1} """Use :class:`nipype.interfaces.spm.EstimateContrast` to estimate the first level contrasts specified in a few steps above. """ contrastestimate = pe.Node(interface=spm.EstimateContrast(), name="contrastestimate") """Use :class: `nipype.interfaces.utility.Select` to select each contrast for reporting. """ selectcontrast = pe.Node(interface=util.Select(), name="selectcontrast") """Use :class:`nipype.interfaces.fsl.Overlay` to combine the statistical output of the contrast estimate and a background image into one volume. """ overlaystats = pe.Node(interface=fsl.Overlay(), name="overlaystats") overlaystats.inputs.stat_thresh = (3, 10) overlaystats.inputs.show_negative_stats = True overlaystats.inputs.auto_thresh_bg = True """Use :class:`nipype.interfaces.fsl.Slicer` to create images of the overlaid statistical volumes for a report of the first-level results. """ slicestats = pe.Node(interface=fsl.Slicer(), name="slicestats")
# Add default arguments in the parser default_parser_argument(parser) # Parse the input arguments args = parser.parse_args() # Create the output folder if it does not exists result_dir = os.path.abspath(args.output_dir) if not os.path.exists(result_dir): os.mkdir(result_dir) input_images = [os.path.abspath(f) for f in args.input_img] # Create an iterable to loop over all input images selector = pe.Node(niu.Select(inlist=input_images), name='selector', iterables=('index', range(len(input_images)))) # Create the workflow db = os.path.abspath(args.database) workflow = create_steps_propagation_pipeline(name='steps_propagation', aligned_templates=get_db_template_alignment(db)) workflow.inputs.input_node.database_file = db workflow.base_dir = result_dir workflow.connect(selector, 'out', workflow.get_node('input_node'), 'in_file') # output the graph if required if args.graph is True: generate_graph(workflow=workflow) sys.exit(0)
def create_gif_pseudoct_workflow(in_ute_echo2_file, in_ute_umap_dir, in_db_file, cpp_dir, in_t1_file=None, in_t2_file=None, in_mask_file=None, in_nac_pet_dir=None, name='gif_pseudoct'): """create_niftyseg_gif_propagation_pipeline. @param in_ute_echo2_file input UTE echo file @param in_ute_umap_dir input UTE umap file @param in_db_file input database xml file for the GIF algorithm @param cpp_dir cpp directory @param in_t1_file input T1 target file @param in_t2_file input T2 target file @param in_mask_file optional input mask for the target T1 file @param name optional name of the pipeline """ in_file = in_t1_file if in_t1_file else in_t2_file subject_id = split_filename(os.path.basename(in_file))[1] workflow = pe.Workflow(name=name) workflow.base_output_dir = name gif = pe.Node(interface=Gif(database_file=in_db_file, cpp_dir=cpp_dir, lncc_ker=3, regNMI=True, regBE=0.01), name='gif') if in_mask_file: gif.inputs.mask_file = in_mask_file # Create empty masks for the bias correction to cover the full image t1_full_mask = pe.Node(interface=niu.Function(input_names=['in_file'], output_names=['out_file'], function=create_full_mask), name='t1_full_mask') t1_full_mask.inputs.in_file = in_t1_file t2_full_mask = pe.Node(interface=niu.Function(input_names=['in_file'], output_names=['out_file'], function=create_full_mask), name='t2_full_mask') t2_full_mask.inputs.in_file = in_t2_file # Create bias correction nodes that are adapted to our needs. i.e. Boost the T2 bias correction bias_correction_t1 = pe.Node(interface=N4BiasCorrection(), name='bias_correction_t1') if in_t1_file: bias_correction_t1.inputs.in_file = in_t1_file # Create bias correction nodes that are adapted to our needs. i.e. Boost the T2 bias correction bias_correction_t2 = pe.Node(interface=N4BiasCorrection( in_maxiter=300, in_convergence=0.0001), name='bias_correction_t2') if in_t2_file: bias_correction_t2.inputs.in_file = in_t2_file # Only connect the nodes if the input image exist respectively if in_t1_file: workflow.connect(t1_full_mask, 'out_file', bias_correction_t1, 'mask_file') if in_t2_file: workflow.connect(t2_full_mask, 'out_file', bias_correction_t2, 'mask_file') if in_t1_file and in_t2_file: affine_mr_target = pe.Node(interface=niftyreg.RegAladin(maxit_val=10), name='affine_mr_target') workflow.connect(bias_correction_t1, 'out_file', affine_mr_target, 'ref_file') workflow.connect(bias_correction_t2, 'out_file', affine_mr_target, 'flo_file') resample_mr_target = pe.Node( interface=niftyreg.RegResample(pad_val=float('nan')), name='resample_MR_target') workflow.connect(bias_correction_t1, 'out_file', resample_mr_target, 'ref_file') workflow.connect(bias_correction_t2, 'out_file', resample_mr_target, 'flo_file') lister = pe.Node(interface=niu.Merge(2), name='lister') merger = pe.Node(interface=fsl.Merge(dimension='t', output_type='NIFTI_GZ'), name='fsl_merge') workflow.connect(affine_mr_target, 'aff_file', resample_mr_target, 'trans_file') workflow.connect(bias_correction_t1, 'out_file', lister, 'in1') workflow.connect(resample_mr_target, 'out_file', lister, 'in2') workflow.connect(lister, 'out', merger, 'in_files') workflow.connect(merger, 'merged_file', gif, 'in_file') else: if in_t1_file: workflow.connect(bias_correction_t1, 'out_file', gif, 'in_file') if in_t2_file: workflow.connect(bias_correction_t2, 'out_file', gif, 'in_file') pct_hu_to_umap = pe.Node(interface=niu.Function( input_names=['pCT_file', 'structural_mri_file', 'ute_echo2_file'], output_names=['pct_umap_file'], function=convert_pct_hu_to_umap), name='pCT_HU_to_umap') pct_hu_to_umap.inputs.structural_mri_file = in_file pct_hu_to_umap.inputs.ute_echo2_file = in_ute_echo2_file workflow.connect(gif, 'synth_file', pct_hu_to_umap, 'pCT_file') pct2dcm_pct_umap = pe.Node(interface=Pct2Dcm(in_umap_name='pCT_umap'), name='pct2dcm_pct_umap') workflow.connect(pct_hu_to_umap, 'pct_umap_file', pct2dcm_pct_umap, 'in_umap_file') pct2dcm_pct_umap.inputs.in_ute_umap_dir = os.path.abspath(in_ute_umap_dir) merger_output_number = 2 pct2dcm_ute_umap_end = None pct2dcm_pct_umap_end = None if in_nac_pet_dir: ute_umap_dcm2nii = pe.Node( interface=Dcm2nii(source_dir=in_ute_umap_dir), name='ute_umap_dcm2nii') first_item_selector = pe.Node(interface=niu.Select(index=0), name='first_item_selector') workflow.connect(ute_umap_dcm2nii, 'converted_files', first_item_selector, 'inlist') nac_extractor = pe.Node(interface=niu.Function( input_names=['dicom_folder'], output_names=['nifti_file'], function=extract_nac_pet), name='nac_extractor') nac_extractor.inputs.dicom_folder = in_nac_pet_dir ute_to_nac_registration = pe.Node( interface=niftyreg.RegAladin(rig_only_flag=True), name='ute_to_nac_registration') workflow.connect(nac_extractor, 'nifti_file', ute_to_nac_registration, 'ref_file') ute_to_nac_registration.inputs.flo_file = in_ute_echo2_file ute_resample = pe.Node(interface=niftyreg.RegResample(), name='ute_resample') workflow.connect(first_item_selector, 'out', ute_resample, 'ref_file') workflow.connect(first_item_selector, 'out', ute_resample, 'flo_file') workflow.connect(ute_to_nac_registration, 'aff_file', ute_resample, 'aff_file') pct2dcm_ute_umap_end = pe.Node( interface=Pct2Dcm(in_umap_name='UTE_umap_end'), name='pct2dcm_ute_umap_end') workflow.connect(ute_resample, 'res_file', pct2dcm_ute_umap_end, 'in_umap_file') pct2dcm_ute_umap_end.inputs.in_ute_umap_dir = os.path.abspath( in_ute_umap_dir) pct_resample = pe.Node(interface=niftyreg.RegResample(), name='pct_resample') workflow.connect(pct_hu_to_umap, 'pct_umap_file', pct_resample, 'ref_file') workflow.connect(pct_hu_to_umap, 'pct_umap_file', pct_resample, 'flo_file') workflow.connect(ute_to_nac_registration, 'aff_file', pct_resample, 'aff_file') pct2dcm_pct_umap_end = pe.Node( interface=Pct2Dcm(in_umap_name='pCT_umap_end'), name='pct2dcm_pct_umap_end') workflow.connect(pct_resample, 'res_file', pct2dcm_pct_umap_end, 'in_umap_file') pct2dcm_pct_umap_end.inputs.in_ute_umap_dir = os.path.abspath( in_ute_umap_dir) merger_output_number = 4 # merge output output_merger = pe.Node( interface=niu.Merge(numinputs=merger_output_number), name='output_merger') workflow.connect(gif, 'synth_file', output_merger, 'in1') workflow.connect(pct2dcm_pct_umap, 'output_file', output_merger, 'in2') renamer = pe.Node(interface=niu.Rename(format_string=subject_id + "_%(type)s", keep_ext=True), name='renamer') if in_nac_pet_dir: workflow.connect(pct2dcm_ute_umap_end, 'output_file', output_merger, 'in3') workflow.connect(pct2dcm_pct_umap_end, 'output_file', output_merger, 'in4') renamer.inputs.type = ['synth', 'pct', 'ute_end', 'pct_end'] else: renamer.inputs.type = ['synth', 'pct'] workflow.connect(output_merger, 'out', renamer, 'in_file') return workflow
def build_core_nodes(self): """Build and connect the core nodes of the pipeline. Notes: - If `FSLOUTPUTTYPE` environment variable is not set, `nipype` takes NIFTI by default. Todo: - [x] Detect space automatically. - [ ] Allow for custom parcellations (See TODOs in utils). """ import nipype.interfaces.freesurfer as fs import nipype.interfaces.fsl as fsl import nipype.interfaces.mrtrix3 as mrtrix3 import nipype.interfaces.utility as niu import nipype.pipeline.engine as npe from nipype.interfaces.mrtrix3.tracking import Tractography from nipype.interfaces.mrtrix.preprocess import MRTransform import clinica.pipelines.dwi_connectome.dwi_connectome_utils as utils from clinica.lib.nipype.interfaces.mrtrix3.reconst import EstimateFOD from clinica.utils.exceptions import ClinicaCAPSError from clinica.utils.mri_registration import ( convert_flirt_transformation_to_mrtrix_transformation, ) # Nodes # ===== # B0 Extraction (only if space=b0) # ------------- split_node = npe.Node(name="Reg-0-DWI-B0Extraction", interface=fsl.Split()) split_node.inputs.output_type = "NIFTI_GZ" split_node.inputs.dimension = "t" select_node = npe.Node(name="Reg-0-DWI-B0Selection", interface=niu.Select()) select_node.inputs.index = 0 # B0 Brain Extraction (only if space=b0) # ------------------- mask_node = npe.Node(name="Reg-0-DWI-BrainMasking", interface=fsl.ApplyMask()) mask_node.inputs.output_type = "NIFTI_GZ" # T1-to-B0 Registration (only if space=b0) # --------------------- t12b0_reg_node = npe.Node( name="Reg-1-T12B0Registration", interface=fsl.FLIRT( dof=6, interp="spline", cost="normmi", cost_func="normmi", ), ) t12b0_reg_node.inputs.output_type = "NIFTI_GZ" # MGZ File Conversion (only if space=b0) # ------------------- t1_brain_conv_node = npe.Node( name="Reg-0-T1-T1BrainConvertion", interface=fs.MRIConvert() ) wm_mask_conv_node = npe.Node( name="Reg-0-T1-WMMaskConvertion", interface=fs.MRIConvert() ) # WM Transformation (only if space=b0) # ----------------- wm_transform_node = npe.Node( name="Reg-2-WMTransformation", interface=fsl.ApplyXFM() ) wm_transform_node.inputs.apply_xfm = True # Nodes Generation # ---------------- label_convert_node = npe.MapNode( name="0-LabelsConversion", iterfield=["in_file", "in_config", "in_lut", "out_file"], interface=mrtrix3.LabelConvert(), ) label_convert_node.inputs.in_config = utils.get_conversion_luts() label_convert_node.inputs.in_lut = utils.get_luts() # FSL flirt matrix to MRtrix matrix Conversion (only if space=b0) # -------------------------------------------- fsl2mrtrix_conv_node = npe.Node( name="Reg-2-FSL2MrtrixConversion", interface=niu.Function( input_names=[ "in_source_image", "in_reference_image", "in_flirt_matrix", "name_output_matrix", ], output_names=["out_mrtrix_matrix"], function=convert_flirt_transformation_to_mrtrix_transformation, ), ) # Parc. Transformation (only if space=b0) # -------------------- parc_transform_node = npe.MapNode( name="Reg-2-ParcTransformation", iterfield=["in_files", "out_filename"], interface=MRTransform(), ) # Response Estimation # ------------------- resp_estim_node = npe.Node( name="1a-ResponseEstimation", interface=mrtrix3.ResponseSD() ) resp_estim_node.inputs.algorithm = "tournier" # FOD Estimation # -------------- fod_estim_node = npe.Node(name="1b-FODEstimation", interface=EstimateFOD()) fod_estim_node.inputs.algorithm = "csd" # Tracts Generation # ----------------- tck_gen_node = npe.Node(name="2-TractsGeneration", interface=Tractography()) tck_gen_node.inputs.select = self.parameters["n_tracks"] tck_gen_node.inputs.algorithm = "iFOD2" # Connectome Generation # --------------------- # only the parcellation and output filename should be iterable, the tck # file stays the same. conn_gen_node = npe.MapNode( name="3-ConnectomeGeneration", iterfield=["in_parc", "out_file"], interface=mrtrix3.BuildConnectome(), ) # Print begin message # ------------------- print_begin_message = npe.MapNode( interface=niu.Function( input_names=["in_bids_or_caps_file"], function=utils.print_begin_pipeline, ), iterfield="in_bids_or_caps_file", name="WriteBeginMessage", ) # Print end message # ----------------- print_end_message = npe.MapNode( interface=niu.Function( input_names=["in_bids_or_caps_file", "final_file"], function=utils.print_end_pipeline, ), iterfield=["in_bids_or_caps_file"], name="WriteEndMessage", ) # CAPS File names Generation # -------------------------- caps_filenames_node = npe.Node( name="CAPSFilenamesGeneration", interface=niu.Function( input_names="dwi_file", output_names=self.get_output_fields(), function=utils.get_caps_filenames, ), ) # Connections # =========== # Computation of the diffusion model, tractography & connectome # ------------------------------------------------------------- # fmt: off self.connect( [ (self.input_node, print_begin_message, [("dwi_file", "in_bids_or_caps_file")]), (self.input_node, caps_filenames_node, [("dwi_file", "dwi_file")]), # Response Estimation (self.input_node, resp_estim_node, [("dwi_file", "in_file")]), # Preproc. DWI (self.input_node, resp_estim_node, [("dwi_brainmask_file", "in_mask")]), # B0 brain mask (self.input_node, resp_estim_node, [("grad_fsl", "grad_fsl")]), # bvecs and bvals (caps_filenames_node, resp_estim_node, [("response", "wm_file")]), # output response filename # FOD Estimation (self.input_node, fod_estim_node, [("dwi_file", "in_file")]), # Preproc. DWI (resp_estim_node, fod_estim_node, [("wm_file", "wm_txt")]), # Response (txt file) (self.input_node, fod_estim_node, [("dwi_brainmask_file", "mask_file")]), # B0 brain mask (self.input_node, fod_estim_node, [("grad_fsl", "grad_fsl")]), # T1-to-B0 matrix file (caps_filenames_node, fod_estim_node, [("fod", "wm_odf")]), # output odf filename # Tracts Generation (fod_estim_node, tck_gen_node, [("wm_odf", "in_file")]), # ODF file (caps_filenames_node, tck_gen_node, [("tracts", "out_file")]), # output tck filename # Label Conversion (self.input_node, label_convert_node, [("atlas_files", "in_file")]), # atlas image files (caps_filenames_node, label_convert_node, [("nodes", "out_file")]), # converted atlas image filenames # Connectomes Generation (tck_gen_node, conn_gen_node, [("out_file", "in_file")]), (caps_filenames_node, conn_gen_node, [("connectomes", "out_file")]), ] ) # Registration T1-DWI (only if space=b0) # ------------------- if self.parameters["dwi_space"] == "b0": self.connect( [ # MGZ Files Conversion (self.input_node, t1_brain_conv_node, [("t1_brain_file", "in_file")]), (self.input_node, wm_mask_conv_node, [("wm_mask_file", "in_file")]), # B0 Extraction (self.input_node, split_node, [("dwi_file", "in_file")]), (split_node, select_node, [("out_files", "inlist")]), # Masking (select_node, mask_node, [("out", "in_file")]), # B0 (self.input_node, mask_node, [("dwi_brainmask_file", "mask_file")]), # Brain mask # T1-to-B0 Registration (t1_brain_conv_node, t12b0_reg_node, [("out_file", "in_file")]), # Brain (mask_node, t12b0_reg_node, [("out_file", "reference")]), # B0 brain-masked # WM Transformation (wm_mask_conv_node, wm_transform_node, [("out_file", "in_file")]), # Brain mask (mask_node, wm_transform_node, [("out_file", "reference")]), # BO brain-masked (t12b0_reg_node, wm_transform_node, [("out_matrix_file", "in_matrix_file")]), # T1-to-B0 matrix file # FSL flirt matrix to MRtrix matrix Conversion (t1_brain_conv_node, fsl2mrtrix_conv_node, [("out_file", "in_source_image")]), (mask_node, fsl2mrtrix_conv_node, [("out_file", "in_reference_image")]), (t12b0_reg_node, fsl2mrtrix_conv_node, [("out_matrix_file", "in_flirt_matrix")]), # Apply registration without resampling on parcellations (label_convert_node, parc_transform_node, [("out_file", "in_files")]), (fsl2mrtrix_conv_node, parc_transform_node, [("out_mrtrix_matrix", "linear_transform")]), (caps_filenames_node, parc_transform_node, [("nodes", "out_filename")]), ] ) # Special care for Parcellation & WM mask # --------------------------------------- if self.parameters["dwi_space"] == "b0": self.connect( [ (wm_transform_node, tck_gen_node, [("out_file", "seed_image")]), (parc_transform_node, conn_gen_node, [("out_file", "in_parc")]), (parc_transform_node, self.output_node, [("out_file", "nodes")]), ] ) elif self.parameters["dwi_space"] == "T1w": self.connect( [ (self.input_node, tck_gen_node, [("wm_mask_file", "seed_image")]), (label_convert_node, conn_gen_node, [("out_file", "in_parc")]), (label_convert_node, self.output_node, [("out_file", "nodes")]), ] ) else: raise ClinicaCAPSError( "Bad preprocessed DWI space. Please check your CAPS folder." ) # Outputs # ------- self.connect( [ (resp_estim_node, self.output_node, [("wm_file", "response")]), (fod_estim_node, self.output_node, [("wm_odf", "fod")]), (tck_gen_node, self.output_node, [("out_file", "tracts")]), (conn_gen_node, self.output_node, [("out_file", "connectomes")]), (self.input_node, print_end_message, [("dwi_file", "in_bids_or_caps_file")]), (conn_gen_node, print_end_message, [("out_file", "final_file")]), ] )
def init_bbreg_wf( *, omp_nthreads, debug=False, epi2t1w_init="register", epi2t1w_dof=6, name="bbreg_wf", use_bbr=None, ): """ Build a workflow to run FreeSurfer's ``bbregister``. This workflow uses FreeSurfer's ``bbregister`` to register a EPI image to a T1-weighted structural image. It is a counterpart to :py:func:`~fmriprep.workflows.bold.registration.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 Graph .. workflow :: :graph2use: orig :simple_form: yes from niworkflows.anat.coregistration import init_bbreg_wf wf = init_bbreg_wf(omp_nthreads=1) Parameters ---------- use_bbr : :obj:`bool` or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. epi2t1w_dof : 6, 9 or 12 Degrees-of-freedom for EPI-T1w registration epi2t1w_init : :obj:`str`, ``"header"`` or ``"register"`` If ``"header"``, use header information for initialization of EPI and T1 images. If ``"register"``, align volumes by their centers. name : :obj:`str`, optional Workflow name (default: ``bbreg_wf``) Inputs ------ in_file Reference EPI image to be registered fsnative2t1w_xfm FSL-style affine matrix translating from FreeSurfer T1.mgz to T1w subjects_dir Sets FreeSurfer's ``$SUBJECTS_DIR`` subject_id FreeSurfer subject ID (must have a corresponding folder in ``$SUBJECTS_DIR``) t1w_brain Unused (see :py:func:`~fmriprep.workflows.bold.registration.init_fsl_bbr_wf`) t1w_dseg Unused (see :py:func:`~fmriprep.workflows.bold.registration.init_fsl_bbr_wf`) Outputs ------- itk_epi_to_t1w Affine transform from the reference EPI to T1w space (ITK format) itk_t1w_to_epi Affine transform from T1w space to EPI space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (mri_coreg registration returned) """ from ..engine.workflows import LiterateWorkflow as Workflow # See https://github.com/nipreps/fmriprep/issues/768 from ..interfaces.freesurfer import ( PatchedBBRegisterRPT as BBRegisterRPT, PatchedMRICoregRPT as MRICoregRPT, PatchedLTAConvert as LTAConvert, ) from ..interfaces.nitransforms import ConcatenateXFMs workflow = Workflow(name=name) workflow.__desc__ = """\ The EPI reference was then co-registered to the T1w reference using `bbregister` (FreeSurfer) which implements boundary-based registration [@bbr]. Co-registration was configured with {dof} degrees of freedom{reason}. """.format( dof={ 6: "six", 9: "nine", 12: "twelve" }[epi2t1w_dof], reason="" if epi2t1w_dof == 6 else "to account for distortions remaining in the EPI reference", ) inputnode = pe.Node( niu.IdentityInterface([ "in_file", "fsnative2t1w_xfm", "subjects_dir", "subject_id", # BBRegister "t1w_dseg", # FLIRT BBR "t1w_brain", # FLIRT BBR ]), name="inputnode", ) outputnode = pe.Node( niu.IdentityInterface( ["itk_epi_to_t1w", "itk_t1w_to_epi", "out_report", "fallback"]), name="outputnode", ) if epi2t1w_init not in ("register", "header"): raise ValueError( f"Unknown EPI-T1w initialization option: {epi2t1w_init}") # For now make BBR unconditional - in the future, we can fall back to identity, # but adding the flexibility without testing seems a bit dangerous if epi2t1w_init == "header": if use_bbr is False: raise ValueError("Cannot disable BBR and use header registration") if use_bbr is None: LOGGER.warning( "Initializing BBR with header; affine fallback disabled") use_bbr = True merge_ltas = pe.Node(niu.Merge(2), name="merge_ltas", run_without_submitting=True) concat_xfm = pe.Node(ConcatenateXFMs(inverse=True), name="concat_xfm") # fmt:off workflow.connect([ # Output ITK transforms (inputnode, merge_ltas, [("fsnative2t1w_xfm", "in2")]), (merge_ltas, concat_xfm, [("out", "in_xfms")]), (concat_xfm, outputnode, [("out_xfm", "itk_epi_to_t1w")]), (concat_xfm, outputnode, [("out_inv", "itk_t1w_to_epi")]), ]) # fmt:on if debug is True: from ..interfaces.nibabel import RegridToZooms downsample = pe.Node(RegridToZooms(zooms=(4.0, 4.0, 4.0), smooth=True), name="downsample") workflow.connect([(inputnode, downsample, [("in_file", "in_file")])]) mri_coreg = pe.Node( MRICoregRPT( dof=epi2t1w_dof, sep=[4], ftol=0.0001, linmintol=0.01, generate_report=not use_bbr, ), name="mri_coreg", n_procs=omp_nthreads, mem_gb=5, ) # Use mri_coreg if epi2t1w_init == "register": # fmt:off workflow.connect([ (inputnode, mri_coreg, [("subjects_dir", "subjects_dir"), ("subject_id", "subject_id")]), ]) # fmt:on if not debug: workflow.connect(inputnode, "in_file", mri_coreg, "source_file") else: workflow.connect(downsample, "out_file", mri_coreg, "source_file") # Short-circuit workflow building, use initial registration if use_bbr is False: # fmt:off workflow.connect([ (mri_coreg, outputnode, [("out_report", "out_report")]), (mri_coreg, merge_ltas, [("out_lta_file", "in1")]), ]) # fmt:on outputnode.inputs.fallback = True return workflow # Use bbregister bbregister = pe.Node( BBRegisterRPT( dof=epi2t1w_dof, contrast_type="t2", registered_file=True, out_lta_file=True, generate_report=True, ), name="bbregister", mem_gb=12, ) # fmt:off workflow.connect([ (inputnode, bbregister, [("subjects_dir", "subjects_dir"), ("subject_id", "subject_id")]), ]) # fmt:on if not debug: workflow.connect(inputnode, "in_file", bbregister, "source_file") else: workflow.connect(downsample, "out_file", bbregister, "source_file") if epi2t1w_init == "header": bbregister.inputs.init = "header" else: workflow.connect([(mri_coreg, bbregister, [("out_lta_file", "init_reg_file")])]) # Short-circuit workflow building, use boundary-based registration if use_bbr is True: # fmt:off workflow.connect([ (bbregister, outputnode, [("out_report", "out_report")]), (bbregister, merge_ltas, [("out_lta_file", "in1")]), ]) # fmt:on outputnode.inputs.fallback = False return workflow # Only reach this point if epi2t1w_init is "register" and use_bbr is None 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(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") # fmt:off 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, merge_ltas, [("out", "in1")]), # 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")]), ]) # fmt:on return workflow
def init_brain_extraction_wf(name='brain_extraction_wf', in_template='OASIS30ANTs', template_spec=None, use_float=True, normalization_quality='precise', omp_nthreads=None, mem_gb=3.0, bids_suffix='T1w', atropos_refine=True, atropos_use_random_seed=True, atropos_model=None, use_laplacian=True, bspline_fitting_distance=200): """ A Nipype implementation of the official ANTs' ``antsBrainExtraction.sh`` workflow (only for 3D images). The official workflow is built as follows (and this implementation follows the same organization): 1. Step 1 performs several clerical tasks (adding padding, calculating the Laplacian of inputs, affine initialization) and the core spatial normalization. 2. Maps the brain mask into target space using the normalization calculated in 1. 3. Superstep 1b: smart binarization of the brain mask 4. Superstep 6: apply ATROPOS and massage its outputs 5. Superstep 7: use results from 4 to refine the brain mask .. workflow:: :graph2use: orig :simple_form: yes from niworkflows.anat import init_brain_extraction_wf wf = init_brain_extraction_wf() **Parameters** in_template : str Name of the skull-stripping template ('OASIS30ANTs', 'NKI', or path). The brain template from which regions will be projected Anatomical template created using e.g. LPBA40 data set with ``buildtemplateparallel.sh`` in ANTs. The workflow will automatically search for a brain probability mask created using e.g. LPBA40 data set which have brain masks defined, and warped to anatomical template and averaged resulting in a probability image. use_float : bool Whether single precision should be used normalization_quality : str Use more precise or faster registration parameters (default: ``precise``, other possible values: ``testing``) omp_nthreads : int Maximum number of threads an individual process may use mem_gb : float Estimated peak memory consumption of the most hungry nodes in the workflow bids_suffix : str Sequence type of the first input image. For a list of acceptable values see https://bids-specification.readthedocs.io/en/latest/\ 04-modality-specific-files/01-magnetic-resonance-imaging-data.html#anatomy-imaging-data atropos_refine : bool Enables or disables the whole ATROPOS sub-workflow atropos_use_random_seed : bool Whether ATROPOS should generate a random seed based on the system's clock atropos_model : tuple or None Allows to specify a particular segmentation model, overwriting the defaults based on ``bids_suffix`` use_laplacian : bool Enables or disables alignment of the Laplacian as an additional criterion for image registration quality (default: True) bspline_fitting_distance : float The size of the b-spline mesh grid elements, in mm (default: 200) name : str, optional Workflow name (default: antsBrainExtraction) **Inputs** in_files List of input anatomical images to be brain-extracted, typically T1-weighted. If a list of anatomical images is provided, subsequently specified images are used during the segmentation process. However, only the first image is used in the registration of priors. Our suggestion would be to specify the T1w as the first image. in_mask (optional) Mask used for registration to limit the metric computation to a specific region. **Outputs** out_file Skull-stripped and :abbr:`INU (intensity non-uniformity)`-corrected ``in_files`` out_mask Calculated brain mask bias_corrected The ``in_files`` input images, after :abbr:`INU (intensity non-uniformity)` correction, before skull-stripping. bias_image The :abbr:`INU (intensity non-uniformity)` field estimated for each input in ``in_files`` out_segm Output segmentation by ATROPOS out_tpms Output :abbr:`TPMs (tissue probability maps)` by ATROPOS """ from templateflow.api import get as get_template wf = pe.Workflow(name) template_spec = template_spec or {} # suffix passed via spec takes precedence template_spec['suffix'] = template_spec.get('suffix', bids_suffix) tpl_target_path, common_spec = get_template_specs( in_template, template_spec=template_spec) # Get probabilistic brain mask if available tpl_mask_path = get_template( in_template, label='brain', suffix='probseg', **common_spec) or \ get_template(in_template, desc='brain', suffix='mask', **common_spec) if omp_nthreads is None or omp_nthreads < 1: omp_nthreads = cpu_count() inputnode = pe.Node(niu.IdentityInterface(fields=['in_files', 'in_mask']), name='inputnode') # Try to find a registration mask, set if available tpl_regmask_path = get_template( in_template, desc='BrainCerebellumExtraction', suffix='mask', **common_spec) if tpl_regmask_path: inputnode.inputs.in_mask = str(tpl_regmask_path) outputnode = pe.Node(niu.IdentityInterface( fields=['out_file', 'out_mask', 'bias_corrected', 'bias_image', 'out_segm', 'out_tpms']), name='outputnode') copy_xform = pe.Node(CopyXForm( fields=['out_file', 'out_mask', 'bias_corrected', 'bias_image']), name='copy_xform', run_without_submitting=True) trunc = pe.MapNode(ImageMath(operation='TruncateImageIntensity', op2='0.01 0.999 256'), name='truncate_images', iterfield=['op1']) inu_n4 = pe.MapNode( N4BiasFieldCorrection( dimension=3, save_bias=False, copy_header=True, n_iterations=[50] * 4, convergence_threshold=1e-7, shrink_factor=4, bspline_fitting_distance=bspline_fitting_distance), n_procs=omp_nthreads, name='inu_n4', iterfield=['input_image']) res_tmpl = pe.Node(ResampleImageBySpacing( out_spacing=(4, 4, 4), apply_smoothing=True), name='res_tmpl') res_tmpl.inputs.input_image = tpl_target_path res_target = pe.Node(ResampleImageBySpacing( out_spacing=(4, 4, 4), apply_smoothing=True), name='res_target') lap_tmpl = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'), name='lap_tmpl') lap_tmpl.inputs.op1 = tpl_target_path lap_target = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'), name='lap_target') mrg_tmpl = pe.Node(niu.Merge(2), name='mrg_tmpl') mrg_tmpl.inputs.in1 = tpl_target_path mrg_target = pe.Node(niu.Merge(2), name='mrg_target') # Initialize transforms with antsAI init_aff = pe.Node(AI( metric=('Mattes', 32, 'Regular', 0.25), transform=('Affine', 0.1), search_factor=(15, 0.1), principal_axes=False, convergence=(10, 1e-6, 10), verbose=True), name='init_aff', n_procs=omp_nthreads) # Tolerate missing ANTs at construction time _ants_version = Registration().version if _ants_version and parseversion(_ants_version) >= Version('2.3.0'): init_aff.inputs.search_grid = (40, (0, 40, 40)) # Set up spatial normalization settings_file = 'antsBrainExtraction_%s.json' if use_laplacian \ else 'antsBrainExtractionNoLaplacian_%s.json' norm = pe.Node(Registration(from_file=pkgr_fn( 'niworkflows.data', settings_file % normalization_quality)), name='norm', n_procs=omp_nthreads, mem_gb=mem_gb) norm.inputs.float = use_float fixed_mask_trait = 'fixed_image_mask' if _ants_version and parseversion(_ants_version) >= Version('2.2.0'): fixed_mask_trait += 's' map_brainmask = pe.Node( ApplyTransforms(interpolation='Gaussian', float=True), name='map_brainmask', mem_gb=1 ) map_brainmask.inputs.input_image = str(tpl_mask_path) thr_brainmask = pe.Node(ThresholdImage( dimension=3, th_low=0.5, th_high=1.0, inside_value=1, outside_value=0), name='thr_brainmask') # Morphological dilation, radius=2 dil_brainmask = pe.Node(ImageMath(operation='MD', op2='2'), name='dil_brainmask') # Get largest connected component get_brainmask = pe.Node(ImageMath(operation='GetLargestComponent'), name='get_brainmask') # Refine INU correction inu_n4_final = pe.MapNode( N4BiasFieldCorrection( dimension=3, save_bias=True, copy_header=True, n_iterations=[50] * 5, convergence_threshold=1e-7, shrink_factor=4, bspline_fitting_distance=bspline_fitting_distance), n_procs=omp_nthreads, name='inu_n4_final', iterfield=['input_image']) # Apply mask apply_mask = pe.MapNode(ApplyMask(), iterfield=['in_file'], name='apply_mask') wf.connect([ (inputnode, trunc, [('in_files', 'op1')]), (inputnode, copy_xform, [(('in_files', _pop), 'hdr_file')]), (inputnode, inu_n4_final, [('in_files', 'input_image')]), (inputnode, init_aff, [('in_mask', 'fixed_image_mask')]), (inputnode, norm, [('in_mask', fixed_mask_trait)]), (inputnode, map_brainmask, [(('in_files', _pop), 'reference_image')]), (trunc, inu_n4, [('output_image', 'input_image')]), (inu_n4, res_target, [ (('output_image', _pop), 'input_image')]), (res_tmpl, init_aff, [('output_image', 'fixed_image')]), (res_target, init_aff, [('output_image', 'moving_image')]), (init_aff, norm, [('output_transform', 'initial_moving_transform')]), (norm, map_brainmask, [ ('reverse_transforms', 'transforms'), ('reverse_invert_flags', 'invert_transform_flags')]), (map_brainmask, thr_brainmask, [('output_image', 'input_image')]), (thr_brainmask, dil_brainmask, [('output_image', 'op1')]), (dil_brainmask, get_brainmask, [('output_image', 'op1')]), (inu_n4_final, apply_mask, [('output_image', 'in_file')]), (get_brainmask, apply_mask, [('output_image', 'mask_file')]), (get_brainmask, copy_xform, [('output_image', 'out_mask')]), (apply_mask, copy_xform, [('out_file', 'out_file')]), (inu_n4_final, copy_xform, [('output_image', 'bias_corrected'), ('bias_image', 'bias_image')]), (copy_xform, outputnode, [ ('out_file', 'out_file'), ('out_mask', 'out_mask'), ('bias_corrected', 'bias_corrected'), ('bias_image', 'bias_image')]), ]) if use_laplacian: lap_tmpl = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'), name='lap_tmpl') lap_tmpl.inputs.op1 = tpl_target_path lap_target = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'), name='lap_target') mrg_tmpl = pe.Node(niu.Merge(2), name='mrg_tmpl') mrg_tmpl.inputs.in1 = tpl_target_path mrg_target = pe.Node(niu.Merge(2), name='mrg_target') wf.connect([ (inu_n4, lap_target, [ (('output_image', _pop), 'op1')]), (lap_tmpl, mrg_tmpl, [('output_image', 'in2')]), (inu_n4, mrg_target, [('output_image', 'in1')]), (lap_target, mrg_target, [('output_image', 'in2')]), (mrg_tmpl, norm, [('out', 'fixed_image')]), (mrg_target, norm, [('out', 'moving_image')]), ]) else: norm.inputs.fixed_image = tpl_target_path wf.connect([ (inu_n4, norm, [ (('output_image', _pop), 'moving_image')]), ]) if atropos_refine: atropos_model = atropos_model or list(ATROPOS_MODELS[bids_suffix].values()) atropos_wf = init_atropos_wf( use_random_seed=atropos_use_random_seed, omp_nthreads=omp_nthreads, mem_gb=mem_gb, in_segmentation_model=atropos_model, ) sel_wm = pe.Node(niu.Select(index=atropos_model[-1] - 1), name='sel_wm', run_without_submitting=True) wf.disconnect([ (get_brainmask, apply_mask, [('output_image', 'mask_file')]), (copy_xform, outputnode, [('out_mask', 'out_mask')]), ]) wf.connect([ (inu_n4, atropos_wf, [ ('output_image', 'inputnode.in_files')]), (thr_brainmask, atropos_wf, [ ('output_image', 'inputnode.in_mask')]), (get_brainmask, atropos_wf, [ ('output_image', 'inputnode.in_mask_dilated')]), (atropos_wf, sel_wm, [('outputnode.out_tpms', 'inlist')]), (sel_wm, inu_n4_final, [('out', 'weight_image')]), (atropos_wf, apply_mask, [ ('outputnode.out_mask', 'mask_file')]), (atropos_wf, outputnode, [ ('outputnode.out_mask', 'out_mask'), ('outputnode.out_segm', 'out_segm'), ('outputnode.out_tpms', 'out_tpms')]), ]) return wf
def susceptibility_distortion_correction_using_t1( name='susceptibility_distortion_correction_using_t1'): """ Susceptibility distortion correction using the T1w image. This workflow allows to correct for echo-planar induced susceptibility artifacts without fieldmap (e.g. ADNI Database) by elastically register DWIs to their respective baseline T1-weighted structural scans using an inverse consistent registration algorithm with a mutual information cost function (SyN algorithm). Args: name (Optional[str]): Name of the workflow. Inputnode: in_t1 (str): T1w image. in_dwi (str): DWI dataset Outputnode: out_dwi (str): Corrected DWI dataset out_warp (str): Out warp allowing DWI to T1 registration and susceptibilty induced artifacts correction out_b0_to_t1_rigid_body_matrix (str): B0 to T1 image FLIRT rigid body FSL coregistration matrix out_t1_to_b0_rigid_body_matrix (str): T1 to B0 image FLIRT rigid body FSL coregistration matrix out_t1_coregistered_to_b0 (str): T1 image rigid body coregistered to the B0 image out_b0_to_t1_syn_deformation_field (str): B0 to T1 image ANTs SyN ITK warp out_b0_to_t1_affine_matrix (str): B0 to T1 image ANTs affine ITK coregistration matrix References: .. Nir et al. (Neurobiology of Aging 2015): Connectivity network measures predict volumetric atrophy in mild cognitive impairment .. Leow et al. (IEEE Trans Med Imaging 2007): Statistical Properties of Jacobian Maps and the Realization of Unbiased Large Deformation Nonlinear Image Registration Returns: The workflow Example: >>> epi = susceptibility_distortion_correction_using_t1() >>> epi.inputs.inputnode.in_dwi = 'dwi.nii' >>> epi.inputs.inputnode.in_t1 = 'T1w.nii' >>> epi.run() # doctest: +SKIP """ import nipype import nipype.pipeline.engine as pe import nipype.interfaces.utility as niu import nipype.interfaces.fsl as fsl import clinica.pipelines.dwi_preprocessing_using_t1.dwi_preprocessing_using_t1_utils as utils def expend_matrix_list(in_matrix, in_bvec): import numpy as np bvecs = np.loadtxt(in_bvec).T out_matrix_list = [in_matrix] out_matrix_list = out_matrix_list * len(bvecs) return out_matrix_list def rotate_bvecs(in_bvec, in_matrix): """ Rotates the input bvec file accordingly with a list of matrices. .. note:: the input affine matrix transforms points in the destination image to their corresponding coordinates in the original image. Therefore, this matrix should be inverted first, as we want to know the target position of :math:`\\vec{r}`. """ import os import numpy as np name, fext = os.path.splitext(os.path.basename(in_bvec)) if fext == '.gz': name, _ = os.path.splitext(name) out_file = os.path.abspath('%s_rotated.bvec' % name) bvecs = np.loadtxt( in_bvec).T # Warning, bvecs.txt are not in the good configuration, need to put '.T' new_bvecs = [] if len(bvecs) != len(in_matrix): raise RuntimeError(('Number of b-vectors (%d) and rotation ' 'matrices (%d) should match.') % (len(bvecs), len(in_matrix))) for bvec, mat in zip(bvecs, in_matrix): if np.all(bvec == 0.0): new_bvecs.append(bvec) else: invrot = np.linalg.inv(np.loadtxt(mat))[:3, :3] newbvec = invrot.dot(bvec) new_bvecs.append((newbvec / np.linalg.norm(newbvec))) np.savetxt(out_file, np.array(new_bvecs).T, fmt='%0.15f') return out_file inputnode = pe.Node( niu.IdentityInterface(fields=['in_t1', 'in_dwi', 'in_bvec']), name='inputnode') split = pe.Node(fsl.Split(dimension='t'), name='SplitDWIs') pick_ref = pe.Node(niu.Select(), name='Pick_b0') pick_ref.inputs.index = [0] flirt_b0_to_t1 = pe.Node(interface=fsl.FLIRT(dof=6), name='flirt_b0_to_t1') flirt_b0_to_t1.inputs.interp = "spline" flirt_b0_to_t1.inputs.cost = 'normmi' flirt_b0_to_t1.inputs.cost_func = 'normmi' if nipype.__version__.split('.') < ['0', '13', '0']: apply_xfm = pe.Node(interface=fsl.ApplyXfm(), name='apply_xfm') else: apply_xfm = pe.Node(interface=fsl.ApplyXFM(), name='apply_xfm') apply_xfm.inputs.apply_xfm = True expend_matrix = pe.Node( interface=niu.Function(input_names=['in_matrix', 'in_bvec'], output_names=['out_matrix_list'], function=expend_matrix_list), name='expend_matrix') rot_bvec = pe.Node(niu.Function(input_names=['in_matrix', 'in_bvec'], output_names=['out_file'], function=rotate_bvecs), name='Rotate_Bvec') ants_registration_syn_quick = pe.Node(interface=niu.Function( input_names=['fix_image', 'moving_image'], output_names=['image_warped', 'affine_matrix', 'warp', 'inverse_warped', 'inverse_warp'], function=utils.ants_registration_syn_quick), name='ants_registration_syn_quick') merge_transform = pe.Node(niu.Merge(2), name='MergeTransforms') combine_warp = pe.Node(interface=niu.Function( input_names=['in_file', 'transforms_list', 'reference'], output_names=['out_warp'], function=utils.ants_combine_transform), name='combine_warp') coeffs = pe.Node(fsl.WarpUtils(out_format='spline'), name='CoeffComp') fsl_transf = pe.Node(fsl.WarpUtils(out_format='field'), name='fsl_transf') warp_epi = pe.Node(fsl.ConvertWarp(), name='warp_epi') apply_warp = pe.MapNode(interface=fsl.ApplyWarp(), iterfield=['in_file'], name='apply_warp') apply_warp.inputs.interp = 'spline' thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=['in_file'], name='RemoveNegative') merge = pe.Node(fsl.Merge(dimension='t'), name='MergeDWIs') outputnode = pe.Node(niu.IdentityInterface( fields=['dwi_to_t1_coregistration_matrix', 'itk_dwi_t1_coregistration_matrix', 'epi_correction_deformation_field', 'epi_correction_affine_transform', 'merge_epi_transform', 'out_dwi', 'out_warp', 'out_bvec']), name='outputnode') wf = pe.Workflow(name=name) wf.connect([ (inputnode, split, [('in_dwi', 'in_file')]), # noqa (split, pick_ref, [('out_files', 'inlist')]), # noqa (pick_ref, flirt_b0_to_t1, [('out', 'in_file')]), # noqa (inputnode, flirt_b0_to_t1, [('in_t1', 'reference')]), # noqa (flirt_b0_to_t1, expend_matrix, [('out_matrix_file', 'in_matrix')]), # noqa (inputnode, expend_matrix, [('in_bvec', 'in_bvec')]), # noqa (inputnode, rot_bvec, [('in_bvec', 'in_bvec')]), # noqa (expend_matrix, rot_bvec, [('out_matrix_list', 'in_matrix')]), # noqa (inputnode, ants_registration_syn_quick, [('in_t1', 'fix_image')]), # noqa (flirt_b0_to_t1, ants_registration_syn_quick, [('out_file', 'moving_image')]), # noqa (ants_registration_syn_quick, merge_transform, [('affine_matrix', 'in2'), # noqa ('warp', 'in1')]), # noqa (flirt_b0_to_t1, combine_warp, [('out_file', 'in_file')]), # noqa (merge_transform, combine_warp, [('out', 'transforms_list')]), # noqa (inputnode, combine_warp, [('in_t1', 'reference')]), # noqa (inputnode, coeffs, [('in_t1', 'reference')]), # noqa (combine_warp, coeffs, [('out_warp', 'in_file')]), # noqa (coeffs, fsl_transf, [('out_file', 'in_file')]), # noqa (inputnode, fsl_transf, [('in_t1', 'reference')]), # noqa (inputnode, warp_epi, [('in_t1', 'reference')]), # noqa (flirt_b0_to_t1, warp_epi, [('out_matrix_file', 'premat')]), # noqa (fsl_transf, warp_epi, [('out_file', 'warp1')]), # noqa (warp_epi, apply_warp, [('out_file', 'field_file')]), # noqa (split, apply_warp, [('out_files', 'in_file')]), # noqa (inputnode, apply_warp, [('in_t1', 'ref_file')]), # noqa (apply_warp, thres, [('out_file', 'in_file')]), # noqa (thres, merge, [('out_file', 'in_files')]), # noqa # Outputnode (merge, outputnode, [('merged_file', 'out_dwi')]), # noqa (flirt_b0_to_t1, outputnode, [('out_matrix_file', 'dwi_to_t1_coregistration_matrix')]), # noqa (ants_registration_syn_quick, outputnode, [('warp', 'epi_correction_deformation_field'), # noqa ('affine_matrix', 'epi_correction_affine_transform')]), # noqa (warp_epi, outputnode, [('out_file', 'out_warp')]), # noqa (rot_bvec, outputnode, [('out_file', 'out_bvec')]), # noqa ]) return wf
def create_featreg_preproc(name='featpreproc', whichvol='middle', deletetimepoints=4): """Create a FEAT preprocessing workflow with registration to one volume of the first run Parameters ---------- name : name of workflow (default: featpreproc) whichvol : which volume of the first run to register to ('first', 'middle', 'mean') Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.meanscaled_files : meanscaled data Example ------- >>> from nipype.workflows.fsl import create_featreg_preproc >>> import os >>> preproc = create_featreg_preproc() >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP >>> preproc = create_featreg_preproc(highpass=False, whichvol='mean') >>> preproc.inputs.inputspec.func = 'f3.nii' >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) # Set up a node to define all inputs and outputs required for the # preprocessing workflow inputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'func', 'fwhm', ]), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'reference', 'motion_parameters', 'realigned_files', 'motion_rot_plots', 'motion_trans_plots', 'mask', 'maskfunc_files', 'smoothed_files', 'meanscaled_files', ]), name='outputspec') # Convert functional images to float representation. Since there can # be more than one functional run we use a MapNode to convert each # run. deletevol = pe.MapNode(interface=fsl.ExtractROI(t_min=deletetimepoints), iterfield=['in_file', 't_size'], name='deletevol') img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', deletevol, 'in_file') featpreproc.connect(inputnode, ('func', getTTP, deletetimepoints), deletevol, 't_size') featpreproc.connect(deletevol, 'roi_file', img2float, 'in_file') # Extract the first volume of the each run as the reference if whichvol != 'mean': extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file'], name='extractref') featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') # Realign the functional runs to the reference (1st volume of first run) motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats=True, save_plots=True, save_rms=True, interpolation='sinc'), name='realign', iterfield=['in_file', 'ref_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') else: motion_correct.inputs.mean_vol = True featpreproc.connect(motion_correct, 'mean_img', outputnode, 'reference') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') # Plot the estimated motion parameters plot_rotations = pe.MapNode(interface=fsl.PlotMotionParams( in_source='fsl', plot_type='rotations'), name='plot_rotations', iterfield=['in_file']) plot_translations = pe.MapNode(interface=fsl.PlotMotionParams( in_source='fsl', plot_type='translations'), name='plot_translations', iterfield=['in_file']) # plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_rotations, 'in_file') featpreproc.connect(motion_correct, 'par_file', plot_translations, 'in_file') featpreproc.connect(plot_rotations, 'out_file', outputnode, 'motion_rot_plots') featpreproc.connect(plot_translations, 'out_file', outputnode, 'motion_trans_plots') # Extract the mean volume of the first functional run meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc') featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file') # Strip the skull from the mean functional to generate a mask meanfuncmask = pe.MapNode(interface=fsl.BET(mask=True, no_output=True, frac=0.3), iterfield=['in_file'], name='meanfuncmask') featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file') # Mask the functional runs with the extracted mask maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2') # Determine the 2nd and 98th percentile intensities of each functional run getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'), iterfield=['in_file'], name='getthreshold') featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file') # Threshold the first run of the functional data at 10% of the 98th percentile threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char', suffix='_thresh'), iterfield=['in_file', 'op_string'], name='threshold') featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file') # Define a function to get 10% of the intensity featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') # Determine the median value of the functional runs using the mask medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield=['in_file', 'mask_file'], name='medianval') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') # Dilate the mask dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask') # Mask the motion corrected functional runs with the dilated mask maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc2') featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2') featpreproc.connect(maskfunc2, 'out_file', outputnode, 'maskfunc_files') # Smooth each run using SUSAN with the brightness threshold set to 75% # of the median value for each run and a mask consituting the mean # functional smooth = create_susan_smooth() featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') # Mask the smoothed data with the dilated mask maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') featpreproc.connect(maskfunc2, ('out_file', tolist), concatnode, 'in1') featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2') # The following nodes select smooth or unsmoothed data depending on the # fwhm. This is because SUSAN defaults to smoothing the data with about the # voxel size of the input data if the fwhm parameter is less than 1/3 of the # voxel size. selectnode = pe.Node(interface=util.Select(), name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files') # Scale the median value of the run is set to 10000 meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file', 'op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') # Mask the scaled data with the dilated mask maskfunc4 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc4') featpreproc.connect(meanscale, 'out_file', maskfunc4, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc4, 'in_file2') featpreproc.connect(maskfunc4, 'out_file', outputnode, 'meanscaled_files') return featpreproc
def init_n4_only_wf(atropos_model=None, atropos_refine=True, atropos_use_random_seed=True, bids_suffix='T1w', mem_gb=3.0, name='n4_only_wf', omp_nthreads=None): """ Build a workflow to sidetrack brain extraction on skull-stripped datasets. An alternative workflow to "init_brain_extraction_wf", for anatomical images which have already been brain extracted. 1. Creates brain mask assuming all zero voxels are outside the brain 2. Applies N4 bias field correction 3. (Optional) apply ATROPOS and massage its outputs 4. Use results from 3 to refine N4 bias field correction Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from niworkflows.anat.ants import init_n4_only_wf wf = init_n4_only_wf() Parameters ---------- omp_nthreads : int Maximum number of threads an individual process may use mem_gb : float Estimated peak memory consumption of the most hungry nodes bids_suffix : str Sequence type of the first input image. For a list of acceptable values see https://bids-specification.readthedocs.io/en/latest/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#anatomy-imaging-data atropos_refine : bool Enables or disables the whole ATROPOS sub-workflow atropos_use_random_seed : bool Whether ATROPOS should generate a random seed based on the system's clock atropos_model : tuple or None Allows to specify a particular segmentation model, overwriting the defaults based on ``bids_suffix`` name : str, optional Workflow name (default: ``'n4_only_wf'``). Inputs ------ in_files List of input anatomical images to be bias corrected, typically T1-weighted. If a list of anatomical images is provided, subsequently specified images are used during the segmentation process. However, only the first image is used in the registration of priors. Our suggestion would be to specify the T1w as the first image. Outputs ------- out_file :abbr:`INU (intensity non-uniformity)`-corrected ``in_files`` out_mask Calculated brain mask bias_corrected Same as "out_file", provided for consistency with brain extraction bias_image The :abbr:`INU (intensity non-uniformity)` field estimated for each input in ``in_files`` out_segm Output segmentation by ATROPOS out_tpms Output :abbr:`TPMs (tissue probability maps)` by ATROPOS """ wf = pe.Workflow(name) inputnode = pe.Node(niu.IdentityInterface(fields=['in_files', 'in_mask']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_file', 'out_mask', 'bias_corrected', 'bias_image', 'out_segm', 'out_tpms' ]), name='outputnode') # Create brain mask thr_brainmask = pe.Node(Binarize(thresh_low=2), name='binarize') # INU correction inu_n4_final = pe.MapNode(N4BiasFieldCorrection( dimension=3, save_bias=True, copy_header=True, n_iterations=[50] * 5, convergence_threshold=1e-7, shrink_factor=4, bspline_fitting_distance=200), n_procs=omp_nthreads, name='inu_n4_final', iterfield=['input_image']) # Check ANTs version try: inu_n4_final.inputs.rescale_intensities = True except ValueError: warn( "The installed ANTs version too old. Please consider upgrading to " "2.1.0 or greater.", DeprecationWarning) wf.connect([(inputnode, inu_n4_final, [('in_files', 'input_image')]), (inputnode, thr_brainmask, [(('in_files', _pop), 'in_file')]), (thr_brainmask, outputnode, [('out_mask', 'out_mask')]), (inu_n4_final, outputnode, [('output_image', 'out_file')]), (inu_n4_final, outputnode, [('output_image', 'bias_corrected') ]), (inu_n4_final, outputnode, [('bias_image', 'bias_image')])]) # If atropos refine, do in4 twice if atropos_refine: # Morphological dilation, radius=2 dil_brainmask = pe.Node(ImageMath(operation='MD', op2='2'), name='dil_brainmask') # Get largest connected component get_brainmask = pe.Node(ImageMath(operation='GetLargestComponent'), name='get_brainmask') atropos_model = atropos_model or list( ATROPOS_MODELS[bids_suffix].values()) atropos_wf = init_atropos_wf( use_random_seed=atropos_use_random_seed, omp_nthreads=omp_nthreads, mem_gb=mem_gb, in_segmentation_model=atropos_model, ) sel_wm = pe.Node(niu.Select(index=atropos_model[-1] - 1), name='sel_wm', run_without_submitting=True) gunzip_4n4 = pe.MapNode(Gunzip(), name="gunzip_con", iterfield=['in_file']) inu_n4 = pe.MapNode( Segment(gm_output_type=[False, False, False], wm_output_type=[False, False, False], csf_output_type=[False, False, False], clean_masks="thorough", save_bias_corrected=True, bias_regularization=0.001, bias_fwhm=30, sampling_distance=3, use_mcr=True, affine_regularization="mni"), iterfield=['data'], name='inu_n4', mem_gb=32, serial=True, run_without_submitting=True) # n_procs=1 for reproducibility wf.connect([ (inputnode, gunzip_4n4, [('in_files', 'in_file')]), (gunzip_4n4, inu_n4, [('out_file', 'data')]), (inu_n4, atropos_wf, [('bias_corrected_image', 'inputnode.in_files')]), (thr_brainmask, atropos_wf, [('out_mask', 'inputnode.in_mask')]), (thr_brainmask, dil_brainmask, [('out_mask', 'op1')]), (dil_brainmask, get_brainmask, [('output_image', 'op1')]), (get_brainmask, atropos_wf, [('output_image', 'inputnode.in_mask_dilated')]), (atropos_wf, sel_wm, [('outputnode.out_tpms', 'inlist')]), (sel_wm, inu_n4_final, [('out', 'weight_image')]), (atropos_wf, outputnode, [('outputnode.out_segm', 'out_segm'), ('outputnode.out_tpms', 'out_tpms')]), ]) return wf
def bspline(name='BSplineEvaluation', shapes=['gyrus'], snr_list=[300], N=1): """ A workflow to evaluate registration methods generating a gold standard with random bspline deformations. A list of nipype workflows can be plugged-in, using the methods input. If methods is None, then a default regseg method is run. methods = [identity_wf(n_tissues=2), default_regseg()] Inputs in methods workflows --------------------------- methods workflows must define the following inputs: inputnode.in_surf - the input prior / surfaces in orig space inputnode.in_dist - the distorted images inputnode.in_tpms - the distorted TPMs (tissue probability maps) inputnode.in_orig - the original images, undistorted Outputs in methods workflows ---------------------------- outputnode.out_corr - the distorted images, after correction outputnode.out_tpms - the corrected TPMs outputnode.out_surf - the original priors after distortion (if available) outputnode.out_disp - the displacement field, at image grid resoluton """ wf = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'grid_size', 'out_csv', 'lo_matrix', 'rep_id', 'hi_matrix', 'snr', 'cortex', 'shape' ]), name='inputnode') inputnode.iterables = [ ('shape', shapes), ('snr', snr_list), ('rep_id', list(range(N))), ] # inputnode.iterables = [('shape', np.atleast_1d(shapes).tolist()), # ('snr', snr_list), # ('rep_id', list(range(N)))] outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_file', 'out_tpms', 'out_surfs', 'out_field', 'out_coeff', 'out_overlap' ]), name='outputnode') phantom = generate_phantom() wf.connect([(inputnode, phantom, [('shape', 'inputnode.shape'), ('grid_size', 'inputnode.grid_size'), ('lo_matrix', 'inputnode.lo_matrix'), ('hi_matrix', 'inputnode.hi_matrix'), ('snr', 'inputnode.snr'), ('cortex', 'inputnode.cortex'), ('rep_id', 'inputnode.repetition_id')])]) regseg_low = default_regseg('REGSEG_low') ev_regseg_low = registration_ev(name=('Ev_%s' % regseg_low.name)) ev_regseg_low.inputs.infonode.method = 'REGSEG' ev_regseg_low.inputs.infonode.resolution = 'lo' norm_low = pe.Node(Normalize(), name='NormalizeFinal_low') export0 = pe.Node(ExportSlices(all_axis=True), name='Export_lo') sel0 = pe.Node(niu.Select(index=[0]), name='SelectT1w_lo') wf.connect([ (inputnode, ev_regseg_low, [('shape', 'infonode.shape'), ('snr', 'infonode.snr'), ('rep_id', 'infonode.repetition')]), (phantom, ev_regseg_low, [('refnode.out_signal', 'refnode.in_imag'), ('refnode.out_tpms', 'refnode.in_tpms'), ('out_lowres.out_surfs', 'refnode.in_surf'), ('refnode.out_mask', 'refnode.in_mask'), ('out_lowres.out_field', 'refnode.in_field') ]), (phantom, regseg_low, [('refnode.out_surfs', 'inputnode.in_surf'), ('out_lowres.out_signal', 'inputnode.in_fixed'), ('out_lowres.out_mask', 'inputnode.in_mask')]), # (regseg_low, norm_low, [ # ('outputnode.out_tpms', 'in_files')]), (regseg_low, ev_regseg_low, [('outputnode.out_corr', 'tstnode.in_imag'), ('outputnode.out_surf', 'tstnode.in_surf'), ('outputnode.out_field', 'tstnode.in_field')]), (norm_low, ev_regseg_low, [('out_files', 'tstnode.in_tpms')]), (phantom, sel0, [('out_lowres.out_signal', 'inlist')]), (sel0, export0, [('out', 'reference')]), (phantom, export0, [('out_lowres.out_surfs', 'sgreen')]), (regseg_low, export0, [('outputnode.out_surf', 'syellow')]), (inputnode, ev_regseg_low, [('out_csv', 'infonode.out_csv')]) ]) regseg_hi = default_regseg('REGSEG_hi') ev_regseg_hi = registration_ev(name=('Ev_%s' % regseg_hi.name)) ev_regseg_hi.inputs.infonode.method = 'REGSEG' ev_regseg_hi.inputs.infonode.resolution = 'hi' norm_hi = pe.Node(Normalize(), name='NormalizeFinal_hi') export1 = pe.Node(ExportSlices(all_axis=True), name='Export_hi') sel1 = pe.Node(niu.Select(index=[0]), name='SelectT1w_hi') wf.connect([ (inputnode, ev_regseg_hi, [('shape', 'infonode.shape'), ('snr', 'infonode.snr'), ('rep_id', 'infonode.repetition')]), (phantom, ev_regseg_hi, [('refnode.out_signal', 'refnode.in_imag'), ('refnode.out_tpms', 'refnode.in_tpms'), ('out_hires.out_surfs', 'refnode.in_surf'), ('refnode.out_mask', 'refnode.in_mask'), ('out_hires.out_field', 'refnode.in_field')]), (phantom, regseg_hi, [('refnode.out_surfs', 'inputnode.in_surf'), ('out_hires.out_signal', 'inputnode.in_fixed'), ('out_hires.out_mask', 'inputnode.in_mask')]), # (regseg_hi, norm_hi, [ # ('outputnode.out_tpms', 'in_files')]), (regseg_hi, ev_regseg_hi, [('outputnode.out_corr', 'tstnode.in_imag'), ('outputnode.out_surf', 'tstnode.in_surf'), ('outputnode.out_field', 'tstnode.in_field') ]), (norm_hi, ev_regseg_hi, [('out_files', 'tstnode.in_tpms')]), (phantom, sel1, [('out_hires.out_signal', 'inlist')]), (sel0, export1, [('out', 'reference')]), (phantom, export1, [('out_hires.out_surfs', 'sgreen')]), (regseg_hi, export1, [('outputnode.out_surf', 'syellow')]), (inputnode, ev_regseg_hi, [('out_csv', 'infonode.out_csv')]) ]) # Connect in_field in case it is an identity workflow # if 'in_field' in [item[0] for item in reg.inputs.inputnode.items()]: # wf.connect(phantom, 'out_lowres.out_field', # reg, 'inputnode.in_field') return wf
def build_core_nodes(self): """Build and connect the core nodes of the pipeline. Notes: - If `FSLOUTPUTTYPE` environment variable is not set, `nipype` takes NIFTI by default. Todos: - [x] Detect space automatically. - [ ] Allow for custom parcellations (See TODOs in utils). """ import nipype.interfaces.utility as niu import nipype.pipeline.engine as npe import nipype.interfaces.fsl as fsl import nipype.interfaces.freesurfer as fs import nipype.interfaces.mrtrix3 as mrtrix3 from clinica.lib.nipype.interfaces.mrtrix3.reconst import EstimateFOD from clinica.lib.nipype.interfaces.mrtrix3.tracking import Tractography from clinica.utils.exceptions import ClinicaCAPSError from clinica.utils.stream import cprint import clinica.pipelines.dwi_connectome.dwi_connectome_utils as utils # cprint('Building the pipeline...') # Nodes # ===== # B0 Extraction # ------------- split_node = npe.Node(name="B0Extraction", interface=fsl.Split()) split_node.inputs.output_type = "NIFTI_GZ" split_node.inputs.dimension = 't' select_node = npe.Node(name="B0Selection", interface=niu.Select()) select_node.inputs.index = 0 # B0 Brain Extraction # ------------------- mask_node = npe.Node(name="BrainMasking", interface=fsl.ApplyMask()) mask_node.inputs.output_type = "NIFTI_GZ" # T1-to-B0 Registration # --------------------- t12b0_reg_node = npe.Node(name="T12B0Registration", interface=fsl.FLIRT()) t12b0_reg_node.inputs.output_type = "NIFTI_GZ" # MGZ File Convertion # ------------------- t1_brain_conv_node = npe.Node(name="T1BrainConvertion", interface=fs.MRIConvert()) wm_mask_conv_node = npe.Node(name="WMMaskConvertion", interface=fs.MRIConvert()) # WM Transformation # ----------------- wm_transform_node = npe.Node(name="WMTransformation", interface=fsl.ApplyXFM()) wm_transform_node.inputs.apply_xfm = True # Response Estimation # ------------------- resp_estim_node = npe.Node(name="ResponseEstimation", interface=mrtrix3.ResponseSD()) resp_estim_node.inputs.algorithm = 'tournier' # FOD Estimation # -------------- fod_estim_node = npe.Node(name="FODEstimation", interface=EstimateFOD()) fod_estim_node.inputs.algorithm = 'csd' # Tracts Generation # ----------------- tck_gen_node = npe.Node(name="TractsGeneration", interface=utils.Tractography()) tck_gen_node.inputs.n_tracks = self.parameters['n_tracks'] # BUG: Info package does not exist # from nipype.interfaces.mrtrix3.base import Info # from distutils.version import LooseVersion # # if Info.looseversion() >= LooseVersion("3.0"): # tck_gen_node.inputs.select = self.parameters['n_tracks'] # elif Info.looseversion() <= LooseVersion("0.4"): # tck_gen_node.inputs.n_tracks = self.parameters['n_tracks'] # else: # from clinica.utils.exceptions import ClinicaException # raise ClinicaException("Your MRtrix version is not supported.") # Nodes Generation # ---------------- label_convert_node = npe.MapNode(name="LabelsConversion", iterfield=['in_file', 'in_config', 'in_lut', 'out_file'], interface=mrtrix3.LabelConvert()) label_convert_node.inputs.in_config = utils.get_conversion_luts() label_convert_node.inputs.in_lut = utils.get_luts() # Connectome Generation # --------------------- # only the parcellation and output filename should be iterable, the tck # file stays the same. conn_gen_node = npe.MapNode(name="ConnectomeGeneration", iterfield=['in_parc', 'out_file'], interface=mrtrix3.BuildConnectome()) # Print begin message # ------------------- print_begin_message = npe.Node( interface=niu.Function( input_names=['in_bids_or_caps_file'], function=utils.print_begin_pipeline), iterfield='in_bids_or_caps_file', name='WriteBeginMessage') # Print end message # ----------------- print_end_message = npe.Node( interface=niu.Function( input_names=['in_bids_or_caps_file', 'final_file', 'final_file'], function=utils.print_end_pipeline), iterfield=['in_bids_or_caps_file', 'final_file'], name='WriteEndMessage') # CAPS FIlenames Generation # ------------------------- caps_filenames_node = npe.Node(name='CAPSFilenamesGeneration', interface=niu.Function( input_names='dwi_file', output_names=self.get_output_fields(), function=utils.get_caps_filenames)) # Connections # =========== self.connect([ (self.input_node, print_begin_message, [('dwi_file', 'in_bids_or_caps_file')]), # noqa ]) # WM in B0-space Transformation # ----------------------------- if self.parameters['dwi_space'] == 'b0': self.connect([ # MGZ Files Convertion (self.input_node, t1_brain_conv_node, [('t1_brain_file', 'in_file')]), # noqa (self.input_node, wm_mask_conv_node, [('wm_mask_file', 'in_file')]), # noqa # B0 Extraction (self.input_node, split_node, [('dwi_file', 'in_file')]), # noqa (split_node, select_node, [('out_files', 'inlist')]), # noqa # Masking (select_node, mask_node, [('out', 'in_file')]), # B0 #noqa (self.input_node, mask_node, [('dwi_brainmask_file', 'mask_file')]), # Brain mask #noqa #noqa # T1-to-B0 Registration (t1_brain_conv_node, t12b0_reg_node, [('out_file', 'in_file')]), # Brain #noqa (mask_node, t12b0_reg_node, [('out_file', 'reference')]), # B0 brain-masked #noqa # WM Transformation (wm_mask_conv_node, wm_transform_node, [('out_file', 'in_file')]), # Brain mask #noqa (mask_node, wm_transform_node, [('out_file', 'reference')]), # BO brain-masked #noqa (t12b0_reg_node, wm_transform_node, [('out_matrix_file', 'in_matrix_file')]), # T1-to-B0 matrix file #noqa ]) # Tractography # ------------ self.connect([ (self.input_node, caps_filenames_node, [('dwi_file', 'dwi_file')]), # Response Estimation (self.input_node, resp_estim_node, [('dwi_file', 'in_file')]), # Preproc. DWI #noqa (self.input_node, resp_estim_node, [('dwi_brainmask_file', 'in_mask')]), # B0 brain mask #noqa (self.input_node, resp_estim_node, [('grad_fsl', 'grad_fsl')]), # bvecs and bvals #noqa (caps_filenames_node, resp_estim_node, [('response', 'wm_file')]), # output response filename #noqa # FOD Estimation (self.input_node, fod_estim_node, [('dwi_file', 'in_file')]), # Preproc. DWI #noqa (resp_estim_node, fod_estim_node, [('wm_file', 'wm_txt')]), # Response (txt file) #noqa (self.input_node, fod_estim_node, [('dwi_brainmask_file', 'mask_file')]), # B0 brain mask #noqa (self.input_node, fod_estim_node, [('grad_fsl', 'grad_fsl')]), # T1-to-B0 matrix file #noqa (caps_filenames_node, fod_estim_node, [('fod', 'wm_odf')]), # output odf filename #noqa # Tracts Generation (fod_estim_node, tck_gen_node, [('wm_odf', 'in_file')]), # ODF file #noqa (caps_filenames_node, tck_gen_node, [('tracts', 'out_file')]), # output tck filename #noqa # Label Conversion (self.input_node, label_convert_node, [('atlas_files', 'in_file')]), # atlas image files #noqa (caps_filenames_node, label_convert_node, [('nodes', 'out_file')]), # converted atlas image filenames #noqa # Connectomes Generation (tck_gen_node, conn_gen_node, [('out_file', 'in_file')]), # output odf filename #noqa (label_convert_node, conn_gen_node, [('out_file', 'in_parc')]), # output odf filename #noqa (caps_filenames_node, conn_gen_node, [('connectomes', 'out_file')]), # output odf filename #noqa ]) if self.parameters['dwi_space'] == 'b0': self.connect([ (wm_transform_node, tck_gen_node, [('out_file', 'seed_image')]) # ODF file #noqa ]) elif self.parameters['dwi_space'] == 'T1w': self.connect([ ( self.input_node, tck_gen_node, [('wm_mask_file', 'seed_image')]) # ODF file #noqa ]) else: raise ClinicaCAPSError( 'Bad preprocessed DWI space. Please check your CAPS ' 'folder.') # Outputs # ------ self.connect([ (resp_estim_node, self.output_node, [('wm_file', 'response')]), # T1-to-B0 matrix file #noqa (fod_estim_node, self.output_node, [('wm_odf', 'fod')]), # T1-to-B0 matrix file #noqa (tck_gen_node, self.output_node, [('out_file', 'tracts')]), # T1-to-B0 matrix file #noqa (label_convert_node, self.output_node, [('out_file', 'nodes')]), # T1-to-B0 matrix file #noqa (conn_gen_node, self.output_node, [('out_file', 'connectomes')]), # T1-to-B0 matrix file #noqa (self.input_node, print_end_message, [('dwi_file', 'in_bids_or_caps_file')]), # noqa (conn_gen_node, print_end_message, [('out_file', 'final_file')]), # noqa ])
def init_bbreg_wf(use_bbr, asl2t1w_dof, asl2t1w_init, omp_nthreads, name='bbreg_wf'): """ Build a workflow to run FreeSurfer's ``bbregister``. This workflow uses FreeSurfer's ``bbregister`` to register a ASL image to a T1-weighted structural image. It is a counterpart to :py:func:`~aslprep.workflows.asl.registration.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 Graph .. workflow :: :graph2use: orig :simple_form: yes from aslprep.workflows.asl.registration import init_bbreg_wf wf = init_bbreg_wf(use_bbr=True, asl2t1w_dof=9, asl2t1w_init='register', omp_nthreads=1) Parameters ---------- use_bbr : :obj:`bool` or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. asl2t1w_dof : 6, 9 or 12 Degrees-of-freedom for ASL-T1w registration asl2t1w_init : str, 'header' or 'register' If ``'header'``, use header information for initialization of ASL and T1 images. If ``'register'``, align volumes by their centers. name : :obj:`str`, optional Workflow name (default: bbreg_wf) Inputs ------ in_file Reference ASL image to be registered fsnative2t1w_xfm 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) t1w_brain Unused (see :py:func:`~aslprep.workflows.asl.registration.init_fsl_bbr_wf`) t1w_dseg Unused (see :py:func:`~aslprep.workflows.asl.registration.init_fsl_bbr_wf`) Outputs ------- itk_asl_to_t1 Affine transform from ``ref_asl_brain`` to T1 space (ITK format) itk_t1_to_asl Affine transform from T1 space to ASL space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (mri_coreg registration returned) """ from ...niworkflows.engine.workflows import LiterateWorkflow as Workflow from ...niworkflows.interfaces.freesurfer import ( PatchedBBRegisterRPT as BBRegisterRPT, PatchedMRICoregRPT as MRICoregRPT, PatchedLTAConvert as LTAConvert) from ...niworkflows.interfaces.nitransforms import ConcatenateXFMs workflow = Workflow(name=name) workflow.__desc__ = """\ The ASL reference was then co-registered to the T1w reference using `bbregister` (FreeSurfer) which implements boundary-based registration [@bbr]. Co-registration was configured with {dof} degrees of freedom{reason}. """.format(dof={ 6: 'six', 9: 'nine', 12: 'twelve' }[asl2t1w_dof], reason='' if asl2t1w_dof == 6 else 'to account for distortions remaining in the ASL reference') inputnode = pe.Node( niu.IdentityInterface([ 'in_file', 'fsnative2t1w_xfm', 'subjects_dir', 'subject_id', # BBRegister 't1w_dseg', 't1w_brain' ]), # FLIRT BBR name='inputnode') outputnode = pe.Node(niu.IdentityInterface( ['itk_asl_to_t1', 'itk_t1_to_asl', 'out_report', 'fallback']), name='outputnode') if asl2t1w_init not in ("register", "header"): raise ValueError( f"Unknown ASL-T1w initialization option: {asl2t1w_init}") # For now make BBR unconditional - in the future, we can fall back to identity, # but adding the flexibility without testing seems a bit dangerous if asl2t1w_init == "header": if use_bbr is False: raise ValueError("Cannot disable BBR and use header registration") if use_bbr is None: LOGGER.warning( "Initializing BBR with header; affine fallback disabled") use_bbr = True merge_ltas = pe.Node(niu.Merge(2), name='merge_ltas', run_without_submitting=True) concat_xfm = pe.Node(ConcatenateXFMs(inverse=True), name='concat_xfm') workflow.connect([ # Output ITK transforms (inputnode, merge_ltas, [('fsnative2t1w_xfm', 'in2')]), (merge_ltas, concat_xfm, [('out', 'in_xfms')]), (concat_xfm, outputnode, [('out_xfm', 'itk_asl_to_t1')]), (concat_xfm, outputnode, [('out_inv', 'itk_t1_to_asl')]), ]) # Define both nodes, but only connect conditionally mri_coreg = pe.Node(MRICoregRPT(dof=asl2t1w_dof, sep=[4], ftol=0.0001, linmintol=0.01, generate_report=not use_bbr), name='mri_coreg', n_procs=omp_nthreads, mem_gb=5) bbregister = pe.Node(BBRegisterRPT(dof=asl2t1w_dof, contrast_type='t2', registered_file=True, out_lta_file=True, generate_report=True), name='bbregister', mem_gb=12) # Use mri_coreg if asl2t1w_init == "register": workflow.connect([ (inputnode, mri_coreg, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id'), ('in_file', 'source_file')]), ]) # Short-circuit workflow building, use initial registration if use_bbr is False: workflow.connect([ (mri_coreg, outputnode, [('out_report', 'out_report')]), (mri_coreg, merge_ltas, [('out_lta_file', 'in1')]) ]) outputnode.inputs.fallback = True return workflow # Use bbregister workflow.connect([ (inputnode, bbregister, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id'), ('in_file', 'source_file')]), ]) if asl2t1w_init == "header": bbregister.inputs.init = "header" else: workflow.connect([(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, merge_ltas, [('out_lta_file', 'in1')])]) outputnode.inputs.fallback = False return workflow # Only reach this point if asl2t1w_init is "register" and use_bbr is None 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(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, merge_ltas, [('out', 'in1')]), # 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 create_parallelfeat_preproc(name='featpreproc', highpass=True): """Preprocess each run with FSL independently of the others Parameters ---------- :: name : name of workflow (default: featpreproc) highpass : boolean (default: True) Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.highpassed_files : highpassed functional data (if highpass=True) outputspec.mean : mean file Example ------- >>> preproc = create_parallelfeat_preproc() >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP >>> preproc = create_parallelfeat_preproc(highpass=False) >>> preproc.inputs.inputspec.func = 'f3.nii' >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) """ Set up a node to define all inputs required for the preprocessing workflow """ if highpass: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'highpass']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask', 'smoothed_files', 'highpassed_files', 'mean']), name='outputspec') else: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask', 'smoothed_files', 'mean']), name='outputspec') """ Set up a node to define outputs for the preprocessing workflow """ """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string = '', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', img2float, 'in_file') """ Extract the first volume of the first run as the reference """ extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file', 't_min'], name = 'extractref') featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickmiddle), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') """ Realign the functional runs to the reference (1st volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True, save_plots = True), name='realign', iterfield = ['in_file', 'ref_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """ Extract the mean volume of the first functional run """ meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string = '-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc') featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file') """ Strip the skull from the mean functional to generate a mask """ meanfuncmask = pe.MapNode(interface=fsl.BET(mask = True, no_output=True, frac = 0.3), iterfield=['in_file'], name = 'meanfuncmask') featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file') """ Mask the functional runs with the extracted mask """ maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file', 'in_file2'], name = 'maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2') """ Determine the 2nd and 98th percentile intensities of each functional run """ getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'), iterfield = ['in_file'], name='getthreshold') featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file') """ Threshold the first run of the functional data at 10% of the 98th percentile """ threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char', suffix='_thresh'), iterfield=['in_file', 'op_string'], name='threshold') featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield = ['in_file', 'mask_file'], name='medianval') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') """ Dilate the mask """ dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask') """ Mask the motion corrected functional runs with the dilated mask """ maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc2') featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2') """ Smooth each run using SUSAN with the brightness threshold set to 75% of the median value for each run and a mask consituting the mean functional """ smooth = create_susan_smooth() featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') featpreproc.connect(maskfunc2,('out_file', tolist), concatnode, 'in1') featpreproc.connect(maskfunc3,('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(),name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files') """ Scale the median value of the run is set to 10000 """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file','op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') """ Perform temporal highpass filtering on the data """ if highpass: highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') """ Generate a mean functional image from the first run """ meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc3') if highpass: featpreproc.connect(highpass, 'out_file', meanfunc3, 'in_file') else: featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean') return featpreproc
def init_anat_template_wf(*, longitudinal, omp_nthreads, num_t1w, name='anat_template_wf'): """ Generate a canonically-oriented, structural average from all input T1w images. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from smriprep.workflows.anatomical import init_anat_template_wf wf = init_anat_template_wf( longitudinal=False, omp_nthreads=1, num_t1w=1) Parameters ---------- longitudinal : :obj:`bool` Create unbiased structural average, regardless of number of inputs (may increase runtime) omp_nthreads : :obj:`int` Maximum number of threads an individual process may use num_t1w : :obj:`int` Number of T1w images name : :obj:`str`, optional Workflow name (default: anat_template_wf) Inputs ------ t1w List of T1-weighted structural images Outputs ------- t1w_ref Structural reference averaging input T1w images, defining the T1w space. t1w_realign_xfm List of affine transforms to realign input T1w images out_report Conformation report """ workflow = Workflow(name=name) if num_t1w > 1: workflow.__desc__ = """\ A T1w-reference map was computed after registration of {num_t1w} T1w images (after INU-correction) using `mri_robust_template` [FreeSurfer {fs_ver}, @fs_template]. """.format(num_t1w=num_t1w, fs_ver=fs.Info().looseversion() or '<ver>') inputnode = pe.Node(niu.IdentityInterface(fields=['t1w']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['t1w_ref', 't1w_valid_list', 't1w_realign_xfm', 'out_report']), name='outputnode') # 0. Reorient T1w image(s) to RAS and resample to common voxel space t1w_ref_dimensions = pe.Node(TemplateDimensions(), name='t1w_ref_dimensions') t1w_conform = pe.MapNode(Conform(), iterfield='in_file', name='t1w_conform') workflow.connect([ (inputnode, t1w_ref_dimensions, [('t1w', 't1w_list')]), (t1w_ref_dimensions, t1w_conform, [('t1w_valid_list', 'in_file'), ('target_zooms', 'target_zooms'), ('target_shape', 'target_shape')]), (t1w_ref_dimensions, outputnode, [('out_report', 'out_report'), ('t1w_valid_list', 't1w_valid_list') ]), ]) if num_t1w == 1: get1st = pe.Node(niu.Select(index=[0]), name='get1st') outputnode.inputs.t1w_realign_xfm = [ pkgr('smriprep', 'data/itkIdentityTransform.txt') ] workflow.connect([ (t1w_conform, get1st, [('out_file', 'inlist')]), (get1st, outputnode, [('out', 't1w_ref')]), ]) return workflow t1w_conform_xfm = pe.MapNode(LTAConvert(in_lta='identity.nofile', out_lta=True), iterfield=['source_file', 'target_file'], name='t1w_conform_xfm') # 1. Template (only if several T1w images) # 1a. Correct for bias field: the bias field is an additive factor # in log-transformed intensity units. Therefore, it is not a linear # combination of fields and N4 fails with merged images. # 1b. Align and merge if several T1w images are provided n4_correct = pe.MapNode(N4BiasFieldCorrection(dimension=3, copy_header=True), iterfield='input_image', name='n4_correct', n_procs=1) # n_procs=1 for reproducibility # StructuralReference is fs.RobustTemplate if > 1 volume, copying otherwise t1w_merge = pe.Node( StructuralReference( auto_detect_sensitivity=True, initial_timepoint=1, # For deterministic behavior intensity_scaling=True, # 7-DOF (rigid + intensity) subsample_threshold=200, fixed_timepoint=not longitudinal, no_iteration=not longitudinal, transform_outputs=True, ), mem_gb=2 * num_t1w - 1, name='t1w_merge') # 2. Reorient template to RAS, if needed (mri_robust_template may set to LIA) t1w_reorient = pe.Node(image.Reorient(), name='t1w_reorient') merge_xfm = pe.MapNode(niu.Merge(2), name='merge_xfm', iterfield=['in1', 'in2'], run_without_submitting=True) concat_xfms = pe.MapNode(ConcatenateXFMs(inverse=True), name="concat_xfms", iterfield=['in_xfms'], run_without_submitting=True) def _set_threads(in_list, maximum): return min(len(in_list), maximum) workflow.connect([ (t1w_ref_dimensions, t1w_conform_xfm, [('t1w_valid_list', 'source_file')]), (t1w_conform, t1w_conform_xfm, [('out_file', 'target_file')]), (t1w_conform, n4_correct, [('out_file', 'input_image')]), (t1w_conform, t1w_merge, [(('out_file', _set_threads, omp_nthreads), 'num_threads'), (('out_file', add_suffix, '_template'), 'out_file')]), (n4_correct, t1w_merge, [('output_image', 'in_files')]), (t1w_merge, t1w_reorient, [('out_file', 'in_file')]), # Combine orientation and template transforms (t1w_conform_xfm, merge_xfm, [('out_lta', 'in1')]), (t1w_merge, merge_xfm, [('transform_outputs', 'in2')]), (merge_xfm, concat_xfms, [('out', 'in_xfms')]), # Output (t1w_reorient, outputnode, [('out_file', 't1w_ref')]), (concat_xfms, outputnode, [('out_xfm', 't1w_realign_xfm')]), ]) return workflow
def create_fsl_fs_preproc(name='preproc', highpass=True, whichvol='middle'): """Create a FEAT preprocessing workflow together with freesurfer Parameters ---------- :: name : name of workflow (default: preproc) highpass : boolean (default: True) whichvol : which volume of the first run to register to ('first', 'middle', 'mean') Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) inputspec.subject_id : freesurfer subject id inputspec.subjects_dir : freesurfer subjects dir Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask_file : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.highpassed_files : highpassed functional data (if highpass=True) outputspec.reg_file : bbregister registration files outputspec.reg_cost : bbregister registration cost files Example ------- >>> preproc = create_fsl_fs_preproc(whichvol='first') >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.subjects_dir = '.' >>> preproc.inputs.inputspec.subject_id = 's1' >>> preproc.inputs.inputspec.fwhm = 6 >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) """ Set up a node to define all inputs required for the preprocessing workflow """ if highpass: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'subject_id', 'subjects_dir', 'highpass']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask_file', 'smoothed_files', 'highpassed_files', 'reg_file', 'reg_cost' ]), name='outputspec') else: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'subject_id', 'subjects_dir' ]), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask_file', 'smoothed_files', 'reg_file', 'reg_cost' ]), name='outputspec') """ Set up a node to define outputs for the preprocessing workflow """ """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string = '', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', img2float, 'in_file') """ Extract the first volume of the first run as the reference """ if whichvol != 'mean': extract_ref = pe.Node(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file'], name = 'extractref') featpreproc.connect(img2float, ('out_file', pickfirst), extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') """ Realign the functional runs to the reference (1st volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True, save_plots = True, interpolation = 'sinc'), name='realign', iterfield = ['in_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') else: motion_correct.inputs.mean_vol = True featpreproc.connect(motion_correct, 'mean_img', outputnode, 'reference') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """Get the mask from subject for each run """ maskflow = create_getmask_flow() featpreproc.connect([(inputnode, maskflow, [('subject_id','inputspec.subject_id'), ('subjects_dir', 'inputspec.subjects_dir')])]) maskflow.inputs.inputspec.contrast_type = 't2' if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', maskflow, 'inputspec.source_file') else: featpreproc.connect(motion_correct, ('mean_img', pickfirst), maskflow, 'inputspec.source_file') """ Mask the functional runs with the extracted mask """ maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name = 'maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), maskfunc, 'in_file2') """ Smooth each run using SUSAN with the brightness threshold set to 75% of the median value for each run and a mask consituting the mean functional """ smooth = create_susan_smooth(separate_masks=False) featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), smooth, 'inputnode.mask_file') """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') featpreproc.connect(maskfunc, ('out_file', tolist), concatnode, 'in1') featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(),name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files') """ Scale the median value of the run is set to 10000 """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file','op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield = ['in_file'], name='medianval') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), medianval, 'mask_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') """ Perform temporal highpass filtering on the data """ if highpass: highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), outputnode, 'mask_file') featpreproc.connect(maskflow, 'outputspec.reg_file', outputnode, 'reg_file') featpreproc.connect(maskflow, 'outputspec.reg_cost', outputnode, 'reg_cost') return featpreproc
def 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 = Workflow(name=name) workflow.__desc__ = """\ The BOLD reference was then co-registered to the T1w reference using `flirt` [FSL {fsl_ver}, @flirt] with the boundary-based registration [@bbr] cost-function. Co-registration was configured with nine degrees of freedom to account for distortions remaining in the BOLD reference. """.format(fsl_ver=FLIRTRPT().version or '<ver>') 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, uses_qform=True), 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 apply_all_corrections(name='UnwarpArtifacts'): """ Combines two lists of linear transforms with the deformation field map obtained typically after the SDC process. Additionally, computes the corresponding bspline coefficients and the map of determinants of the jacobian. """ inputnode = pe.Node( niu.IdentityInterface(fields=['in_sdc', 'in_hmc', 'in_ecc', 'in_dwi']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_file', 'out_warp', 'out_coeff', 'out_jacobian']), name='outputnode') warps = pe.MapNode(fsl.ConvertWarp(relwarp=True), iterfield=['premat', 'postmat'], name='ConvertWarp') selref = pe.Node(niu.Select(index=[0]), name='Reference') split = pe.Node(fsl.Split(dimension='t'), name='SplitDWIs') unwarp = pe.MapNode(fsl.ApplyWarp(), iterfield=['in_file', 'field_file'], name='UnwarpDWIs') coeffs = pe.MapNode(fsl.WarpUtils(out_format='spline'), iterfield=['in_file'], name='CoeffComp') jacobian = pe.MapNode(fsl.WarpUtils(write_jacobian=True), iterfield=['in_file'], name='JacobianComp') jacmult = pe.MapNode(fsl.MultiImageMaths(op_string='-mul %s'), iterfield=['in_file', 'operand_files'], name='ModulateDWIs') thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=['in_file'], name='RemoveNegative') merge = pe.Node(fsl.Merge(dimension='t'), name='MergeDWIs') wf = pe.Workflow(name=name) wf.connect([(inputnode, warps, [('in_sdc', 'warp1'), ('in_hmc', 'premat'), ('in_ecc', 'postmat'), ('in_dwi', 'reference')]), (inputnode, split, [('in_dwi', 'in_file')]), (split, selref, [('out_files', 'inlist')]), (warps, unwarp, [('out_file', 'field_file')]), (split, unwarp, [('out_files', 'in_file')]), (selref, unwarp, [('out', 'ref_file')]), (selref, coeffs, [('out', 'reference')]), (warps, coeffs, [('out_file', 'in_file')]), (selref, jacobian, [('out', 'reference')]), (coeffs, jacobian, [('out_file', 'in_file')]), (unwarp, jacmult, [('out_file', 'in_file')]), (jacobian, jacmult, [('out_jacobian', 'operand_files')]), (jacmult, thres, [('out_file', 'in_file')]), (thres, merge, [('out_file', 'in_files')]), (warps, outputnode, [('out_file', 'out_warp')]), (coeffs, outputnode, [('out_file', 'out_coeff')]), (jacobian, outputnode, [('out_jacobian', 'out_jacobian')]), (merge, outputnode, [('merged_file', 'out_file')])]) return wf
def create_fixedfx(c, first_c=foo0, name='fixedfx'): from nipype.workflows.fmri.fsl.estimate import create_fixed_effects_flow import nipype.interfaces.io as nio # i/o routines import nipype.interfaces.utility as util # utility import nipype.pipeline.engine as pe # pypeline engine selectnode = pe.Node(interface=util.IdentityInterface(fields=['runs']), name='idselect') selectnode.iterables = ('runs', [range(0, c.num_runs)]) # this is really bad. copeselect = pe.MapNode(interface=util.Select(), name='copeselect', iterfield=['inlist']) varcopeselect = pe.MapNode(interface=util.Select(), name='varcopeselect', iterfield=['inlist']) dofselect = pe.Node(interface=util.Select(), name='dofselect') """if c.test_mode: infosource = pe.Node(interface=util.IdentityInterface(fields=['subject_id','fwhm']), name="infosource") infosource.iterables = [('subject_id', [c.subjects[0]]), ('fwhm',prep_c.fwhm)] else: infosource = pe.Node(interface=util.IdentityInterface(fields=['subject_id','fwhm']), name="infosource") infosource.iterables = [('subject_id', c.subjects),('fwhm',prep_c.fwhm)] datasource = pe.Node(interface=nio.DataGrabber(infields=['subject_id','fwhm'], outfields=['copes', 'varcopes', 'dof_files', 'mask_file']), name = 'datasource') datasource.inputs.base_directory = c.sink_dir datasource.inputs.template ='*' datasource.inputs.sort_filelist = True datasource.inputs.field_template = dict(copes='%s/modelfit/contrasts/fwhm_%s/_estimate_contrast*/cope%02d*.nii*', varcopes='%s/modelfit/contrasts/fwhm_%s/_estimate_contrast*/varcope%02d*.nii*', dof_files='%s/modelfit/dofs/fwhm_%s/*/*', mask_file='%s/preproc/mask/*.nii*')""" datasource = c.datagrabber.create_dataflow() infosource = datasource.get_node('subject_id_iterable') fixedfx = create_fixed_effects_flow() fixedfxflow = pe.Workflow(name=name) fixedfxflow.config = {'execution': {'crashdump_dir': c.crash_dir}} #overlay = create_overlay_workflow(c,name='overlay') subjectinfo = pe.Node(util.Function(input_names=['subject_id'], output_names=['output']), name='subjectinfo') subjectinfo.inputs.function_str = first_c.subjectinfo contrasts = pe.Node(util.Function(input_names=['subject_id'], output_names=['contrasts']), name='getcontrasts') contrasts.inputs.function_str = first_c.contrasts #get_info = pe.Node(util.Function(input_names=['cons','info'], output_names=['info'], function=getinfo), name='getinfo') get_subs = pe.Node(util.Function(input_names=['subject_id', 'cons'], output_names=['subs'], function=getsubs), name='getsubs') #fixedfxflow.connect(infosource, 'subject_id', datasource, 'subject_id') #fixedfxflow.connect(infosource, ('subject_id',getinfo, c.getcontrasts, c.subjectinfo), datasource, 'template_args') fixedfxflow.connect(infosource, 'subject_id', contrasts, 'subject_id') fixedfxflow.connect(infosource, 'subject_id', subjectinfo, 'subject_id') #fixedfxflow.connect(contrasts, 'contrasts', get_info, 'cons') #fixedfxflow.connect(subjectinfo, 'output', get_info, 'info') #fixedfxflow.connect(get_info,'info',datasource,'template_args') #fixedfxflow.connect(infosource, 'fwhm', datasource, 'fwhm') fixedfxflow.connect(datasource, 'datagrabber.copes', copeselect, 'inlist') fixedfxflow.connect(selectnode, 'runs', copeselect, 'index') fixedfxflow.connect(datasource, 'datagrabber.copes', fixedfx, 'inputspec.copes') fixedfxflow.connect(datasource, 'datagrabber.varcopes', varcopeselect, 'inlist') fixedfxflow.connect(selectnode, 'runs', varcopeselect, 'index') fixedfxflow.connect(datasource, 'datagrabber.varcopes', fixedfx, 'inputspec.varcopes') fixedfxflow.connect(datasource, 'datagrabber.dof_files', dofselect, 'inlist') fixedfxflow.connect(selectnode, 'runs', dofselect, 'index') fixedfxflow.connect(datasource, 'datagrabber.dof_files', fixedfx, 'inputspec.dof_files') fixedfxflow.connect(datasource, ('datagrabber.copes', num_copes), fixedfx, 'l2model.num_copes') fixedfxflow.connect(datasource, 'datagrabber.mask_file', fixedfx, 'flameo.mask_file') #fixedfxflow.connect(infosource, 'subject_id', overlay, 'inputspec.subject_id') #fixedfxflow.connect(infosource, 'fwhm', overlay, 'inputspec.fwhm') #fixedfxflow.connect(fixedfx, 'outputspec.zstats', overlay, 'inputspec.stat_image') datasink = pe.Node(interface=nio.DataSink(), name="datasink") datasink.inputs.base_directory = c.sink_dir # store relevant outputs from various stages of the 1st level analysis fixedfxflow.connect([ ( infosource, datasink, [ ('subject_id', 'container'), #(('subject_id', getsubs, c.getcontrasts), 'substitutions') ]), (fixedfx, datasink, [ ('outputspec.copes', 'fixedfx.@copes'), ('outputspec.varcopes', 'fixedfx.@varcopes'), ('outputspec.tstats', 'fixedfx.@tstats'), ('outputspec.zstats', 'fixedfx.@zstats'), ('outputspec.res4d', 'fixedfx.@pvals'), ]) ]) fixedfxflow.connect(infosource, 'subject_id', get_subs, 'subject_id') fixedfxflow.connect(contrasts, 'contrasts', get_subs, 'cons') fixedfxflow.connect(get_subs, 'subs', datasink, 'substitutions') #fixedfxflow.connect(overlay, 'slicestats.out_file', datasink, 'overlays') return fixedfxflow
NodeHash_17173a00.inputs.local_directory = '/tmp' NodeHash_17173a00.inputs.sort_filelist = True NodeHash_17173a00.inputs.template = '*' NodeHash_17173a00.inputs.template_args = dict(func=[['sub_id', 'sub_id']], events=[['sub_id', 'sub_id']], anat=[['sub_id', 'sub_id']]) NodeHash_17173a00.inputs.field_template = dict(func='%s/func/%s_task-simon_run-1_bold.nii.gz', events='%s/func/%s_task-simon_run-1_events.tsv', anat='%s/anat/%s_T1w.nii.gz') #Wraps command **bet** NodeHash_20af2180 = pe.MapNode(interface = fsl.BET(), name = 'NodeName_20af2180', iterfield = ['in_file']) NodeHash_20af2180.inputs.frac = 0.3 NodeHash_20af2180.inputs.robust = True #Wraps command **fast** NodeHash_10d41ed0 = pe.MapNode(interface = fsl.FAST(), name = 'NodeName_10d41ed0', iterfield = ['in_files']) #Basic interface class to select specific elements from a list NodeHash_282e9070 = pe.MapNode(interface = utility.Select(), name = 'NodeName_282e9070', iterfield = ['inlist']) NodeHash_282e9070.inputs.index = 2 #Wraps command **fslmaths** NodeHash_313f8780 = pe.MapNode(interface = fsl.Threshold(), name = 'NodeName_313f8780', iterfield = ['in_file']) NodeHash_313f8780.inputs.args = '-bin' NodeHash_313f8780.inputs.thresh = 0.5 #Wraps command **fslroi** NodeHash_1af07380 = pe.MapNode(interface = fsl.ExtractROI(), name = 'NodeName_1af07380', iterfield = ['in_file']) NodeHash_1af07380.inputs.t_min = 1 NodeHash_1af07380.inputs.t_size = 1 #Wraps command **flirt** NodeHash_5accf90 = pe.MapNode(interface = fsl.FLIRT(), name = 'NodeName_5accf90', iterfield = ['in_file', 'reference'])