Skip to content

Commit 726870e

Browse files
committed
Merge pull request #1126 from oesteban/enh/MRTrix3
[ENH] Write interfaces for MRTrix3
2 parents 81aa774 + fda2ac5 commit 726870e

30 files changed

+2250
-0
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Next release
22
============
33

4+
* ENH: New interfaces for MRTrix3 (https://github.com/nipy/nipype/pull/1126)
45
* ENH: New option in afni.3dRefit - zdel, ydel, zdel etc. (https://github.com/nipy/nipype/pull/1079)
56
* FIX: ants.Registration composite transform outputs are no longer returned as lists (https://github.com/nipy/nipype/pull/1183)
67
* BUG: ANTs Registration interface failed with multi-modal inputs

nipype/interfaces/mrtrix3/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
# -*- coding: utf-8 -*-
4+
5+
from utils import (Mesh2PVE, Generate5tt, BrainMask, TensorMetrics,
6+
ComputeTDI, TCK2VTK)
7+
from preprocess import ResponseSD, ACTPrepareFSL, ReplaceFSwithFIRST
8+
from tracking import Tractography
9+
from reconst import FitTensor, EstimateFOD
10+
from connectivity import LabelConfig, BuildConnectome

nipype/interfaces/mrtrix3/base.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
# -*- coding: utf-8 -*-
4+
5+
"""
6+
Change directory to provide relative paths for doctests
7+
>>> import os
8+
>>> filepath = os.path.dirname(os.path.realpath(__file__ ))
9+
>>> datadir = os.path.realpath(os.path.join(filepath,
10+
... '../../testing/data'))
11+
>>> os.chdir(datadir)
12+
13+
"""
14+
import os
15+
import os.path as op
16+
17+
from nipype.interfaces.base import (
18+
CommandLineInputSpec, CommandLine, traits, TraitedSpec, File,
19+
InputMultiPath)
20+
21+
from nipype.utils.filemanip import split_filename
22+
from nipype.interfaces.traits_extension import isdefined
23+
24+
from ... import logging
25+
logger = logging.getLogger('interface')
26+
27+
28+
class MRTrix3BaseInputSpec(CommandLineInputSpec):
29+
nthreads = traits.Int(
30+
argstr='-nthreads %d', desc='number of threads. if zero, the number'
31+
' of available cpus will be used', nohash=True)
32+
# DW gradient table import options
33+
grad_file = File(exists=True, argstr='-grad %s',
34+
desc='dw gradient scheme (MRTrix format')
35+
grad_fsl = traits.Tuple(
36+
File(exists=True), File(exists=True), argstr='-fslgrad %s %s',
37+
desc='(bvecs, bvals) dw gradient scheme (FSL format')
38+
bval_scale = traits.Enum(
39+
'yes', 'no', argstr='-bvalue_scaling %s',
40+
desc='specifies whether the b - values should be scaled by the square'
41+
' of the corresponding DW gradient norm, as often required for '
42+
'multishell or DSI DW acquisition schemes. The default action '
43+
'can also be set in the MRtrix config file, under the '
44+
'BValueScaling entry. Valid choices are yes / no, true / '
45+
'false, 0 / 1 (default: true).')
46+
47+
in_bvec = File(exists=True, argstr='-fslgrad %s %s',
48+
desc='bvecs file in FSL format')
49+
in_bval = File(exists=True, desc='bvals file in FSL format')
50+
51+
52+
class MRTrix3Base(CommandLine):
53+
54+
def _format_arg(self, name, trait_spec, value):
55+
if name == 'nthreads' and value == 0:
56+
value = 1
57+
try:
58+
from multiprocessing import cpu_count
59+
value = cpu_count()
60+
except:
61+
logger.warn('Number of threads could not be computed')
62+
pass
63+
return trait_spec.argstr % value
64+
65+
if name == 'in_bvec':
66+
return trait_spec.argstr % (value, self.inputs.in_bval)
67+
68+
return super(MRTrix3Base, self)._format_arg(name, trait_spec, value)
69+
70+
def _parse_inputs(self, skip=None):
71+
if skip is None:
72+
skip = []
73+
74+
try:
75+
if (isdefined(self.inputs.grad_file) or
76+
isdefined(self.inputs.grad_fsl)):
77+
skip += ['in_bvec', 'in_bval']
78+
79+
is_bvec = isdefined(self.inputs.in_bvec)
80+
is_bval = isdefined(self.inputs.in_bval)
81+
if is_bvec or is_bval:
82+
if not is_bvec or not is_bval:
83+
raise RuntimeError('If using bvecs and bvals inputs, both'
84+
'should be defined')
85+
skip += ['in_bval']
86+
except AttributeError:
87+
pass
88+
89+
return super(MRTrix3Base, self)._parse_inputs(skip=skip)
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
# -*- coding: utf-8 -*-
4+
5+
"""
6+
Change directory to provide relative paths for doctests
7+
>>> import os
8+
>>> filepath = os.path.dirname(os.path.realpath(__file__ ))
9+
>>> datadir = os.path.realpath(os.path.join(filepath,
10+
... '../../testing/data'))
11+
>>> os.chdir(datadir)
12+
13+
"""
14+
import os
15+
import os.path as op
16+
17+
from base import MRTrix3BaseInputSpec, MRTrix3Base
18+
from nipype.interfaces.base import (
19+
CommandLineInputSpec, CommandLine, traits, TraitedSpec, File)
20+
21+
from nipype.utils.filemanip import split_filename
22+
from nipype.interfaces.traits_extension import isdefined
23+
24+
25+
class BuildConnectomeInputSpec(CommandLineInputSpec):
26+
in_file = File(exists=True, argstr='%s', mandatory=True, position=-3,
27+
desc='input tractography')
28+
in_parc = File(exists=True, argstr='%s', position=-2,
29+
desc='parcellation file')
30+
out_file = File(
31+
'connectome.csv', argstr='%s', mandatory=True, position=-1,
32+
usedefault=True, desc='output file after processing')
33+
34+
nthreads = traits.Int(
35+
argstr='-nthreads %d', desc='number of threads. if zero, the number'
36+
' of available cpus will be used', nohash=True)
37+
38+
vox_lookup = traits.Bool(
39+
argstr='-assignment_voxel_lookup',
40+
desc='use a simple voxel lookup value at each streamline endpoint')
41+
search_radius = traits.Float(
42+
argstr='-assignment_radial_search %f',
43+
desc='perform a radial search from each streamline endpoint to locate '
44+
'the nearest node. Argument is the maximum radius in mm; if no node is'
45+
' found within this radius, the streamline endpoint is not assigned to'
46+
' any node.')
47+
search_reverse = traits.Float(
48+
argstr='-assignment_reverse_search %f',
49+
desc='traverse from each streamline endpoint inwards along the '
50+
'streamline, in search of the last node traversed by the streamline. '
51+
'Argument is the maximum traversal length in mm (set to 0 to allow '
52+
'search to continue to the streamline midpoint).')
53+
search_forward = traits.Float(
54+
argstr='-assignment_forward_search %f',
55+
desc='project the streamline forwards from the endpoint in search of a'
56+
'parcellation node voxel. Argument is the maximum traversal length in '
57+
'mm.')
58+
59+
metric = traits.Enum(
60+
'count', 'meanlength', 'invlength', 'invnodevolume', 'mean_scalar',
61+
'invlength_invnodevolume', argstr='-metric %s', desc='specify the edge'
62+
' weight metric')
63+
64+
in_scalar = File(
65+
exists=True, argstr='-image %s', desc='provide the associated image '
66+
'for the mean_scalar metric')
67+
68+
in_weights = File(
69+
exists=True, argstr='-tck_weights_in %s', desc='specify a text scalar '
70+
'file containing the streamline weights')
71+
72+
keep_unassigned = traits.Bool(
73+
argstr='-keep_unassigned', desc='By default, the program discards the'
74+
' information regarding those streamlines that are not successfully '
75+
'assigned to a node pair. Set this option to keep these values (will '
76+
'be the first row/column in the output matrix)')
77+
zero_diagonal = traits.Bool(
78+
argstr='-zero_diagonal', desc='set all diagonal entries in the matrix '
79+
'to zero (these represent streamlines that connect to the same node at'
80+
' both ends)')
81+
82+
83+
class BuildConnectomeOutputSpec(TraitedSpec):
84+
out_file = File(exists=True, desc='the output response file')
85+
86+
87+
class BuildConnectome(MRTrix3Base):
88+
89+
"""
90+
Generate a connectome matrix from a streamlines file and a node
91+
parcellation image
92+
93+
Example
94+
-------
95+
96+
>>> import nipype.interfaces.mrtrix3 as mrt
97+
>>> mat = mrt.BuildConnectome()
98+
>>> mat.inputs.in_file = 'tracks.tck'
99+
>>> mat.inputs.in_parc = 'aparc+aseg.nii'
100+
>>> mat.cmdline # doctest: +ELLIPSIS
101+
'tck2connectome tracks.tck aparc+aseg.nii connectome.csv'
102+
>>> mat.run() # doctest: +SKIP
103+
"""
104+
105+
_cmd = 'tck2connectome'
106+
input_spec = BuildConnectomeInputSpec
107+
output_spec = BuildConnectomeOutputSpec
108+
109+
def _list_outputs(self):
110+
outputs = self.output_spec().get()
111+
outputs['out_file'] = op.abspath(self.inputs.out_file)
112+
return outputs
113+
114+
115+
class LabelConfigInputSpec(CommandLineInputSpec):
116+
in_file = File(exists=True, argstr='%s', mandatory=True, position=-3,
117+
desc='input anatomical image')
118+
in_config = File(exists=True, argstr='%s', position=-2,
119+
desc='connectome configuration file')
120+
out_file = File(
121+
'parcellation.mif', argstr='%s', mandatory=True, position=-1,
122+
usedefault=True, desc='output file after processing')
123+
124+
lut_basic = File(argstr='-lut_basic %s', desc='get information from '
125+
'a basic lookup table consisting of index / name pairs')
126+
lut_fs = File(argstr='-lut_freesurfer %s', desc='get information from '
127+
'a FreeSurfer lookup table(typically "FreeSurferColorLUT'
128+
'.txt")')
129+
lut_aal = File(argstr='-lut_aal %s', desc='get information from the AAL '
130+
'lookup table (typically "ROI_MNI_V4.txt")')
131+
lut_itksnap = File(argstr='-lut_itksnap %s', desc='get information from an'
132+
' ITK - SNAP lookup table(this includes the IIT atlas '
133+
'file "LUT_GM.txt")')
134+
spine = File(argstr='-spine %s', desc='provide a manually-defined '
135+
'segmentation of the base of the spine where the streamlines'
136+
' terminate, so that this can become a node in the connection'
137+
' matrix.')
138+
nthreads = traits.Int(
139+
argstr='-nthreads %d', desc='number of threads. if zero, the number'
140+
' of available cpus will be used', nohash=True)
141+
142+
143+
class LabelConfigOutputSpec(TraitedSpec):
144+
out_file = File(exists=True, desc='the output response file')
145+
146+
147+
class LabelConfig(MRTrix3Base):
148+
149+
"""
150+
Re-configure parcellation to be incrementally defined.
151+
152+
Example
153+
-------
154+
155+
>>> import nipype.interfaces.mrtrix3 as mrt
156+
>>> labels = mrt.LabelConfig()
157+
>>> labels.inputs.in_file = 'aparc+aseg.nii'
158+
>>> labels.inputs.in_config = 'mrtrix3_labelconfig.txt'
159+
>>> labels.cmdline # doctest: +ELLIPSIS
160+
'labelconfig aparc+aseg.nii mrtrix3_labelconfig.txt parcellation.mif'
161+
>>> labels.run() # doctest: +SKIP
162+
"""
163+
164+
_cmd = 'labelconfig'
165+
input_spec = LabelConfigInputSpec
166+
output_spec = LabelConfigOutputSpec
167+
168+
def _parse_inputs(self, skip=None):
169+
if skip is None:
170+
skip = []
171+
172+
if not isdefined(self.inputs.in_config):
173+
from distutils.spawn import find_executable
174+
path = find_executable(self._cmd)
175+
if path is None:
176+
path = os.getenv(MRTRIX3_HOME, '/opt/mrtrix3')
177+
else:
178+
path = op.dirname(op.dirname(path))
179+
180+
self.inputs.in_config = op.join(
181+
path, 'src/dwi/tractography/connectomics/'
182+
'example_configs/fs_default.txt')
183+
184+
return super(LabelConfig, self)._parse_inputs(skip=skip)
185+
186+
def _list_outputs(self):
187+
outputs = self.output_spec().get()
188+
outputs['out_file'] = op.abspath(self.inputs.out_file)
189+
return outputs

0 commit comments

Comments
 (0)