Skip to content

Commit 7e4bce7

Browse files
authored
Merge pull request #85 from oesteban/maint/overhaul-2
MAINT: Revisions after previous maintenance commit (#83)
2 parents 7c33d27 + 35abaae commit 7e4bce7

File tree

4 files changed

+79
-79
lines changed

4 files changed

+79
-79
lines changed

dmriprep/cli/run.py

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def check_deps(workflow):
3333

3434
def get_parser():
3535
"""Build parser object."""
36-
from niworkflows.utils.spaces import Reference, SpatialReferences, OutputReferencesAction
36+
from niworkflows.utils.spaces import Reference, OutputReferencesAction
3737
from packaging.version import Version
3838
from ..__about__ import __version__
3939
from .version import check_latest, is_flagged
@@ -102,17 +102,19 @@ def get_parser():
102102
'--longitudinal', action='store_true',
103103
help='treat dataset as longitudinal - may increase runtime')
104104
g_conf.add_argument(
105-
'--output-spaces', nargs='*', action=OutputReferencesAction, default=SpatialReferences(),
105+
'--output-spaces', nargs='*', action=OutputReferencesAction,
106106
help="""\
107107
Standard and non-standard spaces to resample anatomical and functional images to. \
108108
Standard spaces may be specified by the form \
109109
``<SPACE>[:cohort-<label>][:res-<resolution>][...]``, where ``<SPACE>`` is \
110110
a keyword designating a spatial reference, and may be followed by optional, \
111111
colon-separated parameters. \
112112
Non-standard spaces imply specific orientations and sampling grids. \
113+
The default value of this flag (meaning, if the argument is not include in the command line) \
114+
is ``--output-spaces run`` - the original space and sampling grid of the original DWI run. \
113115
Important to note, the ``res-*`` modifier does not define the resolution used for \
114-
the spatial normalization. To generate no DWI outputs, use this option without specifying \
115-
any spatial references. For further details, please check out \
116+
the spatial normalization. To generate no DWI outputs (if that is intended for some reason), \
117+
use this option without specifying any spatial references. For further details, please check out \
116118
https://www.nipreps.org/dmriprep/en/%s/spaces.html""" % (currentv.base_version
117119
if is_release else 'latest'))
118120

@@ -146,6 +148,10 @@ def get_parser():
146148
'--fs-license-file', metavar='PATH', type=Path,
147149
help='Path to FreeSurfer license key file. Get it (for free) by registering'
148150
' 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)')
149155

150156
# Surface generation xor
151157
g_surfs = parser.add_argument_group('Surface preprocessing options')
@@ -416,7 +422,11 @@ def build_workflow(opts, retval):
416422
retval['return_code'] = 1
417423
return retval
418424

419-
output_spaces = parse_spaces(opts)
425+
# Initialize --output-spaces if not defined
426+
output_spaces = opts.output_spaces
427+
if opts.output_spaces is None:
428+
from niworkflows.utils.spaces import Reference, SpatialReferences
429+
output_spaces = SpatialReferences([Reference("run", {})])
420430

421431
# Set up some instrumental utilities
422432
run_uuid = '%s_%s' % (strftime('%Y%m%d-%H%M%S'), uuid.uuid4())
@@ -523,17 +533,18 @@ def build_workflow(opts, retval):
523533
debug=opts.debug,
524534
force_syn=opts.force_syn,
525535
freesurfer=opts.run_reconall,
536+
fs_subjects_dir=opts.fs_subjects_dir,
526537
hires=opts.hires,
527538
ignore=opts.ignore,
528539
layout=layout,
529540
longitudinal=opts.longitudinal,
530541
low_mem=opts.low_mem,
531542
omp_nthreads=omp_nthreads,
532543
output_dir=str(output_dir),
533-
output_spaces=output_spaces,
534544
run_uuid=run_uuid,
535545
skull_strip_fixed_seed=opts.skull_strip_fixed_seed,
536-
skull_strip_template=opts.skull_strip_template,
546+
skull_strip_template=opts.skull_strip_template[0],
547+
spaces=output_spaces,
537548
subject_list=subject_list,
538549
use_syn=opts.use_syn_sdc,
539550
work_dir=str(work_dir),
@@ -563,23 +574,6 @@ def build_workflow(opts, retval):
563574
return retval
564575

565576

566-
def parse_spaces(opts):
567-
"""Ensure the spaces are correctly parsed."""
568-
from sys import stderr
569-
from collections import OrderedDict
570-
# Set the default template to 'MNI152NLin2009cAsym'
571-
output_spaces = opts.output_spaces or OrderedDict([('MNI152NLin2009cAsym', {})])
572-
573-
FS_SPACES = set(['fsnative', 'fsaverage', 'fsaverage6', 'fsaverage5'])
574-
if opts.run_reconall and not list(FS_SPACES.intersection(output_spaces.keys())):
575-
print("""\
576-
Although ``--fs-no-reconall`` was not set (i.e., FreeSurfer is to be run), no FreeSurfer \
577-
output space (valid values are: %s) was selected. Adding default "fsaverage5" to the \
578-
list of output spaces.""" % ', '.join(FS_SPACES), file=stderr)
579-
output_spaces['fsaverage5'] = {}
580-
return output_spaces
581-
582-
583577
if __name__ == '__main__':
584578
raise RuntimeError("dmriprep/cli/run.py should not be run directly;\n"
585579
"Please `pip install` dmriprep and use the `dmriprep` command")

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([

docs/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
git+https://github.com/AleksandarPetrov/napoleon.git@0dc3f28a309ad602be5f44a9049785a1026451b3#egg=sphinxcontrib-napoleon
22
git+https://github.com/rwblair/sphinxcontrib-versioning.git@39b40b0b84bf872fc398feff05344051bbce0f63#egg=sphinxcontrib-versioning
33
nbsphinx
4-
nipype ~= 1.3.1
5-
niworkflows ~= 1.1.0
4+
nipype ~= 1.4.2
5+
niworkflows ~= 1.1.9
66
packaging
77
pydot>=1.2.3
88
pydotplus
9-
smriprep ~= 0.4.1
9+
smriprep ~= 0.5.2
1010
sphinx-argparse
1111
sphinx>=2.1.2
1212
sphinx_rtd_theme

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ install_requires =
2323
dipy >=1.0.0
2424
indexed_gzip >=0.8.8
2525
nibabel ~= 3.0
26-
nipype ~= 1.4.0
26+
nipype ~= 1.4.2
2727
niworkflows ~= 1.1.9
2828
numpy
2929
pybids ~= 0.9.4

0 commit comments

Comments
 (0)