Skip to content

Commit 050a083

Browse files
authored
Merge pull request #399 from mgxd/fix/mcribs-no-segmentation
FIX: MCRIBS auto surface reconstruction logic
2 parents 5890267 + ae0b008 commit 050a083

File tree

3 files changed

+64
-20
lines changed

3 files changed

+64
-20
lines changed

nibabies/cli/parser.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ def _build_parser():
2828
# parser attribute name: (replacement flag, version slated to be removed in)
2929
'bold2t1w_init': ('--bold2anat-init', '24.2.0'),
3030
'bold2t1w_dof': ('--bold2anat-dof', '24.2.0'),
31+
'force_reconall': ('--surface-recon-method freesurfer', '24.2.0'),
32+
'fs_no_reconall': ('--surface-recon-method none', '24.2.0'),
3133
}
3234

3335
class DeprecatedAction(Action):
@@ -148,6 +150,11 @@ def _slice_time_ref(value, parser):
148150
raise parser.error(f'Slice time reference must be in range 0-1. Received {value}.')
149151
return value
150152

153+
def _str_none(val):
154+
if not isinstance(val, str):
155+
return val
156+
return None if val.lower() == 'none' else val
157+
151158
verstr = f'NiBabies v{config.environment.version}'
152159
currentv = Version(config.environment.version)
153160

@@ -619,9 +626,9 @@ def _slice_time_ref(value, parser):
619626
)
620627
g_surfs_xor.add_argument(
621628
'--fs-no-reconall',
622-
action='store_false',
629+
action=DeprecatedAction,
623630
dest='run_reconall',
624-
help='disable FreeSurfer surface preprocessing.',
631+
help='Deprecated - use `--surface-recon-method none` instead.',
625632
)
626633

627634
g_other = parser.add_argument_group('Other options')
@@ -750,14 +757,15 @@ def _slice_time_ref(value, parser):
750757
g_baby.add_argument(
751758
'--force-reconall',
752759
default=False,
753-
action='store_true',
754-
help='Force traditional FreeSurfer surface reconstruction.',
760+
action=DeprecatedAction,
761+
help='Deprecated - use `--surface-recon-method freesurfer` instead.',
755762
)
756763
g_baby.add_argument(
757764
'--surface-recon-method',
758-
choices=('infantfs', 'freesurfer', 'mcribs', 'auto'),
765+
choices=('auto', 'infantfs', 'freesurfer', 'mcribs', None),
766+
type=_str_none,
759767
default='auto',
760-
help='Method to use for surface reconstruction',
768+
help='Method to use for surface reconstruction.',
761769
)
762770
g_baby.add_argument(
763771
'--reference-anat',

nibabies/cli/tests/test_parser.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,34 @@ def test_derivatives(tmp_path):
270270
parser.parse_args(temp_args)
271271

272272
_reset_config()
273+
274+
275+
@pytest.mark.parametrize(
276+
('args', 'expectation'),
277+
[
278+
([], 'auto'),
279+
(['--surface-recon-method', 'auto'], 'auto'),
280+
(['--surface-recon-method', 'mcribs'], 'mcribs'),
281+
(['--surface-recon-method', 'infantfs'], 'infantfs'),
282+
(['--surface-recon-method', 'freesurfer'], 'freesurfer'),
283+
(['--surface-recon-method', 'none'], None),
284+
(['--surface-recon-method', 'None'], None),
285+
(['--surface-recon-method', 123], (TypeError,)),
286+
],
287+
)
288+
def test_surface_recon_method(tmp_path, args, expectation):
289+
"""Check the correct parsing of the memory argument."""
290+
datapath = tmp_path / 'data'
291+
datapath.mkdir(exist_ok=True)
292+
_fs_file = tmp_path / 'license.txt'
293+
_fs_file.write_text('')
294+
295+
args = [str(datapath)] + MIN_ARGS[1:] + ['--fs-license-file', str(_fs_file)] + args
296+
297+
cm = nullcontext()
298+
if isinstance(expectation, tuple):
299+
cm = pytest.raises(expectation)
300+
301+
with cm:
302+
opts = _build_parser().parse_args(args)
303+
assert opts.surface_recon_method == expectation

nibabies/workflows/base.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@
7171
from niworkflows.utils.spaces import SpatialReferences
7272

7373

74+
AUTO_T2W_MAX_AGE = 8
75+
76+
7477
def init_nibabies_wf(subworkflows_list):
7578
"""
7679
Build *NiBabies*'s pipeline.
@@ -105,8 +108,8 @@ def init_nibabies_wf(subworkflows_list):
105108
nibabies_wf.base_dir = config.execution.work_dir
106109

107110
execution_spaces = init_execution_spaces()
108-
freesurfer = config.workflow.surface_recon_method is not None
109-
if freesurfer:
111+
surface_recon = config.workflow.surface_recon_method is not None
112+
if surface_recon:
110113
fsdir = pe.Node(
111114
BIDSFreeSurferDir(
112115
derivatives=config.execution.output_dir,
@@ -154,7 +157,7 @@ def init_nibabies_wf(subworkflows_list):
154157
single_subject_wf.config['execution']['crashdump_dir'] = str(log_dir)
155158
for node in single_subject_wf._get_all_nodes():
156159
node.config = deepcopy(single_subject_wf.config)
157-
if freesurfer:
160+
if surface_recon:
158161
nibabies_wf.connect(fsdir, 'subjects_dir', single_subject_wf, 'inputnode.subjects_dir')
159162
else:
160163
nibabies_wf.add_nodes([single_subject_wf])
@@ -327,30 +330,32 @@ def init_single_subject_wf(
327330
# Determine some session level options here, as we should have
328331
# all the required information
329332
if recon_method == 'auto':
330-
if age <= 8:
333+
if age <= AUTO_T2W_MAX_AGE and anatomical_cache.get('t2w_aseg'):
334+
# do not force mcribs without a vetted segmentation
331335
recon_method = 'mcribs'
332336
elif age <= 24:
333337
recon_method = 'infantfs'
334338
else:
335339
recon_method = 'freesurfer'
336340

337-
preferred_anat = config.execution.reference_anat
341+
requested_anat = config.execution.reference_anat
338342
t1w = subject_data['t1w']
339343
t2w = subject_data['t2w']
340344
single_anat = False
341-
if not t1w and t2w:
345+
346+
if not (t1w and t2w):
342347
single_anat = True
343348
reference_anat = 'T1w' if t1w else 'T2w'
344-
if preferred_anat and reference_anat != preferred_anat:
349+
if requested_anat and reference_anat != requested_anat:
345350
raise AttributeError(
346-
f'Requested to use {preferred_anat} as anatomical reference but none available'
351+
f'Requested to use {requested_anat} as anatomical reference but none available'
347352
)
348-
else:
349-
if not (reference_anat := preferred_anat):
350-
if recon_method is None:
351-
reference_anat = 'T2w' if age <= 8 else 'T1w'
352-
else:
353-
reference_anat = 'T2w' if recon_method == 'mcribs' else 'T1w'
353+
elif (reference_anat := requested_anat) is None: # Both available with no preference
354+
reference_anat = (
355+
'T2w'
356+
if any((recon_method == 'none' and age <= AUTO_T2W_MAX_AGE, recon_method == 'mcribs'))
357+
else 'T1w'
358+
)
354359

355360
anat = reference_anat.lower() # To be used for workflow connections
356361

0 commit comments

Comments
 (0)