def apply_topup(self): applytopup = fsl.ApplyTOPUP() applytopup.inputs.in_files = [self.dwi_base + '.nii.gz'] applytopup.inputs.encoding_file = self.acq_file applytopup.inputs.in_index = [1, 2] applytopup.inputs.in_topup = "my_topup_results" # applytopup.cmdline res = applytopup.run()
def create_unwarp_workflow(name="unwarp", fieldmap_pe=("y", "y-")): """Unwarp functional timeseries using reverse phase-blipped images.""" inputnode = Node(IdentityInterface(["timeseries", "fieldmap"]), "inputs") # Calculate the shift field # Note that setting readout_times to 1 will give a fine # map of the field, but the units will be off # Since we don't write out the map of the field itself, it does # not seem worth it to add another parameter for the readout times. # (It does require that they are the same, but when wouldn't they be?) topup = MapNode( fsl.TOPUP(encoding_direction=fieldmap_pe, readout_times=[1] * len(fieldmap_pe)), ["in_file"], "topup") # Unwarp the timeseries applytopup = MapNode(fsl.ApplyTOPUP(method="jac", in_index=[ 1 ]), ["in_files", "in_topup_fieldcoef", "in_topup_movpar", "encoding_file"], "applytopup") # Make a figure summarize the unwarping report = MapNode(UnwarpReport(), ["orig_file", "corrected_file"], "unwarp_report") # Define the outputs outputnode = Node(IdentityInterface(["timeseries", "report"]), "outputs") # Define and connect the workflow unwarp = Workflow(name) unwarp.connect([ (inputnode, topup, [("fieldmap", "in_file")]), (inputnode, applytopup, [("timeseries", "in_files")]), (topup, applytopup, [("out_fieldcoef", "in_topup_fieldcoef"), ("out_movpar", "in_topup_movpar"), ("out_enc_file", "encoding_file")]), (inputnode, report, [("fieldmap", "orig_file")]), (topup, report, [("out_corrected", "corrected_file")]), (applytopup, outputnode, [("out_corrected", "timeseries")]), (report, outputnode, [("out_file", "report")]), ]) return unwarp
def topup_correction(name='topup_correction'): """ .. deprecated:: 0.9.3 Use :func:`nipype.workflows.dmri.preprocess.epi.sdc_peb` instead. Corrects for susceptibilty distortion of EPI images when one reverse encoding dataset has been acquired Example ------- >>> nipype_epicorrect = topup_correction('nipype_topup') >>> nipype_epicorrect.inputs.inputnode.in_file_dir = 'epi.nii' >>> nipype_epicorrect.inputs.inputnode.in_file_rev = 'epi_rev.nii' >>> nipype_epicorrect.inputs.inputnode.encoding_direction = ['y', 'y-'] >>> nipype_epicorrect.inputs.inputnode.ref_num = 0 >>> nipype_epicorrect.run() # doctest: +SKIP Inputs:: inputnode.in_file_dir - EPI volume acquired in 'forward' phase encoding inputnode.in_file_rev - EPI volume acquired in 'reversed' phase encoding inputnode.encoding_direction - Direction encoding of in_file_dir inputnode.ref_num - Identifier of the reference volumes (usually B0 volume) Outputs:: outputnode.epi_corrected """ warnings.warn(('This workflow is deprecated from v.1.0.0, use ' 'nipype.workflows.dmri.preprocess.epi.sdc_peb instead'), DeprecationWarning) pipeline = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_file_dir', 'in_file_rev', 'encoding_direction', 'readout_times', 'ref_num' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_fieldcoef', 'out_movpar', 'out_enc_file', 'epi_corrected' ]), name='outputnode') b0_dir = pe.Node(fsl.ExtractROI(t_size=1), name='b0_1') b0_rev = pe.Node(fsl.ExtractROI(t_size=1), name='b0_2') combin = pe.Node(niu.Merge(2), name='merge') combin2 = pe.Node(niu.Merge(2), name='merge2') merged = pe.Node(fsl.Merge(dimension='t'), name='b0_comb') topup = pe.Node(fsl.TOPUP(), name='topup') applytopup = pe.Node(fsl.ApplyTOPUP(in_index=[1, 2]), name='applytopup') pipeline.connect([ (inputnode, b0_dir, [('in_file_dir', 'in_file'), ('ref_num', 't_min')]), (inputnode, b0_rev, [('in_file_rev', 'in_file'), ('ref_num', 't_min')]), (inputnode, combin2, [('in_file_dir', 'in1'), ('in_file_rev', 'in2')]), (b0_dir, combin, [('roi_file', 'in1')]), (b0_rev, combin, [('roi_file', 'in2')]), (combin, merged, [('out', 'in_files')]), (merged, topup, [('merged_file', 'in_file')]), (inputnode, topup, [('encoding_direction', 'encoding_direction'), ('readout_times', 'readout_times')]), (topup, applytopup, [('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar'), ('out_enc_file', 'encoding_file')]), (combin2, applytopup, [('out', 'in_files')]), (topup, outputnode, [('out_fieldcoef', 'out_fieldcoef'), ('out_movpar', 'out_movpar'), ('out_enc_file', 'out_enc_file')]), (applytopup, outputnode, [('out_corrected', 'epi_corrected')]) ]) return pipeline
def sdc_peb(name='peb_correction', epi_params={ 'read_out_times': None, 'enc_dir': 'y-' }, altepi_params={ 'read_out_times': None, 'enc_dir': 'y' }): """ SDC stands for susceptibility distortion correction. PEB stands for phase-encoding-based. The phase-encoding-based (PEB) method implements SDC by acquiring diffusion images with two different enconding directions [Andersson2003]_. The most typical case is acquiring with opposed phase-gradient blips (e.g. *A>>>P* and *P>>>A*, or equivalently, *-y* and *y*) as in [Chiou2000]_, but it is also possible to use orthogonal configurations [Cordes2000]_ (e.g. *A>>>P* and *L>>>R*, or equivalently *-y* and *x*). This workflow uses the implementation of FSL (`TOPUP <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/TOPUP>`_). Example ------- >>> from nipype.workflows.dmri.fsl.artifacts import sdc_peb >>> peb = sdc_peb() >>> peb.inputs.inputnode.in_file = 'epi.nii' >>> peb.inputs.inputnode.alt_file = 'epi_rev.nii' >>> peb.inputs.inputnode.in_bval = 'diffusion.bval' >>> peb.inputs.inputnode.in_mask = 'mask.nii' >>> peb.run() # doctest: +SKIP .. admonition:: References .. [Andersson2003] Andersson JL et al., `How to correct susceptibility distortions in spin-echo echo-planar images: application to diffusion tensor imaging <http://dx.doi.org/10.1016/S1053-8119(03)00336-7>`_. Neuroimage. 2003 Oct;20(2):870-88. doi: 10.1016/S1053-8119(03)00336-7 .. [Cordes2000] Cordes D et al., Geometric distortion correction in EPI using two images with orthogonal phase-encoding directions, in Proc. ISMRM (8), p.1712, Denver, US, 2000. .. [Chiou2000] Chiou JY, and Nalcioglu O, A simple method to correct off-resonance related distortion in echo planar imaging, in Proc. ISMRM (8), p.1712, Denver, US, 2000. """ inputnode = pe.Node(niu.IdentityInterface( fields=['in_file', 'in_bval', 'in_mask', 'alt_file', 'ref_num']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['out_file', 'out_vsm', 'out_warp']), name='outputnode') b0_ref = pe.Node(fsl.ExtractROI(t_size=1), name='b0_ref') b0_alt = pe.Node(fsl.ExtractROI(t_size=1), name='b0_alt') b0_comb = pe.Node(niu.Merge(2), name='b0_list') b0_merge = pe.Node(fsl.Merge(dimension='t'), name='b0_merged') topup = pe.Node(fsl.TOPUP(), name='topup') topup.inputs.encoding_direction = [ epi_params['enc_dir'], altepi_params['enc_dir'] ] readout = epi_params['read_out_time'] topup.inputs.readout_times = [readout, altepi_params['read_out_time']] unwarp = pe.Node(fsl.ApplyTOPUP(in_index=[1], method='jac'), name='unwarp') # scaling = pe.Node(niu.Function(input_names=['in_file', 'enc_dir'], # output_names=['factor'], function=_get_zoom), # name='GetZoom') # scaling.inputs.enc_dir = epi_params['enc_dir'] vsm2dfm = vsm2warp() vsm2dfm.inputs.inputnode.enc_dir = epi_params['enc_dir'] vsm2dfm.inputs.inputnode.scaling = readout wf = pe.Workflow(name=name) wf.connect([ (inputnode, b0_ref, [('in_file', 'in_file'), (('ref_num', _checkrnum), 't_min')]), (inputnode, b0_alt, [('alt_file', 'in_file'), (('ref_num', _checkrnum), 't_min')]), (b0_ref, b0_comb, [('roi_file', 'in1')]), (b0_alt, b0_comb, [('roi_file', 'in2')]), (b0_comb, b0_merge, [('out', 'in_files')]), (b0_merge, topup, [('merged_file', 'in_file')]), (topup, unwarp, [('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar'), ('out_enc_file', 'encoding_file')]), (inputnode, unwarp, [('in_file', 'in_files')]), (unwarp, outputnode, [('out_corrected', 'out_file')]), # (b0_ref, scaling, [('roi_file', 'in_file')]), # (scaling, vsm2dfm, [('factor', 'inputnode.scaling')]), (b0_ref, vsm2dfm, [('roi_file', 'inputnode.in_ref')]), (topup, vsm2dfm, [('out_field', 'inputnode.in_vsm')]), (topup, outputnode, [('out_field', 'out_vsm')]), (vsm2dfm, outputnode, [('outputnode.out_warp', 'out_warp')]) ]) return wf
def se_fmap_workflow(name=WORKFLOW_NAME, settings=None): """ Estimates the fieldmap using TOPUP on series of :abbr:`SE (Spin-Echo)` images acquired with varying :abbr:`PE (phase encoding)` direction. Outputs:: outputnode.mag_brain - The average magnitude image, skull-stripped outputnode.fmap_mask - The brain mask applied to the fieldmap outputnode.fieldmap - The estimated fieldmap in Hz """ if settings is None: settings = {} workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['input_images']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['fieldmap', 'fmap_mask', 'mag_brain']), name='outputnode') # Read metadata meta = pe.MapNode( ReadSidecarJSON(fields=['TotalReadoutTime', 'PhaseEncodingDirection']), iterfield=['in_file'], name='metadata') encfile = pe.Node(interface=niu.Function( input_names=['input_images', 'in_dict'], output_names=['parameters_file'], function=create_encoding_file), name='TopUp_encfile', updatehash=True) # Head motion correction fslmerge = pe.Node(fsl.Merge(dimension='t'), name='SE_merge') hmc_se = pe.Node(fsl.MCFLIRT(cost='normcorr', mean_vol=True), name='SE_head_motion_corr') fslsplit = pe.Node(fsl.Split(dimension='t'), name='SE_split') # Run topup to estimate field distortions, do not estimate movement # since it is done in hmc_se topup = pe.Node(fsl.TOPUP(estmov=0), name='TopUp') # Use the least-squares method to correct the dropout of the SE images unwarp_mag = pe.Node(fsl.ApplyTOPUP(method='lsr'), name='TopUpApply') # Remove bias inu_n4 = pe.Node(N4BiasFieldCorrection(dimension=3), name='SE_bias') # Skull strip corrected SE image to get reference brain and mask mag_bet = pe.Node(fsl.BET(mask=True, robust=True), name='SE_brain') workflow.connect([ (inputnode, meta, [('input_images', 'in_file')]), (inputnode, encfile, [('input_images', 'input_images')]), (inputnode, fslmerge, [('input_images', 'in_files')]), (fslmerge, hmc_se, [('merged_file', 'in_file')]), (meta, encfile, [('out_dict', 'in_dict')]), (encfile, topup, [('parameters_file', 'encoding_file')]), (hmc_se, topup, [('out_file', 'in_file')]), (topup, unwarp_mag, [('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar')]), (encfile, unwarp_mag, [('parameters_file', 'encoding_file')]), (hmc_se, fslsplit, [('out_file', 'in_file')]), (fslsplit, unwarp_mag, [('out_files', 'in_files'), (('out_files', gen_list), 'in_index')]), (unwarp_mag, inu_n4, [('out_corrected', 'input_image')]), (inu_n4, mag_bet, [('output_image', 'in_file')]), (topup, outputnode, [('out_field', 'fieldmap')]), (mag_bet, outputnode, [('out_file', 'mag_brain'), ('mask_file', 'fmap_mask')]) ]) # Reports section se_png = pe.Node(niu.Function( input_names=['in_file', 'overlay_file', 'out_file'], output_names=['out_file'], function=stripped_brain_overlay), name='PNG_SE_corr') se_png.inputs.out_file = 'corrected_SE_and_mask.png' datasink = pe.Node( nio.DataSink(base_directory=op.join(settings['work_dir'], 'images')), name='datasink', parameterization=False) workflow.connect([ (unwarp_mag, se_png, [('out_corrected', 'overlay_file')]), (mag_bet, se_png, [('mask_file', 'in_file')]), (se_png, datasink, [('out_file', '@corrected_SE_and_mask')]) ]) return workflow
def get_fmri2standard_wf( tvols, subject_id, ACQ_PARAMS="/home/didac/LabScripts/fMRI_preprocess/acparams_hcp.txt"): """Estimates transformation from Gradiend Field Distortion-warped BOLD to T1 In general: BOLD is field-inhomogeneity corrected and corregistered into standard space (T1). To do so, the following steps are carried out: 1) Corregister SBref to SEgfmAP (fsl.FLIRT) 2) Realign BOLD to corrected SBref (fsl.MCFLIRT) 3) Field inhomogeneity correction estimation of SBref from SEfm_AP and SEfm_PA (fsl.TOPUP) 4) Apply field inhomogeneity correction to SBref (fsl.ApplyTOPUP) 5) Apply field inhomogeneity correction to BOLD (fsl.ApplyTOPUP) 6) Transform free-surfer brain mask (brain.mgz) to T1 space (freesurfer.ApplyVolTransform ;mri_vol2vol), then binarized (fsl.UnaryMaths) and the mask is extracted from T1 (fsl.BinaryMaths) 7) Corregister BOLD (field-inhomogeneity corrected) to Standard T1 (fsl.Epi2Reg) Parameters ---------- tvols: [t_initial, t_final] volumes included in the preprocess ACQ_params: Path to txt file containing MRI acquisition parameters; needs to be specified for topup correction Returns ------- Workflow with the transformation """ from nipype import Workflow, Node, interfaces from nipype.interfaces import fsl, utility, freesurfer print("defining workflow...") wf = Workflow(name=subject_id, base_dir='') #Setting INPUT node... print("defines input node...") node_input = Node(utility.IdentityInterface(fields=[ 'func_sbref_img', 'func_segfm_ap_img', 'func_segfm_pa_img', 'func_bold_ap_img', 'T1_img', 'T1_brain_freesurfer_mask' ]), name='input_node') print( "Averages the three repeated Spin-Echo images with same Phase Encoding (AP or PA) for Susceptibility Correction (unwarping)..." ) node_average_SEgfm = Node(fsl.maths.MeanImage(), name='Mean_SEgfm_AP') print("Corregister SB-ref to average SEgfm-AP") node_coregister_SBref2SEgfm = Node( fsl.FLIRT(dof=6 #translation and rotation only ), name='Corregister_SBref2SEgfm') print("Eliminates first volumes.") node_eliminate_first_scans = Node( fsl.ExtractROI( t_min=tvols[0], # first included volume t_size=tvols[1] - tvols[0], # number of volumes from the first to the last one ), name="eliminate_first_scans") print("Realigns fMRI BOLD volumes to SBref in SEgfm-AP space") node_realign_bold = Node( fsl.MCFLIRT( save_plots=True, # save transformation matrices ), name="realign_fmri2SBref") print("Concatenates AP and PA SEgfm volumes...") # AP AP AP PA PA PA node_merge_ap_pa_inputs = Node( utility.base.Merge(2 # number of inputs; it concatenates lists ), name='Merge_ap_pa_inputs') node_merge_SEgfm = Node( fsl.Merge(dimension='t' # ¿? ), name='Merge_SEgfm_AP_PA') print( "Estimates TopUp inhomogeneity correction from SEfm_AP and SEfm_PA...") node_topup_SEgfm = Node(fsl.TOPUP(encoding_file=ACQ_PARAMS, ), name='Topup_SEgfm_estimation') print("Applies warp from TOPUP to correct SBref...") node_apply_topup_to_SBref = Node( fsl.ApplyTOPUP( encoding_file=ACQ_PARAMS, method='jac', # jacobian modulation interp='spline', # interpolation method ), name="apply_topup_to_SBref") print("Applies warp from TOPUP to correct realigned BOLD...") node_apply_topup = Node( fsl.ApplyTOPUP( encoding_file=ACQ_PARAMS, method='jac', # jacobian modulation interp='spline', # interpolation method ), name="apply_topup") ## BRAIN MASK #Registration to T1. Epireg without fieldmaps combined (see https://www.fmrib.ox.ac.uk/primers/intro_primer/ExBox20/IntroBox20.html) # print ("Eliminates scalp from brain using T1 high res image"); # node_mask_T1=Node(fsl.BET( # frac=0.7 # umbral # ), # name="mask_T1"); print('Transform brain mask T1 from freesurfer space to T1 space') node_vol2vol_brain = Node( freesurfer.ApplyVolTransform( reg_header=True, # (--regheader) transformed_file='brainmask_warped.nii.gz' #source_file --mov (INPUT; freesurfer brain.mgz) #transformed_file --o (OUTPUT; ...brain.nii.gz) #target_file --targ (REFERENCE; ...T1w.nii.gz) ), name="vol2vol") print('Transform brain mask T1 to binary mask') node_bin_mask_brain = Node( fsl.UnaryMaths( # fslmaths T1_brain -bin T1_binarized mask operation='bin', # (-bin) #in_file (T1_brain) #out_file (T1_binarized_mask) ), name="binarize_mask") print('Extract brain mask from T1 using binary mask') node_extract_mask = Node( fsl. BinaryMaths( # fslmaths T1 -mul T1_binarized_mask T1_extracted_mask operation='mul' # (-mul) #in_file (T1) #out_file (T1_extracted_mask) #operand_file (T1_binarized_mask) ), name="extract_mask") ## print("Estimate and appply transformation from SBref to T1") node_epireg = Node( fsl.EpiReg( #t1_head=SUBJECT_FSTRUCT_DIC['anat_T1'], out_base='SEgfm2T1'), name="epi2reg") ''' EPI2REG ALREADY APPLIED print ("Apply epi2reg to SBRef.."); node_apply_epi2reg_SBref= Node(fsl.ApplyXFM( ), name="node_apply_epi2reg_SBref"); ''' print("Estimates inverse transform from epi2reg...") # quality control node_invert_epi2reg = Node(fsl.ConvertXFM(invert_xfm=True), name="invert_epi2reg") print("...") node_mask_fMRI = Node(fsl.BET(mask=True, ), name='mask_fMRI') #node_fmriMask.overwrite=True print("Setting OUTPUT node...") node_output = Node(interfaces.utility.IdentityInterface(fields=[ 'SBref2SEgfm_mat', 'realign_movpar_txt', 'realign_fmri_img', 'topup_movpar_txt', 'topup_field_coef_img', 'epi2str_mat', 'epi2str_img', 'fmri_mask_img', 'rfmri_unwarped_imgs', 'sb_ref_unwarped_img', ]), name='output_node') print("All nodes created; Starts creating connections") #Connects nodes wf.connect([ #inputs (node_input, node_average_SEgfm, [("func_segfm_ap_img", "in_file")]), (node_input, node_coregister_SBref2SEgfm, [("func_sbref_img", "in_file")]), (node_input, node_eliminate_first_scans, [("func_bold_ap_img", "in_file")]), (node_input, node_merge_ap_pa_inputs, [("func_segfm_ap_img", "in1"), ("func_segfm_pa_img", "in2")]), (node_merge_ap_pa_inputs, node_merge_SEgfm, [("out", "in_files")]), (node_input, node_epireg, [("T1_img", "t1_head")]), (node_input, node_vol2vol_brain, [("T1_brain_freesurfer_mask", "source_file")]), (node_input, node_vol2vol_brain, [("T1_img", "target_file")]), (node_input, node_extract_mask, [("T1_img", "in_file")]), #connections (node_eliminate_first_scans, node_realign_bold, [("roi_file", "in_file")]), (node_average_SEgfm, node_coregister_SBref2SEgfm, [("out_file", "reference")]), (node_coregister_SBref2SEgfm, node_realign_bold, [("out_file", "ref_file")]), #T1 brain mask transformations (change space / vol2vol, binarize and extract) (node_vol2vol_brain, node_bin_mask_brain, [("transformed_file", "in_file")]), (node_bin_mask_brain, node_extract_mask, [("out_file", "operand_file") ]), #(node_realign_bold, node_tsnr, [("out_file", "in_file")]), (node_merge_SEgfm, node_topup_SEgfm, [("merged_file", "in_file")]), (node_realign_bold, node_apply_topup, [("out_file", "in_files")]), (node_topup_SEgfm, node_apply_topup, [("out_fieldcoef", "in_topup_fieldcoef"), ("out_movpar", "in_topup_movpar")]), (node_topup_SEgfm, node_apply_topup_to_SBref, [("out_fieldcoef", "in_topup_fieldcoef"), ("out_movpar", "in_topup_movpar")]), (node_coregister_SBref2SEgfm, node_apply_topup_to_SBref, [("out_file", "in_files")]), #corregister to T1 (node_extract_mask, node_epireg, [("out_file", "t1_brain")]), (node_apply_topup_to_SBref, node_epireg, [("out_corrected", "epi")]), (node_epireg, node_invert_epi2reg, [("epi2str_mat", "in_file")]), (node_coregister_SBref2SEgfm, node_mask_fMRI, [("out_file", "in_file") ]), #yeld relevant data to output node (node_coregister_SBref2SEgfm, node_output, [("out_matrix_file", "SBref2SEgfm_mat")]), (node_realign_bold, node_output, [("par_file", "realign_movpar_txt"), ("out_file", "realign_fmri_img")]), (node_mask_fMRI, node_output, [("mask_file", "fmri_mask_img")]), (node_epireg, node_output, [("epi2str_mat", "epi2str_mat")]), (node_epireg, node_output, [("out_file", "epi2str_img")]), (node_topup_SEgfm, node_output, [("out_fieldcoef", "topup_field_coef_img"), ("out_corrected", "sb_ref_unwarped_img")]), (node_apply_topup, node_output, [("out_corrected", "rfmri_unwarped_imgs")]) ]) print("All connections created") return (wf)
def epi_unwarp(name='EPIUnwarpWorkflow', settings=None): """ A workflow to correct EPI images """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'epi', 'sbref_brain', 'fieldmap', 'fmap_movpar', 'fmap_mask', 'epi_brain' ]), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['epi_correct', 'epi_mean']), name='outputnode') # Read metadata meta = pe.MapNode( ReadSidecarJSON(fields=['TotalReadoutTime', 'PhaseEncodingDirection']), iterfield=['in_file'], name='metadata') encfile = pe.Node(interface=niu.Function( input_names=['input_images', 'in_dict'], output_names=['parameters_file'], function=create_encoding_file), name='TopUp_encfile', updatehash=True) fslsplit = pe.Node(fsl.Split(dimension='t'), name='EPI_split') # Now, we cannot use the LSR method unwarp_epi = pe.MapNode(fsl.ApplyTOPUP(method='jac', in_index=[1]), iterfield=['in_files'], name='TopUpApply') # Merge back fslmerge = pe.Node(fsl.Merge(dimension='t'), name='EPI_corr_merge') # Compute mean epi_mean = pe.Node(fsl.MeanImage(dimension='T'), name='EPI_mean') workflow.connect([ (inputnode, meta, [('epi', 'in_file')]), (inputnode, encfile, [('epi', 'input_images')]), (inputnode, fslsplit, [('epi_brain', 'in_file')]), (meta, encfile, [('out_dict', 'in_dict')]), (inputnode, unwarp_epi, [('fieldmap', 'in_topup_fieldcoef'), ('fmap_movpar', 'in_topup_movpar')]), (encfile, unwarp_epi, [('parameters_file', 'encoding_file')]), (fslsplit, unwarp_epi, [('out_files', 'in_files')]), (unwarp_epi, fslmerge, [('out_corrected', 'in_files')]), (fslmerge, epi_mean, [('merged_file', 'in_file')]), (fslmerge, outputnode, [('merged_file', 'epi_correct')]), (epi_mean, outputnode, [('out_file', 'epi_mean')]) ]) # Plot result png_epi_corr = pe.Node(niu.Function( input_names=['in_file', 'overlay_file', 'out_file'], output_names=['out_file'], function=stripped_brain_overlay), name='PNG_epi_corr') png_epi_corr.inputs.out_file = 'corrected_EPI.png' ds_png = pe.Node(DerivativesDataSink(base_directory=settings['output_dir'], suffix='sdc'), name='DerivativesPNG') workflow.connect([(epi_mean, png_epi_corr, [('out_file', 'overlay_file')]), (inputnode, png_epi_corr, [('fmap_mask', 'in_file')]), (inputnode, ds_png, [('epi', 'source_file')]), (png_epi_corr, ds_png, [('out_file', 'in_file')])]) return workflow
def func_calc_disco_warp(): ''' Method to calculate and apply susceptibility induced distortion warps to a fresh functional image used topup and applytopup inputs inputnode.func inputnode.se_image inputnode.se_invpol inputnode.encoding_file outputnode outputnode.out_enc_file # encoding directions file output for applytopup outputnode.movpar # movpar.txt output file outputnode.fieldcoef # file containing the field coefficients ''' datain = '/scr/sambesi1/workspace/Projects/GluREST/functional/datain.txt' #define worflow flow = Workflow('func_distortion_correction') inputnode = Node( util.IdentityInterface(fields=['func', 'func_se', 'func_se_inv']), name='inputnode') outputnode = Node(util.IdentityInterface(fields=[ 'enc_file ', 'topup_movpar', 'topup_fieldcoef', 'topup_field', 'func_disco' ]), name='outputnode') # define nodes list_blips = Node(util.Merge(2), name='blips_list') make_blips = Node(fsl.Merge(), name='blips_merged') make_blips.inputs.dimension = 't' make_blips.inputs.output_type = 'NIFTI_GZ' topup = Node(interface=fsl.TOPUP(), name='calc_topup') topup.inputs.encoding_file = datain topup.inputs.output_type = "NIFTI_GZ" apply = Node(interface=fsl.ApplyTOPUP(), name='apply_topup') apply.inputs.in_index = [1] apply.inputs.encoding_file = datain apply.inputs.method = 'jac' apply.inputs.output_type = "NIFTI_GZ" # connect nodes flow.connect(inputnode, 'func_se', list_blips, 'in1') flow.connect(inputnode, 'func_se_inv', list_blips, 'in2') flow.connect(list_blips, 'out', make_blips, 'in_files') flow.connect(make_blips, 'merged_file', topup, 'in_file') flow.connect(inputnode, 'func', apply, 'in_files') flow.connect(topup, 'out_fieldcoef', apply, 'in_topup_fieldcoef') flow.connect(topup, 'out_movpar', apply, 'in_topup_movpar') flow.connect(topup, 'out_enc_file', outputnode, 'enc_file') flow.connect(topup, 'out_movpar', outputnode, 'topup_movpar') flow.connect(topup, 'out_fieldcoef', outputnode, 'topup_fieldcoef') flow.connect(topup, 'out_field', outputnode, 'topup_field') flow.connect(apply, 'out_corrected', outputnode, 'func_disco') return flow
def create_topup_workflow(analysis_info, name='topup'): ########################################################################### # NODES ########################################################################### input_node = pe.Node(IdentityInterface(fields=[ 'in_files', 'alt_files', 'conf_file', 'output_directory', 'echo_time', 'phase_encoding_direction', 'epi_factor' ]), name='inputspec') output_node = pe.Node( IdentityInterface(fields=['out_files', 'field_coefs']), name='outputspec') get_info = pe.MapNode(interface=Get_scaninfo, name='get_scaninfo', iterfield=['in_file']) dyns_min_1_node = pe.MapNode(interface=Dyns_min_1, name='dyns_min_1_node', iterfield=['dyns']) topup_scan_params_node = pe.Node(interface=Topup_scan_params, name='topup_scan_params') apply_scan_params_node = pe.MapNode(interface=Apply_scan_params, name='apply_scan_params', iterfield=['nr_trs']) PE_ref = pe.MapNode(fsl.ExtractROI(t_size=1), name='PE_ref', iterfield=['in_file', 't_min']) # hard-coded the timepoint for this node, no more need for alt_t. PE_alt = pe.MapNode(fsl.ExtractROI(t_min=0, t_size=1), name='PE_alt', iterfield=['in_file']) PE_comb = pe.MapNode(Merge(2), name='PE_list', iterfield=['in1', 'in2']) PE_merge = pe.MapNode(fsl.Merge(dimension='t'), name='PE_merged', iterfield=['in_files']) # implementing the contents of b02b0.cnf in the args, # while supplying an emtpy text file as a --config option # gets topup going on our server. topup_args = """--warpres=20,16,14,12,10,6,4,4,4 --subsamp=1,1,1,1,1,1,1,1,1 --fwhm=8,6,4,3,3,2,1,0,0 --miter=5,5,5,5,5,10,10,20,20 --lambda=0.005,0.001,0.0001,0.000015,0.000005,0.0000005,0.00000005,0.0000000005,0.00000000001 --ssqlambda=1 --regmod=bending_energy --estmov=1,1,1,1,1,0,0,0,0 --minmet=0,0,0,0,0,1,1,1,1 --splineorder=3 --numprec=double --interp=spline --scale=1 -v""" topup_node = pe.MapNode(fsl.TOPUP(args=topup_args), name='topup', iterfield=['in_file']) unwarp = pe.MapNode(fsl.ApplyTOPUP(in_index=[1], method='jac'), name='unwarp', iterfield=[ 'in_files', 'in_topup_fieldcoef', 'in_topup_movpar', 'encoding_file' ]) ########################################################################### # WORKFLOW ########################################################################### topup_workflow = pe.Workflow(name=name) # these are now mapnodes because they split up over files topup_workflow.connect(input_node, 'in_files', get_info, 'in_file') topup_workflow.connect(input_node, 'in_files', PE_ref, 'in_file') topup_workflow.connect(input_node, 'alt_files', PE_alt, 'in_file') # this is a simple node, connecting to the input node topup_workflow.connect(input_node, 'phase_encoding_direction', topup_scan_params_node, 'pe_direction') topup_workflow.connect(input_node, 'echo_time', topup_scan_params_node, 'te') topup_workflow.connect(input_node, 'epi_factor', topup_scan_params_node, 'epi_factor') # preparing a node here, which automatically iterates over dyns output of the get_info mapnode topup_workflow.connect(input_node, 'echo_time', apply_scan_params_node, 'te') topup_workflow.connect(input_node, 'phase_encoding_direction', apply_scan_params_node, 'pe_direction') topup_workflow.connect(input_node, 'epi_factor', apply_scan_params_node, 'epi_factor') topup_workflow.connect(get_info, 'dyns', apply_scan_params_node, 'nr_trs') # the nr_trs and in_files both propagate into the PR_ref node topup_workflow.connect(get_info, 'dyns', dyns_min_1_node, 'dyns') topup_workflow.connect(dyns_min_1_node, 'dyns_1', PE_ref, 't_min') topup_workflow.connect(PE_ref, 'roi_file', PE_comb, 'in1') topup_workflow.connect(PE_alt, 'roi_file', PE_comb, 'in2') topup_workflow.connect(PE_comb, 'out', PE_merge, 'in_files') topup_workflow.connect(topup_scan_params_node, 'fn', topup_node, 'encoding_file') topup_workflow.connect(PE_merge, 'merged_file', topup_node, 'in_file') topup_workflow.connect(input_node, 'conf_file', topup_node, 'config') topup_workflow.connect(input_node, 'in_files', unwarp, 'in_files') topup_workflow.connect(apply_scan_params_node, 'fn', unwarp, 'encoding_file') topup_workflow.connect(topup_node, 'out_fieldcoef', unwarp, 'in_topup_fieldcoef') topup_workflow.connect(topup_node, 'out_movpar', unwarp, 'in_topup_movpar') topup_workflow.connect(unwarp, 'out_corrected', output_node, 'out_files') topup_workflow.connect(topup_node, 'out_fieldcoef', output_node, 'field_coefs') # ToDo: automatic datasink? return topup_workflow
def sdc_unwarp(name=SDC_UNWARP_NAME, ref_vol=None, method='jac'): """ This workflow takes an estimated fieldmap and a target image and applies TOPUP, an :abbr:`SDC (susceptibility-derived distortion correction)` method in FSL to unwarp the target image. Input fields: ~~~~~~~~~~~~~ inputnode.in_file - the image(s) to which this correction will be applied inputnode.in_mask - a brain mask corresponding to the in_file image inputnode.fmap_ref - the fieldmap reference (generally, a *magnitude* image or the resulting SE image) inputnode.fmap_mask - a brain mask in fieldmap-space inputnode.fmap - a fieldmap in Hz inputnode.hmc_movpar - the head motion parameters (iff inputnode.in_file is only one 4D file) Output fields: ~~~~~~~~~~~~~~ outputnode.out_file - the in_file after susceptibility-distortion correction. """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_file', 'fmap_ref', 'fmap_mask', 'fmap', 'hmc_movpar']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_file']), name='outputnode') # Compute movpar file iff we have several images with different # PE directions. align = pe.Node(niu.Function( input_names=['in_files', 'in_movpar'], output_names=['out_file', 'ref_vol', 'ref_mask', 'out_movpar'], function=_multiple_pe_hmc), name='AlignMultiplePE') align.inputs.in_ref = ref_vol # Read metadata meta = pe.MapNode(niu.Function( input_names=['in_file'], output_names=['out_dict'], function=_get_metadata), iterfield=['in_file'], name='metadata') encfile = pe.Node(interface=niu.Function( input_names=['input_images', 'in_dict'], output_names=['parameters_file'], function=create_encoding_file), name='TopUp_encfile', updatehash=True) fslsplit = pe.Node(fsl.Split(dimension='t'), name='ImageHMCSplit') # Register the reference of the fieldmap to the reference # of the target image (the one that shall be corrected) fmap2ref = pe.Node(ants.Registration(output_warped_image=True), name='Fieldmap2ImageRegistration') grabber = nio.JSONFileGrabber() setattr(grabber, '_always_run', False) fmap2ref_params = pe.Node(grabber, name='Fieldmap2ImageRegistration_params') fmap2ref_params.inputs.in_file = ( pkgr.resource_filename('fmriprep', 'data/fmap-any_registration.json')) applyxfm = pe.Node(ants.ApplyTransforms( dimension=3, interpolation='Linear'), name='Fieldmap2ImageApply') topup_adapt = pe.Node(niu.Function( input_names=['in_file', 'in_ref', 'in_movpar'], output_names=['out_fieldcoef', 'out_movpar'], function=_gen_coeff), name='TopUpAdapt') # Use the least-squares method to correct the dropout of the SBRef images unwarp = pe.Node(fsl.ApplyTOPUP(method=method), name='TopUpApply') workflow.connect([ (inputnode, meta, [('in_file', 'in_file')]), (inputnode, align, [('in_file', 'in_files'), ('hmc_movpar', 'in_movpar')]), (inputnode, applyxfm, [('fmap', 'input_image')]), (inputnode, encfile, [('in_file', 'input_images')]), (inputnode, fmap2ref, [('fmap_ref', 'moving_image'), ('fmap_mask', 'moving_image_mask')]), (align, fmap2ref, [('ref_vol', 'fixed_image'), ('ref_mask', 'fixed_image_mask')]), (align, applyxfm, [('ref_vol', 'reference_image')]), (align, topup_adapt, [('ref_vol', 'in_ref'), ('out_movpar', 'in_movpar')]), (meta, encfile, [('out_dict', 'in_dict')]), (fmap2ref, applyxfm, [ ('forward_transforms', 'transforms'), ('forward_invert_flags', 'invert_transform_flags')]), (align, fslsplit, [('out_file', 'in_file')]), (applyxfm, topup_adapt, [('output_image', 'in_file')]), (fslsplit, unwarp, [('out_files', 'in_files'), (('out_files', gen_list), 'in_index')]), (topup_adapt, unwarp, [('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar')]), (encfile, unwarp, [('parameters_file', 'encoding_file')]), (unwarp, outputnode, [('out_corrected', 'out_file')]) ]) # Connect registration settings in the end, not to clutter the code workflow.connect([ (fmap2ref_params, fmap2ref, [ ('transforms', 'transforms'), ('transform_parameters', 'transform_parameters'), ('number_of_iterations', 'number_of_iterations'), ('dimension', 'dimension'), ('metric', 'metric'), ('metric_weight', 'metric_weight'), ('radius_or_number_of_bins', 'radius_or_number_of_bins'), ('sampling_strategy', 'sampling_strategy'), ('sampling_percentage', 'sampling_percentage'), ('convergence_threshold', 'convergence_threshold'), ('convergence_window_size', 'convergence_window_size'), ('smoothing_sigmas', 'smoothing_sigmas'), ('sigma_units', 'sigma_units'), ('shrink_factors', 'shrink_factors'), ('use_estimate_learning_rate_once', 'use_estimate_learning_rate_once'), ('use_histogram_matching', 'use_histogram_matching'), ('initial_moving_transform_com', 'initial_moving_transform_com'), ('collapse_output_transforms', 'collapse_output_transforms'), ('winsorize_upper_quantile', 'winsorize_upper_quantile'), ('winsorize_lower_quantile', 'winsorize_lower_quantile') ]) ]) return workflow
def create_preproc_workflow(session): """ Defines simple functional preprocessing workflow, including motion correction, registration to distortion scans, and unwarping. Assumes recon-all has been performed on T1, and computes but does not apply registration to anatomy. """ #---Create workflow--- wf = Workflow(name='workflow', base_dir=session['working_dir']) #---EPI Realignment--- # Realign every TR in each functional run to the sbref image using mcflirt realign = MapNode(fsl.MCFLIRT(ref_file=session['sbref'], save_mats=True, save_plots=True), iterfield=['in_file'], name='realign') realign.inputs.in_file = session['epis'] wf.add_nodes([realign]) #---Registration to distortion scan--- # Register the sbref scan to the distortion scan with the same PE using flirt reg2dist = Node(fsl.FLIRT(in_file=session['sbref'], reference=session['distort_PE'], out_file='sbref_reg.nii.gz', out_matrix_file='sbref2dist.mat', dof=6), name='reg2distort') wf.add_nodes([reg2dist]) #---Distortion correction--- # Merge the two distortion scans for unwarping distort_scans = [session['distort_PE'], session['distort_revPE']] merge_dist = Node(fsl.Merge(in_files=distort_scans, dimension='t', merged_file='distortion_merged.nii.gz'), name='merge_distort') wf.add_nodes([merge_dist]) # Run topup to estimate warpfield and create unwarped distortion scans if '-' not in session['PE_dim']: PEs = np.repeat([session['PE_dim'], session['PE_dim'] + '-'], 3) else: PEs = np.repeat( [session['PE_dim'], session['PE_dim'].replace('-', '')], 3) unwarp_dist = Node(fsl.TOPUP(encoding_direction=list(PEs), readout_times=[1, 1, 1, 1, 1, 1], config='b02b0.cnf', fwhm=0), name='unwarp_distort') wf.connect(merge_dist, 'merged_file', unwarp_dist, 'in_file') # Unwarp sbref image in case it's useful unwarp_sbref = Node(fsl.ApplyTOPUP(in_index=[1], method='jac'), name='unwarp_sbref') wf.connect([(reg2dist, unwarp_sbref, [('out_file', 'in_files')]), (unwarp_dist, unwarp_sbref, [('out_enc_file', 'encoding_file'), ('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar')])]) #---Registration to anatomy--- # Create mean unwarped distortion scan mean_unwarped_dist = Node(fsl.MeanImage(dimension='T'), name='mean_unwarped_distort') wf.connect(unwarp_dist, 'out_corrected', mean_unwarped_dist, 'in_file') # Register mean unwarped distortion scan to anatomy using bbregister reg2anat = Node(fs.BBRegister( subject_id=session['Freesurfer_subject_name'], contrast_type='t2', init='fsl', out_reg_file='distort2anat_tkreg.dat', out_fsl_file='distort2anat_flirt.mat'), name='reg2anat') wf.connect(mean_unwarped_dist, 'out_file', reg2anat, 'source_file') #---Combine and apply transforms to EPIs--- # Split EPI runs into 3D files split_epis = MapNode(fsl.Split(dimension='t'), iterfield=['in_file'], name='split_epis') split_epis.inputs.in_file = session['epis'] wf.add_nodes([split_epis]) # Combine the rigid transforms to be applied to each EPI volume concat_rigids = MapNode(fsl.ConvertXFM(concat_xfm=True), iterfield=['in_file'], nested=True, name='concat_rigids') wf.connect([(realign, concat_rigids, [('mat_file', 'in_file')]), (reg2dist, concat_rigids, [('out_matrix_file', 'in_file2')])]) # Apply rigid transforms and warpfield to each EPI volume correct_epis = MapNode(fsl.ApplyWarp(interp='spline', relwarp=True), iterfield=['in_file', 'ref_file', 'premat'], nested=True, name='correct_epis') get_warp = lambda warpfields: warpfields[0] wf.connect([(split_epis, correct_epis, [('out_files', 'in_file'), ('out_files', 'ref_file')]), (concat_rigids, correct_epis, [('out_file', 'premat')]), (unwarp_dist, correct_epis, [(('out_warps', get_warp), 'field_file')])]) # Merge processed files back into 4D nifti merge_epis = MapNode(fsl.Merge(dimension='t', merged_file='timeseries_corrected.nii.gz'), iterfield='in_files', name='merge_epis') wf.connect([(correct_epis, merge_epis, [('out_file', 'in_files')])]) #---Copy important files to main directory--- substitutions = [('_merge_epis%d/timeseries_corrected.nii.gz' % i, n) for i, n in enumerate(session['out_names'])] ds = Node(DataSink(base_directory=os.path.abspath(session['out']), substitutions=substitutions), name='outfiles') wf.connect(unwarp_dist, 'out_corrected', ds, '@unwarp_dist') wf.connect(mean_unwarped_dist, 'out_file', ds, '@mean_unwarped_dist') wf.connect(unwarp_sbref, 'out_corrected', ds, '@unwarp_sbref') wf.connect(reg2anat, 'out_reg_file', ds, '@reg2anat') wf.connect(merge_epis, 'merged_file', ds, '@merge_epis') return wf