def skullstrip_wf(name='SkullStripWorkflow'): """ Skull-stripping workflow """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['out_file', 'out_mask', 'head_mask']), name='outputnode') sstrip = pe.Node(afp.SkullStrip(outputtype='NIFTI_GZ'), name='skullstrip') sstrip_orig_vol = pe.Node(afp.Calc(expr='a*step(b)', outputtype='NIFTI_GZ'), name='sstrip_orig_vol') binarize = pe.Node(fsl.Threshold(args='-bin', thresh=1.e-3), name='binarize') workflow.connect([(inputnode, sstrip, [('in_file', 'in_file')]), (inputnode, sstrip_orig_vol, [('in_file', 'in_file_a')]), (sstrip, sstrip_orig_vol, [('out_file', 'in_file_b')]), (sstrip_orig_vol, binarize, [('out_file', 'in_file')]), (sstrip_orig_vol, outputnode, [('out_file', 'out_file') ]), (binarize, outputnode, [('out_file', 'out_mask')])]) return workflow
def hmc_afni(name='fMRI_HMC_afni', st_correct=False): """A head motion correction (HMC) workflow for functional scans""" workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_file', 'fd_radius', 'start_idx', 'stop_idx']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['out_file', 'out_movpar']), name='outputnode') drop_trs = pe.Node(afp.Calc(expr='a', outputtype='NIFTI_GZ'), name='drop_trs') deoblique = pe.Node(afp.Refit(deoblique=True), name='deoblique') reorient = pe.Node(afp.Resample(orientation='RPI', outputtype='NIFTI_GZ'), name='reorient') get_mean_RPI = pe.Node(afp.TStat(options='-mean', outputtype='NIFTI_GZ'), name='get_mean_RPI') # calculate hmc parameters hmc = pe.Node(afp.Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'), name='motion_correct') get_mean_motion = get_mean_RPI.clone('get_mean_motion') hmc_A = hmc.clone('motion_correct_A') hmc_A.inputs.md1d_file = 'max_displacement.1D' movpar = pe.Node(niu.Function(function=fd_jenkinson, input_names=['in_file', 'rmax'], output_names=['out_file']), name='Mat2Movpar') workflow.connect([(inputnode, drop_trs, [('in_file', 'in_file_a'), ('start_idx', 'start_idx'), ('stop_idx', 'stop_idx')]), (inputnode, movpar, [('fd_radius', 'rmax')]), (deoblique, reorient, [('out_file', 'in_file')]), (reorient, get_mean_RPI, [('out_file', 'in_file')]), (reorient, hmc, [('out_file', 'in_file')]), (get_mean_RPI, hmc, [('out_file', 'basefile')]), (hmc, get_mean_motion, [('out_file', 'in_file')]), (reorient, hmc_A, [('out_file', 'in_file')]), (get_mean_motion, hmc_A, [('out_file', 'basefile')]), (hmc_A, outputnode, [('out_file', 'out_file')]), (hmc_A, movpar, [('oned_matrix_save', 'in_file')]), (movpar, outputnode, [('out_file', 'out_movpar')])]) if st_correct: st_corr = pe.Node(afp.TShift(outputtype='NIFTI_GZ'), name='TimeShifts') workflow.connect([(drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, deoblique, [('out_file', 'in_file')])]) else: workflow.connect([(drop_trs, deoblique, [('out_file', 'in_file')])]) return workflow
def anatomical_skullstrip_workflow(workflow, resource_pool, config): # resource pool should have: # anatomical_reorient import os import sys import nipype.interfaces.io as nio import nipype.pipeline.engine as pe import nipype.interfaces.utility as util from nipype.interfaces.afni import preprocess if "anatomical_reorient" not in resource_pool.keys(): from anatomical_preproc import anatomical_reorient_workflow workflow, resource_pool = \ anatomical_reorient_workflow(workflow, resource_pool, config) #check_input_resources(resource_pool, "anatomical_reorient") anat_skullstrip = pe.Node(interface=preprocess.SkullStrip(), name='anat_skullstrip') anat_skullstrip.inputs.outputtype = 'NIFTI_GZ' anat_skullstrip_orig_vol = pe.Node(interface=preprocess.Calc(), name='anat_skullstrip_orig_vol') anat_skullstrip_orig_vol.inputs.expr = 'a*step(b)' anat_skullstrip_orig_vol.inputs.outputtype = 'NIFTI_GZ' if len(resource_pool["anatomical_reorient"]) == 2: node, out_file = resource_pool["anatomical_reorient"] workflow.connect(node, out_file, anat_skullstrip, 'in_file') else: anat_skullstrip.inputs.in_file = \ resource_pool["anatomical_reorient"] if len(resource_pool["anatomical_reorient"]) == 2: node, out_file = resource_pool["anatomical_reorient"] workflow.connect(node, out_file, anat_skullstrip_orig_vol, 'in_file_a') else: anat_skullstrip_orig_vol.inputs.in_file_a = \ resource_pool["anatomical_reorient"] workflow.connect(anat_skullstrip, 'out_file', anat_skullstrip_orig_vol, 'in_file_b') resource_pool["anatomical_brain"] = (anat_skullstrip_orig_vol, 'out_file') return workflow, resource_pool
def anatomical_skullstrip_workflow(workflow, resource_pool, config, name="_"): """Build a Nipype workflow to skullstrip an anatomical image using AFNI's 3dSkullStrip. - If any resources/outputs required by this workflow are not in the resource pool, this workflow will call pre-requisite workflow builder functions to further populate the pipeline with workflows which will calculate/generate these necessary pre-requisites. Expected Resources in Resource Pool - anatomical_reorient: The deobliqued, reoriented anatomical scan. New Resources Added to Resource Pool - anatomical_brain: The skull-stripped anatomical image (brain only). Workflow Steps 1. AFNI 3dSkullStrip to create a binary mask selecting only the brain. 2. AFNI 3dcalc to multiply the anatomical image with this mask. :type workflow: Nipype workflow object :param workflow: A Nipype workflow object which can already contain other connected nodes; this function will insert the following workflow into this one provided. :type resource_pool: dict :param resource_pool: A dictionary defining input files and pointers to Nipype node outputs / workflow connections; the keys are the resource names. :type config: dict :param config: A dictionary defining the configuration settings for the workflow, such as directory paths or toggled options. :type name: str :param name: (default: "_") A string to append to the end of each node name. :rtype: Nipype workflow object :return: The Nipype workflow originally provided, but with this function's sub-workflow connected into it. :rtype: dict :return: The resource pool originally provided, but updated (if applicable) with the newest outputs and connections. """ import copy import nipype.pipeline.engine as pe from nipype.interfaces.afni import preprocess if "anatomical_reorient" not in resource_pool.keys(): from anatomical_preproc import anatomical_reorient_workflow old_rp = copy.copy(resource_pool) workflow, new_resource_pool = \ anatomical_reorient_workflow(workflow, resource_pool, config, name) if resource_pool == old_rp: return workflow, resource_pool anat_skullstrip = pe.Node(interface=preprocess.SkullStrip(), name='anat_skullstrip%s' % name) anat_skullstrip.inputs.outputtype = 'NIFTI_GZ' anat_skullstrip_orig_vol = pe.Node(interface=preprocess.Calc(), name='anat_skullstrip_orig_vol%s' % name) anat_skullstrip_orig_vol.inputs.expr = 'a*step(b)' anat_skullstrip_orig_vol.inputs.outputtype = 'NIFTI_GZ' if len(resource_pool["anatomical_reorient"]) == 2: node, out_file = resource_pool["anatomical_reorient"] workflow.connect(node, out_file, anat_skullstrip, 'in_file') else: anat_skullstrip.inputs.in_file = \ resource_pool["anatomical_reorient"] if len(resource_pool["anatomical_reorient"]) == 2: node, out_file = resource_pool["anatomical_reorient"] workflow.connect(node, out_file, anat_skullstrip_orig_vol, 'in_file_a') else: anat_skullstrip_orig_vol.inputs.in_file_a = \ resource_pool["anatomical_reorient"] workflow.connect(anat_skullstrip, 'out_file', anat_skullstrip_orig_vol, 'in_file_b') resource_pool["anatomical_brain"] = (anat_skullstrip_orig_vol, 'out_file') return workflow, resource_pool
def func_motion_correct_workflow(workflow, resource_pool, config): # resource pool should have: # functional_scan import os import sys import nipype.interfaces.io as nio import nipype.pipeline.engine as pe import nipype.interfaces.utility as util import nipype.interfaces.fsl.maths as fsl from nipype.interfaces.afni import preprocess from workflow_utils import check_input_resources, \ check_config_settings check_input_resources(resource_pool, "functional_scan") check_config_settings(config, "start_idx") check_config_settings(config, "stop_idx") check_config_settings(config, "slice_timing_correction") func_get_idx = pe.Node(util.Function( input_names=['in_files', 'stop_idx', 'start_idx'], output_names=['stopidx', 'startidx'], function=get_idx), name='func_get_idx') func_get_idx.inputs.in_files = resource_pool["functional_scan"] func_get_idx.inputs.start_idx = config["start_idx"] func_get_idx.inputs.stop_idx = config["stop_idx"] func_drop_trs = pe.Node(interface=preprocess.Calc(), name='func_drop_trs') func_drop_trs.inputs.in_file_a = resource_pool["functional_scan"] func_drop_trs.inputs.expr = 'a' func_drop_trs.inputs.outputtype = 'NIFTI_GZ' workflow.connect(func_get_idx, 'startidx', func_drop_trs, 'start_idx') workflow.connect(func_get_idx, 'stopidx', func_drop_trs, 'stop_idx') #workflow.connect(func_drop_trs, 'out_file', # outputNode, 'drop_tr') func_slice_timing_correction = pe.Node(interface=preprocess.TShift(), name='func_slice_time_correction') func_slice_timing_correction.inputs.outputtype = 'NIFTI_GZ' func_deoblique = pe.Node(interface=preprocess.Refit(), name='func_deoblique') func_deoblique.inputs.deoblique = True if config["slice_timing_correction"] == True: workflow.connect(func_drop_trs, 'out_file', func_slice_timing_correction, 'in_file') workflow.connect(func_slice_timing_correction, 'out_file', func_deoblique, 'in_file') else: workflow.connect(func_drop_trs, 'out_file', func_deoblique, 'in_file') func_reorient = pe.Node(interface=preprocess.Resample(), name='func_reorient') func_reorient.inputs.orientation = 'RPI' func_reorient.inputs.outputtype = 'NIFTI_GZ' workflow.connect(func_deoblique, 'out_file', func_reorient, 'in_file') func_get_mean_RPI = pe.Node(interface=preprocess.TStat(), name='func_get_mean_RPI') func_get_mean_RPI.inputs.options = '-mean' func_get_mean_RPI.inputs.outputtype = 'NIFTI_GZ' workflow.connect(func_reorient, 'out_file', func_get_mean_RPI, 'in_file') # calculate motion parameters func_motion_correct = pe.Node(interface=preprocess.Volreg(), name='func_motion_correct') func_motion_correct.inputs.args = '-Fourier -twopass' func_motion_correct.inputs.zpad = 4 func_motion_correct.inputs.outputtype = 'NIFTI_GZ' workflow.connect(func_reorient, 'out_file', func_motion_correct, 'in_file') workflow.connect(func_get_mean_RPI, 'out_file', func_motion_correct, 'basefile') func_get_mean_motion = func_get_mean_RPI.clone('func_get_mean_motion') workflow.connect(func_motion_correct, 'out_file', func_get_mean_motion, 'in_file') func_motion_correct_A = func_motion_correct.clone('func_motion_correct_A') func_motion_correct_A.inputs.md1d_file = 'max_displacement.1D' workflow.connect(func_reorient, 'out_file', func_motion_correct_A, 'in_file') workflow.connect(func_get_mean_motion, 'out_file', func_motion_correct_A, 'basefile') resource_pool["func_motion_correct"] = (func_motion_correct_A, 'out_file') resource_pool["coordinate_transformation"] = \ (func_motion_correct_A, 'oned_matrix_save') return workflow, resource_pool
def create_vmhc(use_ants, name='vmhc_workflow', ants_threads=1): """ Compute the map of brain functional homotopy, the high degree of synchrony in spontaneous activity between geometrically corresponding interhemispheric (i.e., homotopic) regions. Parameters ---------- None Returns ------- vmhc_workflow : workflow Voxel Mirrored Homotopic Connectivity Analysis Workflow Notes ----- `Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/vmhc/vmhc.py>`_ Workflow Inputs:: inputspec.brain : string (existing nifti file) Anatomical image(without skull) inputspec.symmetric_brain : string (existing nifti file) MNI152_T1_2mm_symmetric_brain.nii.gz inputspec.rest_res_filt : string (existing nifti file) Band passed Image with nuisance signal regressed out(and optionally scrubbed). Recommended bandpass filter (0.001,0.1) ) inputspec.reorient : string (existing nifti file) RPI oriented anatomical data inputspec.example_func2highres_mat : string (existing affine transformation .mat file) Specifies an affine transform that should be applied to the example_func before non linear warping inputspec.standard_for_func: string (existing nifti file) MNI152_T1_standard_resolution_brain.nii.gz inputspec.symmetric_skull : string (existing nifti file) MNI152_T1_2mm_symmetric.nii.gz inputspec.twomm_brain_mask_dil : string (existing nifti file) MNI152_T1_2mm_brain_mask_symmetric_dil.nii.gz inputspec.config_file_twomm_symmetric : string (existing .cnf file) T1_2_MNI152_2mm_symmetric.cnf inputspec.rest_mask : string (existing nifti file) A mask functional volume(derived by dilation from motion corrected functional volume) fwhm_input.fwhm : list (float) For spatial smoothing the Z-transformed correlations in MNI space. Generally the value of this parameter is 1.5 or 2 times the voxel size of the input Image. inputspec.mean_functional : string (existing nifti file) The mean functional image for use in the func-to-anat registration matrix conversion to ITK (ANTS) format, if the user selects to use ANTS. Workflow Outputs:: outputspec.highres2symmstandard : string (nifti file) Linear registration of T1 image to symmetric standard image outputspec.highres2symmstandard_mat : string (affine transformation .mat file) An affine transformation .mat file from linear registration and used in non linear registration outputspec.highres2symmstandard_warp : string (nifti file) warp file from Non Linear registration of T1 to symmetrical standard brain outputspec.fnirt_highres2symmstandard : string (nifti file) Non Linear registration of T1 to symmetrical standard brain outputspec.highres2symmstandard_jac : string (nifti file) jacobian determinant image from Non Linear registration of T1 to symmetrical standard brain outputspec.rest_res_2symmstandard : string (nifti file) nonlinear registration (func to standard) image outputspec.VMHC_FWHM_img : string (nifti file) pearson correlation between res2standard and flipped res2standard outputspec.VMHC_Z_FWHM_img : string (nifti file) Fisher Z transform map outputspec.VMHC_Z_stat_FWHM_img : string (nifti file) Z statistic map Order of commands: - Perform linear registration of Anatomical brain in T1 space to symmetric standard space. For details see `flirt <http://www.fmrib.ox.ac.uk/fsl/flirt/index.html>`_:: flirt -ref MNI152_T1_2mm_symmetric_brain.nii.gz -in mprage_brain.nii.gz -out highres2symmstandard.nii.gz -omat highres2symmstandard.mat -cost corratio -searchcost corratio -dof 12 -interp trilinear - Perform nonlinear registration (higres to standard) to symmetric standard brain. For details see `fnirt <http://fsl.fmrib.ox.ac.uk/fsl/fnirt/>`_:: fnirt --in=head.nii.gz --aff=highres2symmstandard.mat --cout=highres2symmstandard_warp.nii.gz --iout=fnirt_highres2symmstandard.nii.gz --jout=highres2symmstandard_jac.nii.gz --config=T1_2_MNI152_2mm_symmetric.cnf --ref=MNI152_T1_2mm_symmetric.nii.gz --refmask=MNI152_T1_2mm_brain_mask_symmetric_dil.nii.gz --warpres=10,10,10 - Perform spatial smoothing on the input functional image(inputspec.rest_res_filt). For details see `PrinciplesSmoothing <http://imaging.mrc-cbu.cam.ac.uk/imaging/PrinciplesSmoothing>`_ `fslmaths <http://www.fmrib.ox.ac.uk/fslcourse/lectures/practicals/intro/index.htm>`_:: fslmaths rest_res_filt.nii.gz -kernel gauss FWHM/ sqrt(8-ln(2)) -fmean -mas rest_mask.nii.gz rest_res_filt_FWHM.nii.gz - Apply nonlinear registration (func to standard). For details see `applywarp <http://www.fmrib.ox.ac.uk/fsl/fnirt/warp_utils.html#applywarp>`_:: applywarp --ref=MNI152_T1_2mm_symmetric.nii.gz --in=rest_res_filt_FWHM.nii.gz --out=rest_res_2symmstandard.nii.gz --warp=highres2symmstandard_warp.nii.gz --premat=example_func2highres.mat - Copy and L/R swap the output of applywarp command (rest_res_2symmstandard.nii.gz). For details see `fslswapdim <http://fsl.fmrib.ox.ac.uk/fsl/fsl4.0/avwutils/index.html>`_:: fslswapdim rest_res_2symmstandard.nii.gz -x y z tmp_LRflipped.nii.gz - Calculate pearson correlation between rest_res_2symmstandard.nii.gz and flipped rest_res_2symmstandard.nii.gz(tmp_LRflipped.nii.gz). For details see `3dTcorrelate <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTcorrelate.html>`_:: 3dTcorrelate -pearson -polort -1 -prefix VMHC_FWHM.nii.gz rest_res_2symmstandard.nii.gz tmp_LRflipped.nii.gz - Fisher Z Transform the correlation. For details see `3dcalc <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dcalc.html>`_:: 3dcalc -a VMHC_FWHM.nii.gz -expr 'log((a+1)/(1-a))/2' -prefix VMHC_FWHM_Z.nii.gz - Calculate the number of volumes(nvols) in flipped rest_res_2symmstandard.nii.gz(tmp_LRflipped.nii.gz) :: -Use Nibabel to do this - Compute the Z statistic map :: 3dcalc -a VMHC_FWHM_Z.nii.gz -expr 'a*sqrt('${nvols}'-3)' -prefix VMHC_FWHM_Z_stat.nii.gz Workflow: .. image:: ../images/vmhc_graph.dot.png :width: 500 Workflow Detailed: .. image:: ../images/vmhc_detailed_graph.dot.png :width: 500 References ---------- .. [1] Zuo, X.-N., Kelly, C., Di Martino, A., Mennes, M., Margulies, D. S., Bangaru, S., Grzadzinski, R., et al. (2010). Growing together and growing apart: regional and sex differences in the lifespan developmental trajectories of functional homotopy. The Journal of neuroscience : the official journal of the Society for Neuroscience, 30(45), 15034-43. doi:10.1523/JNEUROSCI.2612-10.2010 Examples -------- >>> vmhc_w = create_vmhc() >>> vmhc_w.inputs.inputspec.symmetric_brain = 'MNI152_T1_2mm_symmetric_brain.nii.gz' >>> vmhc_w.inputs.inputspec.symmetric_skull = 'MNI152_T1_2mm_symmetric.nii.gz' >>> vmhc_w.inputs.inputspec.twomm_brain_mask_dil = 'MNI152_T1_2mm_brain_mask_symmetric_dil.nii.gz' >>> vmhc_w.inputs.inputspec.config_file_twomm = 'T1_2_MNI152_2mm_symmetric.cnf' >>> vmhc_w.inputs.inputspec.standard_for_func= 'MNI152_T1_2mm.nii.gz' >>> vmhc_w.inputs.fwhm_input.fwhm = [4.5, 6] >>> vmhc_w.get_node('fwhm_input').iterables = ('fwhm', [4.5, 6]) >>> vmhc_w.inputs.inputspec.rest_res = os.path.abspath('/home/data/Projects/Pipelines_testing/Dickstein/subjects/s1001/func/original/rest_res_filt.nii.gz') >>> vmhc_w.inputs.inputspec.reorient = os.path.abspath('/home/data/Projects/Pipelines_testing/Dickstein/subjects/s1001/anat/mprage_RPI.nii.gz') >>> vmhc_w.inputs.inputspec.brain = os.path.abspath('/home/data/Projects/Pipelines_testing/Dickstein/subjects/s1001/anat/mprage_brain.nii.gz') >>> vmhc_w.inputs.inputspec.example_func2highres_mat = os.path.abspath('/home/data/Projects/Pipelines_testing/Dickstein/subjects/s1001/func/original/reg/example_func2highres.mat') >>> vmhc_w.inputs.inputspec.rest_mask = os.path.abspath('/home/data/Projects/Pipelines_testing/Dickstein/subjects/s1001/func/original/rest_mask.nii.gz') >>> vmhc_w.run() # doctest: +SKIP """ vmhc = pe.Workflow(name=name) inputNode = pe.Node(util.IdentityInterface(fields=[ 'rest_res', 'example_func2highres_mat', 'rest_mask', 'standard_for_func', 'mean_functional', 'brain', 'fnirt_nonlinear_warp', 'ants_symm_initial_xfm', 'ants_symm_rigid_xfm', 'ants_symm_affine_xfm', 'ants_symm_warp_field' ]), name='inputspec') outputNode = pe.Node(util.IdentityInterface(fields=[ 'rest_res_2symmstandard', 'VMHC_FWHM_img', 'VMHC_Z_FWHM_img', 'VMHC_Z_stat_FWHM_img' ]), name='outputspec') inputnode_fwhm = pe.Node(util.IdentityInterface(fields=['fwhm']), name='fwhm_input') if use_ants == False: # Apply nonlinear registration (func to standard) nonlinear_func_to_standard = pe.Node(interface=fsl.ApplyWarp(), name='nonlinear_func_to_standard') elif use_ants == True: # ANTS warp image etc. fsl_to_itk_vmhc = create_wf_c3d_fsl_to_itk(0, name='fsl_to_itk_vmhc') collect_transforms_vmhc = create_wf_collect_transforms( 0, name='collect_transforms_vmhc') apply_ants_xfm_vmhc = create_wf_apply_ants_warp( 0, name='apply_ants_xfm_vmhc', ants_threads=ants_threads) # this has to be 3 instead of default 0 because it is a 4D file apply_ants_xfm_vmhc.inputs.inputspec.input_image_type = 3 # copy and L/R swap file copy_and_L_R_swap = pe.Node(interface=fsl.SwapDimensions(), name='copy_and_L_R_swap') copy_and_L_R_swap.inputs.new_dims = ('-x', 'y', 'z') # calculate vmhc pearson_correlation = pe.Node(interface=preprocess.TCorrelate(), name='pearson_correlation') pearson_correlation.inputs.pearson = True pearson_correlation.inputs.polort = -1 pearson_correlation.inputs.outputtype = 'NIFTI_GZ' try: z_trans = pe.Node(interface=preprocess.Calc(), name='z_trans') z_stat = pe.Node(interface=preprocess.Calc(), name='z_stat') except AttributeError: from nipype.interfaces.afni import utils as afni_utils z_trans = pe.Node(interface=afni_utils.Calc(), name='z_trans') z_stat = pe.Node(interface=afni_utils.Calc(), name='z_stat') z_trans.inputs.expr = 'log((1+a)/(1-a))/2' z_trans.inputs.outputtype = 'NIFTI_GZ' z_stat.inputs.outputtype = 'NIFTI_GZ' NVOLS = pe.Node(util.Function(input_names=['in_files'], output_names=['nvols'], function=get_img_nvols), name='NVOLS') generateEXP = pe.Node(util.Function(input_names=['nvols'], output_names=['expr'], function=get_operand_expression), name='generateEXP') smooth = pe.Node(interface=fsl.MultiImageMaths(), name='smooth') if use_ants == False: vmhc.connect(inputNode, 'rest_res', smooth, 'in_file') vmhc.connect(inputnode_fwhm, ('fwhm', set_gauss), smooth, 'op_string') vmhc.connect(inputNode, 'rest_mask', smooth, 'operand_files') vmhc.connect(smooth, 'out_file', nonlinear_func_to_standard, 'in_file') vmhc.connect(inputNode, 'standard_for_func', nonlinear_func_to_standard, 'ref_file') vmhc.connect(inputNode, 'fnirt_nonlinear_warp', nonlinear_func_to_standard, 'field_file') ## func->anat matrix (bbreg) vmhc.connect(inputNode, 'example_func2highres_mat', nonlinear_func_to_standard, 'premat') vmhc.connect(nonlinear_func_to_standard, 'out_file', copy_and_L_R_swap, 'in_file') vmhc.connect(nonlinear_func_to_standard, 'out_file', pearson_correlation, 'xset') elif use_ants == True: # connections for ANTS stuff # functional apply warp stuff vmhc.connect(inputNode, 'rest_res', smooth, 'in_file') vmhc.connect(inputnode_fwhm, ('fwhm', set_gauss), smooth, 'op_string') vmhc.connect(inputNode, 'rest_mask', smooth, 'operand_files') vmhc.connect(smooth, 'out_file', apply_ants_xfm_vmhc, 'inputspec.input_image') vmhc.connect(inputNode, 'ants_symm_initial_xfm', collect_transforms_vmhc, 'inputspec.linear_initial') vmhc.connect(inputNode, 'ants_symm_rigid_xfm', collect_transforms_vmhc, 'inputspec.linear_rigid') vmhc.connect(inputNode, 'ants_symm_affine_xfm', collect_transforms_vmhc, 'inputspec.linear_affine') vmhc.connect(inputNode, 'ants_symm_warp_field', collect_transforms_vmhc, 'inputspec.warp_file') # func->anat matrix (bbreg) vmhc.connect(inputNode, 'example_func2highres_mat', fsl_to_itk_vmhc, 'inputspec.affine_file') vmhc.connect(inputNode, 'brain', fsl_to_itk_vmhc, 'inputspec.reference_file') vmhc.connect(inputNode, 'mean_functional', fsl_to_itk_vmhc, 'inputspec.source_file') vmhc.connect(fsl_to_itk_vmhc, 'outputspec.itk_transform', collect_transforms_vmhc, 'inputspec.fsl_to_itk_affine') vmhc.connect(inputNode, 'standard_for_func', apply_ants_xfm_vmhc, 'inputspec.reference_image') vmhc.connect(collect_transforms_vmhc, 'outputspec.transformation_series', apply_ants_xfm_vmhc, 'inputspec.transforms') vmhc.connect(apply_ants_xfm_vmhc, 'outputspec.output_image', copy_and_L_R_swap, 'in_file') vmhc.connect(apply_ants_xfm_vmhc, 'outputspec.output_image', pearson_correlation, 'xset') vmhc.connect(copy_and_L_R_swap, 'out_file', pearson_correlation, 'yset') vmhc.connect(pearson_correlation, 'out_file', z_trans, 'in_file_a') vmhc.connect(copy_and_L_R_swap, 'out_file', NVOLS, 'in_files') vmhc.connect(NVOLS, 'nvols', generateEXP, 'nvols') vmhc.connect(z_trans, 'out_file', z_stat, 'in_file_a') vmhc.connect(generateEXP, 'expr', z_stat, 'expr') if use_ants == False: vmhc.connect(nonlinear_func_to_standard, 'out_file', outputNode, 'rest_res_2symmstandard') elif use_ants == True: # ANTS warp outputs to outputnode vmhc.connect(apply_ants_xfm_vmhc, 'outputspec.output_image', outputNode, 'rest_res_2symmstandard') vmhc.connect(pearson_correlation, 'out_file', outputNode, 'VMHC_FWHM_img') vmhc.connect(z_trans, 'out_file', outputNode, 'VMHC_Z_FWHM_img') vmhc.connect(z_stat, 'out_file', outputNode, 'VMHC_Z_stat_FWHM_img') return vmhc
def create_anat_preproc(already_skullstripped=False): """ The main purpose of this workflow is to process T1 scans. Raw mprage file is deobliqued, reoriented into RPI and skullstripped. Also, a whole brain only mask is generated from the skull stripped image for later use in registration. Returns ------- anat_preproc : workflow Anatomical Preprocessing Workflow Notes ----- `Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/anat_preproc/anat_preproc.py>`_ Workflow Inputs:: inputspec.anat : mprage file or a list of mprage nifti file User input anatomical(T1) Image, in any of the 8 orientations Workflow Outputs:: outputspec.refit : nifti file Deobliqued anatomical data outputspec.reorient : nifti file RPI oriented anatomical data outputspec.skullstrip : nifti file Skull Stripped RPI oriented mprage file with normalized intensities. outputspec.brain : nifti file Skull Stripped RPI Brain Image with original intensity values and not normalized or scaled. Order of commands: - Deobliqing the scans. For details see `3drefit <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3drefit.html>`_:: 3drefit -deoblique mprage.nii.gz - Re-orienting the Image into Right-to-Left Posterior-to-Anterior Inferior-to-Superior (RPI) orientation. For details see `3dresample <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dresample.html>`_:: 3dresample -orient RPI -prefix mprage_RPI.nii.gz -inset mprage.nii.gz - SkullStripping the image. For details see `3dSkullStrip <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dSkullStrip.html>`_:: 3dSkullStrip -input mprage_RPI.nii.gz -o_ply mprage_RPI_3dT.nii.gz - The skull stripping step modifies the intensity values. To get back the original intensity values, we do an element wise product of RPI data with step function of skull Stripped data. For details see `3dcalc <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dcalc.html>`_:: 3dcalc -a mprage_RPI.nii.gz -b mprage_RPI_3dT.nii.gz -expr 'a*step(b)' -prefix mprage_RPI_3dc.nii.gz High Level Workflow Graph: .. image:: ../images/anatpreproc_graph.dot.png :width: 500 Detailed Workflow Graph: .. image:: ../images/anatpreproc_graph_detailed.dot.png :width: 500 Examples -------- >>> import anat >>> preproc = create_anat_preproc() >>> preproc.inputs.inputspec.anat='sub1/anat/mprage.nii.gz' >>> preproc.run() #doctest: +SKIP """ preproc = pe.Workflow(name='anat_preproc') inputNode = pe.Node(util.IdentityInterface(fields=['anat']), name='inputspec') outputNode = pe.Node(util.IdentityInterface( fields=['refit', 'reorient', 'skullstrip', 'brain']), name='outputspec') anat_deoblique = pe.Node(interface=preprocess.Refit(), name='anat_deoblique') anat_deoblique.inputs.deoblique = True anat_reorient = pe.Node(interface=preprocess.Resample(), name='anat_reorient') anat_reorient.inputs.orientation = 'RPI' anat_reorient.inputs.outputtype = 'NIFTI_GZ' if not already_skullstripped: anat_skullstrip = pe.Node(interface=preprocess.SkullStrip(), name='anat_skullstrip') #anat_skullstrip.inputs.options = '-o_ply' anat_skullstrip.inputs.outputtype = 'NIFTI_GZ' anat_skullstrip_orig_vol = pe.Node(interface=preprocess.Calc(), name='anat_skullstrip_orig_vol') anat_skullstrip_orig_vol.inputs.expr = 'a*step(b)' anat_skullstrip_orig_vol.inputs.outputtype = 'NIFTI_GZ' preproc.connect(inputNode, 'anat', anat_deoblique, 'in_file') preproc.connect(anat_deoblique, 'out_file', anat_reorient, 'in_file') if not already_skullstripped: preproc.connect(anat_reorient, 'out_file', anat_skullstrip, 'in_file') preproc.connect(anat_skullstrip, 'out_file', anat_skullstrip_orig_vol, 'in_file_b') else: preproc.connect(anat_reorient, 'out_file', anat_skullstrip_orig_vol, 'in_file_b') preproc.connect(anat_reorient, 'out_file', anat_skullstrip_orig_vol, 'in_file_a') preproc.connect(anat_deoblique, 'out_file', outputNode, 'refit') preproc.connect(anat_reorient, 'out_file', outputNode, 'reorient') if not already_skullstripped: preproc.connect(anat_skullstrip, 'out_file', outputNode, 'skullstrip') preproc.connect(anat_skullstrip_orig_vol, 'out_file', outputNode, 'brain') return preproc
def create_func_preproc(use_bet=False, wf_name='func_preproc'): """ The main purpose of this workflow is to process functional data. Raw rest file is deobliqued and reoriented into RPI. Then take the mean intensity values over all time points for each voxel and use this image to calculate motion parameters. The image is then skullstripped, normalized and a processed mask is obtained to use it further in Image analysis. Parameters ---------- wf_name : string Workflow name Returns ------- func_preproc : workflow object Functional Preprocessing workflow object Notes ----- `Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/func_preproc/func_preproc.py>`_ Workflow Inputs:: inputspec.rest : func/rest file or a list of func/rest nifti file User input functional(T2) Image, in any of the 8 orientations scan_params.tr : string Subject TR scan_params.acquistion : string Acquisition pattern (interleaved/sequential, ascending/descending) scan_params.ref_slice : integer Reference slice for slice timing correction Workflow Outputs:: outputspec.refit : string (nifti file) Path to deobliqued anatomical data outputspec.reorient : string (nifti file) Path to RPI oriented anatomical data outputspec.motion_correct_ref : string (nifti file) Path to Mean intensity Motion corrected image (base reference image for the second motion correction run) outputspec.motion_correct : string (nifti file) Path to motion corrected output file outputspec.max_displacement : string (Mat file) Path to maximum displacement (in mm) for brain voxels in each volume outputspec.movement_parameters : string (Mat file) Path to 1D file containing six movement/motion parameters(3 Translation, 3 Rotations) in different columns (roll pitch yaw dS dL dP) outputspec.skullstrip : string (nifti file) Path to skull stripped Motion Corrected Image outputspec.mask : string (nifti file) Path to brain-only mask outputspec.example_func : string (nifti file) Mean, Skull Stripped, Motion Corrected output T2 Image path (Image with mean intensity values across voxels) outputpsec.preprocessed : string (nifti file) output skull stripped, motion corrected T2 image with normalized intensity values outputspec.preprocessed_mask : string (nifti file) Mask obtained from normalized preprocessed image Order of commands: - Deobliqing the scans. For details see `3drefit <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3drefit.html>`_:: 3drefit -deoblique rest_3dc.nii.gz - Re-orienting the Image into Right-to-Left Posterior-to-Anterior Inferior-to-Superior (RPI) orientation. For details see `3dresample <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dresample.html>`_:: 3dresample -orient RPI -prefix rest_3dc_RPI.nii.gz -inset rest_3dc.nii.gz - Calculate voxel wise statistics. Get the RPI Image with mean intensity values over all timepoints for each voxel. For details see `3dTstat <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTstat.html>`_:: 3dTstat -mean -prefix rest_3dc_RPI_3dT.nii.gz rest_3dc_RPI.nii.gz - Motion Correction. For details see `3dvolreg <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dvolreg.html>`_:: 3dvolreg -Fourier -twopass -base rest_3dc_RPI_3dT.nii.gz/ -zpad 4 -maxdisp1D rest_3dc_RPI_3dvmd1D.1D -1Dfile rest_3dc_RPI_3dv1D.1D -prefix rest_3dc_RPI_3dv.nii.gz rest_3dc_RPI.nii.gz The base image or the reference image is the mean intensity RPI image obtained in the above the step.For each volume in RPI-oriented T2 image, the command, aligns the image with the base mean image and calculates the motion, displacement and movement parameters. It also outputs the aligned 4D volume and movement and displacement parameters for each volume. - Calculate voxel wise statistics. Get the motion corrected output Image from the above step, with mean intensity values over all timepoints for each voxel. For details see `3dTstat <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTstat.html>`_:: 3dTstat -mean -prefix rest_3dc_RPI_3dv_3dT.nii.gz rest_3dc_RPI_3dv.nii.gz - Motion Correction and get motion, movement and displacement parameters. For details see `3dvolreg <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dvolreg.html>`_:: 3dvolreg -Fourier -twopass -base rest_3dc_RPI_3dv_3dT.nii.gz -zpad 4 -maxdisp1D rest_3dc_RPI_3dvmd1D.1D -1Dfile rest_3dc_RPI_3dv1D.1D -prefix rest_3dc_RPI_3dv.nii.gz rest_3dc_RPI.nii.gz The base image or the reference image is the mean intensity motion corrected image obtained from the above the step (first 3dvolreg run). For each volume in RPI-oriented T2 image, the command, aligns the image with the base mean image and calculates the motion, displacement and movement parameters. It also outputs the aligned 4D volume and movement and displacement parameters for each volume. - Create a brain-only mask. For details see `3dautomask <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dAutomask.html>`_:: 3dAutomask -prefix rest_3dc_RPI_3dv_automask.nii.gz rest_3dc_RPI_3dv.nii.gz - Edge Detect(remove skull) and get the brain only. For details see `3dcalc <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dcalc.html>`_:: 3dcalc -a rest_3dc_RPI_3dv.nii.gz -b rest_3dc_RPI_3dv_automask.nii.gz -expr 'a*b' -prefix rest_3dc_RPI_3dv_3dc.nii.gz - Normalizing the image intensity values. For details see `fslmaths <http://www.fmrib.ox.ac.uk/fsl/avwutils/index.html>`_:: fslmaths rest_3dc_RPI_3dv_3dc.nii.gz -ing 10000 rest_3dc_RPI_3dv_3dc_maths.nii.gz -odt float Normalized intensity = (TrueValue*10000)/global4Dmean - Calculate mean of skull stripped image. For details see `3dTstat <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTstat.html>`_:: 3dTstat -mean -prefix rest_3dc_RPI_3dv_3dc_3dT.nii.gz rest_3dc_RPI_3dv_3dc.nii.gz - Create Mask (Generate mask from Normalized data). For details see `fslmaths <http://www.fmrib.ox.ac.uk/fsl/avwutils/index.html>`_:: fslmaths rest_3dc_RPI_3dv_3dc_maths.nii.gz -Tmin -bin rest_3dc_RPI_3dv_3dc_maths_maths.nii.gz -odt char High Level Workflow Graph: .. image:: ../images/func_preproc.dot.png :width: 1000 Detailed Workflow Graph: .. image:: ../images/func_preproc_detailed.dot.png :width: 1000 Examples -------- >>> import func_preproc >>> preproc = create_func_preproc(bet=True) >>> preproc.inputs.inputspec.func='sub1/func/rest.nii.gz' >>> preproc.run() #doctest: +SKIP >>> import func_preproc >>> preproc = create_func_preproc(bet=False) >>> preproc.inputs.inputspec.func='sub1/func/rest.nii.gz' >>> preproc.run() #doctest: +SKIP """ preproc = pe.Workflow(name=wf_name) inputNode = pe.Node(util.IdentityInterface(fields=['func']), name='inputspec') outputNode = pe.Node( util.IdentityInterface(fields=[ 'refit', 'reorient', 'reorient_mean', 'motion_correct', 'motion_correct_ref', 'movement_parameters', 'max_displacement', # 'xform_matrix', 'mask', 'skullstrip', 'example_func', 'preprocessed', 'preprocessed_mask', 'slice_time_corrected', 'oned_matrix_save' ]), name='outputspec') try: from nipype.interfaces.afni import utils as afni_utils func_deoblique = pe.Node(interface=afni_utils.Refit(), name='func_deoblique') except ImportError: func_deoblique = pe.Node(interface=preprocess.Refit(), name='func_deoblique') func_deoblique.inputs.deoblique = True preproc.connect(inputNode, 'func', func_deoblique, 'in_file') try: func_reorient = pe.Node(interface=afni_utils.Resample(), name='func_reorient') except UnboundLocalError: func_reorient = pe.Node(interface=preprocess.Resample(), name='func_reorient') func_reorient.inputs.orientation = 'RPI' func_reorient.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_deoblique, 'out_file', func_reorient, 'in_file') preproc.connect(func_reorient, 'out_file', outputNode, 'reorient') try: func_get_mean_RPI = pe.Node(interface=afni_utils.TStat(), name='func_get_mean_RPI') except UnboundLocalError: func_get_mean_RPI = pe.Node(interface=preprocess.TStat(), name='func_get_mean_RPI') func_get_mean_RPI.inputs.options = '-mean' func_get_mean_RPI.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_reorient, 'out_file', func_get_mean_RPI, 'in_file') # calculate motion parameters func_motion_correct = pe.Node(interface=preprocess.Volreg(), name='func_motion_correct') func_motion_correct.inputs.args = '-Fourier -twopass' func_motion_correct.inputs.zpad = 4 func_motion_correct.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_reorient, 'out_file', func_motion_correct, 'in_file') preproc.connect(func_get_mean_RPI, 'out_file', func_motion_correct, 'basefile') func_get_mean_motion = func_get_mean_RPI.clone('func_get_mean_motion') preproc.connect(func_motion_correct, 'out_file', func_get_mean_motion, 'in_file') preproc.connect(func_get_mean_motion, 'out_file', outputNode, 'motion_correct_ref') func_motion_correct_A = func_motion_correct.clone('func_motion_correct_A') func_motion_correct_A.inputs.md1d_file = 'max_displacement.1D' preproc.connect(func_reorient, 'out_file', func_motion_correct_A, 'in_file') preproc.connect(func_get_mean_motion, 'out_file', func_motion_correct_A, 'basefile') preproc.connect(func_motion_correct_A, 'out_file', outputNode, 'motion_correct') preproc.connect(func_motion_correct_A, 'md1d_file', outputNode, 'max_displacement') preproc.connect(func_motion_correct_A, 'oned_file', outputNode, 'movement_parameters') preproc.connect(func_motion_correct_A, 'oned_matrix_save', outputNode, 'oned_matrix_save') if not use_bet: func_get_brain_mask = pe.Node(interface=preprocess.Automask(), name='func_get_brain_mask') func_get_brain_mask.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_motion_correct_A, 'out_file', func_get_brain_mask, 'in_file') preproc.connect(func_get_brain_mask, 'out_file', outputNode, 'mask') else: func_get_brain_mask = pe.Node(interface=fsl.BET(), name='func_get_brain_mask_BET') func_get_brain_mask.inputs.mask = True func_get_brain_mask.inputs.functional = True erode_one_voxel = pe.Node(interface=fsl.ErodeImage(), name='erode_one_voxel') erode_one_voxel.inputs.kernel_shape = 'box' erode_one_voxel.inputs.kernel_size = 1.0 preproc.connect(func_motion_correct_A, 'out_file', func_get_brain_mask, 'in_file') preproc.connect(func_get_brain_mask, 'mask_file', erode_one_voxel, 'in_file') preproc.connect(erode_one_voxel, 'out_file', outputNode, 'mask') try: func_edge_detect = pe.Node(interface=afni_utils.Calc(), name='func_edge_detect') except UnboundLocalError: func_edge_detect = pe.Node(interface=preprocess.Calc(), name='func_edge_detect') func_edge_detect.inputs.expr = 'a*b' func_edge_detect.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_motion_correct_A, 'out_file', func_edge_detect, 'in_file_a') if not use_bet: preproc.connect(func_get_brain_mask, 'out_file', func_edge_detect, 'in_file_b') else: preproc.connect(erode_one_voxel, 'out_file', func_edge_detect, 'in_file_b') preproc.connect(func_edge_detect, 'out_file', outputNode, 'skullstrip') try: func_mean_skullstrip = pe.Node(interface=afni_utils.TStat(), name='func_mean_skullstrip') except UnboundLocalError: func_mean_skullstrip = pe.Node(interface=preprocess.TStat(), name='func_mean_skullstrip') func_mean_skullstrip.inputs.options = '-mean' func_mean_skullstrip.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_edge_detect, 'out_file', func_mean_skullstrip, 'in_file') preproc.connect(func_mean_skullstrip, 'out_file', outputNode, 'example_func') func_normalize = pe.Node(interface=fsl.ImageMaths(), name='func_normalize') func_normalize.inputs.op_string = '-ing 10000' func_normalize.inputs.out_data_type = 'float' preproc.connect(func_edge_detect, 'out_file', func_normalize, 'in_file') preproc.connect(func_normalize, 'out_file', outputNode, 'preprocessed') func_mask_normalize = pe.Node(interface=fsl.ImageMaths(), name='func_mask_normalize') func_mask_normalize.inputs.op_string = '-Tmin -bin' func_mask_normalize.inputs.out_data_type = 'char' preproc.connect(func_normalize, 'out_file', func_mask_normalize, 'in_file') preproc.connect(func_mask_normalize, 'out_file', outputNode, 'preprocessed_mask') return preproc
def create_wf_edit_func(wf_name="edit_func"): """ Workflow Inputs:: inputspec.func : func file or a list of func/rest nifti file User input functional(T2*) Image inputspec.start_idx : string Starting volume/slice of the functional image (optional) inputspec.stop_idx : string Last volume/slice of the functional image (optional) Workflow Outputs:: outputspec.edited_func : string (nifti file) Path to Output image with the initial few slices dropped Order of commands: - Get the start and the end volume index of the functional run. If not defined by the user, return the first and last volume. get_idx(in_files, stop_idx, start_idx) - Dropping the initial TRs. For details see `3dcalc <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dcalc.html>`_:: 3dcalc -a rest.nii.gz[4..299] -expr 'a' -prefix rest_3dc.nii.gz """ # allocate a workflow object try: preproc = pe.Workflow(name=wf_name) except: logger.info("Error allocating workflow %s." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise # configure the workflow's input spec try: inputNode = pe.Node( util.IdentityInterface(fields=['func', 'start_idx', 'stop_idx']), name='inputspec') except: logger.info("Error allocating inputspec (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise # configure the workflow's output spec try: outputNode = pe.Node(util.IdentityInterface(fields=['edited_func']), name='outputspec') except: logger.info("Error allocating output spec (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise # allocate a node to check that the requested edits are # reasonable given the data try: func_get_idx = pe.Node(util.Function( input_names=['in_files', 'stop_idx', 'start_idx'], output_names=['stopidx', 'startidx'], function=get_idx), name='func_get_idx') except: logger.info("Error allocating get_idx function node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise # wire in the func_get_idx node try: preproc.connect(inputNode, 'func', func_get_idx, 'in_files') except: logger.info( "Error connecting 'in_files' input to get_idx function node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise try: preproc.connect(inputNode, 'start_idx', func_get_idx, 'start_idx') except: logger.info( "Error connecting 'start_idx' input to get_idx function node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise try: preproc.connect(inputNode, 'stop_idx', func_get_idx, 'stop_idx') except: logger.info( "Error connecting 'stop_idx' input to get_idx function node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise # allocate a node to edit the functional file try: from nipype.interfaces.afni import utils as afni_utils func_drop_trs = pe.Node(interface=afni_utils.Calc(), name='func_drop_trs') except ImportError: func_drop_trs = pe.Node(interface=preprocess.Calc(), name='func_drop_trs') except: logger.info("Error allocating afni Calc node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise func_drop_trs.inputs.expr = 'a' func_drop_trs.inputs.outputtype = 'NIFTI_GZ' # wire in the inputs try: preproc.connect(inputNode, 'func', func_drop_trs, 'in_file_a') except: logger.info( "Error connecting 'in_file_a' input to afni Calc node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise try: preproc.connect(func_get_idx, 'startidx', func_drop_trs, 'start_idx') except: logger.info( "Error connecting 'start_idx' input to afni Calc node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise try: preproc.connect(func_get_idx, 'stopidx', func_drop_trs, 'stop_idx') except: logger.info( "Error connecting 'stop_idx' input to afni Calc node (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise try: # wire the output preproc.connect(func_drop_trs, 'out_file', outputNode, 'edited_func') except: logger.info("Error connecting output (wflow %s)." + \ " (%s:%d)" % (wf_name, dbg_file_lineno()[1])) raise return preproc
def func_preproc_workflow(workflow, resource_pool, config, name="_"): """Build and run a Nipype workflow to deoblique and reorient a functional scan from a NIFTI file. - This is a seminal workflow that can only take an input directly from disk (i.e. no Nipype workflow connections/pointers, and this is where the pipeline will actually begin). For the sake of building the pipeine in reverse, if this workflow is called when there is no input file available, this function will return the unmodified workflow and resource pool directly back. - In conjunction with the other workflow-building functions, if this function returns the workflow and resource pool unmodified, each function up will do the same until it reaches the top level, allowing the pipeline builder to continue "searching" for a base-level input without crashing at this one. Expected Resources in Resource Pool - functional_scan: The raw functional 4D timeseries in a NIFTI file. New Resources Added to Resource Pool - func_reorient: The deobliqued, reoriented functional timeseries. Workflow Steps 1. get_idx function node (if a start_idx and/or stop_idx is set in the configuration) to generate the volume range to keep in the timeseries 2. AFNI 3dcalc to drop volumes not included in the range (if a start_idx and/or stop_idx has been set in the configuration only) 3. AFNI 3drefit to deoblique the file 4. AFNI 3dresample to reorient the file to RPI :type workflow: Nipype workflow object :param workflow: A Nipype workflow object which can already contain other connected nodes; this function will insert the following workflow into this one provided. :type resource_pool: dict :param resource_pool: A dictionary defining input files and pointers to Nipype node outputs / workflow connections; the keys are the resource names. :type config: dict :param config: A dictionary defining the configuration settings for the workflow, such as directory paths or toggled options. :type name: str :param name: (default: "_") A string to append to the end of each node name. :rtype: Nipype workflow object :return: The Nipype workflow originally provided, but with this function's sub-workflow connected into it. :rtype: dict :return: The resource pool originally provided, but updated (if applicable) with the newest outputs and connections. """ import nipype.pipeline.engine as pe import nipype.interfaces.utility as util from nipype.interfaces.afni import preprocess if "functional_scan" not in resource_pool.keys(): return workflow, resource_pool if "start_idx" not in config.keys(): config["start_idx"] = 0 if "stop_idx" not in config.keys(): config["stop_idx"] = None drop_trs = False if (config["start_idx"] != 0) and (config["stop_idx"] != None): drop_trs = True func_get_idx = pe.Node(util.Function(input_names=['in_files', 'stop_idx', 'start_idx'], output_names=['stopidx', 'startidx'], function=get_idx), name='func_get_idx%s' % name) func_get_idx.inputs.in_files = resource_pool["functional_scan"] func_get_idx.inputs.start_idx = config["start_idx"] func_get_idx.inputs.stop_idx = config["stop_idx"] if drop_trs: func_drop_trs = pe.Node(interface=preprocess.Calc(), name='func_drop_trs%s' % name) func_drop_trs.inputs.in_file_a = resource_pool["functional_scan"] func_drop_trs.inputs.expr = 'a' func_drop_trs.inputs.outputtype = 'NIFTI_GZ' workflow.connect(func_get_idx, 'startidx', func_drop_trs, 'start_idx') workflow.connect(func_get_idx, 'stopidx', func_drop_trs, 'stop_idx') func_deoblique = pe.Node(interface=preprocess.Refit(), name='func_deoblique%s' % name) func_deoblique.inputs.deoblique = True if drop_trs: workflow.connect(func_drop_trs, 'out_file', func_deoblique, 'in_file') else: func_deoblique.inputs.in_file = resource_pool["functional_scan"] func_reorient = pe.Node(interface=preprocess.Resample(), name='func_reorient%s' % name) func_reorient.inputs.orientation = 'RPI' func_reorient.inputs.outputtype = 'NIFTI_GZ' workflow.connect(func_deoblique, 'out_file', func_reorient, 'in_file') resource_pool["func_reorient"] = (func_reorient, 'out_file') return workflow, resource_pool
def func_equilibrate(): ''' Workflow to get the scanner data ready. Anatomical and functional images are deobliqued. 5 TRs are removed from func data. inputs inputnode.verio_anat inputnode.verio_func inputnode.verio_func_se inputnode.verio_func_se_inv outputs outputnode.analyze_anat outputnode.analyze_func outputnode.analyze_func_se outputnode.analyze_func_se_inv ''' flow = Workflow('func_equilibrate') inputnode = Node(util.IdentityInterface( fields=['verio_func', 'verio_func_se', 'verio_func_seinv']), name='inputnode') outputnode = Node(util.IdentityInterface(fields=[ 'analyze_func', 'func_mask', 'analyze_func_se', 'analyze_func_seinv' ]), name='outputnode') ## functional image # 1. remove TRS remove_trs = Node(interface=preprocess.Calc(), name='func_drop_trs') remove_trs.inputs.start_idx = 5 remove_trs.inputs.stop_idx = 421 remove_trs.inputs.expr = 'a' remove_trs.inputs.outputtype = 'NIFTI_GZ' # 2. to RPI func_rpi = Node(interface=preprocess.Resample(), name='func_rpi') func_rpi.inputs.orientation = 'RPI' func_rpi.inputs.outputtype = 'NIFTI_GZ' # 3. func deoblique func_deoblique = Node(interface=preprocess.Refit(), name='func_deoblique') func_deoblique.inputs.deoblique = True flow.connect(inputnode, 'verio_func', remove_trs, 'in_file_a') flow.connect(remove_trs, 'out_file', func_rpi, 'in_file') flow.connect(func_rpi, 'out_file', func_deoblique, 'in_file') flow.connect(func_deoblique, 'out_file', outputnode, 'analyze_func') ########################################################################################################### ########################################################################################################### # se to RPI se_rpi = Node(interface=preprocess.Resample(), name='se_rpi') se_rpi.inputs.orientation = 'RPI' se_rpi.inputs.outputtype = 'NIFTI_GZ' # 3. func deoblique se_deoblique = Node(interface=preprocess.Refit(), name='se_deoblique') se_deoblique.inputs.deoblique = True flow.connect(inputnode, 'verio_func_se', se_rpi, 'in_file') flow.connect(se_rpi, 'out_file', se_deoblique, 'in_file') flow.connect(se_deoblique, 'out_file', outputnode, 'analyze_func_se') ########################################################################################################### ########################################################################################################### ########################################################################################################### # se_inv to RPI se_inv_rpi = Node(interface=preprocess.Resample(), name='seinv_rpi') se_inv_rpi.inputs.orientation = 'RPI' se_inv_rpi.inputs.outputtype = 'NIFTI_GZ' # 3. func deoblique se_inv_deoblique = Node(interface=preprocess.Refit(), name='seinv_deoblique') se_inv_deoblique.inputs.deoblique = True flow.connect(inputnode, 'verio_func_seinv', se_inv_rpi, 'in_file') flow.connect(se_inv_rpi, 'out_file', se_inv_deoblique, 'in_file') flow.connect(se_inv_deoblique, 'out_file', outputnode, 'analyze_func_seinv') return flow
def create_scrubbing_preproc(wf_name='scrubbing'): """ This workflow essentially takes the list of offending timepoints that are to be removed and removes it from the motion corrected input image. Also, it removes the information of discarded time points from the movement parameters file obtained during motion correction. Parameters ---------- wf_name : string Name of the workflow Returns ------- scrub : object Scrubbing workfow object Notes ----- `Source <https://github.com/openconnectome/C-PAC/blob/master/CPAC/scrubbing/scrubbing.py>`_ Workflow Inputs:: inputspec.frames_in_ID : string (mat file) path to file containing list of time points for which FD > threshold inputspec.movement_parameters : string (mat file) path to file containing 1D file containing six movement/motion parameters (3 Translation, 3 Rotations) in different columns inputspec.preprocessed : string (nifti file) preprocessed input image path Workflow Outputs:: outputspec.preprocessed : string (nifti file) preprocessed scrubbed output image outputspec.scrubbed_movement_parameters : string (mat file) path to 1D file containing six movement/motion parameters for the timepoints which are not discarded by scrubbing Order of Commands: - Remove all movement parameters for all the time frames other than those that are present in the frames_in_1D file - Remove the discarded timepoints from the input image. For details see `3dcalc <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dcalc.html>`_:: 3dcalc -a bandpassed_demeaned_filtered.nii.gz[0,1,5,6,7,8,9,10,15,16,17,18,19,20,24,25,287,288,289,290,291,292,293,294,295] -expr 'a' -prefix bandpassed_demeaned_filtered_3dc.nii.gz High Level Workflow Graph: .. image:: ../images/scrubbing.dot.png :width: 500 Detailed Workflow Graph: .. image:: ../images/scrubbing_detailed.dot.png :width: 500 Example ------- >>> import scrubbing >>> sc = scrubbing.create_scrubbing_preproc() >>> sc.inputs.inputspec.frames_in_ID = 'frames_in.1D' >>> sc.inputs.inputpsec.movement_parameters = 'rest_mc.1D' >>> sc.inputs.inputpsec.preprocessed = 'rest_pp.nii.gz' >>> sc.run() -- SKIP doctest """ scrub = pe.Workflow(name=wf_name) inputNode = pe.Node(util.IdentityInterface( fields=['frames_in_1D', 'movement_parameters', 'preprocessed']), name='inputspec') outputNode = pe.Node(util.IdentityInterface( fields=['preprocessed', 'scrubbed_movement_parameters']), name='outputspec') scrubbed_movement_parameters = pe.Node(util.Function( input_names=['infile_a', 'infile_b'], output_names=['out_file'], function=get_mov_parameters), name='scrubbed_movement_parameters') scrubbed_preprocessed = pe.Node(interface=e_afni.Calc(), name='scrubbed_preprocessed') scrubbed_preprocessed.inputs.expr = 'a' scrubbed_preprocessed.inputs.outputtype = 'NIFTI_GZ' scrub.connect(inputNode, 'preprocessed', scrubbed_preprocessed, 'in_file_a') #scrub.connect(inputNode, ('frames_in_1D', get_indx), scrubbed_preprocessed, 'list_idx') scrub.connect(inputNode, 'movement_parameters', scrubbed_movement_parameters, 'infile_b') scrub.connect(inputNode, 'frames_in_1D', scrubbed_movement_parameters, 'infile_a') scrub.connect(scrubbed_preprocessed, 'out_file', outputNode, 'preprocessed') scrub.connect(scrubbed_movement_parameters, 'out_file', outputNode, 'scrubbed_movement_parameters') return scrub
def invert_functional_brain_mask_workflow(workflow, resource_pool, config, name="_"): """Build and run a Nipype workflow to generate a background mask of a functional scan (the inversion of the functional brain mask) using AFNI's 3dCalc. - If any resources/outputs required by this workflow are not in the resource pool, this workflow will call pre-requisite workflow builder functions to further populate the pipeline with workflows which will calculate/generate these necessary pre-requisites. Expected Resources in Resource Pool - functional_brain_mask: The binary brain mask of the functional time series. New Resources Added to Resource Pool - inverted_functional_brain_mask: The inversion of the functional brain mask, a binary brain mask of the background of the functional time series. Workflow Steps: 1. AFNI's 3dcalc to invert the functional brain mask :type workflow: Nipype workflow object :param workflow: A Nipype workflow object which can already contain other connected nodes; this function will insert the following workflow into this one provided. :type resource_pool: dict :param resource_pool: A dictionary defining input files and pointers to Nipype node outputs / workflow connections; the keys are the resource names. :type config: dict :param config: A dictionary defining the configuration settings for the workflow, such as directory paths or toggled options. :type name: str :param name: (default: "_") A string to append to the end of each node name. :rtype: Nipype workflow object :return: The Nipype workflow originally provided, but with this function's sub-workflow connected into it. :rtype: dict :return: The resource pool originally provided, but updated (if applicable) with the newest outputs and connections. """ import copy import nipype.pipeline.engine as pe from nipype.interfaces.afni import preprocess if "functional_brain_mask" not in resource_pool.keys(): from functional_preproc import functional_brain_mask_workflow old_rp = copy.copy(resource_pool) workflow, resource_pool = \ functional_brain_mask_workflow(workflow, resource_pool, config, name) if resource_pool == old_rp: return workflow, resource_pool # 3dcalc to invert the binary functional brain mask invert_mask = pe.Node(interface=preprocess.Calc(), name='invert_mask%s' % name) invert_mask.inputs.expr = "iszero(a)" invert_mask.inputs.outputtype = "NIFTI_GZ" # functional_brain_mask -> 3dcalc if len(resource_pool["functional_brain_mask"]) == 2: node, out_file = resource_pool["functional_brain_mask"] workflow.connect(node, out_file, invert_mask, 'in_file_a') else: invert_mask.inputs.in_file_a = resource_pool["functional_brain_mask"] resource_pool["inverted_functional_brain_mask"] = (invert_mask, 'out_file') return workflow, resource_pool
def afni_segmentation_workflow(workflow, resource_pool, config, name="_"): """Build a Nipype workflow to generate anatomical tissue segmentation maps using AFNI's 3dSeg. - If any resources/outputs required by this workflow are not in the resource pool, this workflow will call pre-requisite workflow builder functions to further populate the pipeline with workflows which will calculate/generate these necessary pre-requisites. Expected Resources in Resource Pool anatomical_brain: The skull-stripped anatomical image (brain only). New Resources Added to Resource Pool anatomical_csf_mask: The binary mask mapping the CSF voxels. anatomical_gm_mask: The binary mask mapping the gray matter voxels. anatomical_wm_mask: The binary mask mapping the white matter voxels. Workflow Steps 1. AFNI 3dSeg to run tissue segmentation on the anatomical brain. 2. AFNI 3dAFNItoNIFTI to convert the AFNI-format 3dSeg output into a NIFTI file (as of Oct 2016 3dSeg cannot be configured to write to NIFTI). 3. AFNI 3dcalc to separate the three masks within the output file into three separate images. :type workflow: Nipype workflow object :param workflow: A Nipype workflow object which can already contain other connected nodes; this function will insert the following workflow into this one provided. :type resource_pool: dict :param resource_pool: A dictionary defining input files and pointers to Nipype node outputs / workflow connections; the keys are the resource names. :type config: dict :param config: A dictionary defining the configuration settings for the workflow, such as directory paths or toggled options. :type name: str :param name: (default: "_") A string to append to the end of each node name. :rtype: Nipype workflow object :return: The Nipype workflow originally provided, but with this function's sub-workflow connected into it. :rtype: dict :return: The resource pool originally provided, but updated (if applicable) with the newest outputs and connections. """ import copy import nipype.pipeline.engine as pe from nipype.interfaces.afni import preprocess if "anatomical_brain" not in resource_pool.keys(): from anatomical_preproc import anatomical_skullstrip_workflow old_rp = copy.copy(resource_pool) workflow, new_resource_pool = \ anatomical_skullstrip_workflow(workflow, resource_pool, config, name) if resource_pool == old_rp: return workflow, resource_pool segment = pe.Node(interface=preprocess.Seg(), name='segmentation%s' % name) segment.inputs.mask = 'AUTO' if len(resource_pool["anatomical_brain"]) == 2: node, out_file = resource_pool["anatomical_brain"] workflow.connect(node, out_file, segment, 'in_file') else: segment.inputs.in_file = resource_pool["anatomical_brain"] # output processing AFNItoNIFTI = pe.Node(interface=preprocess.AFNItoNIFTI(), name="segment_AFNItoNIFTI%s" % name) AFNItoNIFTI.inputs.out_file = "classes.nii.gz" workflow.connect(segment, 'out_file', AFNItoNIFTI, 'in_file') # break out each of the three tissue types into # three separate NIFTI files extract_CSF = pe.Node(interface=preprocess.Calc(), name='extract_CSF_mask%s' % name) extract_CSF.inputs.expr = "within(a,1,1)" extract_CSF.inputs.out_file = "anatomical_csf_mask.nii.gz" extract_GM = pe.Node(interface=preprocess.Calc(), name='extract_GM_mask%s' % name) extract_GM.inputs.expr = "within(a,2,2)" extract_GM.inputs.out_file = "anatomical_gm_mask.nii.gz" extract_WM = pe.Node(interface=preprocess.Calc(), name='extract_WM_mask%s' % name) extract_WM.inputs.expr = "within(a,3,3)" extract_WM.inputs.out_file = "anatomical_wm_mask.nii.gz" workflow.connect(AFNItoNIFTI, 'out_file', extract_CSF, 'in_file_a') workflow.connect(AFNItoNIFTI, 'out_file', extract_GM, 'in_file_a') workflow.connect(AFNItoNIFTI, 'out_file', extract_WM, 'in_file_a') resource_pool["anatomical_csf_mask"] = (extract_CSF, 'out_file') resource_pool["anatomical_gm_mask"] = (extract_GM, 'out_file') resource_pool["anatomical_wm_mask"] = (extract_WM, 'out_file') return workflow, resource_pool
def create_alff(wf_name='alff_workflow'): """ Calculate Amplitude of low frequency oscillations (ALFF) and fractional ALFF maps Parameters ---------- wf_name : string Workflow name Returns ------- alff_workflow : workflow object ALFF workflow Notes ----- `Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/alff/alff.py>`_ Workflow Inputs:: hp_input.hp : list of float high pass frequencies lp_input.lp : list of float low pass frequencies inputspec.rest_res : string Path to existing Nifti file. Nuisance signal regressed functional image. inputspec.rest_mask : string Path to existing Nifti file. A mask volume(derived by dilating the motion corrected functional volume) in native space Workflow Outputs:: outputspec.alff_img : string Path to Nifti file. Image containing the sum of the amplitudes in the low frequency band outputspec.falff_img : string Path to Nifti file. Image containing the sum of the amplitudes in the low frequency band divided by the amplitude of the total frequency outputspec.alff_Z_img : string Path to Nifti file. Image containing Normalized ALFF Z scores across full brain in native space outputspec.falff_Z_img : string Path to Nifti file. Image containing Normalized fALFF Z scores across full brain in native space Order of Commands: - Filter the input file rest file( slice-time, motion corrected and nuisance regressed) :: 3dBandpass -prefix residual_filtered.nii.gz 0.009 0.08 residual.nii.gz - Calculate ALFF by taking the standard deviation of the filtered file :: 3dTstat -stdev -mask rest_mask.nii.gz -prefix residual_filtered_3dT.nii.gz residual_filtered.nii.gz - Calculate the standard deviation of the unfiltered file :: 3dTstat -stdev -mask rest_mask.nii.gz -prefix residual_3dT.nii.gz residual.nii.gz - Calculate fALFF :: 3dcalc -a rest_mask.nii.gz -b residual_filtered_3dT.nii.gz -c residual_3dT.nii.gz -expr '(1.0*bool(a))*((1.0*b)/(1.0*c))' -float - Normalize ALFF/fALFF to Z-score across full brain :: fslstats ALFF.nii.gz -k rest_mask.nii.gz -m > mean_ALFF.txt ; mean=$( cat mean_ALFF.txt ) fslstats ALFF.nii.gz -k rest_mask.nii.gz -s > std_ALFF.txt ; std=$( cat std_ALFF.txt ) fslmaths ALFF.nii.gz -sub ${mean} -div ${std} -mas rest_mask.nii.gz ALFF_Z.nii.gz fslstats fALFF.nii.gz -k rest_mask.nii.gz -m > mean_fALFF.txt ; mean=$( cat mean_fALFF.txt ) fslstats fALFF.nii.gz -k rest_mask.nii.gz -s > std_fALFF.txt std=$( cat std_fALFF.txt ) fslmaths fALFF.nii.gz -sub ${mean} -div ${std} -mas rest_mask.nii.gz fALFF_Z.nii.gz High Level Workflow Graph: .. image:: ../images/alff.dot.png :width: 500 Detailed Workflow Graph: .. image:: ../images/alff_detailed.dot.png :width: 500 References ---------- .. [1] Zou, Q.-H., Zhu, C.-Z., Yang, Y., Zuo, X.-N., Long, X.-Y., Cao, Q.-J., Wang, Y.-F., et al. (2008). An improved approach to detection of amplitude of low-frequency fluctuation (ALFF) for resting-state fMRI: fractional ALFF. Journal of neuroscience methods, 172(1), 137-41. doi:10.10 Examples -------- >>> alff_w = create_alff() >>> alff_w.inputs.hp_input.hp = [0.01] >>> alff_w.inputs.lp_input.lp = [0.1] >>> alff_w.get_node('hp_input').iterables = ('hp', [0.01]) >>> alff_w.get_node('lp_input').iterables = ('lp', [0.1]) >>> alff_w.inputs.inputspec.rest_res = '/home/data/subject/func/rest_bandpassed.nii.gz' >>> alff_w.inputs.inputspec.rest_mask= '/home/data/subject/func/rest_mask.nii.gz' >>> alff_w.run() # doctest: +SKIP """ wf = pe.Workflow(name=wf_name) input_node = pe.Node( util.IdentityInterface(fields=['rest_res', 'rest_mask']), name='inputspec') input_node_hp = pe.Node(util.IdentityInterface(fields=['hp']), name='hp_input') input_node_lp = pe.Node(util.IdentityInterface(fields=['lp']), name='lp_input') output_node = pe.Node( util.IdentityInterface(fields=['alff_img', 'falff_img']), name='outputspec') # filtering bandpass = pe.Node(interface=preprocess.Bandpass(), name='bandpass_filtering') bandpass.inputs.outputtype = 'NIFTI_GZ' bandpass.inputs.out_file = os.path.join(os.path.curdir, 'residual_filtered.nii.gz') wf.connect(input_node_hp, 'hp', bandpass, 'highpass') wf.connect(input_node_lp, 'lp', bandpass, 'lowpass') wf.connect(input_node, 'rest_res', bandpass, 'in_file') get_option_string = pe.Node(util.Function(input_names=['mask'], output_names=['option_string'], function=get_opt_string), name='get_option_string') wf.connect(input_node, 'rest_mask', get_option_string, 'mask') # standard deviation over frequency try: from nipype.interfaces.afni import utils as afni_utils stddev_filtered = pe.Node(interface=afni_utils.TStat(), name='stddev_filtered') except ImportError: stddev_filtered = pe.Node(interface=preprocess.TStat(), name='stddev_filtered') stddev_filtered.inputs.outputtype = 'NIFTI_GZ' stddev_filtered.inputs.out_file = os.path.join(os.path.curdir, 'alff.nii.gz') wf.connect(bandpass, 'out_file', stddev_filtered, 'in_file') wf.connect(get_option_string, 'option_string', stddev_filtered, 'options') wf.connect(stddev_filtered, 'out_file', output_node, 'alff_img') # standard deviation of the unfiltered nuisance corrected image try: stddev_unfiltered = pe.Node(interface=afni_utils.TStat(), name='stddev_unfiltered') except UnboundLocalError: stddev_unfiltered = pe.Node(interface=preprocess.TStat(), name='stddev_unfiltered') stddev_unfiltered.inputs.outputtype = 'NIFTI_GZ' stddev_unfiltered.inputs.out_file = os.path.join(os.path.curdir, 'residual_3dT.nii.gz') wf.connect(input_node, 'rest_res', stddev_unfiltered, 'in_file') wf.connect(get_option_string, 'option_string', stddev_unfiltered, 'options') # falff calculations try: falff = pe.Node(interface=afni_utils.Calc(), name='falff') except UnboundLocalError: falff = pe.Node(interface=preprocess.Calc(), name='falff') falff.inputs.args = '-float' falff.inputs.expr = '(1.0*bool(a))*((1.0*b)/(1.0*c))' falff.inputs.outputtype = 'NIFTI_GZ' falff.inputs.out_file = os.path.join(os.path.curdir, 'falff.nii.gz') wf.connect(input_node, 'rest_mask', falff, 'in_file_a') wf.connect(stddev_filtered, 'out_file', falff, 'in_file_b') wf.connect(stddev_unfiltered, 'out_file', falff, 'in_file_c') wf.connect(falff, 'out_file', output_node, 'falff_img') return wf
def create_bo_func_preproc(slice_timing_correction = False, wf_name = 'bo_func_preproc'): """ The main purpose of this workflow is to process functional data. Raw rest file is deobliqued and reoriented into RPI. Then take the mean intensity values over all time points for each voxel and use this image to calculate motion parameters. The image is then skullstripped, normalized and a processed mask is obtained to use it further in Image analysis. Parameters ---------- slice_timing_correction : boolean Slice timing Correction option wf_name : string Workflow name Returns ------- func_preproc : workflow object Functional Preprocessing workflow object Notes ----- `Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/func_preproc/func_preproc.py>`_ Workflow Inputs:: inputspec.rest : func/rest file or a list of func/rest nifti file User input functional(T2) Image, in any of the 8 orientations inputspec.start_idx : string Starting volume/slice of the functional image (optional) inputspec.stop_idx : string Last volume/slice of the functional image (optional) scan_params.tr : string Subject TR scan_params.acquistion : string Acquisition pattern (interleaved/sequential, ascending/descending) scan_params.ref_slice : integer Reference slice for slice timing correction Workflow Outputs:: outputspec.drop_tr : string (nifti file) Path to Output image with the initial few slices dropped outputspec.slice_time_corrected : string (nifti file) Path to Slice time corrected image outputspec.refit : string (nifti file) Path to deobliqued anatomical data outputspec.reorient : string (nifti file) Path to RPI oriented anatomical data outputspec.motion_correct_ref : string (nifti file) Path to Mean intensity Motion corrected image (base reference image for the second motion correction run) outputspec.motion_correct : string (nifti file) Path to motion corrected output file outputspec.max_displacement : string (Mat file) Path to maximum displacement (in mm) for brain voxels in each volume outputspec.movement_parameters : string (Mat file) Path to 1D file containing six movement/motion parameters(3 Translation, 3 Rotations) in different columns (roll pitch yaw dS dL dP) outputspec.skullstrip : string (nifti file) Path to skull stripped Motion Corrected Image outputspec.mask : string (nifti file) Path to brain-only mask outputspec.example_func : string (nifti file) Mean, Skull Stripped, Motion Corrected output T2 Image path (Image with mean intensity values across voxels) outputpsec.preprocessed : string (nifti file) output skull stripped, motion corrected T2 image with normalized intensity values outputspec.preprocessed_mask : string (nifti file) Mask obtained from normalized preprocessed image Order of commands: - Get the start and the end volume index of the functional run. If not defined by the user, return the first and last volume. get_idx(in_files, stop_idx, start_idx) - Dropping the initial TRs. For details see `3dcalc <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dcalc.html>`_:: 3dcalc -a rest.nii.gz[4..299] -expr 'a' -prefix rest_3dc.nii.gz - Slice timing correction. For details see `3dshift <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTshift.html>`_:: 3dTshift -TR 2.1s -slice 18 -tpattern alt+z -prefix rest_3dc_shift.nii.gz rest_3dc.nii.gz - Deobliqing the scans. For details see `3drefit <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3drefit.html>`_:: 3drefit -deoblique rest_3dc.nii.gz - Re-orienting the Image into Right-to-Left Posterior-to-Anterior Inferior-to-Superior (RPI) orientation. For details see `3dresample <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dresample.html>`_:: 3dresample -orient RPI -prefix rest_3dc_RPI.nii.gz -inset rest_3dc.nii.gz - Calculate voxel wise statistics. Get the RPI Image with mean intensity values over all timepoints for each voxel. For details see `3dTstat <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTstat.html>`_:: 3dTstat -mean -prefix rest_3dc_RPI_3dT.nii.gz rest_3dc_RPI.nii.gz - Motion Correction. For details see `3dvolreg <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dvolreg.html>`_:: 3dvolreg -Fourier -twopass -base rest_3dc_RPI_3dT.nii.gz/ -zpad 4 -maxdisp1D rest_3dc_RPI_3dvmd1D.1D -1Dfile rest_3dc_RPI_3dv1D.1D -prefix rest_3dc_RPI_3dv.nii.gz rest_3dc_RPI.nii.gz The base image or the reference image is the mean intensity RPI image obtained in the above the step.For each volume in RPI-oriented T2 image, the command, aligns the image with the base mean image and calculates the motion, displacement and movement parameters. It also outputs the aligned 4D volume and movement and displacement parameters for each volume. - Calculate voxel wise statistics. Get the motion corrected output Image from the above step, with mean intensity values over all timepoints for each voxel. For details see `3dTstat <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTstat.html>`_:: 3dTstat -mean -prefix rest_3dc_RPI_3dv_3dT.nii.gz rest_3dc_RPI_3dv.nii.gz - Motion Correction and get motion, movement and displacement parameters. For details see `3dvolreg <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dvolreg.html>`_:: 3dvolreg -Fourier -twopass -base rest_3dc_RPI_3dv_3dT.nii.gz -zpad 4 -maxdisp1D rest_3dc_RPI_3dvmd1D.1D -1Dfile rest_3dc_RPI_3dv1D.1D -prefix rest_3dc_RPI_3dv.nii.gz rest_3dc_RPI.nii.gz The base image or the reference image is the mean intensity motion corrected image obtained from the above the step (first 3dvolreg run). For each volume in RPI-oriented T2 image, the command, aligns the image with the base mean image and calculates the motion, displacement and movement parameters. It also outputs the aligned 4D volume and movement and displacement parameters for each volume. - Unwarp the motion corrected using a regularized B0 field map that is in RPI space, one of the outputs from the calib_preproc using FSL FUGUE fugue --nocheck=on -i rest_3dc_RPI_3dv.nii.gz --loadfmap=cal_reg_bo_RPI --unwarpdir=x --dwell=1 -u rest_3dc_RPI_3dv_unwarped.nii.gz - Create a brain-only mask. For details see `3dautomask <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dAutomask.html>`_:: 3dAutomask -prefix rest_3dc_RPI_3dv_unwarped_automask.nii.gz rest_3dc_RPI_3dv_unwarped.nii.gz - Edge Detect(remove skull) and get the brain only. For details see `3dcalc <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dcalc.html>`_:: 3dcalc -a rest_3dc_RPI_3dv_unwarped.nii.gz -b rest_3dc_RPI_3dv_unwarped_automask.nii.gz -expr 'a*b' -prefix rest_3dc_RPI_3dv_unwarped_3dc.nii.gz - Normalizing the image intensity values. For details see `fslmaths <http://www.fmrib.ox.ac.uk/fsl/avwutils/index.html>`_:: fslmaths rest_3dc_RPI_3dv_unwarped_3dc.nii.gz -ing 10000 rest_3dc_RPI_3dv_unwarped_3dc_maths.nii.gz -odt float Normalized intensity = (TrueValue*10000)/global4Dmean - Calculate mean of skull stripped image. For details see `3dTstat <http://afni.nimh.nih.gov/pub/dist/doc/program_help/3dTstat.html>`_:: 3dTstat -mean -prefix rest_3dc_RPI_3dv_unwarped_3dc_3dT.nii.gz rest_3dc_RPI_3dv_unwarped_3dc.nii.gz - Create Mask (Generate mask from Normalized data). For details see `fslmaths <http://www.fmrib.ox.ac.uk/fsl/avwutils/index.html>`_:: fslmaths rest_3dc_RPI_3dv_unwarped_3dc_maths.nii.gz -Tmin -bin rest_3dc_RPI_3dv_unwarped_3dc_maths_maths.nii.gz -odt char High Level Workflow Graph: .. image:: ../images/func_preproc.dot.png :width: 1000 Detailed Workflow Graph: .. image:: ../images/func_preproc_detailed.dot.png :width: 1000 Examples -------- >>> from func_preproc import * >>> preproc = create_func_preproc(slice_timing_correction=True) >>> preproc.inputs.inputspec.func='sub1/func/rest.nii.gz' >>> preproc.inputs.scan_params.TR = '2.0' >>> preproc.inputs.scan_params.ref_slice = 19 >>> preproc.inputs.scan_params.acquisition = 'alt+z2' >>> preproc.run() #doctest: +SKIP >>> from func_preproc import * >>> preproc = create_func_preproc(slice_timing_correction=False) >>> preproc.inputs.inputspec.func='sub1/func/rest.nii.gz' >>> preproc.inputs.inputspec.start_idx = 4 >>> preproc.inputs.inputspec.stop_idx = 250 >>> preproc.run() #doctest: +SKIP """ preproc = pe.Workflow(name=wf_name) inputNode = pe.Node(util.IdentityInterface(fields=['rest', 'calib_reg_bo_RPI', 'start_idx', 'stop_idx']), name='inputspec') scan_params = pe.Node(util.IdentityInterface(fields=['tr', 'acquisition', 'ref_slice']), name = 'scan_params') outputNode = pe.Node(util.IdentityInterface(fields=['drop_tr', 'refit', 'reorient', 'reorient_mean', 'motion_correct', 'motion_correct_ref', 'movement_parameters', 'max_displacement', 'bo_unwarped', 'mask', 'mask_preunwarp', 'skullstrip', 'example_func', 'preprocessed', 'preprocessed_mask', 'slice_time_corrected']), name='outputspec') func_get_idx = pe.Node(util.Function(input_names=['in_files', 'stop_idx', 'start_idx'], output_names=['stopidx', 'startidx'], function=get_idx), name='func_get_idx') preproc.connect(inputNode, 'rest', func_get_idx, 'in_files') preproc.connect(inputNode, 'start_idx', func_get_idx, 'start_idx') preproc.connect(inputNode, 'stop_idx', func_get_idx, 'stop_idx') func_drop_trs = pe.Node(interface=preprocess.Calc(), name='func_drop_trs') func_drop_trs.inputs.expr = 'a' func_drop_trs.inputs.outputtype = 'NIFTI_GZ' preproc.connect(inputNode, 'rest', func_drop_trs, 'in_file_a') preproc.connect(func_get_idx, 'startidx', func_drop_trs, 'start_idx') preproc.connect(func_get_idx, 'stopidx', func_drop_trs, 'stop_idx') preproc.connect(func_drop_trs, 'out_file', outputNode, 'drop_tr') func_slice_timing_correction = pe.Node(interface=preprocess.TShift(), name = 'func_slice_timing_correction') func_slice_timing_correction.inputs.outputtype = 'NIFTI_GZ' func_deoblique = pe.Node(interface=preprocess.Refit(), name='func_deoblique') func_deoblique.inputs.deoblique = True if slice_timing_correction: preproc.connect(func_drop_trs, 'out_file', func_slice_timing_correction,'in_file') preproc.connect(scan_params, 'tr', func_slice_timing_correction, 'tr') preproc.connect(scan_params, 'acquisition', func_slice_timing_correction, 'tpattern') preproc.connect(scan_params, 'ref_slice', func_slice_timing_correction, 'tslice') preproc.connect(func_slice_timing_correction, 'out_file', func_deoblique, 'in_file') preproc.connect(func_slice_timing_correction, 'out_file', outputNode, 'slice_time_corrected') else: preproc.connect(func_drop_trs, 'out_file', func_deoblique, 'in_file') preproc.connect(func_deoblique, 'out_file', outputNode, 'refit') func_reorient = pe.Node(interface=preprocess.Resample(), name='func_reorient') func_reorient.inputs.orientation = 'RPI' func_reorient.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_deoblique, 'out_file', func_reorient, 'in_file') preproc.connect(func_reorient, 'out_file', outputNode, 'reorient') func_get_mean_RPI = pe.Node(interface=preprocess.TStat(), name='func_get_mean_RPI') func_get_mean_RPI.inputs.options = '-mean' func_get_mean_RPI.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_reorient, 'out_file', func_get_mean_RPI, 'in_file') #calculate motion parameters func_motion_correct = pe.Node(interface=preprocess.Volreg(), name='func_motion_correct') func_motion_correct.inputs.args = '-Fourier -twopass' func_motion_correct.inputs.zpad = 4 func_motion_correct.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_reorient, 'out_file', func_motion_correct, 'in_file') preproc.connect(func_get_mean_RPI, 'out_file', func_motion_correct, 'basefile') func_get_mean_motion = func_get_mean_RPI.clone('func_get_mean_motion') preproc.connect(func_motion_correct, 'out_file', func_get_mean_motion, 'in_file') preproc.connect(func_get_mean_motion, 'out_file', outputNode, 'motion_correct_ref') func_motion_correct_A = func_motion_correct.clone('func_motion_correct_A') func_motion_correct_A.inputs.md1d_file = 'max_displacement.1D' preproc.connect(func_reorient, 'out_file', func_motion_correct_A, 'in_file') preproc.connect(func_get_mean_motion, 'out_file', func_motion_correct_A, 'basefile') preproc.connect(func_motion_correct_A, 'out_file', outputNode, 'motion_correct') preproc.connect(func_motion_correct_A, 'md1d_file', outputNode, 'max_displacement') preproc.connect(func_motion_correct_A, 'oned_file', outputNode, 'movement_parameters') func_get_brain_mask = pe.Node(interface=preprocess.Automask(), name='func_get_brain_mask') # func_get_brain_mask.inputs.dilate = 1 func_get_brain_mask.inputs.outputtype = 'NIFTI_GZ' #------------------------- func_bo_unwarp = pe.Node(interface=fsl.FUGUE(),name='bo_unwarp') #func_bo_unwarp.inputs.in_file='lfo_mc' func_bo_unwarp.inputs.args='--nocheck=on' #func_bo_unwarp.inputs.fmap_in_file='calib' func_bo_unwarp.inputs.dwell_time=1.0 func_bo_unwarp.inputs.unwarp_direction='x' preproc.connect(inputNode, 'calib_reg_bo_RPI', func_bo_unwarp, 'fmap_in_file') preproc.connect(func_motion_correct_A, 'out_file', func_bo_unwarp, 'in_file') preproc.connect(func_bo_unwarp, 'unwarped_file', outputNode, 'bo_unwarped') preproc.connect(func_bo_unwarp, 'unwarped_file', func_get_brain_mask, 'in_file') #-------------------------- preproc.connect(func_get_brain_mask, 'out_file', outputNode, 'mask') #------------ ALSO give the example_func_preunwarp func_get_brain_mask_A = func_get_brain_mask.clone('func_get_brain_mask_A') preproc.connect(func_motion_correct_A, 'out_file', func_get_brain_mask_A, 'in_file') preproc.connect(func_get_brain_mask_A, 'out_file', outputNode, 'mask_preunwarp') func_edge_detect = pe.Node(interface=preprocess.Calc(), name='func_edge_detect') func_edge_detect.inputs.expr = 'a*b' func_edge_detect.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_bo_unwarp, 'unwarped_file', func_edge_detect, 'in_file_a') preproc.connect(func_get_brain_mask, 'out_file', func_edge_detect, 'in_file_b') preproc.connect(func_edge_detect, 'out_file', outputNode, 'skullstrip') func_mean_skullstrip = pe.Node(interface=preprocess.TStat(), name='func_mean_skullstrip') func_mean_skullstrip.inputs.options = '-mean' func_mean_skullstrip.inputs.outputtype = 'NIFTI_GZ' preproc.connect(func_edge_detect, 'out_file', func_mean_skullstrip, 'in_file') preproc.connect(func_mean_skullstrip, 'out_file', outputNode, 'example_func') func_normalize = pe.Node(interface=fsl.ImageMaths(), name='func_normalize') func_normalize.inputs.op_string = '-ing 10000' func_normalize.inputs.out_data_type = 'float' preproc.connect(func_edge_detect, 'out_file', func_normalize, 'in_file') preproc.connect(func_normalize, 'out_file', outputNode, 'preprocessed') func_mask_normalize = pe.Node(interface=fsl.ImageMaths(), name='func_mask_normalize') func_mask_normalize.inputs.op_string = '-Tmin -bin' func_mask_normalize.inputs.out_data_type = 'char' preproc.connect(func_normalize, 'out_file', func_mask_normalize, 'in_file') preproc.connect(func_mask_normalize, 'out_file', outputNode, 'preprocessed_mask') return preproc
def func_motion_correct_workflow(workflow, resource_pool, config, name="_"): """Build and run a Nipype workflow to calculate the motion correction parameters of a functional timeseries using AFNI's 3dvolreg. - If any resources/outputs required by this workflow are not in the resource pool, this workflow will call pre-requisite workflow builder functions to further populate the pipeline with workflows which will calculate/generate these necessary pre-requisites. Expected Resources in Resource Pool - func_reorient: The deobliqued, reoriented functional timeseries. New Resources Added to Resource Pool: - func_motion_correct: The motion-corrected functional timeseries. - coordinate_transformation: The matrix transformation from AFNI's 3dvolreg (--1Dmatrix_save option). Workflow Steps 1. AFNI 3dcalc to extract the first volume of the functional timeseries for the basefile for 3dvolreg. 2. AFNI 3dvolreg to calculate the motion correction parameters. :type workflow: Nipype workflow object :param workflow: A Nipype workflow object which can already contain other connected nodes; this function will insert the following workflow into this one provided. :type resource_pool: dict :param resource_pool: A dictionary defining input files and pointers to Nipype node outputs / workflow connections; the keys are the resource names. :type config: dict :param config: A dictionary defining the configuration settings for the workflow, such as directory paths or toggled options. :type name: str :param name: (default: "_") A string to append to the end of each node name. :rtype: Nipype workflow object :return: The Nipype workflow originally provided, but with this function's sub-workflow connected into it. :rtype: dict :return: The resource pool originally provided, but updated (if applicable) with the newest outputs and connections. """ import copy import nipype.pipeline.engine as pe from nipype.interfaces.afni import preprocess if "func_reorient" not in resource_pool.keys(): from functional_preproc import func_preproc_workflow old_rp = copy.copy(resource_pool) workflow, resource_pool = \ func_preproc_workflow(workflow, resource_pool, config, name) if resource_pool == old_rp: return workflow, resource_pool # get the first volume of the time series get_func_volume = pe.Node(interface=preprocess.Calc(), name='get_func_volume%s' % name) get_func_volume.inputs.expr = 'a' get_func_volume.inputs.single_idx = 0 get_func_volume.inputs.outputtype = 'NIFTI_GZ' if len(resource_pool["func_reorient"]) == 2: node, out_file = resource_pool["func_reorient"] workflow.connect(node, out_file, get_func_volume, 'in_file_a') else: get_func_volume.inputs.in_file_a = resource_pool["func_reorient"] # calculate motion parameters func_motion_correct = pe.Node(interface=preprocess.Volreg(), name='func_motion_correct%s' % name) func_motion_correct.inputs.args = '-Fourier -twopass' func_motion_correct.inputs.zpad = 4 func_motion_correct.inputs.outputtype = 'NIFTI_GZ' if len(resource_pool["func_reorient"]) == 2: node, out_file = resource_pool["func_reorient"] workflow.connect(node, out_file, func_motion_correct, 'in_file') else: func_motion_correct.inputs.in_file = resource_pool["func_reorient"] workflow.connect(get_func_volume, 'out_file', func_motion_correct, 'basefile') resource_pool["func_motion_correct"] = (func_motion_correct, 'out_file') resource_pool["coordinate_transformation"] = \ (func_motion_correct, 'oned_matrix_save') return workflow, resource_pool