def init_qwarp_inversion_wf(omp_nthreads=1, name="qwarp_invert_wf"): """ Invert a warp produced by 3dqwarp and convert it to an ANTS formatted warp Workflow Graph .. workflow :: :graph2use: orig :simple_form: yes from sdcflows.workflows.base import init_qwarp_inversion_wf wf = init_qwarp_inversion_wf() Parameters ---------- name : str Name for this workflow omp_nthreads : int Parallelize internal tasks across the number of CPUs given by this option. Inputs ------ warp : pathlike The warp you want to invert. in_reference : pathlike The baseline reference image (must correspond to ``epi_pe_dir``). Outputs ------- out_warp : pathlike The corresponding inverted :abbr:`DFM (displacements field map)` compatible with ANTs. """ from ..interfaces.afni import InvertWarp workflow = Workflow(name=name) workflow.__desc__ = """\ A warp produced by 3dQwarp was inverted by `3dNwarpCat` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface(fields=['warp', 'in_reference']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_warp']), name='outputnode') invert = pe.Node(InvertWarp(), name='invert', n_procs=omp_nthreads) invert.inputs.outputtype = 'NIFTI_GZ' 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) workflow.connect([ (inputnode, invert, [('warp', 'in_file')]), (invert, cphdr_warp, [('out_file', 'in_file')]), (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]), (cphdr_warp, to_ants, [('out_file', 'in_file')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) return workflow
def init_pepolar_unwarp_wf(fmaps, bold_file, omp_nthreads, layout=None, fmaps_pes=None, bold_file_pe=None, 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.unwarp import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf(fmaps=['/dataset/sub-01/fmap/sub-01_epi.nii.gz'], fmaps_pes=['j-'], bold_file='/dataset/sub-01/func/sub-01_task-rest_bold.nii.gz', bold_file_pe='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`` name_source not used, kept for signature compatibility with ``init_sdc_unwarp_wf`` 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 out_mask_report reportlet for the skullstripping """ if not bold_file_pe: bold_file_pe = layout.get_metadata(bold_file)["PhaseEncodingDirection"] usable_fieldmaps_matching_pe = [] usable_fieldmaps_opposite_pe = [] args = '-noXdis -noYdis -noZdis' rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[bold_file_pe[0]] args = args.replace(rm_arg, '') for i, fmap in enumerate(fmaps): if fmaps_pes: fmap_pe = fmaps_pes[i] else: fmap_pe = layout.get_metadata(fmap)["PhaseEncodingDirection"] if fmap_pe[0] == bold_file_pe[0]: if len(fmap_pe) != len(bold_file_pe): add_list = usable_fieldmaps_opposite_pe else: add_list = usable_fieldmaps_matching_pe add_list.append(fmap) if len(usable_fieldmaps_opposite_pe) == 0: 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_source' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_reference', 'out_reference_brain', 'out_warp', 'out_mask', 'out_mask_report' ]), name='outputnode') prepare_epi_opposite_wf = init_prepare_epi_wf( ants_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': str(omp_nthreads)}, args=args), name='qwarp') qwarp.interface.num_threads = 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( ants_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') cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp') unwarp_reference = pe.Node(ANTSApplyTransformsRPT( dimension=3, generate_report=False, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_reference') enhance_and_skullstrip_epi_wf = init_enhance_and_skullstrip_epi_wf() 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_epi_wf, [('output_image', 'inputnode.in_file')]), (unwarp_reference, outputnode, [('output_image', 'out_reference')]), (enhance_and_skullstrip_epi_wf, outputnode, [('outputnode.mask_file', 'out_mask'), ('outputnode.out_report', 'out_report'), ('outputnode.skull_stripped_file', 'out_reference_brain')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) 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_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 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.__postdesc__ = 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(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 = 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(list(afni.QwarpPlusMinus().version or '<ver>'))) 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