#!/usr/bin/env python
# -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
"""
.. _sdc_base :
Automatic selection of the appropriate SDC method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the dataset metadata indicate tha more than one field map acquisition is
``IntendedFor`` (see BIDS Specification section 8.9) the following priority will
be used:
1. :ref:`sdc_pepolar` (or **blip-up/blip-down**)
2. :ref:`sdc_direct_b0`
3. :ref:`sdc_phasediff`
4. :ref:`sdc_fieldmapless`
Table of behavior (fieldmap use-cases):
=============== =========== ============= ===============
Fieldmaps found ``use_syn`` ``force_syn`` Action
=============== =========== ============= ===============
True * True Fieldmaps + SyN
True * False Fieldmaps
False * True SyN
False True False SyN
False False False HMC only
=============== =========== ============= ===============
"""
from niworkflows.nipype.pipeline import engine as pe
from niworkflows.nipype.interfaces import utility as niu
from niworkflows.nipype import logging
# Fieldmap workflows
from .pepolar import init_pepolar_unwarp_wf
from .syn import init_syn_sdc_wf
from .unwarp import init_sdc_unwarp_wf
LOGGER = logging.getLogger('workflow')
FMAP_PRIORITY = {
'epi': 0,
'fieldmap': 1,
'phasediff': 2,
'syn': 3
}
DEFAULT_MEMORY_MIN_GB = 0.01
[docs]def init_sdc_wf(fmaps, bold_meta, omp_nthreads=1,
debug=False, fmap_bspline=False, fmap_demean=True):
"""
This workflow implements the heuristics to choose a
:abbr:`SDC (susceptibility distortion correction)` strategy.
When no field map information is present within the BIDS inputs,
the EXPERIMENTAL "fieldmap-less SyN" can be performed, using
the ``--use-syn`` argument. When ``--force-syn`` is specified,
then the "fieldmap-less SyN" is always executed and reported
despite of other fieldmaps available with higher priority.
In the latter case (some sort of fieldmap(s) is available and
``--force-syn`` is requested), then the :abbr:`SDC (susceptibility
distortion correction)` method applied is that with the
highest priority.
.. workflow::
:graph2use: orig
:simple_form: yes
from fmriprep.workflows.fieldmap import init_sdc_wf
wf = init_sdc_wf(
fmaps=[{
'type': 'phasediff',
'phasediff': 'sub-03/ses-2/fmap/sub-03_ses-2_run-1_phasediff.nii.gz',
'magnitude1': 'sub-03/ses-2/fmap/sub-03_ses-2_run-1_magnitude1.nii.gz',
'magnitude2': 'sub-03/ses-2/fmap/sub-03_ses-2_run-1_magnitude2.nii.gz',
}],
bold_meta={
'RepetitionTime': 2.0,
'SliceTiming': [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9],
'PhaseEncodingDirection': 'j',
},
)
**Parameters**
fmaps : list of pybids dicts
A list of dictionaries with the available fieldmaps
(and their metadata using the key ``'metadata'`` for the
case of *epi* fieldmaps)
bold_meta : dict
BIDS metadata dictionary corresponding to the BOLD run
omp_nthreads : int
Maximum number of threads an individual process may use
fmap_bspline : bool
**Experimental**: Fit B-Spline field using least-squares
fmap_demean : bool
Demean voxel-shift map during unwarp
debug : bool
Enable debugging outputs
**Inputs**
bold_ref
A BOLD reference calculated at a previous stage
bold_ref_brain
Same as above, but brain-masked
bold_mask
Brain mask for the BOLD run
t1_brain
T1w image, brain-masked, for the fieldmap-less SyN method
t1_2_mni_reverse_transform
MNI-to-T1w transform to map prior knowledge to the T1w
fo the fieldmap-less SyN method
template : str
Name of template targeted by ``template`` output space
**Outputs**
bold_ref
An unwarped BOLD reference
bold_mask
The corresponding new mask after unwarping
bold_ref_brain
Brain-extracted, unwarped BOLD reference
out_warp
The deformation field to unwarp the susceptibility distortions
syn_bold_ref
If ``--force-syn``, an unwarped BOLD reference with this
method (for reporting purposes)
"""
# TODO: To be removed (filter out unsupported fieldmaps):
fmaps = [fmap for fmap in fmaps if fmap['type'] in FMAP_PRIORITY]
workflow = pe.Workflow(name='sdc_wf' if fmaps else 'sdc_bypass_wf')
inputnode = pe.Node(niu.IdentityInterface(
fields=['bold_ref', 'bold_ref_brain', 'bold_mask',
't1_brain', 't1_2_mni_reverse_transform', 'template']),
name='inputnode')
outputnode = pe.Node(niu.IdentityInterface(
fields=['bold_ref', 'bold_mask', 'bold_ref_brain',
'out_warp', 'syn_bold_ref', 'method']),
name='outputnode')
# No fieldmaps - forward inputs to outputs
if not fmaps:
outputnode.inputs.method = 'None'
workflow.connect([
(inputnode, outputnode, [('bold_ref', 'bold_ref'),
('bold_mask', 'bold_mask'),
('bold_ref_brain', 'bold_ref_brain')]),
])
return workflow
# In case there are multiple fieldmaps prefer EPI
fmaps.sort(key=lambda fmap: FMAP_PRIORITY[fmap['type']])
fmap = fmaps[0]
# PEPOLAR path
if fmap['type'] == 'epi':
outputnode.inputs.method = 'PEB/PEPOLAR (phase-encoding based / PE-POLARity)'
# Get EPI polarities and their metadata
epi_fmaps = [(fmap_['epi'], fmap_['metadata']["PhaseEncodingDirection"])
for fmap_ in fmaps if fmap_['type'] == 'epi']
sdc_unwarp_wf = init_pepolar_unwarp_wf(
bold_meta=bold_meta,
epi_fmaps=epi_fmaps,
omp_nthreads=omp_nthreads,
name='pepolar_unwarp_wf')
workflow.connect([
(inputnode, sdc_unwarp_wf, [
('bold_ref', 'inputnode.in_reference'),
('bold_mask', 'inputnode.in_mask'),
('bold_ref_brain', 'inputnode.in_reference_brain')]),
])
# FIELDMAP path
if fmap['type'] in ['fieldmap', 'phasediff']:
outputnode.inputs.method = 'FMB (%s-based)' % fmap['type']
# Import specific workflows here, so we don't break everything with one
# unused workflow.
if fmap['type'] == 'fieldmap':
from .fmap import init_fmap_wf
fmap_estimator_wf = init_fmap_wf(
omp_nthreads=omp_nthreads,
fmap_bspline=fmap_bspline)
# set inputs
fmap_estimator_wf.inputs.inputnode.fieldmap = fmap['fieldmap']
fmap_estimator_wf.inputs.inputnode.magnitude = fmap['magnitude']
if fmap['type'] == 'phasediff':
from .phdiff import init_phdiff_wf
fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads)
# set inputs
fmap_estimator_wf.inputs.inputnode.phasediff = fmap['phasediff']
fmap_estimator_wf.inputs.inputnode.magnitude = [
fmap_ for key, fmap_ in sorted(fmap.items())
if key.startswith("magnitude")
]
sdc_unwarp_wf = init_sdc_unwarp_wf(
omp_nthreads=omp_nthreads,
fmap_demean=fmap_demean,
debug=debug,
name='sdc_unwarp_wf')
sdc_unwarp_wf.inputs.inputnode.metadata = bold_meta
workflow.connect([
(inputnode, sdc_unwarp_wf, [
('bold_ref', 'inputnode.in_reference'),
('bold_ref_brain', 'inputnode.in_reference_brain'),
('bold_mask', 'inputnode.in_mask')]),
(fmap_estimator_wf, sdc_unwarp_wf, [
('outputnode.fmap', 'inputnode.fmap'),
('outputnode.fmap_ref', 'inputnode.fmap_ref'),
('outputnode.fmap_mask', 'inputnode.fmap_mask')]),
])
# FIELDMAP-less path
if any(fm['type'] == 'syn' for fm in fmaps):
syn_sdc_wf = init_syn_sdc_wf(
bold_pe=bold_meta.get('PhaseEncodingDirection', None),
omp_nthreads=omp_nthreads)
workflow.connect([
(inputnode, syn_sdc_wf, [
('t1_brain', 'inputnode.t1_brain'),
('t1_2_mni_reverse_transform', 'inputnode.t1_2_mni_reverse_transform'),
('bold_ref', 'inputnode.bold_ref'),
('bold_ref_brain', 'inputnode.bold_ref_brain'),
('template', 'inputnode.template')]),
])
# XXX Eliminate branch when forcing isn't an option
if fmap['type'] == 'syn': # No fieldmaps, but --use-syn
outputnode.inputs.method = 'FLB ("fieldmap-less", SyN-based)'
sdc_unwarp_wf = syn_sdc_wf
else: # --force-syn was called when other fieldmap was present
workflow.connect([
(syn_sdc_wf, outputnode, [
('outputnode.out_reference', 'syn_bold_ref')]),
])
workflow.connect([
(sdc_unwarp_wf, outputnode, [
('outputnode.out_warp', 'out_warp'),
('outputnode.out_reference', 'bold_ref'),
('outputnode.out_reference_brain', 'bold_ref_brain'),
('outputnode.out_mask', 'bold_mask')]),
])
return workflow