Skip to content

Commit a8fb727

Browse files
authored
Merge pull request #488 from nipreps/fix/correctly-handle-priors
FIX: Re-enable priors respecting ``sd_priors`` argument
2 parents 986d7fd + 290d240 commit a8fb727

File tree

2 files changed

+39
-32
lines changed

2 files changed

+39
-32
lines changed

sdcflows/workflows/fit/syn.py

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
from nipype.pipeline import engine as pe
2828
from nipype.interfaces import utility as niu
29-
from nipype.interfaces.base import Undefined
3029
from niworkflows.engine.workflows import LiterateWorkflow as Workflow
3130

3231
from ... import data
@@ -44,13 +43,11 @@
4443

4544
def init_syn_sdc_wf(
4645
*,
47-
atlas_threshold=3,
4846
sloppy=False,
4947
debug=False,
5048
name="syn_sdc_wf",
5149
omp_nthreads=1,
5250
laplacian_weight=None,
53-
sd_prior=True,
5451
**kwargs,
5552
):
5653
"""
@@ -59,10 +56,6 @@ def init_syn_sdc_wf(
5956
SyN deformation is restricted to the phase-encoding (PE) direction.
6057
If no PE direction is specified, anterior-posterior PE is assumed.
6158
62-
SyN deformation is also restricted to regions that are expected to have a
63-
>3mm (approximately 1 voxel) warp, based on the fieldmap atlas.
64-
65-
6659
Workflow Graph
6760
.. workflow ::
6861
:graph2use: orig
@@ -73,9 +66,6 @@ def init_syn_sdc_wf(
7366
7467
Parameters
7568
----------
76-
atlas_threshold : :obj:`float`
77-
Exclude from the registration metric computation areas with average distortions
78-
below this threshold (in mm).
7969
sloppy : :obj:`bool`
8070
Whether a fast (less accurate) configuration of the workflow should be applied.
8171
debug : :obj:`bool`
@@ -102,9 +92,6 @@ def init_syn_sdc_wf(
10292
A preprocessed, skull-stripped anatomical (T1w or T2w) image resampled in EPI space.
10393
anat_mask : :obj:`str`
10494
Path to the brain mask corresponding to ``anat_ref`` in EPI space.
105-
sd_prior : :obj:`str`
106-
A template map of areas with strong susceptibility distortions (SD) to regularize
107-
the cost function of SyN
10895
10996
Outputs
11097
-------
@@ -150,16 +137,15 @@ def init_syn_sdc_wf(
150137
workflow = Workflow(name=name)
151138
workflow.__desc__ = f"""\
152139
A deformation field to correct for susceptibility distortions was estimated
153-
based on *fMRIPrep*'s *fieldmap-less* approach.
140+
based on *SDCFlows*' *fieldmap-less* approach.
154141
The deformation field is that resulting from co-registering the EPI reference
155-
to the same-subject T1w-reference with its intensity inverted [@fieldmapless1;
156-
@fieldmapless2].
142+
to the same-subject's T1w-reference [@fieldmapless1; @fieldmapless2].
157143
Registration is performed with `antsRegistration`
158144
(ANTs {ants_version or "-- version unknown"}), and
159145
the process regularized by constraining deformation to be nonzero only
160-
along the phase-encoding direction, and modulated with an average fieldmap
161-
template [@fieldmapless3].
146+
along the phase-encoding direction.
162147
"""
148+
163149
inputnode = pe.Node(niu.IdentityInterface(INPUT_FIELDS), name="inputnode")
164150
outputnode = pe.Node(
165151
niu.IdentityInterface(
@@ -211,10 +197,11 @@ def init_syn_sdc_wf(
211197

212198
epi_umask = pe.Node(Union(), name="epi_umask")
213199
moving_masks = pe.Node(
214-
niu.Merge(3),
200+
niu.Merge(2),
215201
name="moving_masks",
216202
run_without_submitting=True,
217203
)
204+
moving_masks.inputs.in1 = "NULL"
218205

219206
fixed_masks = pe.Node(
220207
niu.Merge(2),
@@ -287,7 +274,7 @@ def init_syn_sdc_wf(
287274
(inputnode, amask2epi, [("epi_mask", "reference_image")]),
288275
(inputnode, zooms_bmask, [("anat_mask", "input_image")]),
289276
(inputnode, fixed_masks, [("anat_mask", "in1"),
290-
("anat_mask", "in2")]),
277+
("sd_prior", "in2")]),
291278
(inputnode, anat_dilmsk, [("anat_mask", "in_file")]),
292279
(inputnode, warp_dir, [("anat_ref", "fixed_image")]),
293280
(inputnode, anat_merge, [("anat_ref", "in1")]),
@@ -298,9 +285,7 @@ def init_syn_sdc_wf(
298285
(inputnode, epi_umask, [("epi_mask", "in1")]),
299286
(lap_anat, lap_anat_norm, [("output_image", "in_file")]),
300287
(lap_anat_norm, anat_merge, [("out", "in2")]),
301-
(epi_umask, moving_masks, [("out_file", "in1"),
302-
("out_file", "in2"),
303-
("out_file", "in3")]),
288+
(epi_umask, moving_masks, [("out_file", "in2")]),
304289
(clip_epi, epi_merge, [("out_file", "in1")]),
305290
(clip_epi, lap_epi, [("out_file", "op1")]),
306291
(clip_epi, zooms_epi, [("out_file", "in_file")]),
@@ -339,6 +324,7 @@ def init_syn_sdc_wf(
339324

340325
def init_syn_preprocessing_wf(
341326
*,
327+
atlas_threshold=3,
342328
debug=False,
343329
name="syn_preprocessing_wf",
344330
omp_nthreads=1,
@@ -359,6 +345,9 @@ def init_syn_preprocessing_wf(
359345
360346
Parameters
361347
----------
348+
atlas_threshold : :obj:`float`
349+
Mask excluding areas with average distortions below this threshold (in mm)
350+
on the prior.
362351
debug : :obj:`bool`
363352
Whether a fast (less accurate) configuration of the workflow should be applied.
364353
name : :obj:`str`
@@ -499,6 +488,8 @@ def _remove_first_mask(in_file):
499488
sampling_ref = pe.Node(GenerateSamplingReference(), name="sampling_ref")
500489

501490
if sd_prior:
491+
from niworkflows.interfaces.nibabel import Binarize
492+
502493
# Mapping & preparing prior knowledge
503494
# Concatenate transform files:
504495
# 1) MNI -> anat; 2) ATLAS -> MNI
@@ -521,19 +512,20 @@ def _remove_first_mask(in_file):
521512
mem_gb=0.3,
522513
)
523514

515+
prior_msk = pe.Node(Binarize(thresh_low=atlas_threshold), name="prior_msk")
516+
524517
workflow.connect([
525518
(inputnode, transform_list, [("std2anat_xfm", "in2")]),
526519
(epi2anat, transform_list, [("forward_transforms", "in1")]),
527520
(transform_list, prior2epi, [("out", "transforms")]),
528521
(sampling_ref, prior2epi, [("out_file", "reference_image")]),
529-
(prior2epi, outputnode, [("output_image", "sd_prior")]),
522+
(prior2epi, prior_msk, [("output_image", "in_file")]),
523+
(prior_msk, outputnode, [("out_mask", "sd_prior")]),
530524
]) # fmt:skip
531525

532526
else:
533-
# no prior to be used
534-
# MG: Future goal is to allow using alternative mappings
535-
# i.e. in the case of infants, where priors change depending on development
536-
outputnode.inputs.sd_prior = Undefined
527+
# no prior to be used -> set anatomical mask as prior
528+
workflow.connect(mask_dtype, "out", outputnode, "sd_prior")
537529

538530
workflow.connect([
539531
(inputnode, epi_reference_wf, [("in_epis", "inputnode.in_files")]),

sdcflows/workflows/fit/tests/test_syn.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131

3232
@pytest.mark.veryslow
3333
@pytest.mark.slow
34-
@pytest.mark.parametrize("sd_prior", [True, False])
35-
def test_syn_wf(tmpdir, datadir, workdir, outdir, sloppy_mode, sd_prior):
34+
def test_syn_wf(tmpdir, datadir, workdir, outdir, sloppy_mode):
3635
"""Build and run an SDC-SyN workflow."""
3736
derivs_path = datadir / "ds000054" / "derivatives"
3837
smriprep = derivs_path / "smriprep-0.6" / "sub-100185" / "anat"
@@ -44,7 +43,6 @@ def test_syn_wf(tmpdir, datadir, workdir, outdir, sloppy_mode, sd_prior):
4443
debug=sloppy_mode,
4544
auto_bold_nss=True,
4645
t1w_inversion=True,
47-
sd_prior=sd_prior,
4846
)
4947
prep_wf.inputs.inputnode.in_epis = [
5048
str(
@@ -79,7 +77,6 @@ def test_syn_wf(tmpdir, datadir, workdir, outdir, sloppy_mode, sd_prior):
7977
debug=sloppy_mode,
8078
sloppy=sloppy_mode,
8179
omp_nthreads=4,
82-
sd_prior=sd_prior,
8380
)
8481

8582
# fmt: off
@@ -170,6 +167,24 @@ def test_syn_wf_inputs(sloppy, laplacian_weight):
170167
assert wf.inputs.syn.metric_weight == metric_weight
171168

172169

170+
@pytest.mark.parametrize("sd_prior", [True, False])
171+
def test_syn_preprocessing_wf_inputs(sd_prior):
172+
"""Test appropriate instantiation of the SDC-SyN preprocessing workflow."""
173+
174+
prep_wf = init_syn_preprocessing_wf(
175+
omp_nthreads=4,
176+
sd_prior=sd_prior,
177+
auto_bold_nss=True,
178+
t1w_inversion=True,
179+
)
180+
181+
if not sd_prior:
182+
with pytest.raises(AttributeError):
183+
prep_wf.inputs.prior_msk.in_file
184+
else:
185+
assert prep_wf.inputs.prior_msk.thresh_low
186+
187+
173188
@pytest.mark.parametrize("ants_version", ["2.2.0", "2.1.0", None])
174189
def test_syn_wf_version(monkeypatch, ants_version):
175190
"""Ensure errors are triggered with ANTs < 2.2."""

0 commit comments

Comments
 (0)