Skip to content

Commit 35abaae

Browse files
committed
fix(docs,workflow): include @josephmje's suggestions
- [x] Revise the downstreaming of the new objects in niworkflows to handle spaces. - [x] Add ``--fs-subjects-dir`` flag (see nipreps/fmriprep#1901)
1 parent 03b0302 commit 35abaae

File tree

2 files changed

+68
-57
lines changed

2 files changed

+68
-57
lines changed

dmriprep/cli/run.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ def get_parser():
111111
colon-separated parameters. \
112112
Non-standard spaces imply specific orientations and sampling grids. \
113113
The default value of this flag (meaning, if the argument is not include in the command line) \
114-
is ``--output-spaces orig`` - the original space and sampling grid of the original DWI data. \
114+
is ``--output-spaces run`` - the original space and sampling grid of the original DWI run. \
115115
Important to note, the ``res-*`` modifier does not define the resolution used for \
116116
the spatial normalization. To generate no DWI outputs (if that is intended for some reason), \
117117
use this option without specifying any spatial references. For further details, please check out \
118-
https://fmriprep.readthedocs.io/en/%s/spaces.html""" % (currentv.base_version
119-
if is_release else 'latest'))
118+
https://www.nipreps.org/dmriprep/en/%s/spaces.html""" % (currentv.base_version
119+
if is_release else 'latest'))
120120

121121
# ANTs options
122122
g_ants = parser.add_argument_group('Specific options for ANTs registrations')
@@ -148,6 +148,10 @@ def get_parser():
148148
'--fs-license-file', metavar='PATH', type=Path,
149149
help='Path to FreeSurfer license key file. Get it (for free) by registering'
150150
' at https://surfer.nmr.mgh.harvard.edu/registration.html')
151+
g_fs.add_argument(
152+
'--fs-subjects-dir', metavar='PATH', type=Path,
153+
help='Path to existing FreeSurfer subjects directory to reuse. '
154+
'(default: OUTPUT_DIR/freesurfer)')
151155

152156
# Surface generation xor
153157
g_surfs = parser.add_argument_group('Surface preprocessing options')
@@ -422,7 +426,7 @@ def build_workflow(opts, retval):
422426
output_spaces = opts.output_spaces
423427
if opts.output_spaces is None:
424428
from niworkflows.utils.spaces import Reference, SpatialReferences
425-
output_spaces = SpatialReferences([Reference("orig", {})])
429+
output_spaces = SpatialReferences([Reference("run", {})])
426430

427431
# Set up some instrumental utilities
428432
run_uuid = '%s_%s' % (strftime('%Y%m%d-%H%M%S'), uuid.uuid4())
@@ -529,17 +533,18 @@ def build_workflow(opts, retval):
529533
debug=opts.debug,
530534
force_syn=opts.force_syn,
531535
freesurfer=opts.run_reconall,
536+
fs_subjects_dir=opts.fs_subjects_dir,
532537
hires=opts.hires,
533538
ignore=opts.ignore,
534539
layout=layout,
535540
longitudinal=opts.longitudinal,
536541
low_mem=opts.low_mem,
537542
omp_nthreads=omp_nthreads,
538543
output_dir=str(output_dir),
539-
output_spaces=output_spaces,
540544
run_uuid=run_uuid,
541545
skull_strip_fixed_seed=opts.skull_strip_fixed_seed,
542-
skull_strip_template=opts.skull_strip_template,
546+
skull_strip_template=opts.skull_strip_template[0],
547+
spaces=output_spaces,
543548
subject_list=subject_list,
544549
use_syn=opts.use_syn_sdc,
545550
work_dir=str(work_dir),

dmriprep/workflows/base.py

Lines changed: 57 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import sys
33
import os
44
from packaging.version import Version
5-
from collections import OrderedDict
65
from copy import deepcopy
76

87
from nipype import __version__ as nipype_ver
@@ -28,17 +27,18 @@ def init_dmriprep_wf(
2827
debug,
2928
force_syn,
3029
freesurfer,
30+
fs_subjects_dir,
3131
hires,
3232
ignore,
3333
layout,
3434
longitudinal,
3535
low_mem,
3636
omp_nthreads,
3737
output_dir,
38-
output_spaces,
3938
run_uuid,
4039
skull_strip_fixed_seed,
4140
skull_strip_template,
41+
spaces,
4242
subject_list,
4343
use_syn,
4444
work_dir,
@@ -59,25 +59,30 @@ def init_dmriprep_wf(
5959
from collections import namedtuple, OrderedDict
6060
BIDSLayout = namedtuple('BIDSLayout', ['root'])
6161
from dmriprep.workflows.base import init_dmriprep_wf
62+
from niworkflows.utils.spaces import Reference, SpatialReferences
6263
os.environ['FREESURFER_HOME'] = os.getcwd()
6364
wf = init_dmriprep_wf(
6465
anat_only=False,
6566
debug=False,
6667
force_syn=True,
6768
freesurfer=True,
69+
fs_subjects_dir=None,
6870
hires=True,
6971
ignore=[],
7072
layout=BIDSLayout('.'),
7173
longitudinal=False,
7274
low_mem=False,
7375
omp_nthreads=1,
7476
output_dir='.',
75-
output_spaces=OrderedDict([
76-
('MNI152Lin', {}), ('fsaverage', {'density': '10k'}),
77-
('T1w', {}), ('fsnative', {})]),
7877
run_uuid='X',
7978
skull_strip_fixed_seed=False,
80-
skull_strip_template=('OASIS30ANTs', {}),
79+
skull_strip_template=Reference('OASIS30ANTs'),
80+
spaces=SpatialReferences(
81+
spaces=['MNI152Lin',
82+
('fsaverage', {'density': '10k'}),
83+
'T1w',
84+
'fsnative'],
85+
checkpoint=True),
8186
subject_list=['dmripreptest'],
8287
use_syn=True,
8388
work_dir='.',
@@ -93,6 +98,9 @@ def init_dmriprep_wf(
9398
**Temporary**: Always run SyN-based SDC
9499
freesurfer : bool
95100
Enable FreeSurfer surface reconstruction (may increase runtime)
101+
fs_subjects_dir : :obj:`~pathlib.Path` or :obj:`str`
102+
Designates a custom FreeSurfer subjects directory, other than
103+
``<derivatives>/freesurfer/``.
96104
hires : bool
97105
Enable sub-millimeter preprocessing in FreeSurfer
98106
ignore : list
@@ -108,22 +116,22 @@ def init_dmriprep_wf(
108116
Maximum number of threads an individual process may use
109117
output_dir : str
110118
Directory in which to save derivatives
111-
output_spaces : OrderedDict
112-
Ordered dictionary where keys are TemplateFlow ID strings (e.g., ``MNI152Lin``,
113-
``MNI152NLin6Asym``, ``MNI152NLin2009cAsym``, or ``fsLR``) strings designating
114-
nonstandard references (e.g., ``T1w`` or ``anat``, ``sbref``, ``run``, etc.),
115-
or paths pointing to custom templates organized in a TemplateFlow-like structure.
116-
Values of the dictionary aggregate modifiers (e.g., the value for the key ``MNI152Lin``
117-
could be ``{'resolution': 2}`` if one wants the resampling to be done on the 2mm
118-
resolution version of the selected template).
119119
run_uuid : str
120120
Unique identifier for execution instance
121-
skull_strip_template : tuple
122-
Name of target template for brain extraction with ANTs' ``antsBrainExtraction``,
123-
and corresponding dictionary of output-space modifiers.
121+
skull_strip_template : :py:class:`~niworkflows.utils.space.Reference`
122+
Target template for brain extraction with ANTs' ``antsBrainExtraction``.
124123
skull_strip_fixed_seed : bool
125124
Do not use a random seed for skull-stripping - will ensure
126125
run-to-run replicability when used with --omp-nthreads 1
126+
spaces : :py:class:`~niworkflows.utils.spaces.SpatialReferences`
127+
A container for storing, organizing, and parsing spatial normalizations. Composed of
128+
:py:class:`~niworkflows.utils.spaces.Reference` objects representing spatial references.
129+
Each ``Reference`` contains a space, which is a string of either TemplateFlow template IDs
130+
(e.g., ``MNI152Lin``, ``MNI152NLin6Asym``, ``MNIPediatricAsym``), nonstandard references
131+
(e.g., ``T1w`` or ``anat``, ``sbref``, ``run``, etc.), or a custom template located in
132+
the TemplateFlow root directory. Each ``Reference`` may also contain a spec, which is a
133+
dictionary with template specifications (e.g., a specification of ``{'resolution': 2}``
134+
would lead to resampling on a 2mm resolution of the space).
127135
subject_list : list
128136
List of subject labels
129137
use_syn : bool
@@ -141,9 +149,10 @@ def init_dmriprep_wf(
141149
BIDSFreeSurferDir(
142150
derivatives=output_dir,
143151
freesurfer_home=os.getenv('FREESURFER_HOME'),
144-
spaces=[s for s in output_spaces.keys() if s.startswith('fsaverage')] + [
145-
'fsnative'] * ('fsnative' in output_spaces)),
152+
spaces=spaces.get_fs_spaces()),
146153
name='fsdir_run_' + run_uuid.replace('-', '_'), run_without_submitting=True)
154+
if fs_subjects_dir is not None:
155+
fsdir.inputs.subjects_dir = str(fs_subjects_dir.absolute())
147156

148157
reportlets_dir = os.path.join(work_dir, 'reportlets')
149158
for subject_id in subject_list:
@@ -160,10 +169,10 @@ def init_dmriprep_wf(
160169
name="single_subject_" + subject_id + "_wf",
161170
omp_nthreads=omp_nthreads,
162171
output_dir=output_dir,
163-
output_spaces=output_spaces,
164172
reportlets_dir=reportlets_dir,
165173
skull_strip_fixed_seed=skull_strip_fixed_seed,
166174
skull_strip_template=skull_strip_template,
175+
spaces=spaces,
167176
subject_id=subject_id,
168177
use_syn=use_syn,
169178
)
@@ -195,10 +204,10 @@ def init_single_subject_wf(
195204
name,
196205
omp_nthreads,
197206
output_dir,
198-
output_spaces,
199207
reportlets_dir,
200208
skull_strip_fixed_seed,
201209
skull_strip_template,
210+
spaces,
202211
subject_id,
203212
use_syn,
204213
):
@@ -220,8 +229,9 @@ def init_single_subject_wf(
220229
:graph2use: orig
221230
:simple_form: yes
222231
232+
from collections import namedtuple
233+
from niworkflows.utils.spaces import Reference, SpatialReferences
223234
from dmriprep.workflows.base import init_single_subject_wf
224-
from collections import namedtuple, OrderedDict
225235
BIDSLayout = namedtuple('BIDSLayout', ['root'])
226236
wf = init_single_subject_wf(
227237
anat_only=False,
@@ -236,12 +246,15 @@ def init_single_subject_wf(
236246
name='single_subject_wf',
237247
omp_nthreads=1,
238248
output_dir='.',
239-
output_spaces=OrderedDict([
240-
('MNI152Lin', {}), ('fsaverage', {'density': '10k'}),
241-
('T1w', {}), ('fsnative', {})]),
242249
reportlets_dir='.',
243250
skull_strip_fixed_seed=False,
244-
skull_strip_template=('OASIS30ANTs', {}),
251+
skull_strip_template=Reference('OASIS30ANTs'),
252+
spaces=SpatialReferences(
253+
spaces=['MNI152Lin',
254+
('fsaverage', {'density': '10k'}),
255+
'T1w',
256+
'fsnative'],
257+
checkpoint=True),
245258
subject_id='test',
246259
use_syn=True,
247260
)
@@ -273,22 +286,22 @@ def init_single_subject_wf(
273286
Maximum number of threads an individual process may use
274287
output_dir : str
275288
Directory in which to save derivatives
276-
output_spaces : OrderedDict
277-
Ordered dictionary where keys are TemplateFlow ID strings (e.g., ``MNI152Lin``,
278-
``MNI152NLin6Asym``, ``MNI152NLin2009cAsym``, or ``fsLR``) strings designating
279-
nonstandard references (e.g., ``T1w`` or ``anat``, ``sbref``, ``run``, etc.),
280-
or paths pointing to custom templates organized in a TemplateFlow-like structure.
281-
Values of the dictionary aggregate modifiers (e.g., the value for the key ``MNI152Lin``
282-
could be ``{'resolution': 2}`` if one wants the resampling to be done on the 2mm
283-
resolution version of the selected template).
284289
reportlets_dir : str
285290
Directory in which to save reportlets
286291
skull_strip_fixed_seed : bool
287292
Do not use a random seed for skull-stripping - will ensure
288293
run-to-run replicability when used with --omp-nthreads 1
289-
skull_strip_template : tuple
290-
Name of target template for brain extraction with ANTs' ``antsBrainExtraction``,
291-
and corresponding dictionary of output-space modifiers.
294+
skull_strip_template : :py:class:`~niworkflows.utils.space.Reference`
295+
Target template for brain extraction with ANTs' ``antsBrainExtraction``.
296+
spaces : :py:class:`~niworkflows.utils.spaces.SpatialReferences`
297+
A container for storing, organizing, and parsing spatial normalizations. Composed of
298+
:py:class:`~niworkflows.utils.spaces.Reference` objects representing spatial references.
299+
Each ``Reference`` contains a space, which is a string of either TemplateFlow template IDs
300+
(e.g., ``MNI152Lin``, ``MNI152NLin6Asym``, ``MNIPediatricAsym``), nonstandard references
301+
(e.g., ``T1w`` or ``anat``, ``sbref``, ``run``, etc.), or a custom template located in
302+
the TemplateFlow root directory. Each ``Reference`` may also contain a spec, which is a
303+
dictionary with template specifications (e.g., a specification of ``{'resolution': 2}``
304+
would lead to resampling on a 2mm resolution of the space).
292305
subject_id : str
293306
List of subject labels
294307
use_syn : bool
@@ -301,7 +314,6 @@ def init_single_subject_wf(
301314
FreeSurfer's ``$SUBJECTS_DIR``
302315
303316
"""
304-
from ..config import NONSTANDARD_REFERENCES
305317
if name in ('single_subject_wf', 'single_subject_dmripreptest_wf'):
306318
# for documentation purposes
307319
subject_data = {
@@ -349,11 +361,6 @@ def init_single_subject_wf(
349361
350362
""".format(dmriprep_ver=Version(__version__).public)
351363

352-
# Filter out standard spaces to a separate dict
353-
std_spaces = OrderedDict([
354-
(key, modifiers) for key, modifiers in output_spaces.items()
355-
if key not in NONSTANDARD_REFERENCES])
356-
357364
inputnode = pe.Node(niu.IdentityInterface(fields=['subjects_dir']),
358365
name='inputnode')
359366

@@ -363,10 +370,9 @@ def init_single_subject_wf(
363370
bids_info = pe.Node(BIDSInfo(
364371
bids_dir=layout.root, bids_validate=False), name='bids_info')
365372

366-
summary = pe.Node(SubjectSummary(
367-
std_spaces=list(std_spaces.keys()),
368-
nstd_spaces=list(set(NONSTANDARD_REFERENCES).intersection(output_spaces.keys()))),
369-
name='summary', run_without_submitting=True)
373+
summary = pe.Node(SubjectSummary(std_spaces=spaces.get_spaces(nonstandard=False),
374+
nstd_spaces=spaces.get_spaces(standard=False)),
375+
name='summary', run_without_submitting=True)
370376

371377
about = pe.Node(AboutSummary(version=__version__,
372378
command=' '.join(sys.argv)),
@@ -385,18 +391,18 @@ def init_single_subject_wf(
385391
# Preprocessing of T1w (includes registration to MNI)
386392
anat_preproc_wf = init_anat_preproc_wf(
387393
bids_root=layout.root,
388-
debug=debug,
389394
freesurfer=freesurfer,
390395
hires=hires,
391396
longitudinal=longitudinal,
392-
name="anat_preproc_wf",
393397
num_t1w=len(subject_data['t1w']),
394398
omp_nthreads=omp_nthreads,
395399
output_dir=output_dir,
396-
output_spaces=std_spaces,
397400
reportlets_dir=reportlets_dir,
398-
skull_strip_fixed_seed=skull_strip_fixed_seed,
399401
skull_strip_template=skull_strip_template,
402+
spaces=spaces,
403+
debug=debug,
404+
name="anat_preproc_wf",
405+
skull_strip_fixed_seed=skull_strip_fixed_seed,
400406
)
401407

402408
workflow.connect([

0 commit comments

Comments
 (0)