def test_C3dAffineTool_outputs(): output_map = dict(itk_transform=dict(), ) outputs = C3dAffineTool.output_spec() for key, metadata in output_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(outputs.traits()[key], metakey), value
def test_C3dAffineTool_inputs(): input_map = dict(args=dict(argstr='%s', ), environ=dict(nohash=True, usedefault=True, ), fsl2ras=dict(argstr='-fsl2ras', position=4, ), ignore_exception=dict(nohash=True, usedefault=True, ), itk_transform=dict(argstr='-oitk %s', hash_files=False, position=5, ), reference_file=dict(argstr='-ref %s', position=1, ), source_file=dict(argstr='-src %s', position=2, ), terminal_output=dict(mandatory=True, nohash=True, ), transform_file=dict(argstr='%s', position=3, ), ) inputs = C3dAffineTool.input_spec() for key, metadata in input_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(inputs.traits()[key], metakey), value
def test_C3dAffineTool_inputs(): input_map = dict( args=dict(argstr='%s', ), environ=dict( nohash=True, usedefault=True, ), fsl2ras=dict( argstr='-fsl2ras', position=4, ), ignore_exception=dict( nohash=True, usedefault=True, ), itk_transform=dict( argstr='-oitk %s', hash_files=False, position=5, ), reference_file=dict( argstr='-ref %s', position=1, ), source_file=dict( argstr='-src %s', position=2, ), terminal_output=dict( mandatory=True, nohash=True, ), transform_file=dict( argstr='%s', position=3, ), ) inputs = C3dAffineTool.input_spec() for key, metadata in input_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(inputs.traits()[key], metakey), value
def _mat2itk(args): from nipype.interfaces.c3 import C3dAffineTool from nipype.utils.filemanip import fname_presuffix in_file, in_ref, in_src, index, newpath = args # Generate a temporal file name out_file = fname_presuffix(in_file, suffix='_itk-%05d.txt' % index, newpath=newpath) # Run c3d_affine_tool C3dAffineTool(transform_file=in_file, reference_file=in_ref, source_file=in_src, fsl2ras=True, itk_transform=out_file, resource_monitor=False).run() transform = '#Transform %d\n' % index with open(out_file) as itkfh: transform += ''.join(itkfh.readlines()[2:]) return (index, transform)
def create_reg_workflow(name='registration'): """Create a FEAT preprocessing workflow together with freesurfer Parameters ---------- name : name of workflow (default: 'registration') Inputs:: inputspec.source_files : files (filename or list of filenames to register) inputspec.mean_image : reference image to use inputspec.anatomical_image : anatomical image to coregister to inputspec.target_image : registration target Outputs:: outputspec.func2anat_transform : FLIRT transform outputspec.anat2target_transform : FLIRT+FNIRT transform outputspec.transformed_files : transformed files in target space outputspec.transformed_mean : mean image in target space """ register = Workflow(name=name) inputnode = Node(interface=IdentityInterface(fields=[ 'source_files', 'mean_image', 'subject_id', 'subjects_dir', 'target_image' ]), name='inputspec') outputnode = Node(interface=IdentityInterface(fields=[ 'func2anat_transform', 'out_reg_file', 'anat2target_transform', 'transforms', 'transformed_mean', 'segmentation_files', 'anat2target', 'aparc' ]), name='outputspec') # Get the subject's freesurfer source directory fssource = Node(FreeSurferSource(), name='fssource') fssource.run_without_submitting = True register.connect(inputnode, 'subject_id', fssource, 'subject_id') register.connect(inputnode, 'subjects_dir', fssource, 'subjects_dir') convert = Node(freesurfer.MRIConvert(out_type='nii'), name="convert") register.connect(fssource, 'T1', convert, 'in_file') # Coregister the median to the surface bbregister = Node(freesurfer.BBRegister(), name='bbregister') bbregister.inputs.init = 'fsl' bbregister.inputs.contrast_type = 't2' bbregister.inputs.out_fsl_file = True bbregister.inputs.epi_mask = True register.connect(inputnode, 'subject_id', bbregister, 'subject_id') register.connect(inputnode, 'mean_image', bbregister, 'source_file') register.connect(inputnode, 'subjects_dir', bbregister, 'subjects_dir') """ Estimate the tissue classes from the anatomical image. But use spm's segment as FSL appears to be breaking. """ stripper = Node(fsl.BET(), name='stripper') register.connect(convert, 'out_file', stripper, 'in_file') fast = Node(fsl.FAST(), name='fast') register.connect(stripper, 'out_file', fast, 'in_files') """ Binarize the segmentation """ binarize = MapNode(fsl.ImageMaths(op_string='-nan -thr 0.9 -ero -bin'), iterfield=['in_file'], name='binarize') register.connect(fast, 'partial_volume_files', binarize, 'in_file') """ Apply inverse transform to take segmentations to functional space """ applyxfm = MapNode(freesurfer.ApplyVolTransform(inverse=True, interp='nearest'), iterfield=['target_file'], name='inverse_transform') register.connect(inputnode, 'subjects_dir', applyxfm, 'subjects_dir') register.connect(bbregister, 'out_reg_file', applyxfm, 'reg_file') register.connect(binarize, 'out_file', applyxfm, 'target_file') register.connect(inputnode, 'mean_image', applyxfm, 'source_file') """ Apply inverse transform to aparc file """ aparcxfm = Node(freesurfer.ApplyVolTransform(inverse=True, interp='nearest'), name='aparc_inverse_transform') register.connect(inputnode, 'subjects_dir', aparcxfm, 'subjects_dir') register.connect(bbregister, 'out_reg_file', aparcxfm, 'reg_file') register.connect(fssource, ('aparc_aseg', get_aparc_aseg), aparcxfm, 'target_file') register.connect(inputnode, 'mean_image', aparcxfm, 'source_file') """ Convert the BBRegister transformation to ANTS ITK format """ convert2itk = Node(C3dAffineTool(), name='convert2itk') convert2itk.inputs.fsl2ras = True convert2itk.inputs.itk_transform = True register.connect(bbregister, 'out_fsl_file', convert2itk, 'transform_file') register.connect(inputnode, 'mean_image', convert2itk, 'source_file') register.connect(stripper, 'out_file', convert2itk, 'reference_file') """ Compute registration between the subject's structural and MNI template This is currently set to perform a very quick registration. However, the registration can be made significantly more accurate for cortical structures by increasing the number of iterations All parameters are set using the example from: #https://github.com/stnava/ANTs/blob/master/Scripts/newAntsExample.sh """ reg = Node(ants.Registration(), name='antsRegister') reg.inputs.output_transform_prefix = "output_" reg.inputs.transforms = ['Rigid', 'Affine', 'SyN'] reg.inputs.transform_parameters = [(0.1, ), (0.1, ), (0.2, 3.0, 0.0)] reg.inputs.number_of_iterations = [[10000, 11110, 11110]] * 2 + [[ 100, 30, 20 ]] reg.inputs.dimension = 3 reg.inputs.write_composite_transform = True reg.inputs.collapse_output_transforms = True reg.inputs.initial_moving_transform_com = True reg.inputs.metric = ['Mattes'] * 2 + [['Mattes', 'CC']] reg.inputs.metric_weight = [1] * 2 + [[0.5, 0.5]] reg.inputs.radius_or_number_of_bins = [32] * 2 + [[32, 4]] reg.inputs.sampling_strategy = ['Regular'] * 2 + [[None, None]] reg.inputs.sampling_percentage = [0.3] * 2 + [[None, None]] reg.inputs.convergence_threshold = [1.e-8] * 2 + [-0.01] reg.inputs.convergence_window_size = [20] * 2 + [5] reg.inputs.smoothing_sigmas = [[4, 2, 1]] * 2 + [[1, 0.5, 0]] reg.inputs.sigma_units = ['vox'] * 3 reg.inputs.shrink_factors = [[3, 2, 1]] * 2 + [[4, 2, 1]] reg.inputs.use_estimate_learning_rate_once = [True] * 3 reg.inputs.use_histogram_matching = [False] * 2 + [True] reg.inputs.winsorize_lower_quantile = 0.005 reg.inputs.winsorize_upper_quantile = 0.995 reg.inputs.float = True reg.inputs.output_warped_image = 'output_warped_image.nii.gz' reg.inputs.num_threads = 4 reg.plugin_args = {'qsub_args': '-l nodes=1:ppn=4'} register.connect(stripper, 'out_file', reg, 'moving_image') register.connect(inputnode, 'target_image', reg, 'fixed_image') """ Concatenate the affine and ants transforms into a list """ merge = Node(Merge(2), iterfield=['in2'], name='mergexfm') register.connect(convert2itk, 'itk_transform', merge, 'in2') register.connect(reg, 'composite_transform', merge, 'in1') """ Transform the mean image. First to anatomical and then to target """ warpmean = Node(ants.ApplyTransforms(), name='warpmean') warpmean.inputs.input_image_type = 3 warpmean.inputs.interpolation = 'Linear' warpmean.inputs.invert_transform_flags = [False, False] warpmean.inputs.terminal_output = 'file' warpmean.inputs.args = '--float' warpmean.inputs.num_threads = 4 register.connect(inputnode, 'target_image', warpmean, 'reference_image') register.connect(inputnode, 'mean_image', warpmean, 'input_image') register.connect(merge, 'out', warpmean, 'transforms') """ Assign all the output files """ register.connect(reg, 'warped_image', outputnode, 'anat2target') register.connect(warpmean, 'output_image', outputnode, 'transformed_mean') register.connect(applyxfm, 'transformed_file', outputnode, 'segmentation_files') register.connect(aparcxfm, 'transformed_file', outputnode, 'aparc') register.connect(bbregister, 'out_fsl_file', outputnode, 'func2anat_transform') register.connect(bbregister, 'out_reg_file', outputnode, 'out_reg_file') register.connect(reg, 'composite_transform', outputnode, 'anat2target_transform') register.connect(merge, 'out', outputnode, 'transforms') return register
def create_reg_workflow(name='registration'): """Create a FEAT preprocessing workflow together with freesurfer Parameters ---------- name : name of workflow (default: 'registration') Inputs: inputspec.source_files : files (filename or list of filenames to register) inputspec.mean_image : reference image to use inputspec.anatomical_image : anatomical image to coregister to inputspec.target_image : registration target Outputs: outputspec.func2anat_transform : FLIRT transform outputspec.anat2target_transform : FLIRT+FNIRT transform outputspec.transformed_files : transformed files in target space outputspec.transformed_mean : mean image in target space """ register = pe.Workflow(name=name) inputnode = pe.Node(interface=niu.IdentityInterface(fields=['source_files', 'mean_image', 'anatomical_image', 'target_image', 'target_image_brain', 'config_file']), name='inputspec') outputnode = pe.Node(interface=niu.IdentityInterface(fields=['func2anat_transform', 'anat2target_transform', 'transformed_files', 'transformed_mean', 'anat2target', 'mean2anat_mask' ]), name='outputspec') """ Estimate the tissue classes from the anatomical image. But use spm's segment as FSL appears to be breaking. """ stripper = pe.Node(fsl.BET(), name='stripper') register.connect(inputnode, 'anatomical_image', stripper, 'in_file') fast = pe.Node(fsl.FAST(), name='fast') register.connect(stripper, 'out_file', fast, 'in_files') """ Binarize the segmentation """ binarize = pe.Node(fsl.ImageMaths(op_string='-nan -thr 0.5 -bin'), name='binarize') pickindex = lambda x, i: x[i] register.connect(fast, ('partial_volume_files', pickindex, 2), binarize, 'in_file') """ Calculate rigid transform from mean image to anatomical image """ mean2anat = pe.Node(fsl.FLIRT(), name='mean2anat') mean2anat.inputs.dof = 6 register.connect(inputnode, 'mean_image', mean2anat, 'in_file') register.connect(stripper, 'out_file', mean2anat, 'reference') """ Now use bbr cost function to improve the transform """ mean2anatbbr = pe.Node(fsl.FLIRT(), name='mean2anatbbr') mean2anatbbr.inputs.dof = 6 mean2anatbbr.inputs.cost = 'bbr' mean2anatbbr.inputs.schedule = os.path.join(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch') register.connect(inputnode, 'mean_image', mean2anatbbr, 'in_file') register.connect(binarize, 'out_file', mean2anatbbr, 'wm_seg') register.connect(inputnode, 'anatomical_image', mean2anatbbr, 'reference') register.connect(mean2anat, 'out_matrix_file', mean2anatbbr, 'in_matrix_file') """ Create a mask of the median image coregistered to the anatomical image """ mean2anat_mask = Node(fsl.BET(mask=True), name='mean2anat_mask') register.connect(mean2anatbbr, 'out_file', mean2anat_mask, 'in_file') """ Convert the BBRegister transformation to ANTS ITK format """ convert2itk = pe.Node(C3dAffineTool(), name='convert2itk') convert2itk.inputs.fsl2ras = True convert2itk.inputs.itk_transform = True register.connect(mean2anatbbr, 'out_matrix_file', convert2itk, 'transform_file') register.connect(inputnode, 'mean_image',convert2itk, 'source_file') register.connect(stripper, 'out_file', convert2itk, 'reference_file') """ Compute registration between the subject's structural and MNI template This is currently set to perform a very quick registration. However, the registration can be made significantly more accurate for cortical structures by increasing the number of iterations All parameters are set using the example from: #https://github.com/stnava/ANTs/blob/master/Scripts/newAntsExample.sh """ reg = pe.Node(ants.Registration(), name='antsRegister') reg.inputs.output_transform_prefix = "output_" reg.inputs.transforms = ['Rigid', 'Affine', 'SyN'] reg.inputs.transform_parameters = [(0.1,), (0.1,), (0.2, 3.0, 0.0)] reg.inputs.number_of_iterations = [[10000, 11110, 11110]] * 2 + [[100, 30, 20]] reg.inputs.dimension = 3 reg.inputs.write_composite_transform = True reg.inputs.collapse_output_transforms = True reg.inputs.initial_moving_transform_com = True reg.inputs.metric = ['Mattes'] * 2 + [['Mattes', 'CC']] reg.inputs.metric_weight = [1] * 2 + [[0.5, 0.5]] reg.inputs.radius_or_number_of_bins = [32] * 2 + [[32, 4]] reg.inputs.sampling_strategy = ['Regular'] * 2 + [[None, None]] reg.inputs.sampling_percentage = [0.3] * 2 + [[None, None]] reg.inputs.convergence_threshold = [1.e-8] * 2 + [-0.01] reg.inputs.convergence_window_size = [20] * 2 + [5] reg.inputs.smoothing_sigmas = [[4, 2, 1]] * 2 + [[1, 0.5, 0]] reg.inputs.sigma_units = ['vox'] * 3 reg.inputs.shrink_factors = [[3, 2, 1]]*2 + [[4, 2, 1]] reg.inputs.use_estimate_learning_rate_once = [True] * 3 reg.inputs.use_histogram_matching = [False] * 2 + [True] reg.inputs.winsorize_lower_quantile = 0.005 reg.inputs.winsorize_upper_quantile = 0.995 reg.inputs.args = '--float' reg.inputs.output_warped_image = 'output_warped_image.nii.gz' reg.inputs.num_threads = 4 reg.plugin_args = {'qsub_args': '-pe orte 4', 'sbatch_args': '--mem=6G -c 4'} register.connect(stripper, 'out_file', reg, 'moving_image') register.connect(inputnode,'target_image_brain', reg,'fixed_image') """ Concatenate the affine and ants transforms into a list """ pickfirst = lambda x: x[0] merge = pe.Node(niu.Merge(2), iterfield=['in2'], name='mergexfm') register.connect(convert2itk, 'itk_transform', merge, 'in2') register.connect(reg, 'composite_transform', merge, 'in1') """ Transform the mean image. First to anatomical and then to target """ warpmean = pe.Node(ants.ApplyTransforms(), name='warpmean') warpmean.inputs.input_image_type = 0 warpmean.inputs.interpolation = 'Linear' warpmean.inputs.invert_transform_flags = [False, False] warpmean.inputs.terminal_output = 'file' register.connect(inputnode,'target_image_brain', warpmean,'reference_image') register.connect(inputnode, 'mean_image', warpmean, 'input_image') register.connect(merge, 'out', warpmean, 'transforms') """ Transform the remaining images. First to anatomical and then to target """ warpall = pe.MapNode(ants.ApplyTransforms(), iterfield=['input_image'], name='warpall') warpall.inputs.input_image_type = 0 warpall.inputs.interpolation = 'Linear' warpall.inputs.invert_transform_flags = [False, False] warpall.inputs.terminal_output = 'file' register.connect(inputnode,'target_image_brain',warpall,'reference_image') register.connect(inputnode,'source_files', warpall, 'input_image') register.connect(merge, 'out', warpall, 'transforms') """ Assign all the output files """ register.connect(reg, 'warped_image', outputnode, 'anat2target') register.connect(warpmean, 'output_image', outputnode, 'transformed_mean') register.connect(warpall, 'output_image', outputnode, 'transformed_files') register.connect(mean2anatbbr, 'out_matrix_file', outputnode, 'func2anat_transform') register.connect(mean2anat_mask, 'mask_file', outputnode, 'mean2anat_mask') register.connect(reg, 'composite_transform', outputnode, 'anat2target_transform') return register
def create_workflow(files, subject_id, n_vol=0, despike=True, TR=None, slice_times=None, slice_thickness=None, fieldmap_images=[], norm_threshold=1, num_components=6, vol_fwhm=None, surf_fwhm=None, lowpass_freq=-1, highpass_freq=-1, sink_directory=os.getcwd(), FM_TEdiff=2.46, FM_sigma=2, FM_echo_spacing=.7, target_subject=['fsaverage3', 'fsaverage4'], name='resting'): wf = Workflow(name=name) # Skip starting volumes remove_vol = MapNode(fsl.ExtractROI(t_min=n_vol, t_size=-1), iterfield=['in_file'], name="remove_volumes") remove_vol.inputs.in_file = files # Run AFNI's despike. This is always run, however, whether this is fed to # realign depends on the input configuration despiker = MapNode(afni.Despike(outputtype='NIFTI_GZ'), iterfield=['in_file'], name='despike') #despiker.plugin_args = {'qsub_args': '-l nodes=1:ppn='} wf.connect(remove_vol, 'roi_file', despiker, 'in_file') # Run Nipy joint slice timing and realignment algorithm realign = Node(nipy.SpaceTimeRealigner(), name='realign') realign.inputs.tr = TR realign.inputs.slice_times = slice_times realign.inputs.slice_info = 2 if despike: wf.connect(despiker, 'out_file', realign, 'in_file') else: wf.connect(remove_vol, 'roi_file', realign, 'in_file') # Comute TSNR on realigned data regressing polynomials upto order 2 tsnr = MapNode(TSNR(regress_poly=2), iterfield=['in_file'], name='tsnr') wf.connect(realign, 'out_file', tsnr, 'in_file') # Compute the median image across runs calc_median = Node(Function(input_names=['in_files'], output_names=['median_file'], function=median, imports=imports), name='median') wf.connect(tsnr, 'detrended_file', calc_median, 'in_files') # Coregister the median to the surface register = Node(freesurfer.BBRegister(), name='bbregister') register.inputs.subject_id = subject_id register.inputs.init = 'fsl' register.inputs.contrast_type = 't2' register.inputs.out_fsl_file = True register.inputs.epi_mask = True # Compute fieldmaps and unwarp using them if fieldmap_images: fieldmap = Node(interface=EPIDeWarp(), name='fieldmap_unwarp') fieldmap.inputs.tediff = FM_TEdiff fieldmap.inputs.esp = FM_echo_spacing fieldmap.inputs.sigma = FM_sigma fieldmap.inputs.mag_file = fieldmap_images[0] fieldmap.inputs.dph_file = fieldmap_images[1] wf.connect(calc_median, 'median_file', fieldmap, 'exf_file') dewarper = MapNode(interface=fsl.FUGUE(), iterfield=['in_file'], name='dewarper') wf.connect(tsnr, 'detrended_file', dewarper, 'in_file') wf.connect(fieldmap, 'exf_mask', dewarper, 'mask_file') wf.connect(fieldmap, 'vsm_file', dewarper, 'shift_in_file') wf.connect(fieldmap, 'exfdw', register, 'source_file') else: wf.connect(calc_median, 'median_file', register, 'source_file') # Get the subject's freesurfer source directory fssource = Node(FreeSurferSource(), name='fssource') fssource.inputs.subject_id = subject_id fssource.inputs.subjects_dir = os.environ['SUBJECTS_DIR'] # Extract wm+csf, brain masks by eroding freesurfer labels and then # transform the masks into the space of the median wmcsf = Node(freesurfer.Binarize(), name='wmcsfmask') mask = wmcsf.clone('anatmask') wmcsftransform = Node(freesurfer.ApplyVolTransform(inverse=True, interp='nearest'), name='wmcsftransform') wmcsftransform.inputs.subjects_dir = os.environ['SUBJECTS_DIR'] wmcsf.inputs.wm_ven_csf = True wmcsf.inputs.match = [4, 5, 14, 15, 24, 31, 43, 44, 63] wmcsf.inputs.binary_file = 'wmcsf.nii.gz' wmcsf.inputs.erode = int(np.ceil(slice_thickness)) wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), wmcsf, 'in_file') if fieldmap_images: wf.connect(fieldmap, 'exf_mask', wmcsftransform, 'source_file') else: wf.connect(calc_median, 'median_file', wmcsftransform, 'source_file') wf.connect(register, 'out_reg_file', wmcsftransform, 'reg_file') wf.connect(wmcsf, 'binary_file', wmcsftransform, 'target_file') mask.inputs.binary_file = 'mask.nii.gz' mask.inputs.dilate = int(np.ceil(slice_thickness)) + 1 mask.inputs.erode = int(np.ceil(slice_thickness)) mask.inputs.min = 0.5 wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), mask, 'in_file') masktransform = wmcsftransform.clone("masktransform") if fieldmap_images: wf.connect(fieldmap, 'exf_mask', masktransform, 'source_file') else: wf.connect(calc_median, 'median_file', masktransform, 'source_file') wf.connect(register, 'out_reg_file', masktransform, 'reg_file') wf.connect(mask, 'binary_file', masktransform, 'target_file') # Compute Art outliers art = Node(interface=ArtifactDetect(use_differences=[True, False], use_norm=True, norm_threshold=norm_threshold, zintensity_threshold=3, parameter_source='NiPy', bound_by_brainmask=True, save_plot=False, mask_type='file'), name="art") if fieldmap_images: wf.connect(dewarper, 'unwarped_file', art, 'realigned_files') else: wf.connect(tsnr, 'detrended_file', art, 'realigned_files') wf.connect(realign, 'par_file', art, 'realignment_parameters') wf.connect(masktransform, 'transformed_file', art, 'mask_file') # Compute motion regressors motreg = Node(Function( input_names=['motion_params', 'order', 'derivatives'], output_names=['out_files'], function=motion_regressors, imports=imports), name='getmotionregress') wf.connect(realign, 'par_file', motreg, 'motion_params') # Create a filter to remove motion and art confounds createfilter1 = Node(Function( input_names=['motion_params', 'comp_norm', 'outliers'], output_names=['out_files'], function=build_filter1, imports=imports), name='makemotionbasedfilter') wf.connect(motreg, 'out_files', createfilter1, 'motion_params') wf.connect(art, 'norm_files', createfilter1, 'comp_norm') wf.connect(art, 'outlier_files', createfilter1, 'outliers') # Filter the motion and art confounds filter1 = MapNode(fsl.GLM(out_res_name='timeseries.nii.gz', demean=True), iterfield=['in_file', 'design'], name='filtermotion') if fieldmap_images: wf.connect(dewarper, 'unwarped_file', filter1, 'in_file') else: wf.connect(tsnr, 'detrended_file', filter1, 'in_file') wf.connect(createfilter1, 'out_files', filter1, 'design') wf.connect(masktransform, 'transformed_file', filter1, 'mask') # Create a filter to remove noise components based on white matter and CSF createfilter2 = MapNode(Function( input_names=['realigned_file', 'mask_file', 'num_components'], output_names=['out_files'], function=extract_noise_components, imports=imports), iterfield=['realigned_file'], name='makecompcorrfilter') createfilter2.inputs.num_components = num_components wf.connect(filter1, 'out_res', createfilter2, 'realigned_file') wf.connect(masktransform, 'transformed_file', createfilter2, 'mask_file') # Filter noise components filter2 = MapNode(fsl.GLM(out_res_name='timeseries_cleaned.nii.gz', demean=True), iterfield=['in_file', 'design'], name='filtercompcorr') wf.connect(filter1, 'out_res', filter2, 'in_file') wf.connect(createfilter2, 'out_files', filter2, 'design') wf.connect(masktransform, 'transformed_file', filter2, 'mask') # Smoothing using surface and volume smoothing smooth = MapNode(freesurfer.Smooth(), iterfield=['in_file'], name='smooth') smooth.inputs.proj_frac_avg = (0.1, 0.9, 0.1) if surf_fwhm is None: surf_fwhm = 5 * slice_thickness smooth.inputs.surface_fwhm = surf_fwhm if vol_fwhm is None: vol_fwhm = 2 * slice_thickness smooth.inputs.vol_fwhm = vol_fwhm wf.connect(filter2, 'out_res', smooth, 'in_file') wf.connect(register, 'out_reg_file', smooth, 'reg_file') # Bandpass filter the data bandpass = MapNode(fsl.TemporalFilter(), iterfield=['in_file'], name='bandpassfilter') if highpass_freq < 0: bandpass.inputs.highpass_sigma = -1 else: bandpass.inputs.highpass_sigma = 1. / (2 * TR * highpass_freq) if lowpass_freq < 0: bandpass.inputs.lowpass_sigma = -1 else: bandpass.inputs.lowpass_sigma = 1. / (2 * TR * lowpass_freq) wf.connect(smooth, 'smoothed_file', bandpass, 'in_file') # Convert aparc to subject functional space aparctransform = wmcsftransform.clone("aparctransform") if fieldmap_images: wf.connect(fieldmap, 'exf_mask', aparctransform, 'source_file') else: wf.connect(calc_median, 'median_file', aparctransform, 'source_file') wf.connect(register, 'out_reg_file', aparctransform, 'reg_file') wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), aparctransform, 'target_file') # Sample the average time series in aparc ROIs sampleaparc = MapNode(freesurfer.SegStats(avgwf_txt_file=True, default_color_table=True), iterfield=['in_file'], name='aparc_ts') sampleaparc.inputs.segment_id = ([8] + range(10, 14) + [17, 18, 26, 47] + range(49, 55) + [58] + range(1001, 1036) + range(2001, 2036)) wf.connect(aparctransform, 'transformed_file', sampleaparc, 'segmentation_file') wf.connect(bandpass, 'out_file', sampleaparc, 'in_file') # Sample the time series onto the surface of the target surface. Performs # sampling into left and right hemisphere target = Node(IdentityInterface(fields=['target_subject']), name='target') target.iterables = ('target_subject', filename_to_list(target_subject)) samplerlh = MapNode(freesurfer.SampleToSurface(), iterfield=['source_file'], name='sampler_lh') samplerlh.inputs.sampling_method = "average" samplerlh.inputs.sampling_range = (0.1, 0.9, 0.1) samplerlh.inputs.sampling_units = "frac" samplerlh.inputs.interp_method = "trilinear" #samplerlh.inputs.cortex_mask = True samplerlh.inputs.out_type = 'niigz' samplerlh.inputs.subjects_dir = os.environ['SUBJECTS_DIR'] samplerrh = samplerlh.clone('sampler_rh') samplerlh.inputs.hemi = 'lh' wf.connect(bandpass, 'out_file', samplerlh, 'source_file') wf.connect(register, 'out_reg_file', samplerlh, 'reg_file') wf.connect(target, 'target_subject', samplerlh, 'target_subject') samplerrh.set_input('hemi', 'rh') wf.connect(bandpass, 'out_file', samplerrh, 'source_file') wf.connect(register, 'out_reg_file', samplerrh, 'reg_file') wf.connect(target, 'target_subject', samplerrh, 'target_subject') # Combine left and right hemisphere to text file combiner = MapNode(Function(input_names=['left', 'right'], output_names=['out_file'], function=combine_hemi, imports=imports), iterfield=['left', 'right'], name="combiner") wf.connect(samplerlh, 'out_file', combiner, 'left') wf.connect(samplerrh, 'out_file', combiner, 'right') # Compute registration between the subject's structural and MNI template # This is currently set to perform a very quick registration. However, the # registration can be made significantly more accurate for cortical # structures by increasing the number of iterations # All parameters are set using the example from: # https://github.com/stnava/ANTs/blob/master/Scripts/newAntsExample.sh reg = Node(ants.Registration(), name='antsRegister') reg.inputs.output_transform_prefix = "output_" reg.inputs.transforms = ['Translation', 'Rigid', 'Affine', 'SyN'] reg.inputs.transform_parameters = [(0.1, ), (0.1, ), (0.1, ), (0.2, 3.0, 0.0)] # reg.inputs.number_of_iterations = ([[10000, 111110, 11110]]*3 + # [[100, 50, 30]]) reg.inputs.number_of_iterations = [[100, 100, 100]] * 3 + [[100, 20, 10]] reg.inputs.dimension = 3 reg.inputs.write_composite_transform = True reg.inputs.collapse_output_transforms = False reg.inputs.metric = ['Mattes'] * 3 + [['Mattes', 'CC']] reg.inputs.metric_weight = [1] * 3 + [[0.5, 0.5]] reg.inputs.radius_or_number_of_bins = [32] * 3 + [[32, 4]] reg.inputs.sampling_strategy = ['Regular'] * 3 + [[None, None]] reg.inputs.sampling_percentage = [0.3] * 3 + [[None, None]] reg.inputs.convergence_threshold = [1.e-8] * 3 + [-0.01] reg.inputs.convergence_window_size = [20] * 3 + [5] reg.inputs.smoothing_sigmas = [[4, 2, 1]] * 3 + [[1, 0.5, 0]] reg.inputs.sigma_units = ['vox'] * 4 reg.inputs.shrink_factors = [[6, 4, 2]] + [[3, 2, 1]] * 2 + [[4, 2, 1]] reg.inputs.use_estimate_learning_rate_once = [True] * 4 reg.inputs.use_histogram_matching = [False] * 3 + [True] reg.inputs.output_warped_image = 'output_warped_image.nii.gz' reg.inputs.fixed_image = \ os.path.abspath('OASIS-30_Atropos_template_in_MNI152_2mm.nii.gz') reg.inputs.num_threads = 4 reg.plugin_args = {'qsub_args': '-l nodes=1:ppn=4'} # Convert T1.mgz to nifti for using with ANTS convert = Node(freesurfer.MRIConvert(out_type='niigz'), name='convert2nii') wf.connect(fssource, 'T1', convert, 'in_file') # Mask the T1.mgz file with the brain mask computed earlier maskT1 = Node(fsl.BinaryMaths(operation='mul'), name='maskT1') wf.connect(mask, 'binary_file', maskT1, 'operand_file') wf.connect(convert, 'out_file', maskT1, 'in_file') wf.connect(maskT1, 'out_file', reg, 'moving_image') # Convert the BBRegister transformation to ANTS ITK format convert2itk = MapNode(C3dAffineTool(), iterfield=['transform_file', 'source_file'], name='convert2itk') convert2itk.inputs.fsl2ras = True convert2itk.inputs.itk_transform = True wf.connect(register, 'out_fsl_file', convert2itk, 'transform_file') if fieldmap_images: wf.connect(fieldmap, 'exf_mask', convert2itk, 'source_file') else: wf.connect(calc_median, 'median_file', convert2itk, 'source_file') wf.connect(convert, 'out_file', convert2itk, 'reference_file') # Concatenate the affine and ants transforms into a list pickfirst = lambda x: x[0] merge = MapNode(Merge(2), iterfield=['in2'], name='mergexfm') wf.connect(convert2itk, 'itk_transform', merge, 'in2') wf.connect(reg, ('composite_transform', pickfirst), merge, 'in1') # Apply the combined transform to the time series file sample2mni = MapNode(ants.ApplyTransforms(), iterfield=['input_image', 'transforms'], name='sample2mni') sample2mni.inputs.input_image_type = 3 sample2mni.inputs.interpolation = 'BSpline' sample2mni.inputs.invert_transform_flags = [False, False] sample2mni.inputs.reference_image = \ os.path.abspath('OASIS-30_Atropos_template_in_MNI152_2mm.nii.gz') sample2mni.inputs.terminal_output = 'file' wf.connect(bandpass, 'out_file', sample2mni, 'input_image') wf.connect(merge, 'out', sample2mni, 'transforms') # Sample the time series file for each subcortical roi ts2txt = MapNode(Function( input_names=['timeseries_file', 'label_file', 'indices'], output_names=['out_file'], function=extract_subrois, imports=imports), iterfield=['timeseries_file'], name='getsubcortts') ts2txt.inputs.indices = [8] + range(10, 14) + [17, 18, 26, 47] +\ range(49, 55) + [58] ts2txt.inputs.label_file = \ os.path.abspath(('OASIS-TRT-20_jointfusion_DKT31_CMA_labels_in_MNI152_' '2mm.nii.gz')) wf.connect(sample2mni, 'output_image', ts2txt, 'timeseries_file') # Save the relevant data into an output directory datasink = Node(interface=DataSink(), name="datasink") datasink.inputs.base_directory = sink_directory datasink.inputs.container = subject_id datasink.inputs.substitutions = [('_target_subject_', '')] datasink.inputs.regexp_substitutions = (r'(/_.*(\d+/))', r'/run\2') wf.connect(despiker, 'out_file', datasink, 'resting.qa.despike') wf.connect(realign, 'par_file', datasink, 'resting.qa.motion') wf.connect(tsnr, 'tsnr_file', datasink, 'resting.qa.tsnr') wf.connect(tsnr, 'mean_file', datasink, 'resting.qa.tsnr.@mean') wf.connect(tsnr, 'stddev_file', datasink, 'resting.qa.@tsnr_stddev') if fieldmap_images: wf.connect(fieldmap, 'exf_mask', datasink, 'resting.reference') else: wf.connect(calc_median, 'median_file', datasink, 'resting.reference') wf.connect(art, 'norm_files', datasink, 'resting.qa.art.@norm') wf.connect(art, 'intensity_files', datasink, 'resting.qa.art.@intensity') wf.connect(art, 'outlier_files', datasink, 'resting.qa.art.@outlier_files') wf.connect(mask, 'binary_file', datasink, 'resting.mask') wf.connect(masktransform, 'transformed_file', datasink, 'resting.mask.@transformed_file') wf.connect(register, 'out_reg_file', datasink, 'resting.registration.bbreg') wf.connect(reg, ('composite_transform', pickfirst), datasink, 'resting.registration.ants') wf.connect(register, 'min_cost_file', datasink, 'resting.qa.bbreg.@mincost') wf.connect(smooth, 'smoothed_file', datasink, 'resting.timeseries.fullpass') wf.connect(bandpass, 'out_file', datasink, 'resting.timeseries.bandpassed') wf.connect(sample2mni, 'output_image', datasink, 'resting.timeseries.mni') wf.connect(createfilter1, 'out_files', datasink, 'resting.regress.@regressors') wf.connect(createfilter2, 'out_files', datasink, 'resting.regress.@compcorr') wf.connect(sampleaparc, 'summary_file', datasink, 'resting.parcellations.aparc') wf.connect(sampleaparc, 'avgwf_txt_file', datasink, 'resting.parcellations.aparc.@avgwf') wf.connect(ts2txt, 'out_file', datasink, 'resting.parcellations.grayo.@subcortical') datasink2 = Node(interface=DataSink(), name="datasink2") datasink2.inputs.base_directory = sink_directory datasink2.inputs.container = subject_id datasink2.inputs.substitutions = [('_target_subject_', '')] datasink2.inputs.regexp_substitutions = (r'(/_.*(\d+/))', r'/run\2') wf.connect(combiner, 'out_file', datasink2, 'resting.parcellations.grayo.@surface') return wf
# FreeSurferSource - Data grabber specific for FreeSurfer data fssource = Node(FreeSurferSource(subjects_dir=fs_dir), run_without_submitting=True, name='fssource') # Convert FreeSurfer's MGZ format into NIfTI format convert2nii = Node(MRIConvert(out_type='nii'), name='convert2nii') # Coregister the median to the surface bbregister = Node(BBRegister(init='fsl', contrast_type='t2', out_fsl_file=True), name='bbregister') # Convert the BBRegister transformation to ANTS ITK format convert2itk = Node(C3dAffineTool(fsl2ras=True, itk_transform=True), name='convert2itk') # Concatenate BBRegister's and ANTS' transforms into a list merge = Node(Merge(2), iterfield=['in2'], name='mergexfm') # Transform the contrast images. First to anatomical and then to the target warpall = MapNode(ApplyTransforms(args='--float', input_image_type=3, interpolation='Linear', invert_transform_flags=[False, False], num_threads=1, reference_image=template, terminal_output='file'), name='warpall', iterfield=['input_image'])
btr.inputs.frac = 0.25 btr.out_file = 'dwi_brain.nii.gz' btr.run() ##### registering dwi to T1 flt = fsl.FLIRT() flt.inputs.in_file = 'dwi_brain.nii.gz' flt.inputs.reference = T1_image flt.inputs.dof = 6 flt.inputs.out_matrix_file = 'transform_dwiToT1.mat' flt.inputs.out_file = 'dwi_brain_ToT1.nii.gz' flt.inputs.output_type = "NIFTI_GZ" flt.run() # convert fsl-flirt out into itk format for ants later c3 = C3dAffineTool() c3.inputs.transform_file = 'transform_dwiToT1.mat' c3.inputs.itk_transform = 'transform_dwiToT1_itk.mat' c3.inputs.reference_file = T1_image c3.inputs.source_file = dwi_image c3.inputs.fsl2ras = True c3.run() ##### Step 2, apply all transforms (dwi -->> T1 --> mni) #### # ants transform matrices (T1 -->> mni1mm) ants_dir = os.path.join(data_dir, subject_dayX, 'preprocessed/anat/transforms2mni') # flirt transform matrices (dwi -->> T1) flirt_dir = os.path.join(data_dir, subject_id,
def func2mni(stdreg, carpet_plot="", wf_name='func2mni', SinkTag="func_preproc"): """ stdreg: either globals._RegType_.ANTS or globals._RegType_.FSL (do default value to make sure the user has to decide explicitly) Transaform 4D functional image to MNI space. carpet_plot: string specifying the tag parameter for carpet plot of the standardized MRI measurement (default is "": no carpet plot) if not "", inputs atlaslabels and confounds should be defined (it might work with defaults, though) Workflow inputs: :param func :param linear_reg_mtrx :param nonlinear_reg_mtrx :param reference_brain :param atlas (optional) :param confounds (optional) :param confound_names (optional) Workflow outputs: :return: anat2mni_workflow - workflow anat="/home/balint/Dokumentumok/phd/essen/PAINTER/probe/MS001/highres.nii.gz", brain="/home/balint/Dokumentumok/phd/essen/PAINTER/probe/MS001/highres_brain.nii.gz", Balint Kincses [email protected] 2018 """ import os import nipype.pipeline as pe import nipype.interfaces.utility as utility import nipype.interfaces.fsl as fsl import nipype.interfaces.fsl as fsl import nipype.interfaces.ants as ants from nipype.interfaces.c3 import C3dAffineTool import PUMI.utils.globals as globals import PUMI.func_preproc.Onevol as onevol import PUMI.utils.QC as qc import nipype.interfaces.io as io from nipype.interfaces.utility import Function SinkDir = os.path.abspath(globals._SinkDir_ + "/" + SinkTag) if not os.path.exists(SinkDir): os.makedirs(SinkDir) inputspec = pe.Node( utility.IdentityInterface(fields=[ 'func', 'anat', # only obligatory if stdreg==globals._RegType_.ANTS 'linear_reg_mtrx', 'nonlinear_reg_mtrx', 'reference_brain', 'atlas', 'confounds', 'confound_names' ]), name='inputspec') inputspec.inputs.atlas = globals._FSLDIR_ + '/data/atlases/HarvardOxford/HarvardOxford-cort-maxprob-thr25-2mm.nii.gz' inputspec.inputs.reference_brain = globals._FSLDIR_ + "/data/standard/MNI152_T1_3mm_brain.nii.gz" #3mm by default # TODO: this does not work with the iterfiled definition for ref_file below: # TODO: it should be sepcified in a function argument, whether it shopuld be iterated #TODO_ready: ANTS #TODO: make resampling voxel size for func parametrizable # apply transformation martices if stdreg == globals._RegType_.FSL: applywarp = pe.MapNode(interface=fsl.ApplyWarp(interp="spline", ), iterfield=['in_file', 'field_file', 'premat'], name='applywarp') myqc = qc.vol2png("func2mni", wf_name + "_FSL", overlayiterated=False) myqc.inputs.slicer.image_width = 500 # 500 # for the 2mm template myqc.inputs.slicer.threshold_edges = 0.1 # 0.1 # for the 2mm template else: #ANTs # source file for C3dAffineToolmust not be 4D, so we extract the one example vol myonevol = onevol.onevol_workflow() # concat premat and ants transform bbr2ants = pe.MapNode( interface=C3dAffineTool(fsl2ras=True, itk_transform=True), iterfield=['source_file', 'transform_file', 'reference_file'], # output: 'itk_transform' name="bbr2ants") #concat trfs into a list trflist = pe.MapNode(interface=Function( input_names=['trf_first', 'trf_second'], output_names=['trflist'], function=transformlist), iterfield=['trf_first', 'trf_second'], name="collect_trf") applywarp = pe.MapNode(interface=ants.ApplyTransforms( interpolation="BSpline", input_image_type=3), iterfield=['input_image', 'transforms'], name='applywarp') myqc = qc.vol2png("func2mni", wf_name + "_ANTS3", overlayiterated=False) myqc.inputs.slicer.image_width = 500 # 500 # for the 2mm template myqc.inputs.slicer.threshold_edges = 0.1 # 0.1 # for the 2mm template if carpet_plot: fmri_qc = qc.fMRI2QC("carpet_plots", tag=carpet_plot) outputspec = pe.Node(utility.IdentityInterface(fields=['func_std']), name='outputspec') # Save outputs which are important ds_nii = pe.Node(interface=io.DataSink(), name='ds_nii') ds_nii.inputs.base_directory = SinkDir ds_nii.inputs.regexp_substitutions = [("(\/)[^\/]*$", wf_name + ".nii.gz")] analysisflow = pe.Workflow(wf_name) analysisflow.base_dir = '.' if stdreg == globals._RegType_.FSL: analysisflow.connect(inputspec, 'func', applywarp, 'in_file') analysisflow.connect(inputspec, 'linear_reg_mtrx', applywarp, 'premat') analysisflow.connect(inputspec, 'nonlinear_reg_mtrx', applywarp, 'field_file') analysisflow.connect(inputspec, 'reference_brain', applywarp, 'ref_file') analysisflow.connect(applywarp, 'out_file', outputspec, 'func_std') analysisflow.connect(applywarp, 'out_file', myqc, 'inputspec.bg_image') analysisflow.connect(inputspec, 'reference_brain', myqc, 'inputspec.overlay_image') analysisflow.connect(applywarp, 'out_file', ds_nii, 'func2mni') else: # ANTs analysisflow.connect(inputspec, 'func', myonevol, 'inputspec.func') analysisflow.connect(myonevol, 'outputspec.func1vol', bbr2ants, 'source_file') analysisflow.connect(inputspec, 'linear_reg_mtrx', bbr2ants, 'transform_file') analysisflow.connect(inputspec, 'anat', bbr2ants, 'reference_file') analysisflow.connect(bbr2ants, 'itk_transform', trflist, 'trf_first') analysisflow.connect(inputspec, 'nonlinear_reg_mtrx', trflist, 'trf_second') analysisflow.connect(trflist, 'trflist', applywarp, 'transforms') analysisflow.connect(inputspec, 'func', applywarp, 'input_image') analysisflow.connect(inputspec, 'reference_brain', applywarp, 'reference_image') analysisflow.connect(applywarp, 'output_image', outputspec, 'func_std') analysisflow.connect(applywarp, 'output_image', myqc, 'inputspec.bg_image') analysisflow.connect(inputspec, 'reference_brain', myqc, 'inputspec.overlay_image') analysisflow.connect(applywarp, 'output_image', ds_nii, 'func2mni') if carpet_plot: if stdreg == globals._RegType_.FSL: analysisflow.connect(applywarp, 'out_file', fmri_qc, 'inputspec.func') else: # ANTs analysisflow.connect(applywarp, 'output_image', fmri_qc, 'inputspec.func') analysisflow.connect(inputspec, 'atlas', fmri_qc, 'inputspec.atlas') analysisflow.connect(inputspec, 'confounds', fmri_qc, 'inputspec.confounds') return analysisflow
def preproc_workflow(input_dir, output_dir, subject_list, ses_list, anat_file, func_file, scan_size=477, bet_frac=0.37): """ The preprocessing workflow used in the preparation of the psilocybin vs escitalopram rsFMRI scans. Workflows and notes are defined throughout. Inputs are designed to be general and masks/default MNI space is provided :param input_dir: The input file directory containing all scans in BIDS format :param output_dir: The output file directory :param subject_list: a list of subject numbers :param ses_list: a list of scan numbers (session numbers) :param anat_file: The format of the anatomical scan within the input directory :param func_file: The format of the functional scan within the input directory :param scan_size: The length of the scan by number of images, most 10 minutes scans are around 400-500 depending upon scanner defaults and parameters - confirm by looking at your data :param bet_frac: brain extraction fractional intensity threshold :return: the preprocessing workflow """ preproc = Workflow(name='preproc') preproc.base_dir = output_dir # Infosource - a function free node to iterate over the list of subject names infosource = Node(IdentityInterface(fields=['subject_id', 'ses']), name="infosource") infosource.iterables = [('subject_id', subject_list), ('ses', ses_list)] # SelectFiles - to grab the data (alternative to DataGrabber) templates = { 'anat': anat_file, 'func': func_file } # define the template of each file input selectfiles = Node(SelectFiles(templates, base_directory=input_dir), name="selectfiles") # Datasink - creates output folder for important outputs datasink = Node(DataSink(base_directory=output_dir, container=output_dir), name="datasink") preproc.connect([(infosource, selectfiles, [('subject_id', 'subject_id'), ('ses', 'ses')])]) ''' This is your functional processing workflow, used to trim scans, despike the signal, slice-time correct, and motion correct your data ''' fproc = Workflow(name='fproc') # the functional processing workflow # ExtractROI - skip dummy scans at the beginning of the recording by removing the first three trim = Node(ExtractROI(t_min=3, t_size=scan_size, output_type='NIFTI_GZ'), name="trim") # 3dDespike - despike despike = Node(Despike(outputtype='NIFTI_GZ', args='-NEW'), name="despike") fproc.connect([(trim, despike, [('roi_file', 'in_file')])]) preproc.connect([(selectfiles, fproc, [('func', 'trim.in_file')])]) # 3dTshift - slice time correction slicetime = Node(TShift(outputtype='NIFTI_GZ', tpattern='alt+z2'), name="slicetime") fproc.connect([(despike, slicetime, [('out_file', 'in_file')])]) # 3dVolreg - correct motion and output 1d matrix moco = Node(Volreg(outputtype='NIFTI_GZ', interp='Fourier', zpad=4, args='-twopass'), name="moco") fproc.connect([(slicetime, moco, [('out_file', 'in_file')])]) moco_bpfdt = Node( MOCObpfdt(), name='moco_bpfdt' ) # use the matlab function to correct the motion regressor fproc.connect([(moco, moco_bpfdt, [('oned_file', 'in_file')])]) ''' This is the co-registration workflow using FSL and ANTs ''' coreg = Workflow(name='coreg') # BET - structural data brain extraction bet_anat = Node(BET(output_type='NIFTI_GZ', frac=bet_frac, robust=True), name="bet_anat") # FSL segmentation process to get WM map seg = Node(FAST(bias_iters=6, img_type=1, output_biascorrected=True, output_type='NIFTI_GZ'), name="seg") coreg.connect([(bet_anat, seg, [('out_file', 'in_files')])]) # functional to structural registration mean = Node(MCFLIRT(mean_vol=True, output_type='NIFTI_GZ'), name="mean") # BBR using linear methods for initial transform fit func2struc = Node(FLIRT(cost='bbr', dof=6, output_type='NIFTI_GZ'), name='func2struc') coreg.connect([(seg, func2struc, [('restored_image', 'reference')])]) coreg.connect([(mean, func2struc, [('mean_img', 'in_file')])]) coreg.connect([(seg, func2struc, [(('tissue_class_files', pickindex, 2), 'wm_seg')])]) # convert the FSL linear transform into a C3d format for AFNI f2s_c3d = Node(C3dAffineTool(itk_transform=True, fsl2ras=True), name='f2s_c3d') coreg.connect([(func2struc, f2s_c3d, [('out_matrix_file', 'transform_file') ])]) coreg.connect([(mean, f2s_c3d, [('mean_img', 'source_file')])]) coreg.connect([(seg, f2s_c3d, [('restored_image', 'reference_file')])]) # Functional to structural registration via ANTs non-linear registration reg = Node(Registration( fixed_image='default_images/MNI152_T1_2mm_brain.nii.gz', transforms=['Affine', 'SyN'], transform_parameters=[(0.1, ), (0.1, 3.0, 0.0)], number_of_iterations=[[1500, 1000, 1000], [100, 70, 50, 20]], dimension=3, write_composite_transform=True, collapse_output_transforms=True, metric=['MI'] + ['CC'], metric_weight=[1] * 2, radius_or_number_of_bins=[32] + [4], convergence_threshold=[1.e-8, 1.e-9], convergence_window_size=[20] + [10], smoothing_sigmas=[[2, 1, 0], [4, 2, 1, 0]], sigma_units=['vox'] * 2, shrink_factors=[[4, 2, 1], [6, 4, 2, 1]], use_histogram_matching=[False] + [True], use_estimate_learning_rate_once=[True, True], output_warped_image=True), name='reg') coreg.connect([(seg, reg, [('restored_image', 'moving_image')]) ]) # connect segmentation node to registration node merge1 = Node(niu.Merge(2), iterfield=['in2'], name='merge1') # merge the linear and nonlinear transforms coreg.connect([(f2s_c3d, merge1, [('itk_transform', 'in2')])]) coreg.connect([(reg, merge1, [('composite_transform', 'in1')])]) # warp the functional images into MNI space using the transforms from FLIRT and SYN warp = Node(ApplyTransforms( reference_image='default_images/MNI152_T1_2mm_brain.nii.gz', input_image_type=3), name='warp') coreg.connect([(moco, warp, [('out_file', 'input_image')])]) coreg.connect([(merge1, warp, [('out', 'transforms')])]) preproc.connect([(selectfiles, coreg, [('anat', 'bet_anat.in_file')])]) preproc.connect([(fproc, coreg, [('moco.out_file', 'mean.in_file')])]) ''' Scrubbing workflow - find the motion outliers, bandpass filter, re-mean the data after bpf ''' scrub = Workflow(name='scrub') # Generate the Scrubbing Regressor scrub_metrics = Node(MotionOutliers(dummy=4, out_file='FD_outliers.1D', metric='fd', threshold=0.4), name="scrub_metrics") # regress out timepoints scrub_frames = Node(Bandpass(highpass=0, lowpass=99999, outputtype='NIFTI_GZ'), name='scrub_frames') scrub.connect([(scrub_metrics, scrub_frames, [('out_file', 'orthogonalize_file')])]) preproc.connect([(coreg, scrub, [('warp.output_image', 'scrub_frames.in_file')])]) preproc.connect([(selectfiles, scrub, [('func', 'scrub_metrics.in_file')]) ]) # mean image for remeaning after bandpass premean = Node(TStat(args='-mean', outputtype='NIFTI_GZ'), name='premean') # remean the image remean2 = Node(Calc(expr='a+b', outputtype='NIFTI_GZ'), name='remean2') scrub.connect([(scrub_frames, remean2, [('out_file', 'in_file_a')])]) scrub.connect([(premean, remean2, [('out_file', 'in_file_b')])]) preproc.connect([(coreg, scrub, [('warp.output_image', 'premean.in_file')]) ]) ''' Regressors for final cleaning steps ''' regressors = Workflow(name='regressors') # Using registered structural image to create the masks for both WM and CSF regbet = Node(BET(robust=True, frac=0.37, output_type='NIFTI_GZ'), name='regbet') regseg = Node(FAST(img_type=1, output_type='NIFTI_GZ', no_pve=True, no_bias=True, segments=True), name='regseg') regressors.connect([(regbet, regseg, [('out_file', 'in_files')])]) preproc.connect([(coreg, regressors, [('reg.warped_image', 'regbet.in_file')])]) ''' Create a cerebrospinal fluid (CSF) regressor ''' # subtract subcortical GM from the CSF mask subcortgm = Node(BinaryMaths( operation='sub', operand_file='default_images/subcortical_gm_mask_bin.nii.gz', output_type='NIFTI_GZ', args='-bin'), name='subcortgm') regressors.connect([(regseg, subcortgm, [(('tissue_class_files', pickindex, 0), 'in_file')])]) # Fill the mask holes fillcsf = Node(MaskTool(fill_holes=True, outputtype='NIFTI_GZ'), name='fillcsf') regressors.connect([(subcortgm, fillcsf, [('out_file', 'in_file')])]) # Erode the mask erocsf = Node(MaskTool(outputtype='NIFTI_GZ', dilate_inputs='-1'), name='erocsf') regressors.connect([(fillcsf, erocsf, [('out_file', 'in_file')])]) # Take mean csf signal from functional image meancsf = Node(ImageMeants(output_type='NIFTI_GZ'), name='meancsf') regressors.connect([(erocsf, meancsf, [('out_file', 'mask')])]) preproc.connect([(coreg, regressors, [('warp.output_image', 'meancsf.in_file')])]) bpf_dt_csf = Node(CSFbpfdt(), name='bpf_dt_csf') regressors.connect([(meancsf, bpf_dt_csf, [('out_file', 'in_file')])]) ''' Creates a local white matter regressor ''' # subtract subcortical gm subcortgm2 = Node(BinaryMaths( operation='sub', operand_file='default_images/subcortical_gm_mask_bin.nii.gz', output_type='NIFTI_GZ', args='-bin'), name='subcortgm2') regressors.connect([(regseg, subcortgm2, [(('tissue_class_files', pickindex, 2), 'in_file')])]) # fill mask fillwm = Node(MaskTool(fill_holes=True, outputtype='NIFTI_GZ'), name='fillwm') regressors.connect([(subcortgm2, fillwm, [('out_file', 'in_file')])]) # erod mask erowm = Node(MaskTool(outputtype='NIFTI_GZ', dilate_inputs='-1'), name='erowm') regressors.connect([(fillwm, erowm, [('out_file', 'in_file')])]) # generate local wm localwm = Node(Localstat(neighborhood=('SPHERE', 25), stat='mean', nonmask=True, outputtype='NIFTI_GZ'), name='localwm') regressors.connect([(erowm, localwm, [('out_file', 'mask_file')])]) preproc.connect([(coreg, regressors, [('warp.output_image', 'localwm.in_file')])]) # bandpass filter the local wm regressor localwm_bpf = Node(Fourier(highpass=0.01, lowpass=0.08, args='-retrend', outputtype='NIFTI_GZ'), name='loacwm_bpf') regressors.connect([(localwm, localwm_bpf, [('out_file', 'in_file')])]) # detrend the local wm regressor localwm_bpf_dt = Node(Detrend(args='-polort 2', outputtype='NIFTI_GZ'), name='localwm_bpf_dt') regressors.connect([(localwm_bpf, localwm_bpf_dt, [('out_file', 'in_file') ])]) ''' Clean up your functional image with the regressors you have created above ''' # create a mask for blurring filtering, and detrending clean = Workflow(name='clean') mask = Node(BET(mask=True, functional=True), name='mask') mean_mask = Node(MCFLIRT(mean_vol=True, output_type='NIFTI_GZ'), name="mean_mask") dilf = Node(DilateImage(operation='max', output_type='NIFTI_GZ'), name='dilf') clean.connect([(mask, dilf, [('mask_file', 'in_file')])]) preproc.connect([(scrub, clean, [('remean2.out_file', 'mask.in_file')])]) fill = Node(MaskTool(in_file='default_images/MNI152_T1_2mm_brain.nii.gz', fill_holes=True, outputtype='NIFTI_GZ'), name='fill') axb = Node(Calc(expr='a*b', outputtype='NIFTI_GZ'), name='axb') clean.connect([(dilf, axb, [('out_file', 'in_file_a')])]) clean.connect([(fill, axb, [('out_file', 'in_file_b')])]) bxc = Node(Calc(expr='ispositive(a)*b', outputtype='NIFTI_GZ'), name='bxc') clean.connect([(mean_mask, bxc, [('mean_img', 'in_file_a')])]) clean.connect([(axb, bxc, [('out_file', 'in_file_b')])]) preproc.connect([(scrub, clean, [('remean2.out_file', 'mean_mask.in_file') ])]) #### BLUR, FOURIER BPF, and DETREND blurinmask = Node(BlurInMask(fwhm=6, outputtype='NIFTI_GZ'), name='blurinmask') clean.connect([(bxc, blurinmask, [('out_file', 'mask')])]) preproc.connect([(scrub, clean, [('remean2.out_file', 'blurinmask.in_file') ])]) fourier = Node(Fourier(highpass=0.01, lowpass=0.08, retrend=True, outputtype='NIFTI_GZ'), name='fourier') clean.connect([(blurinmask, fourier, [('out_file', 'in_file')])]) tstat = Node(TStat(args='-mean', outputtype='NIFTI_GZ'), name='tstat') clean.connect([(fourier, tstat, [('out_file', 'in_file')])]) detrend = Node(Detrend(args='-polort 2', outputtype='NIFTI_GZ'), name='detrend') clean.connect([(fourier, detrend, [('out_file', 'in_file')])]) remean = Node(Calc(expr='a+b', outputtype='NIFTI_GZ'), name='remean') clean.connect([(detrend, remean, [('out_file', 'in_file_a')])]) clean.connect([(tstat, remean, [('out_file', 'in_file_b')])]) concat = Node(ConcatModel(), name='concat') # Removes nuisance regressors via regression function clean_rs = Node(Bandpass(highpass=0, lowpass=99999, outputtype='NIFTI_GZ'), name='clean_rs') clean.connect([(concat, clean_rs, [('out_file', 'orthogonalize_file')])]) remean1 = Node(Calc(expr='a+b', outputtype='NIFTI_GZ'), name='remean1') clean.connect([(clean_rs, remean1, [('out_file', 'in_file_a')])]) clean.connect([(tstat, remean1, [('out_file', 'in_file_b')])]) preproc.connect([(regressors, clean, [('bpf_dt_csf.out_file', 'concat.in_file_a')])]) preproc.connect([(fproc, clean, [('moco_bpfdt.out_file', 'concat.in_file_b')])]) preproc.connect([(regressors, clean, [('localwm_bpf_dt.out_file', 'clean_rs.orthogonalize_dset')])]) clean.connect([(remean, clean_rs, [('out_file', 'in_file')])]) ''' Write graphical output detailing the workflows and nodes ''' fproc.write_graph(graph2use='flat', format='png', simple_form=True, dotfilename='./fproc.dot') fproc.write_graph(graph2use='colored', format='png', simple_form=True, dotfilename='./fproc_color.dot') coreg.write_graph(graph2use='flat', format='png', simple_form=True, dotfilename='./coreg.dot') coreg.write_graph(graph2use='colored', format='png', simple_form=True, dotfilename='./coreg_color.dot') scrub.write_graph(graph2use='flat', format='png', simple_form=True, dotfilename='./scrub.dot') scrub.write_graph(graph2use='colored', format='png', simple_form=True, dotfilename='./scrub_color.dot') regressors.write_graph(graph2use='flat', format='png', simple_form=True, dotfilename='./reg.dot') regressors.write_graph(graph2use='colored', format='png', simple_form=True, dotfilename='./reg_color.dot') preproc.write_graph(graph2use='flat', format='png', simple_form=True, dotfilename='./preproc.dot') preproc.write_graph(graph2use='colored', format='png', simple_form=True, dotfilename='./preproc_color.dot') return preproc
preprocessing.connect(meanbetfunc, 'out_file', mean2anatbbr, 'in_file') preprocessing.connect(binarize, 'out_file', mean2anatbbr, 'wm_seg') preprocessing.connect(bet_struct, 'out_file', mean2anatbbr, 'reference') preprocessing.connect(mean2anat, 'out_matrix_file', mean2anatbbr, 'in_matrix_file') preprocessing.connect(mean2anat, 'out_matrix_file', datasink, 'mean2anat.out_matrix') preprocessing.connect(mean2anatbbr, 'out_matrix_file', datasink, 'bbr.out_matrix') preprocessing.connect(mean2anatbbr, 'out_file', datasink, 'bbr.out_file') # convert BBR matrix to ITK for ANTS convert2itk = pe.Node(C3dAffineTool(), name='convert2itk') convert2itk.inputs.fsl2ras = True convert2itk.inputs.itk_transform = True preprocessing.connect(mean2anatbbr, 'out_matrix_file', convert2itk, 'transform_file') preprocessing.connect(meanbetfunc, 'out_file', convert2itk, 'source_file') preprocessing.connect(bet_struct, 'out_file', convert2itk, 'reference_file') # In[ ]: # Concatenate the affine and ants transforms into a list pickfirst = lambda x: x[0]
bbreg_xfm='preproc/%s/bbreg/_fs_register0/%s*.mat', #ANTS transformation matrix created in the antsreg_wf pipeline ants_warp='norm_anat/%s/anat2targ_xfm/_subject_id_%s/%s*.h5', #mean reference image created in preproc pipeline mean_image='preproc/%s/ref/%s*.nii.gz') datasource_norm.inputs.ignore_exception = False datasource_norm.inputs.raise_on_empty = True datasource_norm.inputs.sort_filelist = True datasource_norm.inputs.subject_id = sids datasource_norm.inputs.template = '*' datasource_norm.inputs.template_args = info_norm norm_stats_wf.connect(subj_iterable, "subject_id", datasource_norm, "subject_id") #node to convert fsl-style Affine registration into ANTS compatible itk format convert2itk = Node(C3dAffineTool(), name='convert2itk') convert2itk.inputs.fsl2ras = True #transform to ITK format (Boolean) convert2itk.inputs.itk_transform = True #export ITK transform norm_stats_wf.connect(datasource_norm, 'bbreg_xfm', convert2itk, 'transform_file') norm_stats_wf.connect(datasource_norm, 'mean_image', convert2itk, 'source_file') norm_stats_wf.connect(fs_skullstrip_wf, 'outputspec.skullstripped_file', convert2itk, 'reference_file') #node to concatenate the ITK affine and ANTS transforms into a list merge_xfm = Node(Merge(2), iterfield=['in2'], name='merge_xfm') norm_stats_wf.connect(convert2itk, 'itk_transform', merge_xfm, 'in2') norm_stats_wf.connect(datasource_norm, 'ants_warp', merge_xfm, 'in1') #MapNode to warp copes to target
def init_combine_mp2rage_wf(sourcedata, derivatives, name='combine_mp2rages', n_mp2rages=2): wf = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'sourcedata', 'derivatives', 'subject', 'session', 'acquisition' ]), name='inputnode') inputnode.inputs.sourcedata = sourcedata inputnode.inputs.derivatives = derivatives get_parameters = pe.MapNode(niu.Function( function=get_mp2rage_pars, input_names=['sourcedata', 'subject', 'session', 'acquisition'], output_names=['mp2rage_parameters']), iterfield=['acquisition'], name='get_mp2rage_pars') wf.connect([(inputnode, get_parameters, [('sourcedata', 'sourcedata'), ('subject', 'subject'), ('session', 'session'), ('acquisition', 'acquisition')])]) make_t1w = pe.MapNode(niu.Function(function=fit_mp2rage, input_names=['mp2rage_parameters'], output_names=['t1w_uni', 't1map']), iterfield=['mp2rage_parameters'], name='make_t1w') wf.connect([(get_parameters, make_t1w, [('mp2rage_parameters', 'mp2rage_parameters')])]) get_first_inversion = pe.MapNode(niu.Function( function=get_inv, input_names=['mp2rage_parameters', 'inv', 'echo'], output_names='inv1'), iterfield=['mp2rage_parameters'], name='get_first_inversion') get_first_inversion.inputs.inv = 1 get_first_inversion.inputs.echo = 1 wf.connect(get_parameters, 'mp2rage_parameters', get_first_inversion, 'mp2rage_parameters') split = pe.Node(niu.Split(splits=[1, n_mp2rages - 1]), name='split') wf.connect(get_first_inversion, 'inv1', split, 'inlist') flirt = pe.MapNode(fsl.FLIRT(dof=6), iterfield=['in_file'], name='flirt') wf.connect(split, ('out1', _pickone), flirt, 'reference') wf.connect(split, 'out2', flirt, 'in_file') convert2itk = pe.MapNode(C3dAffineTool(), iterfield=['source_file', 'transform_file'], name='convert2itk') convert2itk.inputs.fsl2ras = True convert2itk.inputs.itk_transform = True wf.connect(flirt, 'out_matrix_file', convert2itk, 'transform_file') wf.connect(split, ('out1', _pickone), convert2itk, 'reference_file') wf.connect(split, 'out2', convert2itk, 'source_file') transform_t1w_wf = init_transform_to_first_image_wf('transforms_t1w', n_images=n_mp2rages) wf.connect(make_t1w, 't1w_uni', transform_t1w_wf, 'inputnode.in_files') wf.connect(convert2itk, 'itk_transform', transform_t1w_wf, 'inputnode.transforms') get_second_inversion = pe.MapNode(niu.Function( function=get_inv, input_names=['mp2rage_parameters', 'inv', 'echo'], output_names='inv2'), iterfield=['mp2rage_parameters'], name='get_second_inversion') get_second_inversion.inputs.inv = 2 transform_inv2_wf = init_transform_to_first_image_wf('transforms_inv2', n_images=n_mp2rages) wf.connect(get_parameters, 'mp2rage_parameters', get_second_inversion, 'mp2rage_parameters') wf.connect(get_second_inversion, 'inv2', transform_inv2_wf, 'inputnode.in_files') wf.connect(convert2itk, 'itk_transform', transform_inv2_wf, 'inputnode.transforms') transform_t1map_wf = init_transform_to_first_image_wf('transform_t1map', n_images=n_mp2rages) wf.connect(make_t1w, 't1map', transform_t1map_wf, 'inputnode.in_files') wf.connect(convert2itk, 'itk_transform', transform_t1map_wf, 'inputnode.transforms') ds_t1w = pe.MapNode(DerivativesDataSink(base_directory=derivatives, keep_dtype=False, out_path_base='t1w', suffix='T1w'), iterfield=['in_file', 'source_file'], name='ds_t1w') reorient_t1w = pe.MapNode(Reorient(), iterfield=['in_file'], name='reorient_t1w') wf.connect(make_t1w, 't1w_uni', reorient_t1w, 'in_file') wf.connect(reorient_t1w, 'out_file', ds_t1w, 'in_file') wf.connect(get_first_inversion, 'inv1', ds_t1w, 'source_file') ds_t1map = pe.MapNode(DerivativesDataSink(base_directory=derivatives, keep_dtype=False, out_path_base='t1map', suffix='T1w'), iterfield=['in_file', 'source_file'], name='ds_t1map') reorient_t1map = pe.MapNode(Reorient(), iterfield=['in_file'], name='reorient_t1map') wf.connect(make_t1w, 't1map', reorient_t1map, 'in_file') wf.connect(reorient_t1map, 'out_file', ds_t1map, 'in_file') wf.connect(get_first_inversion, 'inv1', ds_t1map, 'source_file') ds_t1w_average = pe.Node(DerivativesDataSink( base_directory=derivatives, keep_dtype=False, out_path_base='averaged_mp2rages', suffix='T1w', space='average'), name='ds_t1w_average') rename = pe.Node(niu.Rename(use_fullpath=True), name='rename') rename.inputs.format_string = '%(path)s/sub-%(subject_id)s_ses-%(session)s_MPRAGE.nii.gz' rename.inputs.parse_string = '(?P<path>.+)/sub-(?P<subject_id>.+)_ses-(?P<session>.+)_acq-.+_MPRAGE.nii(.gz)?' wf.connect(get_first_inversion, ('inv1', _pickone), rename, 'in_file') reorient_average_t1w = pe.Node(Reorient(), name='reorient_average_t1w') wf.connect(transform_t1w_wf, 'outputnode.mean_image', reorient_average_t1w, 'in_file') wf.connect(reorient_average_t1w, 'out_file', ds_t1w_average, 'in_file') wf.connect(rename, 'out_file', ds_t1w_average, 'source_file') ds_t1map_average = pe.Node(DerivativesDataSink( base_directory=derivatives, keep_dtype=False, out_path_base='averaged_mp2rages', suffix='T1map', space='average'), name='ds_t1map_average') reorient_t1map_average = pe.Node(Reorient(), name='reorient_t1map_average') wf.connect(rename, 'out_file', ds_t1map_average, 'source_file') wf.connect(transform_t1map_wf, 'outputnode.mean_image', reorient_t1map_average, 'in_file') wf.connect(reorient_t1map_average, 'out_file', ds_t1map_average, 'in_file') ds_inv2 = pe.Node(DerivativesDataSink(base_directory=derivatives, keep_dtype=False, out_path_base='averaged_mp2rages', suffix='INV2', space='average'), name='ds_inv2') reorient_inv2 = pe.Node(Reorient(), name='reorient_inv2') wf.connect(rename, 'out_file', ds_inv2, 'source_file') wf.connect(transform_inv2_wf, 'outputnode.mean_image', reorient_inv2, 'in_file') wf.connect(reorient_inv2, 'out_file', ds_inv2, 'in_file') return wf