Skip to content

Commit f092055

Browse files
authored
Merge pull request #2594 from mgxd/enh/workbench
ENH: initial connectome workbench support
2 parents 1fcc5d1 + 9e61ec7 commit f092055

11 files changed

+356
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
5+
from .metric import MetricResample

nipype/interfaces/workbench/base.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
"""
5+
The workbench module provides classes for interfacing with `connectome workbench
6+
<https://www.humanconnectome.org/software/connectome-workbench>`_ tools.
7+
8+
`Connectome Workbench is an open source, freely available visualization and
9+
discovery tool used to map neuroimaging data, especially data generated by the
10+
Human Connectome Project.
11+
"""
12+
13+
from __future__ import (print_function, division, unicode_literals,
14+
absolute_import)
15+
import os
16+
import re
17+
18+
from ... import logging
19+
from ...utils.filemanip import split_filename
20+
from ..base import CommandLine, PackageInfo
21+
22+
iflogger = logging.getLogger('interface')
23+
24+
25+
class Info(PackageInfo):
26+
"""
27+
Handle `wb_command` version information.
28+
"""
29+
30+
version_cmd = 'wb_command -version'
31+
32+
@staticmethod
33+
def parse_version(raw_info):
34+
m = re.search(r'\nVersion (\S+)', raw_info)
35+
return m.groups()[0] if m else None
36+
37+
38+
class WBCommand(CommandLine):
39+
"""Base support for workbench commands."""
40+
41+
@property
42+
def version(self):
43+
return Info.version()
44+
45+
def _gen_filename(self, name, outdir=None, suffix='', ext=None):
46+
"""Generate a filename based on the given parameters.
47+
The filename will take the form: <basename><suffix><ext>.
48+
Parameters
49+
----------
50+
name : str
51+
Filename to base the new filename on.
52+
suffix : str
53+
Suffix to add to the `basename`. (defaults is '' )
54+
ext : str
55+
Extension to use for the new filename.
56+
Returns
57+
-------
58+
fname : str
59+
New filename based on given parameters.
60+
"""
61+
if not name:
62+
raise ValueError("Cannot generate filename - filename not set")
63+
64+
_, fname, fext = split_filename(name)
65+
if ext is None:
66+
ext = fext
67+
if outdir is None:
68+
outdir = '.'
69+
return os.path.join(outdir, fname + suffix + ext)

nipype/interfaces/workbench/metric.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
"""This module provides interfaces for workbench surface commands"""
5+
from __future__ import (print_function, division, unicode_literals,
6+
absolute_import)
7+
import os
8+
9+
from ..base import (TraitedSpec, File, traits, CommandLineInputSpec)
10+
from .base import WBCommand
11+
from ... import logging
12+
13+
iflogger = logging.getLogger('interface')
14+
15+
16+
class MetricResampleInputSpec(CommandLineInputSpec):
17+
in_file = File(
18+
exists=True,
19+
mandatory=True,
20+
argstr="%s",
21+
position=0,
22+
desc="The metric file to resample")
23+
current_sphere = File(
24+
exists=True,
25+
mandatory=True,
26+
argstr="%s",
27+
position=1,
28+
desc="A sphere surface with the mesh that the metric is currently on")
29+
new_sphere = File(
30+
exists=True,
31+
mandatory=True,
32+
argstr="%s",
33+
position=2,
34+
desc="A sphere surface that is in register with <current-sphere> and"
35+
" has the desired output mesh")
36+
method = traits.Enum(
37+
"ADAP_BARY_AREA",
38+
"BARYCENTRIC",
39+
argstr="%s",
40+
mandatory=True,
41+
position=3,
42+
desc="The method name - ADAP_BARY_AREA method is recommended for"
43+
" ordinary metric data, because it should use all data while"
44+
" downsampling, unlike BARYCENTRIC. If ADAP_BARY_AREA is used,"
45+
" exactly one of area_surfs or area_metrics must be specified")
46+
out_file = File(
47+
name_source=["new_sphere"],
48+
name_template="%s.out",
49+
keep_extension=True,
50+
argstr="%s",
51+
position=4,
52+
desc="The output metric")
53+
area_surfs = traits.Bool(
54+
position=5,
55+
argstr="-area-surfs",
56+
xor=["area_metrics"],
57+
desc="Specify surfaces to do vertex area correction based on")
58+
area_metrics = traits.Bool(
59+
position=5,
60+
argstr="-area-metrics",
61+
xor=["area_surfs"],
62+
desc="Specify vertex area metrics to do area correction based on")
63+
current_area = File(
64+
exists=True,
65+
position=6,
66+
argstr="%s",
67+
desc="A relevant anatomical surface with <current-sphere> mesh OR"
68+
" a metric file with vertex areas for <current-sphere> mesh")
69+
new_area = File(
70+
exists=True,
71+
position=7,
72+
argstr="%s",
73+
desc="A relevant anatomical surface with <current-sphere> mesh OR"
74+
" a metric file with vertex areas for <current-sphere> mesh")
75+
roi_metric = File(
76+
exists=True,
77+
position=8,
78+
argstr="-current-roi %s",
79+
desc="Input roi on the current mesh used to exclude non-data vertices")
80+
valid_roi_out = traits.Bool(
81+
position=9,
82+
argstr="-valid-roi-out",
83+
desc="Output the ROI of vertices that got data from valid source vertices")
84+
largest = traits.Bool(
85+
position=10,
86+
argstr="-largest",
87+
desc="Use only the value of the vertex with the largest weight")
88+
89+
90+
class MetricResampleOutputSpec(TraitedSpec):
91+
out_file = File(exists=True, desc="the output metric")
92+
roi_file = File(desc="ROI of vertices that got data from valid source vertices")
93+
94+
95+
class MetricResample(WBCommand):
96+
"""
97+
Resample a metric file to a different mesh
98+
99+
Resamples a metric file, given two spherical surfaces that are in
100+
register. If ``ADAP_BARY_AREA`` is used, exactly one of -area-surfs or
101+
``-area-metrics`` must be specified.
102+
103+
The ``ADAP_BARY_AREA`` method is recommended for ordinary metric data,
104+
because it should use all data while downsampling, unlike ``BARYCENTRIC``.
105+
The recommended areas option for most data is individual midthicknesses
106+
for individual data, and averaged vertex area metrics from individual
107+
midthicknesses for group average data.
108+
109+
The ``-current-roi`` option only masks the input, the output may be slightly
110+
dilated in comparison, consider using ``-metric-mask`` on the output when
111+
using ``-current-roi``.
112+
113+
The ``-largest option`` results in nearest vertex behavior when used with
114+
``BARYCENTRIC``. When resampling a binary metric, consider thresholding at
115+
0.5 after resampling rather than using ``-largest``.
116+
117+
>>> from nipype.interfaces.workbench import MetricResample
118+
>>> metres = MetricResample()
119+
>>> metres.inputs.in_file = 'sub-01_task-rest_bold_space-fsaverage5.L.func.gii'
120+
>>> metres.inputs.method = 'ADAP_BARY_AREA'
121+
>>> metres.inputs.current_sphere = 'fsaverage5_std_sphere.L.10k_fsavg_L.surf.gii'
122+
>>> metres.inputs.new_sphere = 'fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.gii'
123+
>>> metres.inputs.area_metrics = True
124+
>>> metres.inputs.current_area = 'fsaverage5.L.midthickness_va_avg.10k_fsavg_L.shape.gii'
125+
>>> metres.inputs.new_area = 'fs_LR.L.midthickness_va_avg.32k_fs_LR.shape.gii'
126+
>>> metres.cmdline
127+
'wb_command -metric-resample sub-01_task-rest_bold_space-fsaverage5.L.func.gii \
128+
fsaverage5_std_sphere.L.10k_fsavg_L.surf.gii \
129+
fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.gii \
130+
ADAP_BARY_AREA fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.out \
131+
-area-metrics fsaverage5.L.midthickness_va_avg.10k_fsavg_L.shape.gii \
132+
fs_LR.L.midthickness_va_avg.32k_fs_LR.shape.gii'
133+
"""
134+
input_spec = MetricResampleInputSpec
135+
output_spec = MetricResampleOutputSpec
136+
_cmd = 'wb_command -metric-resample'
137+
138+
def _format_arg(self, opt, spec, val):
139+
if opt in ['current_area', 'new_area']:
140+
if not self.inputs.area_surfs and not self.inputs.area_metrics:
141+
raise ValueError("{} was set but neither area_surfs or"
142+
" area_metrics were set".format(opt))
143+
if opt == "method":
144+
if (val == "ADAP_BARY_AREA" and
145+
not self.inputs.area_surfs and
146+
not self.inputs.area_metrics):
147+
raise ValueError("Exactly one of area_surfs or area_metrics"
148+
" must be specified")
149+
if opt == "valid_roi_out" and val:
150+
# generate a filename and add it to argstr
151+
roi_out = self._gen_filename(self.inputs.in_file, suffix='_roi')
152+
iflogger.info("Setting roi output file as", roi_out)
153+
spec.argstr += " " + roi_out
154+
return super(MetricResample, self)._format_arg(opt, spec, val)
155+
156+
def _list_outputs(self):
157+
outputs = super(MetricResample, self)._list_outputs()
158+
if self.inputs.valid_roi_out:
159+
roi_file = self._gen_filename(self.inputs.in_file, suffix='_roi')
160+
outputs['roi_file'] = os.path.abspath(roi_file)
161+
return outputs

nipype/interfaces/workbench/tests/__init__.py

Whitespace-only changes.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..metric import MetricResample
4+
5+
6+
def test_MetricResample_inputs():
7+
input_map = dict(
8+
area_metrics=dict(
9+
argstr='-area-metrics',
10+
position=5,
11+
xor=['area_surfs'],
12+
),
13+
area_surfs=dict(
14+
argstr='-area-surfs',
15+
position=5,
16+
xor=['area_metrics'],
17+
),
18+
args=dict(argstr='%s', ),
19+
current_area=dict(
20+
argstr='%s',
21+
position=6,
22+
),
23+
current_sphere=dict(
24+
argstr='%s',
25+
mandatory=True,
26+
position=1,
27+
),
28+
environ=dict(
29+
nohash=True,
30+
usedefault=True,
31+
),
32+
ignore_exception=dict(
33+
deprecated='1.0.0',
34+
nohash=True,
35+
usedefault=True,
36+
),
37+
in_file=dict(
38+
argstr='%s',
39+
mandatory=True,
40+
position=0,
41+
),
42+
largest=dict(
43+
argstr='-largest',
44+
position=10,
45+
),
46+
method=dict(
47+
argstr='%s',
48+
mandatory=True,
49+
position=3,
50+
),
51+
new_area=dict(
52+
argstr='%s',
53+
position=7,
54+
),
55+
new_sphere=dict(
56+
argstr='%s',
57+
mandatory=True,
58+
position=2,
59+
),
60+
out_file=dict(
61+
argstr='%s',
62+
keep_extension=True,
63+
name_source=['new_sphere'],
64+
name_template='%s.out',
65+
position=4,
66+
),
67+
roi_metric=dict(
68+
argstr='-current-roi %s',
69+
position=8,
70+
),
71+
terminal_output=dict(
72+
deprecated='1.0.0',
73+
nohash=True,
74+
),
75+
valid_roi_out=dict(
76+
argstr='-valid-roi-out',
77+
position=9,
78+
),
79+
)
80+
inputs = MetricResample.input_spec()
81+
82+
for key, metadata in list(input_map.items()):
83+
for metakey, value in list(metadata.items()):
84+
assert getattr(inputs.traits()[key], metakey) == value
85+
def test_MetricResample_outputs():
86+
output_map = dict(
87+
out_file=dict(),
88+
roi_file=dict(),
89+
)
90+
outputs = MetricResample.output_spec()
91+
92+
for key, metadata in list(output_map.items()):
93+
for metakey, value in list(metadata.items()):
94+
assert getattr(outputs.traits()[key], metakey) == value
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..base import WBCommand
4+
5+
6+
def test_WBCommand_inputs():
7+
input_map = dict(
8+
args=dict(argstr='%s', ),
9+
environ=dict(
10+
nohash=True,
11+
usedefault=True,
12+
),
13+
ignore_exception=dict(
14+
deprecated='1.0.0',
15+
nohash=True,
16+
usedefault=True,
17+
),
18+
terminal_output=dict(
19+
deprecated='1.0.0',
20+
nohash=True,
21+
),
22+
)
23+
inputs = WBCommand.input_spec()
24+
25+
for key, metadata in list(input_map.items()):
26+
for metakey, value in list(metadata.items()):
27+
assert getattr(inputs.traits()[key], metakey) == value

nipype/testing/data/fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.gii

Whitespace-only changes.

nipype/testing/data/fs_LR.L.midthickness_va_avg.32k_fs_LR.shape.gii

Whitespace-only changes.

nipype/testing/data/fsaverage5.L.midthickness_va_avg.10k_fsavg_L.shape.gii

Whitespace-only changes.

nipype/testing/data/fsaverage5_std_sphere.L.10k_fsavg_L.surf.gii

Whitespace-only changes.

nipype/testing/data/sub-01_task-rest_bold_space-fsaverage5.L.func.gii

Whitespace-only changes.

0 commit comments

Comments
 (0)