def init_params(self, dwi_file: str, mask: str, out_file: str, out_noise: str): denoise = mrt.DWIDenoise() denoise.inputs.in_file = dwi_file denoise.inputs.mask = mask denoise.inputs.out_file = out_file denoise.inputs.noise = out_noise return denoise
def denoise_dwi(in_file: Path, in_mask: Path, out_file: Path): denoise = mrt.DWIDenoise() denoise.inputs.in_file = in_file denoise.inputs.noise = in_file.with_name(in_file.stem + "_noise.mif") denoise.inputs.mask = in_mask denoise.inputs.out_file = out_file print(denoise.cmdline) return denoise
def Denoise(in_file, in_mask, out_file): denoise = mrt.DWIDenoise() denoise.inputs.in_file = in_file denoise.inputs.noise = in_file.replace(".mif", "_noise.mif") denoise.inputs.mask = in_mask denoise.inputs.out_file = out_file print(denoise.cmdline) denoise.run() return out_file
def preprocess_dwi_data(self, data, index, acqp, atlas2use, ResponseSD_algorithm='tournier', fod_algorithm='csd', tract_algorithm='iFOD2', streamlines_number='10M'): ''' preprocessing of dwi data and connectome extraction Parameters ---------- subjects_dir = path to the subjects' folders data: tuple | a tuple having the path to dwi, bvecs and bvals files. It is obtained using the function grab_data() index: str | Name of text file specifying the relationship between the images in --imain and the information in --acqp and --topup. E.g. index.txt acqp: str | Name of text file with information about the acquisition of the images in --imain atlas2use: str | The input node parcellation image ResponseSD_algorithm (optional): str | Select the algorithm to be used to complete the script operation; Options are: dhollander, fa, manual, msmt_5tt, tax, tournier (Default is 'tournier') fod_algorithm (optional): str | The algorithm to use for FOD estimation. (options are: csd,msmt_csd) (Default is 'csd') tract_algorithm (optional): str | specify the tractography algorithm to use. Valid choices are: FACT, iFOD1, iFOD2, Nulldist1, Nulldist2, SD_Stream, Seedtest, Tensor_Det, Tensor_Prob (Default is 'iFOD2') streamlines_number (optional): str | set the desired number of streamlines (Default is '10M') ''' if len(data[0]) != len(data[1]): raise ValueError( 'dwi datas do not have the same shape of bvec files') if len(data[0]) != len(data[2]): raise ValueError( 'dwi datas do not have the same shape of bval files') if len(data[1]) != len(data[2]): raise ValueError( 'bvec files do not have the same shape of bvec files') for subj in range(len(data[0])): print('Extracting B0 volume for subject', subj) self.roi = fsl.ExtractROI( in_file=data[0][subj], roi_file=os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_nodiff.nii.gz'), t_min=0, t_size=1) self.roi.run() print('Converting into .mif for subject', subj) self.mrconvert = mrt.MRConvert() self.mrconvert.inputs.in_file = data[0][subj] self.mrconvert.inputs.grad_fsl = (data[1][subj], data[2][subj]) self.mrconvert.inputs.out_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi.mif') self.mrconvert.run() print('Denoising data for subject', subj) self.denoise = mrt.DWIDenoise() self.denoise.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi.mif') self.denoise.inputs.noise = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_noise.mif') self.denoise.inputs.out_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised.mif') self.denoise.run() self.denoise_convert = mrt.MRConvert() self.denoise_convert.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised.mif') self.denoise_convert.inputs.out_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised.nii.gz') self.denoise_convert.run() print('Skull stripping for subject', subj) self.mybet = fsl.BET() self.mybet.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_nodiff.nii.gz') self.mybet.inputs.out_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_denoised_brain.nii.gz') self.mybet.inputs.frac = 0.1 self.mybet.inputs.robust = True self.mybet.inputs.mask = True self.mybet.run() print('Running Eddy for subject', subj) self.eddy = Eddy() self.eddy.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised.nii.gz') self.eddy.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_denoised_brain_mask.nii.gz') self.eddy.inputs.in_acqp = acqp self.eddy.inputs.in_bvec = data[1][subj] self.eddy.inputs.in_bval = data[2][subj] self.eddy.inputs.out_base = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised_eddy.nii.gz') self.eddy.run() print('Running Bias Correction for subject', subj) self.bias_correct = mrt.DWIBiasCorrect() self.bias_correct.inputs.use_ants = True self.bias_correct.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised_eddy.nii.gz') self.bias_correct.inputs.grad_fsl = (os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised_eddy.eddy_rotated_bvecs.bvec'), data[2][subj]) self.bias_correct.inputs.bias = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_bias.mif') self.bias_correct.inputs.out_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised_eddy_unbiased.mif') self.bias_correct.run() print('Calculating Response function for subject', subj) self.resp = mrt.ResponseSD() self.resp.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised_eddy_unbiased.mif') self.resp.inputs.algorithm = ResponseSD_algorithm self.resp.inputs.grad_fsl = (os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised_eddy.eddy_rotated_bvecs.bvec'), data[2][subj]) self.resp.inputs.wm_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_response.txt') self.resp.run() print('Estimating FOD for subject', subj) self.fod = mrt.EstimateFOD() self.fod.inputs.algorithm = fod_algorithm self.fod.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_dwi_denoised_eddy_unbiased.mif') self.fod.inputs.wm_txt = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_response.txt') self.fod.inputs.mask_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_denoised_brain_mask.nii.gz') self.fod.inputs.grad_fsl = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_response.txt') self.fod.run() print('Extracting whole brain tract for subject', subj) self.tk = mrt.Tractography() self.tk.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + 'fods.mif') self.tk.inputs.roi_mask = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_denoised_brain_mask.nii.gz') self.tk.inputs.algorithm = tract_algorithm self.tk.inputs.seed_image = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_denoised_brain_mask.nii.gz') self.tk.inputs.select = streamlines_number self.tk.inputs.out_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_whole_brain_' + streamlines_number + '.tck') self.tk.run() print('Extracting connectome for subject', subj) self.mat = mrt.BuildConnectome() self.mat.inputs.in_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_whole_brain_' + streamlines_number + '.tck') self.mat.inputs.in_parc = atlas2use self.mat.inputs.out_file = os.path.join( os.path.split(data[0][subj])[0] + '/' + os.path.split(data[0][0])[1].split(".nii.gz")[0] + '_connectome.csv') self.mat.run()
import os from os.path import abspath from datetime import datetime from IPython.display import Image import pydot from nipype import Workflow, Node, MapNode, Function, config from nipype.interfaces.fsl import TOPUP, ApplyTOPUP, BET, ExtractROI, Eddy, FLIRT, FUGUE from nipype.interfaces.fsl.maths import MathsCommand import nipype.interfaces.utility as util import nipype.interfaces.mrtrix3 as mrt #Requirements for the workflow to run smoothly: All files as in NIfTI-format and named according to the following standard: #Images are from the tonotopy DKI sequences on the 7T Philips Achieva scanner in Lund. It should work with any DKI sequence and possibly also a standard DTI but the setting for B0-corrections, epi-distortion corrections and eddy current corrections will be wrong. #DKI file has a base name shared with bvec and bval in FSL format. E.g. "DKI.nii.gz" "DKI.bvec" and "DKI.bval". #There is one b0-volume with reversed (P->A) phase encoding called DKIbase+_revenc. E.g. "DKI_revenc.nii.gz". #Philips B0-map magnitude and phase offset (in Hz) images. #One input file for topup describing the images as specified by topup. #Set nbrOfThreads to number of available CPU threads to run the analyses. ### Need to make better revenc for the 15 version if we choose to use it (i.e. same TE and TR) #Set to relevant directory/parameters datadir=os.path.abspath("/Users/ling-men/Documents/MRData/testDKI") rawDKI_base='DKI_15' B0map_base = 'B0map' nbrOfThreads=6 print_graph = True acqparam_file = os.path.join(datadir,'acqparams.txt') index_file = os.path.join(datadir,'index.txt') #### #config.enable_debug_mode() DKI_nii=os.path.join(datadir, rawDKI_base+'.nii.gz') DKI_bval=os.path.join(datadir, rawDKI_base+'.bval')
def create_DWI_workflow( subject_list, bids_dir, work_dir, out_dir, bids_templates, ): # create initial workflow wf = Workflow(name='DWI', base_dir=work_dir) # use infosource to iterate workflow across subject list n_infosource = Node(interface=IdentityInterface(fields=['subject_id']), name="subject_source" # input: 'subject_id' # output: 'subject_id' ) # runs the node with subject_id = each element in subject_list n_infosource.iterables = ('subject_id', subject_list) # select matching files from bids_dir n_selectfiles = Node(interface=SelectFiles(templates=bids_templates, base_directory=bids_dir), name='get_subject_data') wf.connect([(n_infosource, n_selectfiles, [('subject_id', 'subject_id_p')]) ]) # DWIDenoise # https://nipype.readthedocs.io/en/latest/api/generated/nipype.interfaces.mrtrix3.preprocess.html n_denoise = Node(interface=mrt.DWIDenoise(), name='n_denoise') wf.connect([(n_selectfiles, n_denoise, [('DWI_all', 'in_file')])]) # datasink n_datasink = Node(interface=DataSink(base_directory=out_dir), name='datasink') wf.connect([(n_selectfiles, n_datasink, [('all_b0_PA', 'all_b0_PA_unchanged')]), (n_denoise, n_datasink, [('out_file', 'DWI_all_denoised')])]) ########## I'VE ADDED IN ########################################################################## # MRDeGibbs # https://nipype.readthedocs.io/en/latest/api/generated/nipype.interfaces.mrtrix3.preprocess.html n_degibbs = Node( interface=mrt.MRDeGibbs(out_file='DWI_all_denoised_degibbs.mif'), name='n_degibbs') wf.connect([(n_denoise, n_degibbs, [('out_file', 'in_file')])]) wf.connect([(n_degibbs, n_datasink, [('out_file', 'DWI_all_denoised_degibbs.mif')])]) # DWI Extract n_dwiextract = Node(interface=mrt.DWIExtract(bzero=True, out_file='b0vols.mif'), name='n_dwiextract') wf.connect([(n_degibbs, n_dwiextract, [('out_file', 'in_file')])]) wf.connect([(n_dwiextract, n_datasink, [('out_file', 'noddi_b0_degibbs')]) ]) # MRcat n_mrcat = Node( interface=mrcatfunc.MRCat( #axis=3, out_file='b0s.mif'), name='n_mrcat') # Connect DTI_B0_PA to mrcat node wf.connect([(n_selectfiles, n_mrcat, [('DTI_B0_PA', 'in_file1')])]) wf.connect([(n_dwiextract, n_mrcat, [('out_file', 'in_file2')])]) # Output the mrcat file into file 'noddi_and_PA_b0s.mif' wf.connect([(n_mrcat, n_datasink, [('out_file', 'noddi_and_PA_b0s.mif')])]) # DWIfslpreproc n_dwifslpreproc = Node(interface=preprocfunc.DWIFslPreProc( out_file='preprocessedDWIs.mif', use_header=True), name='n_dwifslpreproc') # Connect output of degibbs to dwifslpreproc node wf.connect([(n_degibbs, n_dwifslpreproc, [('out_file', 'in_file')])]) # Connect output of mrcat to se_epi input wf.connect([(n_mrcat, n_dwifslpreproc, [('out_file', 'se_epi_file')])]) # Put output of dwifslpreproc into 'preprocessedDWIs.mif' wf.connect([(n_dwifslpreproc, n_datasink, [('out_file', 'preprocessedDWIs.mif')])]) # DWI bias correct n_dwibiascorrect = Node( interface=preprocess.DWIBiasCorrect(use_ants=True), name='n_dwibiascorrect', ) wf.connect([(n_dwifslpreproc, n_dwibiascorrect, [('out_file', 'in_file')]) ]) wf.connect([(n_dwibiascorrect, n_datasink, [('out_file', 'ANTSpreprocessedDWIs.mif')])]) #DWI2mask n_dwi2mask = Node(interface=mrt.BrainMask(out_file='mask.mif'), name='n_dwi2mask') wf.connect([(n_dwibiascorrect, n_dwi2mask, [('out_file', 'in_file')])]) wf.connect([(n_dwi2mask, n_datasink, [('out_file', 'mask.mif')])]) ## A) Fixel-based analysis #DWI2response n_dwi2response = Node(interface=mrt.ResponseSD(algorithm='dhollander', wm_file='wm_res.txt', gm_file='gm_res.txt', csf_file='csf_res.txt'), name='n_dwi2response') wf.connect([(n_dwibiascorrect, n_dwi2response, [('out_file', 'in_file')])]) wf.connect([(n_dwi2response, n_datasink, [('wm_file', 'wm_res.txt')])]) wf.connect([(n_dwi2response, n_datasink, [('gm_file', 'gm_res.txt')])]) wf.connect([(n_dwi2response, n_datasink, [('csf_file', 'csf_res.txt')])]) #DWI2fod n_dwi2fod = Node(interface=mrt.ConstrainedSphericalDeconvolution( algorithm='msmt_csd', wm_odf='wmfod.mif', gm_odf='gmfod.mif', csf_odf='csffod.mif'), name='n_dwi2fod') # connect outputs of dwi2fod into dwi2response wf.connect([(n_dwibiascorrect, n_dwi2fod, [('out_file', 'in_file')])]) wf.connect([(n_dwi2response, n_dwi2fod, [('wm_file', 'wm_txt')])]) wf.connect([(n_dwi2response, n_dwi2fod, [('gm_file', 'gm_txt')])]) wf.connect([(n_dwi2response, n_dwi2fod, [('csf_file', 'csf_txt')])]) # output wmfod file from dwi2fod wf.connect([(n_dwi2fod, n_datasink, [('wm_odf', 'wmfod.mif')])]) wf.connect([(n_dwi2fod, n_datasink, [('gm_odf', 'gmfod.mif')])]) wf.connect([(n_dwi2fod, n_datasink, [('csf_odf', 'csffod.mif')])]) #mrconvert to extract Z component of wmfod n_mrconvert_fod = Node(interface=utils.MRConvert(out_file='Zwmfod.mif', coord=[3, 0]), name='n_mrconvert_fod') wf.connect([(n_dwi2fod, n_mrconvert_fod, [('wm_odf', 'in_file')])]) wf.connect([(n_mrconvert_fod, n_datasink, [('out_file', 'Zwmfod.mif')])]) # Concatenate all wm, gm, csf fod files to see their distribution throughout Brain n_mrcat_fod = Node(interface=mrcatfunc.MRCat(out_file='vf.mif'), name='n_mrcat_fod') # Connect Zwmfod, gmfod and csffod as inputs wf.connect([(n_mrconvert_fod, n_mrcat_fod, [('out_file', 'in_file1')])]) wf.connect([(n_dwi2fod, n_mrcat_fod, [('gm_odf', 'in_file2')])]) wf.connect([(n_dwi2fod, n_mrcat_fod, [('csf_odf', 'in_file3')])]) # Output the mrcat file into file into 'vf.mif' wf.connect([(n_mrcat_fod, n_datasink, [('out_file', 'vf.mif')])]) #fod2fixel wmfod.mif wmfixels -fmls_peak_value 0 -fmls_integral 0.10 -afd afd.mif -peak peak.mif -disp disp.mif # OUTPUTS: -afd afd.mif -peak peak.mif -disp disp.mif n_fod2fixel = Node( interface=fod2fixelfunc.fod2fixel( out_file='wmfixels', #afd_file = 'afd.mif', peak_file='peak.mif', disp_file='disp.mif'), name='n_fod2fixel') # let the peak value parameter be trialed as multiple values n_fod2fixel.iterables = ('fmls_peak_value', [0, 0.10, 0.50]) n_fod2fixel.iterables = ('fmls_integral', [0, 0.10, 0.50]) # obtain wm fibre image as input wf.connect([(n_dwi2fod, n_fod2fixel, [('wm_odf', 'in_file')])]) # ouputs of fod2fixel wf.connect([(n_fod2fixel, n_datasink, [('out_file', 'wmfixels')])]) wf.connect([(n_fod2fixel, n_datasink, [('afd_file', 'afd.mif')])]) wf.connect([(n_fod2fixel, n_datasink, [('peak_file', 'peak.mif')])]) wf.connect([(n_fod2fixel, n_datasink, [('disp_file', 'disp.mif')])]) ## Fixel2peaks n_fixel2peaks = Node(interface=fixel2peaksfunc.fixel2peaks( out_file='peaks_wmdirections.mif'), name='n_fixel2peaks') n_fixel2peaks.iterables = ('number', [1, 2, 3]) # obtain directions file in output folder of fod2fixel, as input wf.connect([(n_fod2fixel, n_fixel2peaks, [('out_file', 'in_file')])]) # ouputs of fixel2peaks wf.connect([(n_fixel2peaks, n_datasink, [('out_file', 'peaks_wmdirections.mif')])]) #mrmath to find normalised value of peak WM directions n_mrmath = Node(interface=mrt.MRMath( axis=3, operation='norm', out_file='norm_peaks_wmdirections.mif'), name='n_mrmath') wf.connect([(n_fixel2peaks, n_mrmath, [('out_file', 'in_file')])]) wf.connect([(n_mrmath, n_datasink, [('out_file', 'norm_peaks_wmdirections.mif')])]) # mrcalc to divide peak WM direction by normalised value n_mrcalc = Node(interface=mrcalcfunc.MRCalc(operation='divide', out_file='wm_peak_dir.mif'), name='n_mrcalc') wf.connect([(n_fixel2peaks, n_mrcalc, [('out_file', 'in_file1')])]) wf.connect([(n_mrmath, n_mrcalc, [('out_file', 'in_file2')])]) wf.connect([(n_mrcalc, n_datasink, [('out_file', 'WM_peak_dir.mif')])]) #mrconvert to extract Z component of peak directions n_mrconvert2 = Node(interface=utils.MRConvert( out_file='Zpeak_WM_Directions.mif', coord=[3, 2]), name='n_mrconvert2') wf.connect([(n_mrcalc, n_mrconvert2, [('out_file', 'in_file')])]) wf.connect([(n_mrconvert2, n_datasink, [('out_file', 'Zpeak_WM_Directions.mif')])]) # mrcalc to find absolute value n_mrcalc2 = Node(interface=mrcalcfunc.MRCalc( operation='abs', out_file='absZpeak_WM_Directions.mif'), name='n_mrcalc2') wf.connect([(n_mrconvert2, n_mrcalc2, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc2, n_datasink, [('out_file', 'absZpeak_WM_Directions.mif')])]) # mrcalc to get angle by doing inverse cosine n_mrcalc3 = Node(interface=mrcalcfunc.MRCalc( operation='acos', out_file='acosZpeak_WM_Directions.mif'), name='n_mrcalc3') wf.connect([(n_mrcalc2, n_mrcalc3, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc3, n_datasink, [('out_file', 'acosZpeak_WM_Directions.mif')])]) # mrcalc to convert angle to degrees n_mrcalc4 = Node(interface=mrcalcfunc.MRCalc( operation='multiply', operand=180, out_file='Fixel1_Z_angle.mif'), name='n_mrcalc4') wf.connect([(n_mrcalc3, n_mrcalc4, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc4, n_datasink, [('out_file', 'Fixel1_Z_angle.mif')])]) n_mrcalc5 = Node(interface=mrcalcfunc.MRCalc( operation='divide', operand=3.14159265, out_file='Fixel1_Z_cos_deg.mif'), name='n_mrcalc5') wf.connect([(n_mrcalc4, n_mrcalc5, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc5, n_datasink, [('out_file', 'Fixel1_Z_cos_deg.mif')]) ]) ## B) Tensor-based analysis #dwi2tensor n_dwi2tensor = Node(interface=mrt.FitTensor(out_file='dti.mif'), name='n_dwi2tensor') wf.connect([(n_dwibiascorrect, n_dwi2tensor, [('out_file', 'in_file')])]) wf.connect([(n_dwi2mask, n_dwi2tensor, [('out_file', 'in_mask')])]) wf.connect([(n_dwi2tensor, n_datasink, [('out_file', 'dt.mif')])]) #tensor2metric n_tensor2metric = Node(interface=tensor2metricfunc.tensor2metric( modulate='none', num=1, vector_file='eigenvector.mif'), name='n_tensor2metric') wf.connect([(n_dwi2tensor, n_tensor2metric, [('out_file', 'input_file')])]) wf.connect([(n_tensor2metric, n_datasink, [('vector_file', 'eigenvector.mif')])]) #mrconvert to get Z eigenvector n_mrconvert3 = Node(interface=utils.MRConvert(coord=[3, 2], out_file='eigenvectorZ.mif'), name='n_mrconvert3') wf.connect([(n_tensor2metric, n_mrconvert3, [('vector_file', 'in_file')])]) wf.connect([(n_mrconvert3, n_datasink, [('out_file', 'eigenvectorZ.mif')]) ]) #ALL SUBSEQUENT STEPS GET ANGLE IN DEGREES # mrcalc to find absolute value n_mrcalc6 = Node(interface=mrcalcfunc.MRCalc( operation='abs', out_file='abs_eigenvectorZ.mif'), name='n_mrcalc6') wf.connect([(n_mrconvert3, n_mrcalc6, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc6, n_datasink, [('out_file', 'abs_eigenvectorZ.mif')]) ]) # mrcalc to get angle by doing inverse cosine n_mrcalc7 = Node(interface=mrcalcfunc.MRCalc( operation='acos', out_file='acos_eigenvectorZ.mif'), name='n_mrcalc7') wf.connect([(n_mrcalc6, n_mrcalc7, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc7, n_datasink, [('out_file', 'acos_eigenvectorZ.mif') ])]) # mrcalc to convert angle to degrees n_mrcalc8 = Node( interface=mrcalcfunc.MRCalc(operation='multiply', operand=180, out_file='degrees_eigenvectorZ.mif'), name='n_mrcalc8') wf.connect([(n_mrcalc7, n_mrcalc8, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc8, n_datasink, [('out_file', 'degrees_eigenvectorZ.mif')])]) n_mrcalc9 = Node(interface=mrcalcfunc.MRCalc(operation='divide', operand=3.14159265, out_file='dti_z_cos_deg.mif'), name='n_mrcalc9') wf.connect([(n_mrcalc8, n_mrcalc9, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc9, n_datasink, [('out_file', 'dti_z_cos_deg.mif')])]) # Difference image between fixel based and tensor based outputs n_mrcalc10 = Node(interface=mrcalcfunc.MRCalc( operation='subtract', out_file='diff_imag_tensor_minus_fixel.mif'), name='n_mrcalc10') wf.connect([(n_mrcalc9, n_mrcalc10, [('out_file', 'in_file1')])]) wf.connect([(n_mrcalc5, n_mrcalc10, [('out_file', 'in_file2')])]) wf.connect([(n_mrcalc10, n_datasink, [('out_file', 'diff_imag_tensor_minus_fixel.mif')])]) #################################################################################3 return wf
def create_DWI_workflow( subject_list, bids_dir, work_dir, out_dir, bids_templates, ): # create initial workflow wf = Workflow(name='DWI', base_dir=work_dir) # use infosource to iterate workflow across subject list n_infosource = Node(interface=IdentityInterface(fields=['subject_id']), name="subject_source" # input: 'subject_id' # output: 'subject_id' ) # runs the node with subject_id = each element in subject_list n_infosource.iterables = ('subject_id', subject_list) # select matching files from bids_dir n_selectfiles = Node(interface=SelectFiles(templates=bids_templates, base_directory=bids_dir), name='get_subject_data') wf.connect([(n_infosource, n_selectfiles, [('subject_id', 'subject_id_p')]) ]) ########## IMPLEMENTING MRTRIX COMMANDS FOR IMAGE ANALYSIS ####################################### ## 1) Preprocessing of Data # https://nipype.readthedocs.io/en/latest/api/generated/nipype.interfaces.mrtrix3.preprocess.html # DWIDenoise to remove Gaussian noise n_denoise = Node(interface=mrt.DWIDenoise(), name='n_denoise') wf.connect([(n_selectfiles, n_denoise, [('DWI_all', 'in_file')])]) # datasink n_datasink = Node(interface=DataSink(base_directory=out_dir), name='datasink') # output denoised data into 'DWI_all_denoised' wf.connect([(n_selectfiles, n_datasink, [('all_b0_PA', 'all_b0_PA_unchanged')]), (n_denoise, n_datasink, [('out_file', 'DWI_all_denoised')])]) # MRDeGibbs to remove Gibbs ringing artifact n_degibbs = Node( interface=mrt.MRDeGibbs(out_file='DWI_all_denoised_degibbs.mif'), name='n_degibbs') # input denoised data into degibbs function wf.connect([(n_denoise, n_degibbs, [('out_file', 'in_file')])]) # output degibbs data into 'DWI_all_denoised_degibbs.mif' wf.connect([(n_degibbs, n_datasink, [('out_file', 'DWI_all_denoised_degibbs.mif')])]) # DWI Extract to extract b0 volumes from multi-b image data n_dwiextract = Node(interface=mrt.DWIExtract(bzero=True, out_file='b0vols.mif'), name='n_dwiextract') # input degibbs data into dwiextract function wf.connect([(n_degibbs, n_dwiextract, [('out_file', 'in_file')])]) # output extracted b0 volume from degibbs data (contains multiple b values) wf.connect([(n_dwiextract, n_datasink, [('out_file', 'noddi_b0_degibbs')]) ]) # MRcat to combine b0 volumes from input image and reverse phase encoded data n_mrcat = Node( interface=mrcatfunc.MRCat( #axis=3, out_file='b0s.mif'), name='n_mrcat') # input DTI images (all b0 volumes; reverse phase encoded) for concatenating wf.connect([(n_selectfiles, n_mrcat, [('DTI_B0_PA', 'in_file1')])]) # input b0 volumes from NODDI data for concatenating wf.connect([(n_dwiextract, n_mrcat, [('out_file', 'in_file2')])]) # output the mrcat file into 'noddi_and_PA_b0s.mif' wf.connect([(n_mrcat, n_datasink, [('out_file', 'noddi_and_PA_b0s.mif')])]) # DWIfslpreproc for image pre-processing using FSL's eddy tool n_dwifslpreproc = Node(interface=preprocfunc.DWIFslPreProc( out_file='preprocessedDWIs.mif', use_header=True), name='n_dwifslpreproc') # output of degibbs as input for preprocessing wf.connect([(n_degibbs, n_dwifslpreproc, [('out_file', 'in_file')])]) # output of mrcat (extracted b0 volumes) as se_epi input wf.connect([(n_mrcat, n_dwifslpreproc, [('out_file', 'se_epi_file')])]) # output of dwifslpreproc into 'preprocessedDWIs.mif' wf.connect([(n_dwifslpreproc, n_datasink, [('out_file', 'preprocessedDWIs.mif')])]) # DWI bias correct for B1 field inhomogeneity correction n_dwibiascorrect = Node( interface=preprocess.DWIBiasCorrect(use_ants=True), name='n_dwibiascorrect', ) # input preprocessed data wf.connect([(n_dwifslpreproc, n_dwibiascorrect, [('out_file', 'in_file')]) ]) # output biascorrect data into 'ANTSpreprocessedDWIs.mif' wf.connect([(n_dwibiascorrect, n_datasink, [('out_file', 'ANTSpreprocessedDWIs.mif')])]) # DWI2mask to compute whole brain mask from bias corrected data n_dwi2mask = Node(interface=mrt.BrainMask(out_file='mask.mif'), name='n_dwi2mask') wf.connect([(n_dwibiascorrect, n_dwi2mask, [('out_file', 'in_file')])]) wf.connect([(n_dwi2mask, n_datasink, [('out_file', 'mask.mif')])]) ################################################################################## ## 2) Fixel-based analysis # DWI2response for etimation of response function for spherical deconvolution n_dwi2response = Node(interface=mrt.ResponseSD(algorithm='dhollander', wm_file='wm_res.txt', gm_file='gm_res.txt', csf_file='csf_res.txt'), name='n_dwi2response') # input bias corrected data for response function estimation wf.connect([(n_dwibiascorrect, n_dwi2response, [('out_file', 'in_file')])]) # output WM, GM, CSF response text files wf.connect([(n_dwi2response, n_datasink, [('wm_file', 'wm_res.txt')])]) wf.connect([(n_dwi2response, n_datasink, [('gm_file', 'gm_res.txt')])]) wf.connect([(n_dwi2response, n_datasink, [('csf_file', 'csf_res.txt')])]) # DWI2fod for fibre orientation distribution estimation (FOD) n_dwi2fod = Node(interface=mrt.ConstrainedSphericalDeconvolution( algorithm='msmt_csd', wm_odf='wmfod.mif', gm_odf='gmfod.mif', csf_odf='csffod.mif'), name='n_dwi2fod') # utilise dwi2fod response files as input wf.connect([(n_dwibiascorrect, n_dwi2fod, [('out_file', 'in_file')])]) wf.connect([(n_dwi2response, n_dwi2fod, [('wm_file', 'wm_txt')])]) wf.connect([(n_dwi2response, n_dwi2fod, [('gm_file', 'gm_txt')])]) wf.connect([(n_dwi2response, n_dwi2fod, [('csf_file', 'csf_txt')])]) # output WM, GM and CSF FODs for saving wf.connect([(n_dwi2fod, n_datasink, [('wm_odf', 'wmfod.mif')])]) wf.connect([(n_dwi2fod, n_datasink, [('gm_odf', 'gmfod.mif')])]) wf.connect([(n_dwi2fod, n_datasink, [('csf_odf', 'csffod.mif')])]) # Mrconvert to select the first volume of the WM file (is best image out of 45 slices of wmfod file) n_mrconvert_fod = Node(interface=utils.MRConvert(out_file='Zwmfod.mif', coord=[3, 0]), name='n_mrconvert_fod') # utilise WM FOD as input wf.connect([(n_dwi2fod, n_mrconvert_fod, [('wm_odf', 'in_file')])]) # output z component of WM FOD wf.connect([(n_mrconvert_fod, n_datasink, [('out_file', 'Zwmfod.mif')])]) # MRcat to concatenate all WM, GM, CSF FOD files to see their distributions throughout Brain n_mrcat_fod = Node(interface=mrcatfunc.MRCat(out_file='vf.mif'), name='n_mrcat_fod') # connect Zwmfod, gmfod and csffod as inputs wf.connect([(n_mrconvert_fod, n_mrcat_fod, [('out_file', 'in_file1')])]) wf.connect([(n_dwi2fod, n_mrcat_fod, [('gm_odf', 'in_file2')])]) wf.connect([(n_dwi2fod, n_mrcat_fod, [('csf_odf', 'in_file3')])]) # output the mrcat file into file 'vf.mif' wf.connect([(n_mrcat_fod, n_datasink, [('out_file', 'vf.mif')])]) # fod2fixel # Perform segmentation of continuous FODs to produce discrete fixels # OUTPUTS: -afd afd.mif -peak peak.mif -disp disp.mif n_fod2fixel = Node( interface=fod2fixelfunc.fod2fixel( out_file='wmfixels', #afd_file = 'afd.mif', peak_file='peak.mif', disp_file='disp.mif'), name='n_fod2fixel') # let the peak value parameter be trialed as multiple values n_fod2fixel.iterables = ('fmls_peak_value', [0, 0.10, 0.50]) n_fod2fixel.iterables = ('fmls_integral', [0, 0.10, 0.50]) # obtain WM fibre image as input wf.connect([(n_dwi2fod, n_fod2fixel, [('wm_odf', 'in_file')])]) # ouputs of fod2fixel saved wf.connect([(n_fod2fixel, n_datasink, [('out_file', 'wmfixels')])]) wf.connect([(n_fod2fixel, n_datasink, [('afd_file', 'afd.mif')])]) wf.connect([(n_fod2fixel, n_datasink, [('peak_file', 'peak.mif')])]) wf.connect([(n_fod2fixel, n_datasink, [('disp_file', 'disp.mif')])]) # fixel2peaks to convert data in the fixel directory format into 4D image of 3-vectors n_fixel2peaks = Node(interface=fixel2peaksfunc.fixel2peaks( out_file='peaks_wmdirections.mif'), name='n_fixel2peaks') # look at multiple values for maximum number of fixels in each voxel n_fixel2peaks.iterables = ('number', [1, 2, 3]) # obtain directions file in output folder of fod2fixel, as input wf.connect([(n_fod2fixel, n_fixel2peaks, [('out_file', 'in_file')])]) # ouput of fixel2peaks saved in peaks_wmdirections.mif' wf.connect([(n_fixel2peaks, n_datasink, [('out_file', 'peaks_wmdirections.mif')])]) # MRmath to find normalised value of peak WM directions n_mrmath = Node(interface=mrt.MRMath( axis=3, operation='norm', out_file='norm_peaks_wmdirections.mif'), name='n_mrmath') # input peak fixel data wf.connect([(n_fixel2peaks, n_mrmath, [('out_file', 'in_file')])]) # output saved into 'norm_peaks_wmdirections.mif' wf.connect([(n_mrmath, n_datasink, [('out_file', 'norm_peaks_wmdirections.mif')])]) # MRcalc to divide peak WM direction by normalised value n_mrcalc = Node(interface=mrcalcfunc.MRCalc(operation='divide', out_file='wm_peak_dir.mif'), name='n_mrcalc') # fixel2peaks image as input 1 wf.connect([(n_fixel2peaks, n_mrcalc, [('out_file', 'in_file1')])]) # normalised fixel2peak image as input 2 wf.connect([(n_mrmath, n_mrcalc, [('out_file', 'in_file2')])]) # save output image as 'WM_peak_dir.mif' wf.connect([(n_mrcalc, n_datasink, [('out_file', 'WM_peak_dir.mif')])]) # MRconvert to extract Z component of peak directions n_mrconvert2 = Node(interface=utils.MRConvert( out_file='Zpeak_WM_Directions.mif', coord=[3, 2]), name='n_mrconvert2') # input normalised peak direction file wf.connect([(n_mrcalc, n_mrconvert2, [('out_file', 'in_file')])]) # save ouptut as 'Zpeak_WM_Directions.mif' wf.connect([(n_mrconvert2, n_datasink, [('out_file', 'Zpeak_WM_Directions.mif')])]) # MRcalc to find absolute value of peak fibre directions n_mrcalc2 = Node(interface=mrcalcfunc.MRCalc( operation='abs', out_file='absZpeak_WM_Directions.mif'), name='n_mrcalc2') # input z peaks image wf.connect([(n_mrconvert2, n_mrcalc2, [('out_file', 'in_file1')])]) # save output as 'absZpeak_WM_Directions.mif' wf.connect([(n_mrcalc2, n_datasink, [('out_file', 'absZpeak_WM_Directions.mif')])]) # MRcalc to get angle by doing inverse cosine n_mrcalc3 = Node(interface=mrcalcfunc.MRCalc( operation='acos', out_file='acosZpeak_WM_Directions.mif'), name='n_mrcalc3') # input normalised z component of peaks image wf.connect([(n_mrcalc2, n_mrcalc3, [('out_file', 'in_file1')])]) # save ouput as 'acosZpeak_WM_Directions.mif' wf.connect([(n_mrcalc3, n_datasink, [('out_file', 'acosZpeak_WM_Directions.mif')])]) # MRcalc to convert angle of peak fibre (w.r.t z axis), to degrees n_mrcalc4 = Node(interface=mrcalcfunc.MRCalc( operation='multiply', operand=180, out_file='Fixel1_Z_angle.mif'), name='n_mrcalc4') # input inverse cosine image of peak fibre wf.connect([(n_mrcalc3, n_mrcalc4, [('out_file', 'in_file1')])]) # output image as 'Fixel1_Z_angle.mif' wf.connect([(n_mrcalc4, n_datasink, [('out_file', 'Fixel1_Z_angle.mif')])]) # MRcalc to divide by pi to finish converting from radians to degrees n_mrcalc5 = Node(interface=mrcalcfunc.MRCalc( operation='divide', operand=3.14159265, out_file='Fixel1_Z_cos_deg.mif'), name='n_mrcalc5') # input image multiplied by 180 wf.connect([(n_mrcalc4, n_mrcalc5, [('out_file', 'in_file1')])]) # save output as 'Fixel1_Z_cos_deg.mif' wf.connect([(n_mrcalc5, n_datasink, [('out_file', 'Fixel1_Z_cos_deg.mif')]) ]) ################################################################################## ## 3) Tensor-based analysis # dwi2tensor to compute tensor from biascorrected DWI image n_dwi2tensor = Node(interface=mrt.FitTensor(out_file='dti.mif'), name='n_dwi2tensor') # input bias corrected image wf.connect([(n_dwibiascorrect, n_dwi2tensor, [('out_file', 'in_file')])]) # utilise mask to only compute tensors for regions of Brain wf.connect([(n_dwi2mask, n_dwi2tensor, [('out_file', 'in_mask')])]) # output data into 'dt.mif' wf.connect([(n_dwi2tensor, n_datasink, [('out_file', 'dt.mif')])]) # tensor2metric to convert tensors to generate maps of tensor-derived parameters n_tensor2metric = Node(interface=tensor2metricfunc.tensor2metric( modulate='none', num=1, vector_file='eigenvector.mif'), name='n_tensor2metric') # input tensor image wf.connect([(n_dwi2tensor, n_tensor2metric, [('out_file', 'input_file')])]) # save output eigenvectors of the diffusion tensor wf.connect([(n_tensor2metric, n_datasink, [('vector_file', 'eigenvector.mif')])]) # MRconvert to get eigenvector w.r.t z direction (main field) n_mrconvert3 = Node(interface=utils.MRConvert(coord=[3, 2], out_file='eigenvectorZ.mif'), name='n_mrconvert3') # input eigenvector file from tensor2metric wf.connect([(n_tensor2metric, n_mrconvert3, [('vector_file', 'in_file')])]) # save output as 'eigenvectorZ.mif' wf.connect([(n_mrconvert3, n_datasink, [('out_file', 'eigenvectorZ.mif')]) ]) # ALL SUBSEQUENT STEPS GET ANGLE IN DEGREES # MRcalc to find absolute value of z eigenvector file n_mrcalc6 = Node(interface=mrcalcfunc.MRCalc( operation='abs', out_file='abs_eigenvectorZ.mif'), name='n_mrcalc6') # z eigenvector image as input wf.connect([(n_mrconvert3, n_mrcalc6, [('out_file', 'in_file1')])]) # save output as 'abs_eigenvectorZ.mif' wf.connect([(n_mrcalc6, n_datasink, [('out_file', 'abs_eigenvectorZ.mif')]) ]) # MRcalc to get angle by doing inverse cosine n_mrcalc7 = Node(interface=mrcalcfunc.MRCalc( operation='acos', out_file='acos_eigenvectorZ.mif'), name='n_mrcalc7') # input absolute value of z eigenvector image wf.connect([(n_mrcalc6, n_mrcalc7, [('out_file', 'in_file1')])]) # save output as 'acos_eigenvectorZ.mif' wf.connect([(n_mrcalc7, n_datasink, [('out_file', 'acos_eigenvectorZ.mif') ])]) # MRcalc to convert angle to degrees n_mrcalc8 = Node( interface=mrcalcfunc.MRCalc(operation='multiply', operand=180, out_file='degrees_eigenvectorZ.mif'), name='n_mrcalc8') # input inverse cosine image of z eigenvector wf.connect([(n_mrcalc7, n_mrcalc8, [('out_file', 'in_file1')])]) # save output as 'degrees_eigenvectorZ.mif' wf.connect([(n_mrcalc8, n_datasink, [('out_file', 'degrees_eigenvectorZ.mif')])]) # MRcalc to divide by pi to finish converting from radians to degrees n_mrcalc9 = Node(interface=mrcalcfunc.MRCalc(operation='divide', operand=3.14159265, out_file='dti_z_cos_deg.mif'), name='n_mrcalc9') # input z eigenvector image multiplied by 180 wf.connect([(n_mrcalc8, n_mrcalc9, [('out_file', 'in_file1')])]) # save output as 'dti_z_cos_deg.mif' wf.connect([(n_mrcalc9, n_datasink, [('out_file', 'dti_z_cos_deg.mif')])]) # MRcalc to give difference image between fixel based and tensor based outputs n_mrcalc10 = Node(interface=mrcalcfunc.MRCalc( operation='subtract', out_file='diff_imag_tensor_minus_fixel.mif'), name='n_mrcalc10') # input tensor based image of whole Brain wf.connect([(n_mrcalc9, n_mrcalc10, [('out_file', 'in_file1')])]) # input fixel based image of Brain wf.connect([(n_mrcalc5, n_mrcalc10, [('out_file', 'in_file2')])]) # output difference image as 'diff_imag_tensor_minus_fixel.mif' wf.connect([(n_mrcalc10, n_datasink, [('out_file', 'diff_imag_tensor_minus_fixel.mif')])]) ##################################################################################### ## 4) Tensor based analysis on WM fibres only (NOT WHOLE BRAIN TENSORS) # MRthreshold to create WM mask from WM FOD (created earlier) n_mrthreshold = Node(interface=mrthresholdfunc.MRThreshold( out_file='thresholded_wmfod.mif'), name='n_mrthreshold') # input WM FOD wf.connect([(n_dwi2fod, n_mrthreshold, [('wm_odf', 'in_file')])]) # output thresholded WM FOD wf.connect([(n_mrthreshold, n_datasink, [('out_file', 'thresholded_wmfod.mif')])]) # MRconvert to extract 1st volume of thresholded WM FOD n_mrconvert4 = Node(interface=utils.MRConvert(coord=[3, 0], out_file='WMmask.mif'), name='n_mrconvert4') # input thresholded wmfod wf.connect([(n_mrthreshold, n_mrconvert4, [('out_file', 'in_file')])]) # save output as 'WMmask.mif' wf.connect([(n_mrconvert4, n_datasink, [('out_file', 'WMmask.mif')])]) # MRcalc to multiple WM mask with dti image to get tensors only of WM regions n_mrcalc11 = Node(interface=mrcalcfunc.MRCalc(operation='multiply', out_file='WM_dt.mif'), name='n_mrcalc11') # WM mask as input 1 wf.connect([(n_mrconvert4, n_mrcalc11, [('out_file', 'in_file1')])]) # dti image as input 2 wf.connect([(n_dwi2tensor, n_mrcalc11, [('out_file', 'in_file2')])]) # save output as 'WM_dt.mif' wf.connect([(n_mrcalc11, n_datasink, [('out_file', 'WM_dt.mif')])]) # tensor2metric to convert tensors to generate maps of tensor-derived parameters n_tensor2metric2 = Node(interface=tensor2metricfunc.tensor2metric( modulate='none', num=1, vector_file='WMeigenvector.mif'), name='n_tensor2metric2') # input tensor image wf.connect([(n_mrcalc11, n_tensor2metric2, [('out_file', 'input_file')])]) # save output eigenvectors of the diffusion tensor wf.connect([(n_tensor2metric2, n_datasink, [('vector_file', 'WMeigenvector.mif')])]) # MRconvert to get eigenvector w.r.t z direction (main field) n_mrconvert5 = Node(interface=utils.MRConvert( coord=[3, 2], out_file='WMeigenvectorZ.mif'), name='n_mrconvert5') # input eigenvector file from tensor2metric wf.connect([(n_tensor2metric2, n_mrconvert5, [('vector_file', 'in_file')]) ]) # save output as 'eigenvectorZ.mif' wf.connect([(n_mrconvert5, n_datasink, [('out_file', 'WMeigenvectorZ.mif') ])]) # ALL SUBSEQUENT STEPS GET ANGLE IN DEGREES # MRcalc to find absolute value of z eigenvector file n_mrcalc12 = Node(interface=mrcalcfunc.MRCalc( operation='abs', out_file='WM_abs_eigenvectorZ.mif'), name='n_mrcalc12') # z eigenvector image as input wf.connect([(n_mrconvert5, n_mrcalc12, [('out_file', 'in_file1')])]) # save output as 'WM_abs_eigenvectorZ.mif' wf.connect([(n_mrcalc12, n_datasink, [('out_file', 'WM_abs_eigenvectorZ.mif')])]) # MRcalc to get angle by doing inverse cosine n_mrcalc13 = Node(interface=mrcalcfunc.MRCalc( operation='acos', out_file='acos_WMeigenvectorZ.mif'), name='n_mrcalc13') # input absolute value of z eigenvector image wf.connect([(n_mrcalc12, n_mrcalc13, [('out_file', 'in_file1')])]) # save output as 'acos_WMeigenvectorZ.mif' wf.connect([(n_mrcalc13, n_datasink, [('out_file', 'acos_WMeigenvectorZ.mif')])]) # MRcalc to convert angle to degrees n_mrcalc14 = Node(interface=mrcalcfunc.MRCalc( operation='multiply', operand=180, out_file='degrees_WMeigenvectorZ.mif'), name='n_mrcalc14') # input inverse cosine image of WM z eigenvector wf.connect([(n_mrcalc13, n_mrcalc14, [('out_file', 'in_file1')])]) # save output as 'degrees_WMeigenvectorZ.mif' wf.connect([(n_mrcalc14, n_datasink, [('out_file', 'degrees_WMeigenvectorZ.mif')])]) # MRcalc to divide by pi to finish converting from radians to degrees n_mrcalc15 = Node( interface=mrcalcfunc.MRCalc(operation='divide', operand=3.14159265, out_file='WMdti_z_cos_deg.mif'), name='n_mrcalc15') # input WM z eigenvector image multiplied by 180 wf.connect([(n_mrcalc14, n_mrcalc15, [('out_file', 'in_file1')])]) # save output as 'WMdti_z_cos_deg.mif' wf.connect([(n_mrcalc15, n_datasink, [('out_file', 'WMdti_z_cos_deg.mif')]) ]) # MRcalc to give difference image between fixel based and WM tensor based outputs n_mrcalc16 = Node(interface=mrcalcfunc.MRCalc( operation='subtract', out_file='diffImage_WMtensor_minus_fixel.mif'), name='n_mrcalc16') # input fixel image of Brain wf.connect([(n_mrcalc15, n_mrcalc16, [('out_file', 'in_file1')])]) # input tensor image of WM fibres of Brain wf.connect([(n_mrcalc5, n_mrcalc16, [('out_file', 'in_file2')])]) # output difference image as 'diff_imag_WMtensor_minus_fixel.mif' wf.connect([(n_mrcalc16, n_datasink, [('out_file', 'diffImage_WMtensor_minus_fixel.mif')])]) ###################################################################################### return wf