Skip to content

Commit 2bb12f4

Browse files
authored
Merge pull request #43 from mgxd/enh/sdc
ENH: Add susceptibly distortion correction
2 parents 3911935 + 2ee5d46 commit 2bb12f4

File tree

3 files changed

+349
-168
lines changed

3 files changed

+349
-168
lines changed

nibabies/config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@
191191

192192
# Debug modes are names that influence the exposure of internal details to
193193
# the user, either through additional derivatives or increased verbosity
194-
DEBUG_MODES = ("compcor", "registration")
194+
DEBUG_MODES = ("compcor", "registration", "fieldmaps")
195195

196196

197197
class _Config:

nibabies/workflows/base.py

+93-2
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,21 @@ def init_single_subject_wf(subject_id):
378378
if anat_only:
379379
return workflow
380380

381+
# Susceptibility distortion correction
382+
fmap_estimators = None
383+
if "fieldmap" not in config.workflow.ignore:
384+
from sdcflows.utils.wrangler import find_estimators
385+
from sdcflows.workflows.base import init_fmap_preproc_wf
386+
387+
# SDC Step 1: Run basic heuristics to identify available data for fieldmap estimation
388+
# For now, no fmapless
389+
fmap_estimators = find_estimators(
390+
layout=config.execution.layout,
391+
subject=subject_id,
392+
fmapless=False, # config.workflow.use_syn,
393+
force_fmapless=False, # config.workflow.force_syn,
394+
)
395+
381396
# Append the functional section to the existing anatomical exerpt
382397
# That way we do not need to stream down the number of bold datasets
383398
anat_preproc_wf.__postdesc__ = (
@@ -406,16 +421,19 @@ def init_single_subject_wf(subject_id):
406421
print("No BOLD files found for one or more reference groupings")
407422
return workflow
408423

424+
func_preproc_wfs = []
409425
for idx, bold_files in enumerate(bold_groupings):
410426
bold_ref_wf = init_epi_reference_wf(
411427
auto_bold_nss=True,
412428
name=f'bold_reference_wf{idx}',
413429
omp_nthreads=config.nipype.omp_nthreads
414430
)
415431
bold_ref_wf.inputs.inputnode.in_files = bold_files
416-
417432
for idx, bold_file in enumerate(bold_files):
418-
func_preproc_wf = init_func_preproc_wf(bold_file)
433+
func_preproc_wf = init_func_preproc_wf(
434+
bold_file,
435+
has_fieldmap=bool(fmap_estimators)
436+
)
419437
# fmt: off
420438
workflow.connect([
421439
(bold_ref_wf, func_preproc_wf, [
@@ -446,6 +464,79 @@ def init_single_subject_wf(subject_id):
446464
]),
447465
])
448466
# fmt: on
467+
func_preproc_wfs.append(func_preproc_wf)
468+
469+
if not fmap_estimators:
470+
config.loggers.workflow.warning(
471+
"Data for fieldmap estimation not present. Please note that these data "
472+
"will not be corrected for susceptibility distortions."
473+
)
474+
return workflow
475+
476+
config.loggers.workflow.info(
477+
f"Fieldmap estimators found: {[e.method for e in fmap_estimators]}"
478+
)
479+
480+
from sdcflows.workflows.base import init_fmap_preproc_wf
481+
from sdcflows import fieldmaps as fm
482+
483+
fmap_wf = init_fmap_preproc_wf(
484+
debug=bool(config.execution.debug), # TODO: Add debug option for fieldmaps
485+
estimators=fmap_estimators,
486+
omp_nthreads=config.nipype.omp_nthreads,
487+
output_dir=nibabies_dir,
488+
subject=subject_id,
489+
)
490+
fmap_wf.__desc__ = f"""
491+
492+
Fieldmap data preprocessing
493+
494+
: A total of {len(fmap_estimators)} fieldmaps were found available within the input
495+
BIDS structure for this particular subject.
496+
"""
497+
498+
for func_preproc_wf in func_preproc_wfs:
499+
# fmt: off
500+
workflow.connect([
501+
(fmap_wf, func_preproc_wf, [
502+
("outputnode.fmap", "inputnode.fmap"),
503+
("outputnode.fmap_ref", "inputnode.fmap_ref"),
504+
("outputnode.fmap_coeff", "inputnode.fmap_coeff"),
505+
("outputnode.fmap_mask", "inputnode.fmap_mask"),
506+
("outputnode.fmap_id", "inputnode.fmap_id"),
507+
]),
508+
])
509+
# fmt: on
510+
511+
# Overwrite ``out_path_base`` of sdcflows's DataSinks
512+
for node in fmap_wf.list_node_names():
513+
if node.split(".")[-1].startswith("ds_"):
514+
fmap_wf.get_node(node).interface.out_path_base = ""
515+
516+
# Step 3: Manually connect PEPOLAR
517+
for estimator in fmap_estimators:
518+
config.loggers.workflow.info(f"""\
519+
Setting-up fieldmap "{estimator.bids_id}" ({estimator.method}) with \
520+
<{', '.join(s.path.name for s in estimator.sources)}>""")
521+
if estimator.method in (fm.EstimatorType.MAPPED, fm.EstimatorType.PHASEDIFF):
522+
continue
523+
524+
suffices = set(s.suffix for s in estimator.sources)
525+
526+
if estimator.method == fm.EstimatorType.PEPOLAR and sorted(suffices) == ["epi"]:
527+
getattr(fmap_wf.inputs, f"in_{estimator.bids_id}").in_data = [
528+
str(s.path) for s in estimator.sources
529+
]
530+
getattr(fmap_wf.inputs, f"in_{estimator.bids_id}").metadata = [
531+
s.metadata for s in estimator.sources
532+
]
533+
continue
534+
535+
if estimator.method == fm.EstimatorType.PEPOLAR:
536+
raise NotImplementedError(
537+
"Sophisticated PEPOLAR schemes (e.g., using DWI+EPI) are unsupported."
538+
)
539+
449540
return workflow
450541

451542

0 commit comments

Comments
 (0)