Skip to content

Commit c7356c3

Browse files
authored
Merge pull request #390 from effigies/test/smoketest-workflows
TEST: Add smoke tests for main anatomical workflows
2 parents 5e707a6 + c5ca00f commit c7356c3

File tree

2 files changed

+224
-4
lines changed

2 files changed

+224
-4
lines changed

smriprep/workflows/anatomical.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ def init_anat_fit_wf(
794794
if skull_strip_mode == "auto":
795795
run_skull_strip = all(_is_skull_stripped(img) for img in t1w)
796796
else:
797-
run_skull_strip = {"force": True, "skip": False}
797+
run_skull_strip = {"force": True, "skip": False}[skull_strip_mode]
798798

799799
# Brain extraction
800800
if run_skull_strip:
@@ -1155,9 +1155,7 @@ def init_anat_fit_wf(
11551155
(surface_recon_wf, coreg_xfms, [('outputnode.fsnative2t1w_xfm', 'in2')]),
11561156
(coreg_xfms, t2wtot1w_xfm, [('out', 'in_xfms')]),
11571157
(t2w_template_wf, t2w_resample, [('outputnode.anat_ref', 'input_image')]),
1158-
(brain_extraction_wf, t2w_resample, [
1159-
(('outputnode.bias_corrected', _pop), 'reference_image'),
1160-
]),
1158+
(t1w_buffer, t2w_resample, [('T1w_preproc', 'reference_image')]),
11611159
(t2wtot1w_xfm, t2w_resample, [('out_xfm', 'transforms')]),
11621160
(inputnode, ds_t2w_preproc, [('t2w', 'source_file')]),
11631161
(t2w_resample, ds_t2w_preproc, [('output_image', 'in_file')]),
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
from pathlib import Path
2+
3+
import nibabel as nb
4+
import numpy as np
5+
import pytest
6+
7+
from niworkflows.utils.spaces import SpatialReferences, Reference
8+
from niworkflows.utils.testing import generate_bids_skeleton
9+
10+
from ..anatomical import init_anat_preproc_wf, init_anat_fit_wf
11+
12+
BASE_LAYOUT = {
13+
"01": {
14+
"anat": [
15+
{"run": 1, "suffix": "T1w"},
16+
{"run": 2, "suffix": "T1w"},
17+
{"suffix": "T2w"},
18+
],
19+
"func": [
20+
{
21+
"task": "rest",
22+
"run": i,
23+
"suffix": "bold",
24+
"metadata": {"PhaseEncodingDirection": "j", "TotalReadoutTime": 0.6},
25+
}
26+
for i in range(1, 3)
27+
],
28+
"fmap": [
29+
{"suffix": "phasediff", "metadata": {"EchoTime1": 0.005, "EchoTime2": 0.007}},
30+
{"suffix": "magnitude1", "metadata": {"EchoTime": 0.005}},
31+
{
32+
"suffix": "epi",
33+
"direction": "PA",
34+
"metadata": {"PhaseEncodingDirection": "j", "TotalReadoutTime": 0.6},
35+
},
36+
{
37+
"suffix": "epi",
38+
"direction": "AP",
39+
"metadata": {"PhaseEncodingDirection": "j-", "TotalReadoutTime": 0.6},
40+
},
41+
],
42+
},
43+
}
44+
45+
46+
@pytest.fixture(scope="module", autouse=True)
47+
def quiet_logger():
48+
import logging
49+
50+
logger = logging.getLogger("nipype.workflow")
51+
old_level = logger.getEffectiveLevel()
52+
logger.setLevel(logging.ERROR)
53+
yield
54+
logger.setLevel(old_level)
55+
56+
57+
@pytest.fixture(scope="module")
58+
def bids_root(tmp_path_factory):
59+
base = tmp_path_factory.mktemp("base")
60+
bids_dir = base / "bids"
61+
generate_bids_skeleton(bids_dir, BASE_LAYOUT)
62+
yield bids_dir
63+
64+
65+
@pytest.mark.parametrize("freesurfer", [True, False])
66+
@pytest.mark.parametrize("cifti_output", [False, "91k"])
67+
def test_init_anat_preproc_wf(
68+
bids_root: Path,
69+
tmp_path: Path,
70+
freesurfer: bool,
71+
cifti_output: bool,
72+
):
73+
output_dir = tmp_path / 'output'
74+
output_dir.mkdir()
75+
76+
init_anat_preproc_wf(
77+
bids_root=str(bids_root),
78+
output_dir=str(output_dir),
79+
freesurfer=freesurfer,
80+
hires=False,
81+
longitudinal=False,
82+
msm_sulc=False,
83+
t1w=[str(bids_root / "sub-01" / "anat" / "sub-01_T1w.nii.gz")],
84+
t2w=[str(bids_root / "sub-01" / "anat" / "sub-01_T2w.nii.gz")],
85+
skull_strip_mode='force',
86+
skull_strip_template=Reference("OASIS30ANTs"),
87+
spaces=SpatialReferences(
88+
spaces=["MNI152NLin2009cAsym", "fsaverage5"],
89+
checkpoint=True,
90+
),
91+
precomputed={},
92+
omp_nthreads=1,
93+
cifti_output=cifti_output,
94+
)
95+
96+
97+
@pytest.mark.parametrize("msm_sulc", [True, False])
98+
@pytest.mark.parametrize("skull_strip_mode", ['skip', 'force'])
99+
def test_anat_fit_wf(
100+
bids_root: Path,
101+
tmp_path: Path,
102+
msm_sulc: bool,
103+
skull_strip_mode: str,
104+
):
105+
output_dir = tmp_path / 'output'
106+
output_dir.mkdir()
107+
108+
init_anat_fit_wf(
109+
bids_root=str(bids_root),
110+
output_dir=str(output_dir),
111+
freesurfer=True,
112+
hires=False,
113+
longitudinal=False,
114+
msm_sulc=msm_sulc,
115+
t1w=[str(bids_root / "sub-01" / "anat" / "sub-01_T1w.nii.gz")],
116+
t2w=[str(bids_root / "sub-01" / "anat" / "sub-01_T2w.nii.gz")],
117+
skull_strip_mode=skull_strip_mode,
118+
skull_strip_template=Reference("OASIS30ANTs"),
119+
spaces=SpatialReferences(
120+
spaces=["MNI152NLin2009cAsym", "fsaverage5"],
121+
checkpoint=True,
122+
),
123+
precomputed={},
124+
omp_nthreads=1,
125+
)
126+
127+
128+
@pytest.mark.parametrize("t1w", [1, 2])
129+
@pytest.mark.parametrize("t2w", [0, 1])
130+
@pytest.mark.parametrize("skull_strip_mode", ['skip', 'force'])
131+
@pytest.mark.parametrize("t1w_preproc", [False, True])
132+
@pytest.mark.parametrize("t2w_preproc", [False, True])
133+
@pytest.mark.parametrize("t1w_mask", [False, True])
134+
@pytest.mark.parametrize("t1w_dseg", [False, True])
135+
@pytest.mark.parametrize("t1w_tpms", [False, True])
136+
@pytest.mark.parametrize("xfms", [False, True])
137+
@pytest.mark.parametrize("sphere_reg_msm", [0, 1, 2])
138+
def test_anat_fit_precomputes(
139+
bids_root: Path,
140+
tmp_path: Path,
141+
t1w: int,
142+
t2w: int,
143+
skull_strip_mode: str,
144+
t1w_preproc: bool,
145+
t2w_preproc: bool,
146+
t1w_mask: bool,
147+
t1w_dseg: bool,
148+
t1w_tpms: bool,
149+
xfms: bool,
150+
sphere_reg_msm: int,
151+
):
152+
"""Test as many combinations of precomputed files and input
153+
configurations as possible."""
154+
output_dir = tmp_path / 'output'
155+
output_dir.mkdir()
156+
157+
# Construct inputs
158+
t1w_list = [
159+
str(bids_root / "sub-01" / "anat" / "sub-01_run-1_T1w.nii.gz"),
160+
str(bids_root / "sub-01" / "anat" / "sub-01_run-2_T1w.nii.gz"),
161+
][:t1w]
162+
t2w_list = [str(bids_root / "sub-01" / "anat" / "sub-01_T2w.nii.gz")][:t2w]
163+
164+
# Construct precomputed files
165+
empty_img = nb.Nifti1Image(np.zeros((1, 1, 1)), np.eye(4))
166+
precomputed = {}
167+
if t1w_preproc:
168+
precomputed["t1w_preproc"] = str(tmp_path / "t1w_preproc.nii.gz")
169+
if t2w_preproc:
170+
precomputed["t2w_preproc"] = str(tmp_path / "t2w_preproc.nii.gz")
171+
if t1w_mask:
172+
precomputed["t1w_mask"] = str(tmp_path / "t1w_mask.nii.gz")
173+
if t1w_dseg:
174+
precomputed["t1w_dseg"] = str(tmp_path / "t1w_dseg.nii.gz")
175+
if t1w_tpms:
176+
precomputed["t1w_tpms"] = str(tmp_path / "t1w_tpms.nii.gz")
177+
178+
for path in precomputed.values():
179+
empty_img.to_filename(path)
180+
181+
precomputed["sphere_reg_msm"] = [
182+
str(tmp_path / f"sub-01_hemi-{hemi}_desc-msm_sphere.surf.gii")
183+
for hemi in ["L", "R"]
184+
][:sphere_reg_msm]
185+
for path in precomputed["sphere_reg_msm"]:
186+
Path(path).touch()
187+
188+
if xfms:
189+
transforms = precomputed["transforms"] = {}
190+
transforms["MNI152NLin2009cAsym"] = {
191+
"forward": str(tmp_path / "MNI152NLin2009cAsym_forward_xfm.txt"),
192+
"reverse": str(tmp_path / "MNI152NLin2009cAsym_reverse_xfm.txt"),
193+
}
194+
transforms["fsnative"] = {
195+
"forward": str(tmp_path / "fsnative_forward_xfm.txt"),
196+
"reverse": str(tmp_path / "fsnative_reverse_xfm.txt"),
197+
}
198+
199+
# Write dummy transforms
200+
for xfm in transforms.values():
201+
for path in xfm.values():
202+
Path(path).touch()
203+
204+
# Create workflow
205+
init_anat_fit_wf(
206+
bids_root=str(bids_root),
207+
output_dir=str(output_dir),
208+
freesurfer=True,
209+
hires=False,
210+
longitudinal=False,
211+
msm_sulc=True,
212+
t1w=t1w_list,
213+
t2w=t2w_list,
214+
skull_strip_mode=skull_strip_mode,
215+
skull_strip_template=Reference("OASIS30ANTs"),
216+
spaces=SpatialReferences(
217+
spaces=["MNI152NLin2009cAsym", "fsaverage5"],
218+
checkpoint=True,
219+
),
220+
precomputed=precomputed,
221+
omp_nthreads=1,
222+
)

0 commit comments

Comments
 (0)