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 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): # 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_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_motion_correction_workflow(name='moco', method='AFNI', extend_moco_params=False): """uses sub-workflows to perform different registration steps. Requires fsl and freesurfer tools Parameters ---------- name : string name of workflow Example ------- >>> motion_correction_workflow = create_motion_correction_workflow('motion_correction_workflow') >>> motion_correction_workflow.inputs.inputspec.output_directory = '/data/project/raw/BIDS/sj_1/' >>> motion_correction_workflow.inputs.inputspec.in_files = ['sub-001.nii.gz','sub-002.nii.gz'] >>> motion_correction_workflow.inputs.inputspec.which_file_is_EPI_space = 'middle' Inputs:: inputspec.output_directory : directory in which to sink the result files inputspec.in_files : list of functional files inputspec.which_file_is_EPI_space : determines which file is the 'standard EPI space' Outputs:: outputspec.EPI_space_file : standard EPI space file, one timepoint outputspec.motion_corrected_files : motion corrected files outputspec.motion_correction_plots : motion correction plots outputspec.motion_correction_parameters : motion correction parameters """ ### NODES input_node = pe.Node(IdentityInterface(fields=[ 'in_files', 'output_directory', 'which_file_is_EPI_space', 'sub_id', 'tr' ]), name='inputspec') output_node = pe.Node(IdentityInterface(fields=([ 'motion_corrected_files', 'EPI_space_file', 'mask_EPI_space_file', 'motion_correction_plots', 'motion_correction_parameters', 'extended_motion_correction_parameters', 'new_motion_correction_parameters' ])), name='outputspec') ######################################################################################## # Invariant nodes ######################################################################################## EPI_file_selector_node = pe.Node(interface=EPI_file_selector, name='EPI_file_selector_node') mean_bold = pe.Node(interface=fsl.maths.MeanImage(dimension='T'), name='mean_space') rename_mean_bold = pe.Node(niu.Rename(format_string='session_EPI_space', keep_ext=True), name='rename_mean_bold') ######################################################################################## # Workflow ######################################################################################## motion_correction_workflow = pe.Workflow(name=name) motion_correction_workflow.connect(input_node, 'which_file_is_EPI_space', EPI_file_selector_node, 'which_file') motion_correction_workflow.connect(input_node, 'in_files', EPI_file_selector_node, 'in_files') ######################################################################################## # outputs via datasink ######################################################################################## datasink = pe.Node(nio.DataSink(), name='sinker') datasink.inputs.parameterization = False # first link the workflow's output_directory into the datasink. motion_correction_workflow.connect(input_node, 'output_directory', datasink, 'base_directory') motion_correction_workflow.connect(input_node, 'sub_id', datasink, 'container') ######################################################################################## # FSL MCFlirt ######################################################################################## # new approach, which should aid in the joint motion correction of # multiple sessions together, by pre-registering each run. # the strategy would be to, for each run, take the first TR # and FLIRT-align (6dof) it to the EPI_space file. # then we can use this as an --infile argument to mcflirt. if method == 'FSL': rename_motion_files = pe.MapNode( niu.Rename(keep_ext=False), name='rename_motion_files', iterfield=['in_file', 'format_string']) remove_niigz_ext = pe.MapNode(interface=Remove_extension, name='remove_niigz_ext', iterfield=['in_file']) motion_correct_EPI_space = pe.Node(interface=fsl.MCFLIRT( cost='normcorr', interpolation='sinc', mean_vol=True), name='motion_correct_EPI_space') motion_correct_all = pe.MapNode(interface=fsl.MCFLIRT( save_mats=True, save_plots=True, cost='normcorr', interpolation='sinc', stats_imgs=True), name='motion_correct_all', iterfield=['in_file']) plot_motion = pe.MapNode( interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) if extend_moco_params: # make extend_motion_pars node here # extend_motion_pars = pe.MapNode(Function(input_names=['moco_par_file', 'tr'], output_names=['new_out_file', 'ext_out_file'], # function=_extend_motion_parameters), name='extend_motion_pars', iterfield = ['moco_par_file']) pass # create reference: motion_correction_workflow.connect(EPI_file_selector_node, 'out_file', motion_correct_EPI_space, 'in_file') motion_correction_workflow.connect(motion_correct_EPI_space, 'out_file', mean_bold, 'in_file') motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'ref_file') # motion correction across runs motion_correction_workflow.connect(input_node, 'in_files', motion_correct_all, 'in_file') #motion_correction_workflow.connect(motion_correct_all, 'out_file', output_node, 'motion_corrected_files') # motion_correction_workflow.connect(motion_correct_all, 'par_file', extend_motion_pars, 'moco_par_file') # motion_correction_workflow.connect(input_node, 'tr', extend_motion_pars, 'tr') # motion_correction_workflow.connect(extend_motion_pars, 'ext_out_file', output_node, 'extended_motion_correction_parameters') # motion_correction_workflow.connect(extend_motion_pars, 'new_out_file', output_node, 'new_motion_correction_parameters') ######################################################################################## # Plot the estimated motion parameters ######################################################################################## # rename: motion_correction_workflow.connect(mean_bold, 'out_file', rename_mean_bold, 'in_file') motion_correction_workflow.connect(motion_correct_all, 'par_file', rename_motion_files, 'in_file') motion_correction_workflow.connect(motion_correct_all, 'par_file', remove_niigz_ext, 'in_file') motion_correction_workflow.connect(remove_niigz_ext, 'out_file', rename_motion_files, 'format_string') # plots: plot_motion.iterables = ('plot_type', ['rotations', 'translations']) motion_correction_workflow.connect(rename_motion_files, 'out_file', plot_motion, 'in_file') motion_correction_workflow.connect(plot_motion, 'out_file', output_node, 'motion_correction_plots') # output node: motion_correction_workflow.connect(mean_bold, 'out_file', output_node, 'EPI_space_file') motion_correction_workflow.connect(rename_motion_files, 'out_file', output_node, 'motion_correction_parameters') motion_correction_workflow.connect(motion_correct_all, 'out_file', output_node, 'motion_corrected_files') # datasink: motion_correction_workflow.connect(rename_mean_bold, 'out_file', datasink, 'reg') motion_correction_workflow.connect(motion_correct_all, 'out_file', datasink, 'mcf') motion_correction_workflow.connect(rename_motion_files, 'out_file', datasink, 'mcf.motion_pars') motion_correction_workflow.connect(plot_motion, 'out_file', datasink, 'mcf.motion_plots') # motion_correction_workflow.connect(extend_motion_pars, 'ext_out_file', datasink, 'mcf.ext_motion_pars') # motion_correction_workflow.connect(extend_motion_pars, 'new_out_file', datasink, 'mcf.new_motion_pars') ######################################################################################## # AFNI 3DVolReg ######################################################################################## # for speed, we use AFNI's 3DVolReg brute-force. # this loses plotting of motion parameters but increases speed # we hold on to the same setup, first moco the selected run # and then moco everything to that image, but without the # intermediate FLIRT step. if method == 'AFNI': motion_correct_EPI_space = pe.Node( interface=afni.Volreg( outputtype='NIFTI_GZ', zpad=5, args=' -cubic ' # -twopass -Fourier ), name='motion_correct_EPI_space') motion_correct_all = pe.MapNode( interface=afni.Volreg( outputtype='NIFTI_GZ', zpad=5, args=' -cubic ' # -twopass ), name='motion_correct_all', iterfield=['in_file']) # for renaming *_volreg.nii.gz to *_mcf.nii.gz set_postfix_mcf = pe.MapNode(interface=Set_postfix, name='set_postfix_mcf', iterfield=['in_file']) set_postfix_mcf.inputs.postfix = 'mcf' rename_volreg = pe.MapNode(interface=Rename(keep_ext=True), name='rename_volreg', iterfield=['in_file', 'format_string']) # curate for moco between sessions motion_correction_workflow.connect(EPI_file_selector_node, 'out_file', motion_correct_EPI_space, 'in_file') motion_correction_workflow.connect(motion_correct_EPI_space, 'out_file', mean_bold, 'in_file') # motion correction across runs motion_correction_workflow.connect(input_node, 'in_files', motion_correct_all, 'in_file') motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'basefile') # motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'rotparent') # motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'gridparent') # output node: motion_correction_workflow.connect(mean_bold, 'out_file', output_node, 'EPI_space_file') motion_correction_workflow.connect(motion_correct_all, 'md1d_file', output_node, 'max_displacement_info') motion_correction_workflow.connect(motion_correct_all, 'oned_file', output_node, 'motion_correction_parameter_info') motion_correction_workflow.connect( motion_correct_all, 'oned_matrix_save', output_node, 'motion_correction_parameter_matrix') motion_correction_workflow.connect(input_node, 'in_files', set_postfix_mcf, 'in_file') motion_correction_workflow.connect(set_postfix_mcf, 'out_file', rename_volreg, 'format_string') motion_correction_workflow.connect(motion_correct_all, 'out_file', rename_volreg, 'in_file') motion_correction_workflow.connect(rename_volreg, 'out_file', output_node, 'motion_corrected_files') # datasink: motion_correction_workflow.connect(mean_bold, 'out_file', rename_mean_bold, 'in_file') motion_correction_workflow.connect(rename_mean_bold, 'out_file', datasink, 'reg') motion_correction_workflow.connect(rename_volreg, 'out_file', datasink, 'mcf') motion_correction_workflow.connect(motion_correct_all, 'md1d_file', datasink, 'mcf.max_displacement_info') motion_correction_workflow.connect(motion_correct_all, 'oned_file', datasink, 'mcf.parameter_info') motion_correction_workflow.connect(motion_correct_all, 'oned_matrix_save', datasink, 'mcf.motion_pars') return motion_correction_workflow
def create_func_preproc(skullstrip_tool, n4_correction, anatomical_mask_dilation=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.func : func nifti file User input functional(T2) Image, in any of the 8 orientations inputspec.twopass : boolean Perform two-pass on volume registration 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.func_mean : 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 .. exec:: from CPAC.func_preproc import create_func_preproc wf = create_func_preproc() wf.write_graph( graph2use='orig', dotfilename='./images/generated/func_preproc.dot' ) High Level Workflow Graph: .. image:: ../images/generated/func_preproc.png :width: 1000 Detailed Workflow Graph: .. image:: ../images/generated/func_preproc_detailed.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) input_node = pe.Node(util.IdentityInterface( fields=['func', 'twopass', 'anatomical_brain_mask', 'anat_skull']), name='inputspec') output_node = pe.Node(util.IdentityInterface(fields=[ 'refit', 'reorient', 'reorient_mean', 'motion_correct', 'motion_correct_ref', 'movement_parameters', 'max_displacement', 'mask', 'skullstrip', 'func_mean', 'preprocessed', 'preprocessed_mask', 'slice_time_corrected', 'transform_matrices' ]), name='outputspec') func_deoblique = pe.Node(interface=afni_utils.Refit(), name='func_deoblique') func_deoblique.inputs.deoblique = True preproc.connect(input_node, 'func', func_deoblique, 'in_file') func_reorient = pe.Node(interface=afni_utils.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', output_node, 'reorient') func_get_mean_RPI = pe.Node(interface=afni_utils.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_3dvolreg') func_motion_correct.inputs.zpad = 4 func_motion_correct.inputs.outputtype = 'NIFTI_GZ' preproc.connect([ (input_node, func_motion_correct, [(('twopass', collect_arguments, '-twopass', '-Fourier'), 'args')]), ]) preproc.connect(func_get_mean_RPI, 'out_file', func_motion_correct, 'basefile') preproc.connect(func_reorient, 'out_file', func_motion_correct, 'in_file') 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', output_node, '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([ (input_node, func_motion_correct_A, [(('twopass', collect_arguments, '-twopass', '-Fourier'), 'args')]), ]) 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', output_node, 'motion_correct') preproc.connect(func_motion_correct_A, 'md1d_file', output_node, 'max_displacement') preproc.connect(func_motion_correct_A, 'oned_file', output_node, 'movement_parameters') preproc.connect(func_motion_correct_A, 'oned_matrix_save', output_node, 'transform_matrices') skullstrip_func = skullstrip_functional(skullstrip_tool, anatomical_mask_dilation, "{0}_skullstrip".format(wf_name)) preproc.connect(func_motion_correct_A, 'out_file', skullstrip_func, 'inputspec.func') preproc.connect(input_node, 'anatomical_brain_mask', skullstrip_func, 'inputspec.anatomical_brain_mask') preproc.connect(input_node, 'anat_skull', skullstrip_func, 'inputspec.anat_skull') preproc.connect(skullstrip_func, 'outputspec.func_brain', output_node, 'skullstrip') preproc.connect(skullstrip_func, 'outputspec.func_brain_mask', output_node, 'mask') func_mean = pe.Node(interface=afni_utils.TStat(), name='func_mean') func_mean.inputs.options = '-mean' func_mean.inputs.outputtype = 'NIFTI_GZ' preproc.connect(skullstrip_func, 'outputspec.func_brain', func_mean, 'in_file') if n4_correction: func_mean_n4_corrected = pe.Node(interface=ants.N4BiasFieldCorrection( dimension=3, copy_header=True, bspline_fitting_distance=200), shrink_factor=2, name='func_mean_n4_corrected') func_mean_n4_corrected.inputs.args = '-r True' # func_mean_n4_corrected.inputs.rescale_intensities = True preproc.connect(func_mean, 'out_file', func_mean_n4_corrected, 'input_image') preproc.connect(func_mean_n4_corrected, 'output_image', output_node, 'func_mean') else: preproc.connect(func_mean, 'out_file', output_node, 'func_mean') 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(skullstrip_func, 'outputspec.func_brain', func_normalize, 'in_file') preproc.connect(func_normalize, 'out_file', output_node, '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', output_node, 'preprocessed_mask') return preproc
def mc_workflow_afni(reference_vol="mid", FD_mode="Power", SinkTag="func_preproc", wf_name="motion_correction_afni"): from nipype.interfaces.afni import preprocess import sys import os import nipype import nipype.pipeline as pe import nipype.interfaces.utility as utility import PUMI.func_preproc.info.info_get as info_get import nipype.interfaces.io as io import nipype.algorithms.confounds as conf import PUMI.utils.utils_math as utils_math import PUMI.utils.utils_convert as utils_convert import PUMI.utils.globals as globals import PUMI.utils.QC as qc SinkDir = os.path.abspath(globals._SinkDir_ + "/" + SinkTag) if not os.path.exists(SinkDir): os.makedirs(SinkDir) QCDir = os.path.abspath(globals._SinkDir_ + "/" + globals._QCDir_) if not os.path.exists(QCDir): os.makedirs(QCDir) # Basic interface class generates identity mappings inputspec = pe.Node(utility.IdentityInterface( fields=['func', 'ref_vol', 'save_plots', 'stats_imgs']), name='inputspec') inputspec.inputs.save_plots = True inputspec.inputs.stats_imgs = True inputspec.inputs.ref_vol = reference_vol # extract reference volume refvol = pe.MapNode(utility.Function(input_names=['refvol', 'func'], output_names=['refvol'], function=getRefVol), iterfield=['func'], name='getRefVol') if (reference_vol == "mean"): func_motion_correct1 = pe.MapNode(interface=preprocess.Volreg(), iterfield=["in_file", "basefile"], name='mc_afni_init') func_motion_correct1.inputs.args = '-Fourier -twopass' func_motion_correct1.inputs.zpad = 4 func_motion_correct1.inputs.outputtype = 'NIFTI_GZ' # extract reference volume refvol2 = pe.MapNode(utility.Function(input_names=['refvol', 'func'], output_names=['refvol'], function=getRefVol), iterfield=['func'], name='getRefVol2') func_motion_correct = pe.MapNode(interface=preprocess.Volreg(), iterfield=["in_file", "basefile"], name='mc_afni') func_motion_correct.inputs.args = '-Fourier -twopass' func_motion_correct.inputs.zpad = 4 func_motion_correct.inputs.outputtype = 'NIFTI_GZ' myqc = qc.timecourse2png("timeseries", tag="010_motioncorr") # Calculate Friston24 parameters calc_friston = pe.MapNode(utility.Function( input_names=['in_file'], output_names=['out_file'], function=calc_friston_twenty_four), iterfield=['in_file'], name='calc_friston') if FD_mode == "Power": calculate_FD = pe.MapNode(conf.FramewiseDisplacement( parameter_source='AFNI', save_plot=True), iterfield=['in_file'], name='calculate_FD_Power') elif FD_mode == "Jenkinson": calculate_FD = pe.MapNode(utility.Function(input_names=['in_file'], output_names=['out_file'], function=calculate_FD_J), iterfield=['in_file'], name='calculate_FD_Jenkinson') # compute mean and max FD meanFD = pe.MapNode(interface=utils_math.Txt2meanTxt, iterfield=['in_file'], name='meanFD') meanFD.inputs.axis = 0 # global mean meanFD.inputs.header = True # global mean maxFD = pe.MapNode(interface=utils_math.Txt2maxTxt, iterfield=['in_file'], name='maxFD') maxFD.inputs.axis = 0 # global mean maxFD.inputs.header = True # global mean pop_FD = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FD') pop_FDmax = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FDmax') # save data out with Datasink ds_fd = pe.Node(interface=io.DataSink(), name='ds_pop_fd') ds_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD.txt")] ds_fd.inputs.base_directory = SinkDir # save data out with Datasink ds_fd_max = pe.Node(interface=io.DataSink(), name='ds_pop_fd_max') ds_fd_max.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD_max.txt")] ds_fd_max.inputs.base_directory = SinkDir # Save outputs which are important ds_qc_fd = pe.Node(interface=io.DataSink(), name='ds_qc_fd') ds_qc_fd.inputs.base_directory = QCDir ds_qc_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_FD.pdf")] # Basic interface class generates identity mappings outputspec = pe.Node(utility.IdentityInterface(fields=[ 'func_out_file', 'first24_file', 'mat_file', 'mc_par_file', 'FD_file' ]), name='outputspec') # save data out with Datasink ds_nii = pe.Node(interface=io.DataSink(), name='ds_nii') ds_nii.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".nii.gz")] ds_nii.inputs.base_directory = SinkDir # save data out with Datasink ds_text = pe.Node(interface=io.DataSink(), name='ds_txt') ds_text.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".txt")] ds_text.inputs.base_directory = SinkDir # TODO_ready set the proper images which has to be saved in a the datasink specified directory # Create a workflow to connect all those nodes analysisflow = nipype.Workflow(wf_name) analysisflow.connect(inputspec, 'func', refvol, 'func') analysisflow.connect(inputspec, 'ref_vol', refvol, 'refvol') if (reference_vol == "mean"): analysisflow.connect(inputspec, 'func', func_motion_correct1, 'in_file') analysisflow.connect(refvol, 'refvol', func_motion_correct1, 'basefile') analysisflow.connect(func_motion_correct1, 'out_file', refvol2, 'func') analysisflow.connect(inputspec, 'ref_vol', refvol2, 'refvol') analysisflow.connect(inputspec, 'func', func_motion_correct, 'in_file') analysisflow.connect(refvol2, 'refvol', func_motion_correct, 'basefile') else: analysisflow.connect(inputspec, 'func', func_motion_correct, 'in_file') analysisflow.connect(refvol, 'refvol', func_motion_correct, 'basefile') analysisflow.connect(func_motion_correct, 'oned_file', calc_friston, 'in_file') analysisflow.connect(func_motion_correct, 'oned_file', calculate_FD, 'in_file') analysisflow.connect(func_motion_correct, 'out_file', outputspec, 'func_out_file') analysisflow.connect(func_motion_correct, 'oned_matrix_save', outputspec, 'mat_file') analysisflow.connect(func_motion_correct, 'oned_file', outputspec, 'mc_par_file') analysisflow.connect(func_motion_correct, 'out_file', ds_nii, 'mc_func') analysisflow.connect(func_motion_correct, 'oned_file', ds_text, 'mc_par') # analysisflow.connect(func_motion_correct, 'variance_img', ds, 'mc.@variance_img') analysisflow.connect(calc_friston, 'out_file', outputspec, 'first24_file') analysisflow.connect(calc_friston, 'out_file', ds_text, 'mc_first24') analysisflow.connect(calculate_FD, 'out_file', outputspec, 'FD_file') analysisflow.connect(func_motion_correct, 'out_file', myqc, 'inputspec.func') # pop-level mean FD analysisflow.connect(calculate_FD, 'out_file', meanFD, 'in_file') analysisflow.connect(calculate_FD, 'out_file', ds_text, 'mc_fd') analysisflow.connect(meanFD, 'mean_file', pop_FD, 'in_list') analysisflow.connect(pop_FD, 'txt_file', ds_fd, 'pop') analysisflow.connect(calculate_FD, 'out_figure', ds_qc_fd, 'FD') analysisflow.connect(calculate_FD, 'out_file', maxFD, 'in_file') analysisflow.connect(maxFD, 'max_file', pop_FDmax, 'in_list') analysisflow.connect(pop_FDmax, 'txt_file', ds_fd_max, 'pop') return analysisflow
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