-
Notifications
You must be signed in to change notification settings - Fork 533
ENH: initial connectome workbench support #2594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7a91235
44c4c9a
25415e4
ada6e30
a221f1b
853cb9c
e511fbe
82d2715
9e61ec7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# -*- coding: utf-8 -*- | ||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- | ||
# vi: set ft=python sts=4 ts=4 sw=4 et: | ||
|
||
from .metric import MetricResample |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# -*- coding: utf-8 -*- | ||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- | ||
# vi: set ft=python sts=4 ts=4 sw=4 et: | ||
""" | ||
The workbench module provides classes for interfacing with `connectome workbench | ||
<https://www.humanconnectome.org/software/connectome-workbench>`_ tools. | ||
|
||
`Connectome Workbench is an open source, freely available visualization and | ||
discovery tool used to map neuroimaging data, especially data generated by the | ||
Human Connectome Project. | ||
""" | ||
|
||
from __future__ import (print_function, division, unicode_literals, | ||
absolute_import) | ||
import os | ||
import re | ||
|
||
from ... import logging | ||
from ...utils.filemanip import split_filename | ||
from ..base import CommandLine, PackageInfo | ||
|
||
iflogger = logging.getLogger('interface') | ||
|
||
|
||
class Info(PackageInfo): | ||
""" | ||
Handle `wb_command` version information. | ||
""" | ||
|
||
version_cmd = 'wb_command -version' | ||
|
||
@staticmethod | ||
def parse_version(raw_info): | ||
m = re.search(r'\nVersion (\S+)', raw_info) | ||
return m.groups()[0] if m else None | ||
|
||
|
||
class WBCommand(CommandLine): | ||
"""Base support for workbench commands.""" | ||
|
||
@property | ||
def version(self): | ||
return Info.version() | ||
|
||
def _gen_filename(self, name, outdir=None, suffix='', ext=None): | ||
"""Generate a filename based on the given parameters. | ||
The filename will take the form: <basename><suffix><ext>. | ||
Parameters | ||
---------- | ||
name : str | ||
Filename to base the new filename on. | ||
suffix : str | ||
Suffix to add to the `basename`. (defaults is '' ) | ||
ext : str | ||
Extension to use for the new filename. | ||
Returns | ||
------- | ||
fname : str | ||
New filename based on given parameters. | ||
""" | ||
if not name: | ||
raise ValueError("Cannot generate filename - filename not set") | ||
|
||
_, fname, fext = split_filename(name) | ||
if ext is None: | ||
ext = fext | ||
if outdir is None: | ||
outdir = '.' | ||
return os.path.join(outdir, fname + suffix + ext) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
# -*- coding: utf-8 -*- | ||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- | ||
# vi: set ft=python sts=4 ts=4 sw=4 et: | ||
"""This module provides interfaces for workbench surface commands""" | ||
from __future__ import (print_function, division, unicode_literals, | ||
absolute_import) | ||
import os | ||
|
||
from ..base import (TraitedSpec, File, traits, CommandLineInputSpec) | ||
from .base import WBCommand | ||
from ... import logging | ||
|
||
iflogger = logging.getLogger('interface') | ||
|
||
|
||
class MetricResampleInputSpec(CommandLineInputSpec): | ||
in_file = File( | ||
exists=True, | ||
mandatory=True, | ||
argstr="%s", | ||
position=0, | ||
desc="The metric file to resample") | ||
current_sphere = File( | ||
exists=True, | ||
mandatory=True, | ||
argstr="%s", | ||
position=1, | ||
desc="A sphere surface with the mesh that the metric is currently on") | ||
new_sphere = File( | ||
exists=True, | ||
mandatory=True, | ||
argstr="%s", | ||
position=2, | ||
desc="A sphere surface that is in register with <current-sphere> and" | ||
" has the desired output mesh") | ||
method = traits.Enum( | ||
"ADAP_BARY_AREA", | ||
"BARYCENTRIC", | ||
argstr="%s", | ||
mandatory=True, | ||
position=3, | ||
desc="The method name - ADAP_BARY_AREA method is recommended for" | ||
" ordinary metric data, because it should use all data while" | ||
" downsampling, unlike BARYCENTRIC. If ADAP_BARY_AREA is used," | ||
" exactly one of area_surfs or area_metrics must be specified") | ||
out_file = File( | ||
name_source=["new_sphere"], | ||
name_template="%s.out", | ||
keep_extension=True, | ||
argstr="%s", | ||
position=4, | ||
desc="The output metric") | ||
area_surfs = traits.Bool( | ||
position=5, | ||
argstr="-area-surfs", | ||
xor=["area_metrics"], | ||
desc="Specify surfaces to do vertex area correction based on") | ||
area_metrics = traits.Bool( | ||
position=5, | ||
argstr="-area-metrics", | ||
xor=["area_surfs"], | ||
desc="Specify vertex area metrics to do area correction based on") | ||
current_area = File( | ||
exists=True, | ||
position=6, | ||
argstr="%s", | ||
desc="A relevant anatomical surface with <current-sphere> mesh OR" | ||
" a metric file with vertex areas for <current-sphere> mesh") | ||
new_area = File( | ||
exists=True, | ||
position=7, | ||
argstr="%s", | ||
desc="A relevant anatomical surface with <current-sphere> mesh OR" | ||
" a metric file with vertex areas for <current-sphere> mesh") | ||
roi_metric = File( | ||
exists=True, | ||
position=8, | ||
argstr="-current-roi %s", | ||
desc="Input roi on the current mesh used to exclude non-data vertices") | ||
valid_roi_out = traits.Bool( | ||
position=9, | ||
argstr="-valid-roi-out", | ||
desc="Output the ROI of vertices that got data from valid source vertices") | ||
largest = traits.Bool( | ||
position=10, | ||
argstr="-largest", | ||
desc="Use only the value of the vertex with the largest weight") | ||
|
||
|
||
class MetricResampleOutputSpec(TraitedSpec): | ||
out_file = File(exists=True, desc="the output metric") | ||
roi_file = File(desc="ROI of vertices that got data from valid source vertices") | ||
|
||
|
||
class MetricResample(WBCommand): | ||
""" | ||
Resample a metric file to a different mesh | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would help documentation to copy a bit more from their docs:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @oesteban Want to just push this change? It's after 5pm here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure |
||
Resamples a metric file, given two spherical surfaces that are in | ||
register. If ``ADAP_BARY_AREA`` is used, exactly one of -area-surfs or | ||
``-area-metrics`` must be specified. | ||
|
||
The ``ADAP_BARY_AREA`` method is recommended for ordinary metric data, | ||
because it should use all data while downsampling, unlike ``BARYCENTRIC``. | ||
The recommended areas option for most data is individual midthicknesses | ||
for individual data, and averaged vertex area metrics from individual | ||
midthicknesses for group average data. | ||
|
||
The ``-current-roi`` option only masks the input, the output may be slightly | ||
dilated in comparison, consider using ``-metric-mask`` on the output when | ||
using ``-current-roi``. | ||
|
||
The ``-largest option`` results in nearest vertex behavior when used with | ||
``BARYCENTRIC``. When resampling a binary metric, consider thresholding at | ||
0.5 after resampling rather than using ``-largest``. | ||
|
||
>>> from nipype.interfaces.workbench import MetricResample | ||
>>> metres = MetricResample() | ||
>>> metres.inputs.in_file = 'sub-01_task-rest_bold_space-fsaverage5.L.func.gii' | ||
>>> metres.inputs.method = 'ADAP_BARY_AREA' | ||
>>> metres.inputs.current_sphere = 'fsaverage5_std_sphere.L.10k_fsavg_L.surf.gii' | ||
>>> metres.inputs.new_sphere = 'fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.gii' | ||
>>> metres.inputs.area_metrics = True | ||
>>> metres.inputs.current_area = 'fsaverage5.L.midthickness_va_avg.10k_fsavg_L.shape.gii' | ||
>>> metres.inputs.new_area = 'fs_LR.L.midthickness_va_avg.32k_fs_LR.shape.gii' | ||
>>> metres.cmdline | ||
'wb_command -metric-resample sub-01_task-rest_bold_space-fsaverage5.L.func.gii \ | ||
fsaverage5_std_sphere.L.10k_fsavg_L.surf.gii \ | ||
fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.gii \ | ||
ADAP_BARY_AREA fs_LR-deformed_to-fsaverage.L.sphere.32k_fs_LR.surf.out \ | ||
-area-metrics fsaverage5.L.midthickness_va_avg.10k_fsavg_L.shape.gii \ | ||
fs_LR.L.midthickness_va_avg.32k_fs_LR.shape.gii' | ||
""" | ||
input_spec = MetricResampleInputSpec | ||
output_spec = MetricResampleOutputSpec | ||
_cmd = 'wb_command -metric-resample' | ||
|
||
def _format_arg(self, opt, spec, val): | ||
if opt in ['current_area', 'new_area']: | ||
if not self.inputs.area_surfs and not self.inputs.area_metrics: | ||
raise ValueError("{} was set but neither area_surfs or" | ||
" area_metrics were set".format(opt)) | ||
if opt == "method": | ||
if (val == "ADAP_BARY_AREA" and | ||
not self.inputs.area_surfs and | ||
not self.inputs.area_metrics): | ||
raise ValueError("Exactly one of area_surfs or area_metrics" | ||
" must be specified") | ||
if opt == "valid_roi_out" and val: | ||
# generate a filename and add it to argstr | ||
roi_out = self._gen_filename(self.inputs.in_file, suffix='_roi') | ||
iflogger.info("Setting roi output file as", roi_out) | ||
spec.argstr += " " + roi_out | ||
return super(MetricResample, self)._format_arg(opt, spec, val) | ||
|
||
def _list_outputs(self): | ||
outputs = super(MetricResample, self)._list_outputs() | ||
if self.inputs.valid_roi_out: | ||
roi_file = self._gen_filename(self.inputs.in_file, suffix='_roi') | ||
outputs['roi_file'] = os.path.abspath(roi_file) | ||
return outputs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT | ||
from __future__ import unicode_literals | ||
from ..metric import MetricResample | ||
|
||
|
||
def test_MetricResample_inputs(): | ||
input_map = dict( | ||
area_metrics=dict( | ||
argstr='-area-metrics', | ||
position=5, | ||
xor=['area_surfs'], | ||
), | ||
area_surfs=dict( | ||
argstr='-area-surfs', | ||
position=5, | ||
xor=['area_metrics'], | ||
), | ||
args=dict(argstr='%s', ), | ||
current_area=dict( | ||
argstr='%s', | ||
position=6, | ||
), | ||
current_sphere=dict( | ||
argstr='%s', | ||
mandatory=True, | ||
position=1, | ||
), | ||
environ=dict( | ||
nohash=True, | ||
usedefault=True, | ||
), | ||
ignore_exception=dict( | ||
deprecated='1.0.0', | ||
nohash=True, | ||
usedefault=True, | ||
), | ||
in_file=dict( | ||
argstr='%s', | ||
mandatory=True, | ||
position=0, | ||
), | ||
largest=dict( | ||
argstr='-largest', | ||
position=10, | ||
), | ||
method=dict( | ||
argstr='%s', | ||
mandatory=True, | ||
position=3, | ||
), | ||
new_area=dict( | ||
argstr='%s', | ||
position=7, | ||
), | ||
new_sphere=dict( | ||
argstr='%s', | ||
mandatory=True, | ||
position=2, | ||
), | ||
out_file=dict( | ||
argstr='%s', | ||
keep_extension=True, | ||
name_source=['new_sphere'], | ||
name_template='%s.out', | ||
position=4, | ||
), | ||
roi_metric=dict( | ||
argstr='-current-roi %s', | ||
position=8, | ||
), | ||
terminal_output=dict( | ||
deprecated='1.0.0', | ||
nohash=True, | ||
), | ||
valid_roi_out=dict( | ||
argstr='-valid-roi-out', | ||
position=9, | ||
), | ||
) | ||
inputs = MetricResample.input_spec() | ||
|
||
for key, metadata in list(input_map.items()): | ||
for metakey, value in list(metadata.items()): | ||
assert getattr(inputs.traits()[key], metakey) == value | ||
def test_MetricResample_outputs(): | ||
output_map = dict( | ||
out_file=dict(), | ||
roi_file=dict(), | ||
) | ||
outputs = MetricResample.output_spec() | ||
|
||
for key, metadata in list(output_map.items()): | ||
for metakey, value in list(metadata.items()): | ||
assert getattr(outputs.traits()[key], metakey) == value |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT | ||
from __future__ import unicode_literals | ||
from ..base import WBCommand | ||
|
||
|
||
def test_WBCommand_inputs(): | ||
input_map = dict( | ||
args=dict(argstr='%s', ), | ||
environ=dict( | ||
nohash=True, | ||
usedefault=True, | ||
), | ||
ignore_exception=dict( | ||
deprecated='1.0.0', | ||
nohash=True, | ||
usedefault=True, | ||
), | ||
terminal_output=dict( | ||
deprecated='1.0.0', | ||
nohash=True, | ||
), | ||
) | ||
inputs = WBCommand.input_spec() | ||
|
||
for key, metadata in list(input_map.items()): | ||
for metakey, value in list(metadata.items()): | ||
assert getattr(inputs.traits()[key], metakey) == value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe rename this to
out_file
(or rename both here andoutputs.out_file
tometric_file
)? In addition to resolving your trait weirdness, I think it'll be less confusing for people for the connected traits to have the same name.