def afni3dQwarp(oppose_pe,matched_pe,source_warp): qwarp = afni.QwarpPlusMinus() qwarp.inputs.in_file =oppose_pe qwarp.inputs.nopadWARP = True qwarp.inputs.noweight=True qwarp.inputs.pblur = [0.05,0.05] qwarp.inputs.blur=[-1,-1] qwarp.inputs.minpatch=9 qwarp.inputs.noXdis=True qwarp.inputs.noZdis=True qwarp.inputs.base_file = matched_pe qwarp.inputs.out_file= source_warp+'.nii.gz' qwarp.run() return source_warp+'_PLUS_WARP.nii.gz'
def blip_distcor_wf(wf_name='blip_distcor'): """Execute AFNI 3dQWarp to calculate the distortion "unwarp" for phase encoding direction EPI field map distortion correction. 1. Skull-strip the opposite-direction phase encoding EPI. 2. Transform the opposite-direction phase encoding EPI to the skull-stripped functional and pass this as the base_file to AFNI 3dQWarp (plus-minus). 3. If there is a same-direction phase encoding EPI, also skull-strip this, and transform it to the skull-stripped functional. Then, pass this as the in_file to AFNI 3dQWarp (plus-minus). 4. If there isn't a same-direction, pass the functional in as the in_file of AFNI 3dQWarp (plus-minus). 5. Convert the 3dQWarp transforms to ANTs/ITK format. 6. Use antsApplyTransforms, with the original functional as both the input and the reference, and apply the warp from 3dQWarp. The output of this can then proceed to func_preproc. :param wf_name: :return: """ wf = pe.Workflow(name=wf_name) input_node = pe.Node(util.IdentityInterface( fields=['func_mean', 'opposite_pe_epi', 'same_pe_epi']), name='inputspec') output_node = pe.Node(util.IdentityInterface(fields=[ 'blip_warp', 'blip_warp_inverse', 'new_func_mean', 'new_func_mask' ]), name='outputspec') skullstrip_opposite_pe = skullstrip_functional( skullstrip_tool='afni', wf_name="{0}_skullstrip_opp_pe".format(wf_name)) wf.connect(input_node, 'opposite_pe_epi', skullstrip_opposite_pe, 'inputspec.func') opp_pe_to_func = pe.Node(interface=fsl.FLIRT(), name='opp_pe_to_func') opp_pe_to_func.inputs.cost = 'corratio' wf.connect(skullstrip_opposite_pe, 'outputspec.func_brain', opp_pe_to_func, 'in_file') wf.connect(input_node, 'func_mean', opp_pe_to_func, 'reference') prep_qwarp_input_imports = ['import os', 'import subprocess'] prep_qwarp_input = \ pe.Node(function.Function(input_names=['same_pe_epi', 'func_mean'], output_names=['qwarp_input'], function=same_pe_direction_prep, imports=prep_qwarp_input_imports), name='prep_qwarp_input') wf.connect(input_node, 'same_pe_epi', prep_qwarp_input, 'same_pe_epi') wf.connect(input_node, 'func_mean', prep_qwarp_input, 'func_mean') calc_blip_warp = pe.Node(afni.QwarpPlusMinus(), name='calc_blip_warp') calc_blip_warp.inputs.plusminus = True calc_blip_warp.inputs.outputtype = "NIFTI_GZ" calc_blip_warp.inputs.out_file = os.path.abspath("Qwarp.nii.gz") wf.connect(opp_pe_to_func, 'out_file', calc_blip_warp, 'base_file') wf.connect(prep_qwarp_input, 'qwarp_input', calc_blip_warp, 'in_file') convert_afni_warp_imports = ['import os', 'import nibabel as nb'] convert_afni_warp = \ pe.Node(function.Function(input_names=['afni_warp'], output_names=['ants_warp'], function=convert_afni_to_ants, imports=convert_afni_warp_imports), name='convert_afni_warp') wf.connect(calc_blip_warp, 'source_warp', convert_afni_warp, 'afni_warp') # TODO: inverse source_warp (node:source_warp_inverse) # wf.connect(### # output_node, 'blip_warp_inverse') undistort_func_mean = pe.Node(interface=ants.ApplyTransforms(), name='undistort_func_mean', mem_gb=.1) undistort_func_mean.inputs.out_postfix = '_antswarp' undistort_func_mean.interface.num_threads = 1 undistort_func_mean.inputs.interpolation = "LanczosWindowedSinc" undistort_func_mean.inputs.dimension = 3 undistort_func_mean.inputs.input_image_type = 0 wf.connect(input_node, 'func_mean', undistort_func_mean, 'input_image') wf.connect(input_node, 'func_mean', undistort_func_mean, 'reference_image') wf.connect(convert_afni_warp, 'ants_warp', undistort_func_mean, 'transforms') create_new_mask = skullstrip_functional( skullstrip_tool='afni', wf_name="{0}_new_func_mask".format(wf_name)) wf.connect(undistort_func_mean, 'output_image', create_new_mask, 'inputspec.func') wf.connect(convert_afni_warp, 'ants_warp', output_node, 'blip_warp') wf.connect(undistort_func_mean, 'output_image', output_node, 'new_func_mean') wf.connect(create_new_mask, 'outputspec.func_brain_mask', output_node, 'new_func_mask') return wf
def init_3dQwarp_wf(omp_nthreads=1, debug=False, name="pepolar_estimate_wf"): """ Create the PEPOLAR field estimation workflow based on AFNI's ``3dQwarp``. This workflow takes in two EPI files that MUST have opposed :abbr:`PE (phase-encoding)` direction. Therefore, EPIs with orthogonal PE directions are not supported. Workflow Graph .. workflow :: :graph2use: orig :simple_form: yes from sdcflows.workflows.fit.pepolar import init_3dQwarp_wf wf = init_3dQwarp_wf() Parameters ---------- debug : :obj:`bool` Whether a fast configuration of topup (less accurate) should be applied. name : :obj:`str` Name for this workflow omp_nthreads : :obj:`int` Parallelize internal tasks across the number of CPUs given by this option. Inputs ------ in_data : :obj:`list` of :obj:`str` A list of two EPI files, the first of which will be taken as reference. Outputs ------- fmap : :obj:`str` The path of the estimated fieldmap. fmap_ref : :obj:`str` The path of an unwarped conversion of the first element of ``in_data``. """ from nipype.interfaces import afni from niworkflows.interfaces.header import CopyHeader from niworkflows.interfaces.fixes import ( FixHeaderRegistration as Registration, FixHeaderApplyTransforms as ApplyTransforms, ) from niworkflows.interfaces.freesurfer import StructuralReference from niworkflows.func.util import init_enhance_and_skullstrip_bold_wf from ...utils.misc import front as _front, last as _last from ...interfaces.utils import Flatten, ConvertWarp workflow = Workflow(name=name) workflow.__desc__ = f"""{_PEPOLAR_DESC} \ with `3dQwarp` (@afni; AFNI {''.join(['%02d' % v for v in afni.Info().version() or []])}). """ inputnode = pe.Node(niu.IdentityInterface(fields=["in_data", "metadata"]), name="inputnode") outputnode = pe.Node(niu.IdentityInterface(fields=["fmap", "fmap_ref"]), name="outputnode") flatten = pe.Node(Flatten(), name="flatten") sort_pe = pe.Node( niu.Function(function=_sorted_pe, output_names=["sorted", "qwarp_args"]), name="sort_pe", run_without_submitting=True, ) merge_pes = pe.MapNode( StructuralReference( auto_detect_sensitivity=True, initial_timepoint=1, fixed_timepoint=True, # Align to first image intensity_scaling=True, # 7-DOF (rigid + intensity) no_iteration=True, subsample_threshold=200, out_file="template.nii.gz", ), name="merge_pes", iterfield=["in_files"], ) pe0_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads, name="pe0_wf") pe1_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads, name="pe1_wf") align_pes = pe.Node( Registration( from_file=_pkg_fname("sdcflows", "data/translation_rigid.json"), output_warped_image=True, ), name="align_pes", n_procs=omp_nthreads, ) qwarp = pe.Node( afni.QwarpPlusMinus( blur=[-1, -1], environ={"OMP_NUM_THREADS": f"{min(omp_nthreads, 4)}"}, minpatch=9, nopadWARP=True, noweight=True, pblur=[0.05, 0.05], ), name="qwarp", n_procs=min(omp_nthreads, 4), ) to_ants = pe.Node(ConvertWarp(), name="to_ants", mem_gb=0.01) cphdr_warp = pe.Node(CopyHeader(), name="cphdr_warp", mem_gb=0.01) unwarp_reference = pe.Node( ApplyTransforms( dimension=3, float=True, interpolation="LanczosWindowedSinc", ), name="unwarp_reference", ) # fmt: off workflow.connect([ (inputnode, flatten, [("in_data", "in_data"), ("metadata", "in_meta")]), (flatten, sort_pe, [("out_list", "inlist")]), (sort_pe, qwarp, [("qwarp_args", "args")]), (sort_pe, merge_pes, [("sorted", "in_files")]), (merge_pes, pe0_wf, [(("out_file", _front), "inputnode.in_file")]), (merge_pes, pe1_wf, [(("out_file", _last), "inputnode.in_file")]), (pe0_wf, align_pes, [("outputnode.skull_stripped_file", "fixed_image") ]), (pe1_wf, align_pes, [("outputnode.skull_stripped_file", "moving_image") ]), (pe0_wf, qwarp, [("outputnode.skull_stripped_file", "in_file")]), (align_pes, qwarp, [("warped_image", "base_file")]), (inputnode, cphdr_warp, [(("in_data", _front), "hdr_file")]), (qwarp, cphdr_warp, [("source_warp", "in_file")]), (cphdr_warp, to_ants, [("out_file", "in_file")]), (to_ants, unwarp_reference, [("out_file", "transforms")]), (inputnode, unwarp_reference, [("in_reference", "reference_image"), ("in_reference", "input_image")]), (unwarp_reference, outputnode, [("output_image", "fmap_ref")]), (to_ants, outputnode, [("out_file", "fmap")]), ]) # fmt: on return workflow
def init_pepolar_unwarp_wf(omp_nthreads=1, matched_pe=False, name="pepolar_unwarp_wf"): """ Create the PE-Polar field estimation workflow. This workflow takes in a set of EPI files with opposite phase encoding direction than the target file and calculates a displacements field (in other words, an ANTs-compatible warp file). This procedure works if there is only one '_epi' file is present (as long as it has the opposite phase encoding direction to the target file). The target file will be used to estimate the field distortion. However, if there is another '_epi' file present with a matching phase encoding direction to the target it will be used instead. Currently, different phase encoding dimension in the target file and the '_epi' file(s) (for example 'i' and 'j') is not supported. The warp field correcting for the distortions is estimated using AFNI's 3dQwarp, with displacement estimation limited to the target file phase encoding direction. It also calculates a new mask for the input dataset that takes into account the distortions. .. workflow :: :graph2use: orig :simple_form: yes from sdcflows.workflows.pepolar import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf() **Parameters**: matched_pe : bool Whether the input ``fmaps_epi`` will contain images with matched PE blips or not. Please use :func:`sdcflows.workflows.pepolar.check_pes` to determine whether they exist or not. name : str Name for this workflow omp_nthreads : int Parallelize internal tasks across the number of CPUs given by this option. **Inputs**: fmaps_epi : list of tuple(pathlike, str) The list of EPI images that will be used in PE-Polar correction, and their corresponding ``PhaseEncodingDirection`` metadata. The workflow will use the ``bold_pe_dir`` input to separate out those EPI acquisitions with opposed PE blips and those with matched PE blips (the latter could be none, and ``in_reference_brain`` would then be used). The workflow raises a ``ValueError`` when no images with opposed PE blips are found. bold_pe_dir : str The baseline PE direction. in_reference : pathlike The baseline reference image (must correspond to ``bold_pe_dir``). in_reference_brain : pathlike The reference image above, but skullstripped. in_mask : pathlike Not used, present only for consistency across fieldmap estimation workflows. **Outputs**: out_reference : pathlike The ``in_reference`` after unwarping out_reference_brain : pathlike The ``in_reference`` after unwarping and skullstripping out_warp : pathlike The corresponding :abbr:`DFM (displacements field map)` compatible with ANTs. out_mask : pathlike Mask of the unwarped input file """ workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on two echo-planar imaging (EPI) references with opposing phase-encoding directions, using `3dQwarp` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'fmaps_epi', 'in_reference', 'in_reference_brain', 'in_mask', 'bold_pe_dir' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_reference', 'out_reference_brain', 'out_warp', 'out_mask' ]), name='outputnode') prepare_epi_wf = init_prepare_epi_wf(omp_nthreads=omp_nthreads, matched_pe=matched_pe, name="prepare_epi_wf") qwarp = pe.Node(afni.QwarpPlusMinus( pblur=[0.05, 0.05], blur=[-1, -1], noweight=True, minpatch=9, nopadWARP=True, environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}), name='qwarp', n_procs=omp_nthreads) to_ants = pe.Node(niu.Function(function=_fix_hdr), name='to_ants', mem_gb=0.01) cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01) unwarp_reference = pe.Node(ANTSApplyTransformsRPT( dimension=3, generate_report=False, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_reference') enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf( omp_nthreads=omp_nthreads) workflow.connect([ (inputnode, qwarp, [(('bold_pe_dir', _qwarp_args), 'args')]), (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]), (inputnode, prepare_epi_wf, [('fmaps_epi', 'inputnode.maps_pe'), ('bold_pe_dir', 'inputnode.epi_pe'), ('in_reference_brain', 'inputnode.ref_brain')]), (prepare_epi_wf, qwarp, [('outputnode.opposed_pe', 'base_file'), ('outputnode.matched_pe', 'in_file')]), (qwarp, cphdr_warp, [('source_warp', 'in_file')]), (cphdr_warp, to_ants, [('out_file', 'in_file')]), (to_ants, unwarp_reference, [('out', 'transforms')]), (inputnode, unwarp_reference, [('in_reference', 'reference_image'), ('in_reference', 'input_image')]), (unwarp_reference, enhance_and_skullstrip_bold_wf, [('output_image', 'inputnode.in_file')]), (unwarp_reference, outputnode, [('output_image', 'out_reference')]), (enhance_and_skullstrip_bold_wf, outputnode, [('outputnode.mask_file', 'out_mask'), ('outputnode.skull_stripped_file', 'out_reference_brain')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) return workflow
def init_pepolar_unwarp_wf(dwi_meta, epi_fmaps, omp_nthreads=1, name="pepolar_unwarp_wf"): """ This workflow takes in a set of EPI files with opposite phase encoding direction than the target file and calculates a displacements field (in other words, an ANTs-compatible warp file). This procedure works if there is only one '_epi' file is present (as long as it has the opposite phase encoding direction to the target file). The target file will be used to estimate the field distortion. However, if there is another '_epi' file present with a matching phase encoding direction to the target it will be used instead. Currently, different phase encoding dimension in the target file and the '_epi' file(s) (for example 'i' and 'j') is not supported. The warp field correcting for the distortions is estimated using AFNI's 3dQwarp, with displacement estimation limited to the target file phase encoding direction. It also calculates a new mask for the input dataset that takes into account the distortions. .. workflow :: :graph2use: orig :simple_form: yes from qsiprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf( dwi_meta={'PhaseEncodingDirection': 'j'}, epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')], omp_nthreads=8) Inputs in_reference the reference image in_reference_brain the reference image skullstripped in_mask a brain mask corresponding to ``in_reference`` Outputs out_reference the ``in_reference`` after unwarping out_warp the corresponding :abbr:`DFM (displacements field map)` compatible with ANTs """ dwi_file_pe = dwi_meta["PhaseEncodingDirection"] args = '-noXdis -noYdis -noZdis' rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[dwi_file_pe[0]] args = args.replace(rm_arg, '') workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on two echo-planar imaging (EPI) references with opposing phase-encoding directions, using `3dQwarp` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface( fields=['in_reference', 'in_reference_brain', 'in_mask']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['out_reference', 'out_warp']), name='outputnode') prepare_epi_opposite_wf = init_prepare_dwi_epi_wf( omp_nthreads=omp_nthreads, name="prepare_epi_opposite_wf") prepare_epi_opposite_wf.inputs.inputnode.fmaps = epi_fmaps qwarp = pe.Node(afni.QwarpPlusMinus( pblur=[0.05, 0.05], blur=[-1, -1], noweight=True, minpatch=9, nopadWARP=True, environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}, args=args), name='qwarp', n_procs=omp_nthreads) workflow.connect([(inputnode, prepare_epi_opposite_wf, [('in_reference_brain', 'inputnode.ref_brain')]), (prepare_epi_opposite_wf, qwarp, [('outputnode.out_file', 'base_file')]), (inputnode, qwarp, [('in_reference_brain', 'in_file')])]) to_ants = pe.Node(niu.Function(function=_fix_hdr), name='to_ants', mem_gb=0.01) cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01) unwarp_reference = pe.Node(ANTSApplyTransformsRPT( dimension=3, generate_report=False, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_reference') workflow.connect([ (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]), (qwarp, cphdr_warp, [('source_warp', 'in_file')]), (cphdr_warp, to_ants, [('out_file', 'in_file')]), (to_ants, unwarp_reference, [('out', 'transforms')]), (inputnode, unwarp_reference, [('in_reference', 'reference_image'), ('in_reference', 'input_image')]), (unwarp_reference, outputnode, [('output_image', 'out_reference')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) return workflow
def init_bidirectional_b0_unwarping_wf(template_plus_pe, omp_nthreads=1, name="bidirectional_pepolar_unwarping_wf"): """ This workflow takes in a set of b0 files with opposite phase encoding direction and calculates displacement fields (in other words, an ANTs-compatible warp file). This is intended to be run in the case where there are two dwi series in the same session with reverse phase encoding directions. The warp field correcting for the distortions is estimated using AFNI's 3dQwarp, with displacement estimation limited to the target file phase encoding direction. It also calculates a new mask for the input dataset that takes into account the distortions. .. workflow :: :graph2use: orig :simple_form: yes from qsiprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf( bold_meta={'PhaseEncodingDirection': 'j'}, epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')], omp_nthreads=8) Inputs template_plus b0 template in one PE template_minus b0_template in the other PE Outputs out_reference the ``in_reference`` after unwarping out_reference_brain the ``in_reference`` after unwarping and skullstripping out_warp_plus the corresponding :abbr:`DFM (displacements field map)` to correct ``template_plus`` out_warp_minus the corresponding :abbr:`DFM (displacements field map)` to correct ``template_minus`` out_mask mask of the unwarped input file """ args = '-noXdis -noYdis -noZdis' rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[template_plus_pe[0]] args = args.replace(rm_arg, '') workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on two b0 templates created from dwi series with opposing phase-encoding directions, using `3dQwarp` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface( fields=['template_plus', 'template_minus', 't1w_brain']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_reference', 'out_reference_brain', 'out_affine_plus', 'out_warp_plus', 'out_affine_minus', 'out_warp_minus', 'out_mask']), name='outputnode') # Create high-contrast ref images plus_ref_wf = init_dwi_reference_wf(name='plus_ref_wf') minus_ref_wf = init_dwi_reference_wf(name='minus_ref_wf') # Align the two reference images to the midpoint inputs_to_list = pe.Node(niu.Merge(2), name='inputs_to_list') align_reverse_pe_wf = init_b0_hmc_wf(align_to='iterative', transform='Rigid') get_midpoint_transforms = pe.Node(niu.Split(splits=[1, 1], squeeze=True), name="get_midpoint_transforms") plus_to_midpoint = pe.Node(ants.ApplyTransforms(float=True, interpolation='LanczosWindowedSinc', dimension=3), name='plus_to_midpoint') minus_to_midpoint = pe.Node(ants.ApplyTransforms(float=True, interpolation='LanczosWindowedSinc', dimension=3), name='minus_to_midpoint') qwarp = pe.Node(afni.QwarpPlusMinus(pblur=[0.05, 0.05], blur=[-1, -1], noweight=True, minpatch=9, nopadWARP=True, environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}, args=args), name='qwarp', n_procs=omp_nthreads) to_ants_plus = pe.Node(niu.Function(function=_fix_hdr), name='to_ants_plus', mem_gb=0.01) to_ants_minus = pe.Node(niu.Function(function=_fix_hdr), name='to_ants_minus', mem_gb=0.01) cphdr_plus_warp = pe.Node(CopyHeader(), name='cphdr_plus_warp', mem_gb=0.01) cphdr_minus_warp = pe.Node(CopyHeader(), name='cphdr_minus_warp', mem_gb=0.01) unwarp_plus_reference = pe.Node(ants.ApplyTransforms(dimension=3, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_plus_reference') unwarp_minus_reference = pe.Node(ants.ApplyTransforms(dimension=3, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_minus_reference') unwarped_to_list = pe.Node(niu.Merge(2), name="unwarped_to_list") merge_unwarped = pe.Node(ants.AverageImages(dimension=3, normalize=True), name="merge_unwarped") final_ref = init_dwi_reference_wf(name="final_ref") workflow.connect([ (inputnode, plus_ref_wf, [('template_plus', 'inputnode.b0_template')]), (plus_ref_wf, inputs_to_list, [('outputnode.ref_image', 'in1')]), (inputnode, minus_ref_wf, [('template_minus', 'inputnode.b0_template')]), (minus_ref_wf, inputs_to_list, [('outputnode.ref_image', 'in2')]), (inputs_to_list, align_reverse_pe_wf, [('out', 'inputnode.b0_images')]), (align_reverse_pe_wf, get_midpoint_transforms, [('outputnode.forward_transforms', 'inlist')]), (get_midpoint_transforms, outputnode, [('out1', 'out_affine_plus'), ('out2', 'out_affine_minus')]), (plus_ref_wf, plus_to_midpoint, [('outputnode.ref_image', 'input_image')]), (minus_ref_wf, minus_to_midpoint, [('outputnode.ref_image', 'input_image')]), (get_midpoint_transforms, plus_to_midpoint, [('out1', 'transforms')]), (align_reverse_pe_wf, plus_to_midpoint, [('outputnode.final_template', 'reference_image')]), (get_midpoint_transforms, minus_to_midpoint, [('out2', 'transforms')]), (align_reverse_pe_wf, minus_to_midpoint, [('outputnode.final_template', 'reference_image')]), (plus_to_midpoint, qwarp, [('output_image', 'in_file')]), (minus_to_midpoint, qwarp, [('output_image', 'base_file')]), (align_reverse_pe_wf, cphdr_plus_warp, [('outputnode.final_template', 'hdr_file')]), (align_reverse_pe_wf, cphdr_minus_warp, [('outputnode.final_template', 'hdr_file')]), (qwarp, cphdr_plus_warp, [('source_warp', 'in_file')]), (qwarp, cphdr_minus_warp, [('base_warp', 'in_file')]), (cphdr_plus_warp, to_ants_plus, [('out_file', 'in_file')]), (cphdr_minus_warp, to_ants_minus, [('out_file', 'in_file')]), (to_ants_minus, unwarp_minus_reference, [('out', 'transforms')]), (minus_to_midpoint, unwarp_minus_reference, [('output_image', 'reference_image'), ('output_image', 'input_image')]), (to_ants_minus, outputnode, [('out', 'out_warp_minus')]), (to_ants_plus, unwarp_plus_reference, [('out', 'transforms')]), (plus_to_midpoint, unwarp_plus_reference, [('output_image', 'reference_image'), ('output_image', 'input_image')]), (to_ants_plus, outputnode, [('out', 'out_warp_plus')]), (unwarp_plus_reference, unwarped_to_list, [('output_image', 'in1')]), (unwarp_minus_reference, unwarped_to_list, [('output_image', 'in2')]), (unwarped_to_list, merge_unwarped, [('out', 'images')]), (merge_unwarped, final_ref, [('output_average_image', 'inputnode.b0_template')]), (final_ref, outputnode, [('outputnode.ref_image', 'out_reference'), ('outputnode.ref_image_brain', 'out_reference_brain'), ('outputnode.dwi_mask', 'out_mask')]) ]) return workflow
def init_pepolar_unwarp_wf(bold_meta, epi_fmaps, omp_nthreads=1, name="pepolar_unwarp_wf"): """ This workflow takes in a set of EPI files with opposite phase encoding direction than the target file and calculates a displacements field (in other words, an ANTs-compatible warp file). This procedure works if there is only one '_epi' file is present (as long as it has the opposite phase encoding direction to the target file). The target file will be used to estimate the field distortion. However, if there is another '_epi' file present with a matching phase encoding direction to the target it will be used instead. Currently, different phase encoding dimension in the target file and the '_epi' file(s) (for example 'i' and 'j') is not supported. The warp field correcting for the distortions is estimated using AFNI's 3dQwarp, with displacement estimation limited to the target file phase encoding direction. It also calculates a new mask for the input dataset that takes into account the distortions. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf( bold_meta={'PhaseEncodingDirection': 'j'}, epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')], omp_nthreads=8) Inputs in_reference the reference image in_reference_brain the reference image skullstripped in_mask a brain mask corresponding to ``in_reference`` Outputs out_reference the ``in_reference`` after unwarping out_reference_brain the ``in_reference`` after unwarping and skullstripping out_warp the corresponding :abbr:`DFM (displacements field map)` compatible with ANTs out_mask mask of the unwarped input file """ bold_file_pe = bold_meta["PhaseEncodingDirection"] args = '-noXdis -noYdis -noZdis' rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[bold_file_pe[0]] args = args.replace(rm_arg, '') usable_fieldmaps_matching_pe = [] usable_fieldmaps_opposite_pe = [] for fmap, fmap_pe in epi_fmaps: if fmap_pe == bold_file_pe: usable_fieldmaps_matching_pe.append(fmap) elif fmap_pe[0] == bold_file_pe[0]: usable_fieldmaps_opposite_pe.append(fmap) if not usable_fieldmaps_opposite_pe: raise Exception("None of the discovered fieldmaps has the right " "phase encoding direction. Possibly a problem with " "metadata. If not, rerun with '--ignore fieldmaps' to " "skip distortion correction step.") workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_reference', 'in_reference_brain', 'in_mask']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_reference', 'out_reference_brain', 'out_warp', 'out_mask' ]), name='outputnode') prepare_epi_opposite_wf = init_prepare_epi_wf( omp_nthreads=omp_nthreads, name="prepare_epi_opposite_wf") prepare_epi_opposite_wf.inputs.inputnode.fmaps = usable_fieldmaps_opposite_pe qwarp = pe.Node(afni.QwarpPlusMinus( pblur=[0.05, 0.05], blur=[-1, -1], noweight=True, minpatch=9, nopadWARP=True, environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}, args=args), name='qwarp', n_procs=omp_nthreads) workflow.connect([ (inputnode, prepare_epi_opposite_wf, [('in_reference_brain', 'inputnode.ref_brain')]), (prepare_epi_opposite_wf, qwarp, [('outputnode.out_file', 'base_file') ]), ]) if usable_fieldmaps_matching_pe: prepare_epi_matching_wf = init_prepare_epi_wf( omp_nthreads=omp_nthreads, name="prepare_epi_matching_wf") prepare_epi_matching_wf.inputs.inputnode.fmaps = usable_fieldmaps_matching_pe workflow.connect([ (inputnode, prepare_epi_matching_wf, [('in_reference_brain', 'inputnode.ref_brain')]), (prepare_epi_matching_wf, qwarp, [('outputnode.out_file', 'source_file')]), ]) else: workflow.connect([(inputnode, qwarp, [('in_reference_brain', 'source_file')])]) to_ants = pe.Node(niu.Function(function=_fix_hdr), name='to_ants', mem_gb=0.01) cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01) unwarp_reference = pe.Node(ANTSApplyTransformsRPT( dimension=3, generate_report=False, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_reference') enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf( omp_nthreads=omp_nthreads) workflow.connect([ (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]), (qwarp, cphdr_warp, [('source_warp', 'in_file')]), (cphdr_warp, to_ants, [('out_file', 'in_file')]), (to_ants, unwarp_reference, [('out', 'transforms')]), (inputnode, unwarp_reference, [('in_reference', 'reference_image'), ('in_reference', 'input_image')]), (unwarp_reference, enhance_and_skullstrip_bold_wf, [('output_image', 'inputnode.in_file')]), (unwarp_reference, outputnode, [('output_image', 'out_reference')]), (enhance_and_skullstrip_bold_wf, outputnode, [('outputnode.mask_file', 'out_mask'), ('outputnode.skull_stripped_file', 'out_reference_brain')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) return workflow
def run_workflows(session=None, csv_file=None): from nipype import config #config.enable_debug_mode() # ------------------ Specify variables ds_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) data_dir = ds_root output_dir = 'derivatives/undistort' working_dir = 'workingdirs' # ------------------ Input Files infosource = Node(IdentityInterface(fields=[ 'subject_id', 'session_id', 'run_id', 'refsubject_id', ]), name="infosource") if csv_file is not None: print('=== reading csv ===') # Read csv and use pandas to set-up image and ev-processing df = pd.read_csv(csv_file) # init lists sub_img=[]; ses_img=[]; run_img=[]; ref_img=[] # fill lists to iterate mapnodes for index, row in df.iterrows(): for r in row.run.strip("[]").split(" "): sub_img.append(row.subject) ses_img.append(row.session) run_img.append(r) if 'refsubject' in df.columns: if row.refsubject == 'nan': # empty field ref_img.append(row.subject) else: # non-empty field ref_img.append(row.refsubject) else: ref_img.append(row.subject) infosource.iterables = [ ('subject_id', sub_img), ('session_id', ses_img), ('run_id', run_img), ('refsubject_id', ref_img), ] infosource.synchronize = True else: print("No csv-file specified. Don't know what data to process.") # SelectFiles templates = { 'image': 'derivatives/resampled-isotropic-1mm/' 'sub-{subject_id}/ses-{session_id}/func/' 'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc.nii.gz', 'image_invPE': 'derivatives/resampled-isotropic-1mm/' 'sub-{subject_id}/ses-{session_id}/fmap/' 'sub-{subject_id}_ses-{session_id}*run-{run_id}_epi_res-1x1x1_preproc.nii.gz', } inputfiles = Node( nio.SelectFiles(templates, base_directory=data_dir), name="input_files") # Datasink outfiles = Node(nio.DataSink( base_directory=ds_root, container=output_dir, parameterization=True), name="outfiles") # Use the following DataSink output substitutions outfiles.inputs.substitutions = [ ('refsubject_id_', 'ref-'), ('subject_id_', 'sub-'), ('session_id_', 'ses-'), ('resampled-isotropic-1mm','undistort'), ('undistort/ud_func', 'undistort'), ] outfiles.inputs.regexp_substitutions = [ (r'_ses-([a-zA-Z0-9]+)_sub-([a-zA-Z0-9]+)', r'sub-\2/ses-\1/func'), (r'_ref-([a-zA-Z0-9]+)_run_id_[0-9][0-9]', r''), ] templates_mv = { 'ud_minus': 'derivatives/resampled-isotropic-1mm/' 'sub-{subject_id}/ses-{session_id}/func/' 'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_MINUS.nii.gz', 'ud_minus_warp': 'derivatives/resampled-isotropic-1mm/' 'sub-{subject_id}/ses-{session_id}/func/' 'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_MINUS_WARP.nii.gz', 'ud_plus': 'derivatives/resampled-isotropic-1mm/' 'sub-{subject_id}/ses-{session_id}/func/' 'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_PLUS.nii.gz', 'ud_plus_warp': 'derivatives/resampled-isotropic-1mm/' 'sub-{subject_id}/ses-{session_id}/func/' 'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_PLUS_WARP.nii.gz', } mv_infiles = Node( nio.SelectFiles(templates_mv, base_directory=data_dir), name="mv_infiles") # Datasink mv_outfiles = Node(nio.DataSink( base_directory=ds_root, container=output_dir, parameterization=True), name="mv_outfiles") # Use the following DataSink output substitutions mv_outfiles.inputs.substitutions = [ ('refsubject_id_', 'ref-'), ('subject_id_', 'sub-'), ('session_id_', 'ses-'), ('resampled-isotropic-1mm','undistort'), ('undistort/ud_func', 'undistort'), ] mv_outfiles.inputs.regexp_substitutions = [ (r'sub-([a-zA-Z0-9]+)_ses-([a-zA-Z0-9]+)', r'sub-\1/ses-\2/func/qwarp_plusminus/sub-\1_ses-\2'), ] # -------------------------------------------- Create Pipeline undistort = Workflow( name='undistort', base_dir=os.path.join(ds_root, working_dir)) undistort.connect([ (infosource, inputfiles, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id'), ('refsubject_id', 'refsubject_id'), ])]) qwarp = Node(afni.QwarpPlusMinus( nopadWARP=True,outputtype='NIFTI_GZ'), iterfield=('in_file'),name='qwarp') undistort.connect(inputfiles, 'image', qwarp, 'in_file') undistort.connect(inputfiles, 'image_invPE', qwarp, 'base_file') undistort.connect(inputfiles, 'image', qwarp, 'out_file') nwarp = Node(afni.NwarpApply(out_file='%s_undistort.nii.gz'),name='nwarp') undistort.connect(inputfiles, 'image', nwarp, 'in_file') undistort.connect(qwarp, 'source_warp', nwarp, 'warp') undistort.connect(inputfiles, 'image', nwarp, 'master') undistort.connect(nwarp, 'out_file', outfiles, 'ud_func') undistort.stop_on_first_crash = False # True undistort.keep_inputs = True undistort.remove_unnecessary_outputs = False undistort.write_graph() undistort.run() mv_ud = Workflow( name='mv_ud', base_dir=os.path.join(ds_root, working_dir)) mv_ud.connect([ (infosource, mv_infiles, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id'), ('refsubject_id', 'refsubject_id'), ])]) mv_ud.connect(mv_infiles, 'ud_minus', mv_outfiles, 'ud_func.@ud_minus') mv_ud.connect(mv_infiles, 'ud_plus', mv_outfiles, 'ud_func.@ud_plus') mv_ud.connect(mv_infiles, 'ud_minus_warp', mv_outfiles, 'ud_func.@ud_minus_warp') mv_ud.connect(mv_infiles, 'ud_plus_warp', mv_outfiles, 'ud_func.@ud_plus_warp') mv_ud.stop_on_first_crash = False # True mv_ud.keep_inputs = True mv_ud.remove_unnecessary_outputs = False mv_ud.write_graph() mv_ud.run() # remove the undistorted files from the ...derivatives/resampled folder for index, row in df.iterrows(): fpath = os.path.join(data_dir,'derivatives','resampled-isotropic-1mm', 'sub-' + row.subject,'ses-' + str(row.session),'func') for f in glob.glob(os.path.join(fpath,'*US*.nii.gz')): os.remove(f)