Skip to content

Commit 7a91235

Browse files
committed
enh: initial connectome workbench support
1 parent 95c5f37 commit 7a91235

File tree

5 files changed

+341
-0
lines changed

5 files changed

+341
-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: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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, InputMultiObject,
10+
CommandLineInputSpec)
11+
from .base import WBCommand
12+
from ... import logging
13+
14+
iflogger = logging.getLogger('interface')
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+
metric_out = 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+
>>> from nipype.interfaces.workbench import MetricResample
100+
>>> metres = MetricResample()
101+
>>> metres.inputs.in_file = 'sub-01_task-rest_bold_space-fsaverage5.L.func.gii'
102+
>>> metres.inputs.method = 'ADAP_BARY_AREA'
103+
>>> metres.inputs.current_sphere = 'fsaverage5_std_sphere.L.10k_fsavg_L.surf.gii'
104+
>>> metres.inputs.new_sphere = 'fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.gii'
105+
>>> metres.inputs.area_metrics = True
106+
>>> metres.inputs.current_area = 'fsaverage5.L.midthickness_va_avg.10k_fsavg_L.shape.gii'
107+
>>> metres.inputs.new_area = 'fs_LR.L.midthickness_va_avg.32k_fs_LR.shape.gii'
108+
>>> metres.cmdline
109+
'wb_command -metric-resample sub-01_task-rest_bold_space-fsaverage5.L.func.gii \
110+
fsaverage5_std_sphere.L.10k_fsavg_L.surf.gii \
111+
fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.gii \
112+
ADAP_BARY_AREA fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.out \
113+
-area-metrics fsaverage5.L.midthickness_va_avg.10k_fsavg_L.shape.gii \
114+
fs_LR.L.midthickness_va_avg.32k_fs_LR.shape.gii'
115+
"""
116+
input_spec = MetricResampleInputSpec
117+
output_spec = MetricResampleOutputSpec
118+
_cmd = 'wb_command -metric-resample'
119+
120+
def _format_arg(self, opt, spec, val):
121+
if opt == "metric_out":
122+
# ensure generated filename is assigned to trait
123+
self.inputs.trait_set(metric_out=val)
124+
if opt in ['current_area', 'new_area']:
125+
if not self.inputs.area_surfs and not self.inputs.area_metrics:
126+
raise ValueError("{} was set but neither area_surfs or"
127+
" area_metrics were set".format(opt))
128+
if opt == "method":
129+
if (val == "ADAP_BARY_AREA" and
130+
not self.inputs.area_surfs and
131+
not self.inputs.area_metrics):
132+
raise ValueError("Exactly one of area_surfs or area_metrics"
133+
" must be specified")
134+
if opt == "valid_roi_out" and val:
135+
# generate a filename and add it to argstr
136+
roi_out = self._gen_filename(self.inputs.in_file, suffix='_roi')
137+
iflogger.info("Setting roi output file as", roi_out)
138+
spec.argstr += " " + roi_out
139+
return super(MetricResample, self)._format_arg(opt, spec, val)
140+
141+
def _list_outputs(self):
142+
outputs = self._outputs().get()
143+
outputs['out_file'] = os.path.abspath(self.inputs.metric_out)
144+
if self.inputs.valid_roi_out:
145+
roi_file = self._gen_filename(self.inputs.in_file, suffix='_roi')
146+
outputs['roi_file'] = os.path.abspath(roi)
147+
return outputs
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
position=3,
49+
),
50+
metric_out=dict(
51+
argstr='%s',
52+
keep_extension=True,
53+
name_source=['new_sphere'],
54+
name_template='%s.out',
55+
position=4,
56+
),
57+
new_area=dict(
58+
argstr='%s',
59+
position=7,
60+
),
61+
new_sphere=dict(
62+
argstr='%s',
63+
mandatory=True,
64+
position=2,
65+
),
66+
roi_metric=dict(
67+
argstr='-current-roi %s',
68+
position=8,
69+
),
70+
terminal_output=dict(
71+
deprecated='1.0.0',
72+
nohash=True,
73+
),
74+
valid_roi_out=dict(
75+
argstr='-valid-roi-out',
76+
position=9,
77+
),
78+
)
79+
inputs = MetricResample.input_spec()
80+
81+
for key, metadata in list(input_map.items()):
82+
for metakey, value in list(metadata.items()):
83+
assert getattr(inputs.traits()[key], metakey) == value
84+
def test_MetricResample_outputs():
85+
output_map = dict(
86+
out_file=dict(),
87+
roi_file=dict(),
88+
)
89+
outputs = MetricResample.output_spec()
90+
91+
for key, metadata in list(output_map.items()):
92+
for metakey, value in list(metadata.items()):
93+
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

0 commit comments

Comments
 (0)