-
Notifications
You must be signed in to change notification settings - Fork 305
[ENH] Add phase1/phase2 SDC support #1359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b2a937a
f2b45d4
9dec5d8
840d167
bb510de
4d78483
6e0edda
9a0fcde
e5a6dee
d89c776
f6d4aa7
649dafe
8558aa5
1d12256
70b3413
191e155
880d04d
dc5b547
9f9284e
ce36514
0712c5e
4fd6add
09316a9
f9ca876
150b54f
ada0d9d
2c52d49
27c51ae
f57c063
2f6a294
a8fc3bd
0f7cdec
f2e861d
ff2d7d9
d94346f
5ce44ce
db42f74
6c7ec33
ef86939
1be1be1
c27d839
4d371a1
9c5b46b
85fcb4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -336,7 +336,11 @@ def init_func_preproc_wf( | |
if 'fieldmaps' not in ignore: | ||
fmaps = layout.get_fieldmap(ref_file, return_list=True) | ||
for fmap in fmaps: | ||
fmap['metadata'] = layout.get_metadata(fmap[fmap['suffix']]) | ||
if fmap['suffix'] == 'phase': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think there exists such a suffix in BIDS. This works because fmap_key gets renamed to phase1 either way (not sure about how then it digests the phase2 suffix, I'll check below). |
||
fmap_key = 'phase1' | ||
else: | ||
fmap_key = fmap['suffix'] | ||
fmap['metadata'] = layout.get_metadata(fmap[fmap_key]) | ||
|
||
# Run SyN if forced or in the absence of fieldmap correction | ||
if force_syn or (use_syn and not fmaps): | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -53,7 +53,8 @@ | |||||
'epi': 0, | ||||||
'fieldmap': 1, | ||||||
'phasediff': 2, | ||||||
'syn': 3 | ||||||
'phase': 3, | ||||||
'syn': 4 | ||||||
} | ||||||
DEFAULT_MEMORY_MIN_GB = 0.01 | ||||||
|
||||||
|
@@ -199,7 +200,7 @@ def init_sdc_wf(fmaps, bold_meta, omp_nthreads=1, | |||||
]) | ||||||
|
||||||
# FIELDMAP path | ||||||
if fmap['suffix'] in ['fieldmap', 'phasediff']: | ||||||
if fmap['suffix'] in ['fieldmap', 'phasediff', 'phase']: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Along the lines of the previous comment.
Suggested change
|
||||||
outputnode.inputs.method = 'FMB (%s-based)' % fmap['suffix'] | ||||||
# Import specific workflows here, so we don't break everything with one | ||||||
# unused workflow. | ||||||
|
@@ -212,11 +213,30 @@ def init_sdc_wf(fmaps, bold_meta, omp_nthreads=1, | |||||
fmap_estimator_wf.inputs.inputnode.fieldmap = fmap['fieldmap'] | ||||||
fmap_estimator_wf.inputs.inputnode.magnitude = fmap['magnitude'] | ||||||
|
||||||
if fmap['suffix'] == 'phasediff': | ||||||
if fmap['suffix'] in ('phasediff', 'phase'): | ||||||
from .phdiff import init_phdiff_wf | ||||||
fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads) | ||||||
fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads, | ||||||
phasetype=fmap['suffix']) | ||||||
# set inputs | ||||||
fmap_estimator_wf.inputs.inputnode.phasediff = fmap['phasediff'] | ||||||
if fmap['suffix'] == 'phasediff': | ||||||
fmap_estimator_wf.inputs.inputnode.phasediff = fmap['phasediff'] | ||||||
elif fmap['suffix'] == 'phase': | ||||||
# Check that fieldmap is not bipolar | ||||||
fmap_polarity = fmap['metadata'].get('DiffusionScheme', None) | ||||||
if fmap_polarity == 'Bipolar': | ||||||
LOGGER.warning("Bipolar fieldmaps are not supported. Ignoring") | ||||||
workflow.__postdesc__ = "" | ||||||
outputnode.inputs.method = 'None' | ||||||
workflow.connect([ | ||||||
(inputnode, outputnode, [('bold_ref', 'bold_ref'), | ||||||
('bold_mask', 'bold_mask'), | ||||||
('bold_ref_brain', 'bold_ref_brain')]), | ||||||
]) | ||||||
return workflow | ||||||
if fmap_polarity is None: | ||||||
LOGGER.warning("Assuming phase images are Monopolar") | ||||||
|
||||||
fmap_estimator_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] | ||||||
fmap_estimator_wf.inputs.inputnode.magnitude = [ | ||||||
fmap_ for key, fmap_ in sorted(fmap.items()) | ||||||
if key.startswith("magnitude") | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
|
||
Fieldmap preprocessing workflow for fieldmap data structure | ||
8.9.1 in BIDS 1.0.0: one phase diff and at least one magnitude image | ||
8.9.2 in BIDS 1.0.0: two phases and at least one magnitude image | ||
|
||
""" | ||
|
||
|
@@ -27,10 +28,10 @@ | |
from niworkflows.interfaces.images import IntraModalMerge | ||
from niworkflows.interfaces.masks import BETRPT | ||
|
||
from ...interfaces import Phasediff2Fieldmap, DerivativesDataSink | ||
from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap, DerivativesDataSink | ||
|
||
|
||
def init_phdiff_wf(omp_nthreads, name='phdiff_wf'): | ||
def init_phdiff_wf(omp_nthreads, phasetype='phasediff', name='phdiff_wf'): | ||
""" | ||
Estimates the fieldmap using a phase-difference image and one or more | ||
magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` | ||
|
@@ -69,12 +70,6 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'): | |
outputnode = pe.Node(niu.IdentityInterface( | ||
fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') | ||
|
||
def _pick1st(inlist): | ||
return inlist[0] | ||
|
||
# Read phasediff echo times | ||
meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01) | ||
|
||
# Merge input magnitude images | ||
magmrg = pe.Node(IntraModalMerge(), name='magmrg') | ||
|
||
|
@@ -90,9 +85,6 @@ def _pick1st(inlist): | |
# dilate = pe.Node(fsl.maths.MathsCommand( | ||
# nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') | ||
|
||
# phase diff -> radians | ||
pha2rads = pe.Node(niu.Function(function=siemens2rads), name='pha2rads') | ||
|
||
# FSL PRELUDE will perform phase-unwrapping | ||
prelude = pe.Node(fsl.PRELUDE(), name='prelude') | ||
|
||
|
@@ -110,16 +102,47 @@ def _pick1st(inlist): | |
# pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') | ||
# rsec2hz (divide by 2pi) | ||
|
||
if phasetype == "phasediff": | ||
# Read phasediff echo times | ||
meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01) | ||
|
||
# phase diff -> radians | ||
pha2rads = pe.Node(niu.Function(function=siemens2rads), | ||
name='pha2rads') | ||
# Read phasediff echo times | ||
meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01, | ||
run_without_submitting=True) | ||
workflow.connect([ | ||
(meta, compfmap, [('out_dict', 'metadata')]), | ||
(inputnode, pha2rads, [('phasediff', 'in_file')]), | ||
(pha2rads, prelude, [('out', 'phase_file')]), | ||
(inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), | ||
]) | ||
|
||
elif phasetype == "phase": | ||
workflow.__desc__ += """\ | ||
The phase difference used for unwarping was calculated using two separate phase measurements | ||
[@pncprocessing]. | ||
""" | ||
# Special case for phase1, phase2 images | ||
meta = pe.MapNode(ReadSidecarJSON(), name='meta', mem_gb=0.01, | ||
run_without_submitting=True, iterfield=['in_file']) | ||
phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') | ||
workflow.connect([ | ||
(meta, phases2fmap, [('out_dict', 'metadatas')]), | ||
(inputnode, phases2fmap, [('phasediff', 'phase_files')]), | ||
(phases2fmap, prelude, [('out_file', 'phase_file')]), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the difference map needs to be phase unwrapped? aren't the phase maps unwrapped individually? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider: p1 = pi - eps p1 - p2 = 2pi - 2eps = -2eps Phase differences should be between -pi and pi, as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That means you are clipping it again, correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh sorry, I think I misunderstood the context here. You're saying that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I understood before that each phase map needs to be unwrapped (which makes sense to me). If they are phase-unwrapped, there's no point on running PRELUDE (Phase Region Expanding Labeller for Unwrapping Discrete Estimates). Also, I am very curious as to how the individual phase maps are unwrapped, if not done with PRELUDE. I know there are better methods out there (e.g., using graph cuts) but they are rarely used in general. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I have confirmed this point: as per the FUGUE guide, each phase map should be unwrapped with PRELUDE separately (step 3) of the guide. Then they can be subtracted as they are not wrapped within [-pi, pi) anymore. After that point, the pipeline should be exactly the same as for the |
||
(phases2fmap, compfmap, [('phasediff_metadata', 'metadata')]), | ||
(phases2fmap, ds_report_fmap_mask, [('out_file', 'source_file')]) | ||
]) | ||
|
||
workflow.connect([ | ||
(inputnode, meta, [('phasediff', 'in_file')]), | ||
(inputnode, magmrg, [('magnitude', 'in_files')]), | ||
(magmrg, n4, [('out_avg', 'input_image')]), | ||
(n4, prelude, [('output_image', 'magnitude_file')]), | ||
(n4, bet, [('output_image', 'in_file')]), | ||
(bet, prelude, [('mask_file', 'mask_file')]), | ||
(inputnode, pha2rads, [('phasediff', 'in_file')]), | ||
(pha2rads, prelude, [('out', 'phase_file')]), | ||
(meta, compfmap, [('out_dict', 'metadata')]), | ||
(prelude, denoise, [('unwrapped_phase_file', 'in_file')]), | ||
(denoise, demean, [('out_file', 'in_file')]), | ||
(demean, cleanup_wf, [('out', 'inputnode.in_file')]), | ||
|
@@ -128,7 +151,6 @@ def _pick1st(inlist): | |
(compfmap, outputnode, [('out_file', 'fmap')]), | ||
(bet, outputnode, [('mask_file', 'fmap_mask'), | ||
('out_file', 'fmap_ref')]), | ||
(inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), | ||
(bet, ds_report_fmap_mask, [('out_report', 'in_file')]), | ||
]) | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.