Skip to content

Commit fa0d7c4

Browse files
committed
Merge pull request #1121 from arokem/dipy-tensor
NF: First pass at a dipy DTI interface.
2 parents 73f3a73 + 703a3ca commit fa0d7c4

File tree

5 files changed

+139
-26
lines changed

5 files changed

+139
-26
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@
1414
.pydevproject
1515
.idea/
1616
/documentation.zip
17+
.DS_Store

nipype/interfaces/dipy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .tracks import TrackDensityMap
2-
from .tensors import TensorMode
2+
from .tensors import TensorMode, DTI
33
from .preprocess import Resample, Denoise

nipype/interfaces/dipy/tensors.py

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,116 @@
2525
have_dipy = False
2626
else:
2727
import dipy.reconst.dti as dti
28-
from dipy.core.gradients import GradientTable
28+
from dipy.core.gradients import gradient_table
29+
from dipy.io.utils import nifti1_symmat
2930

3031

32+
def tensor_fitting(data, bvals, bvecs, mask_file=None):
33+
"""
34+
Use dipy to fit DTI
35+
36+
Parameters
37+
----------
38+
in_file : str
39+
Full path to a DWI data file.
40+
bvals : str
41+
Full path to a file containing gradient magnitude information (b-values).
42+
bvecs : str
43+
Full path to a file containing gradient direction information (b-vectors).
44+
mask_file : str, optional
45+
Full path to a file containing a binary mask. Defaults to use the entire volume.
46+
47+
Returns
48+
-------
49+
TensorFit object, affine
50+
"""
51+
img = nb.load(in_file).get_data()
52+
data = img.get_data()
53+
affine = img.get_affine()
54+
if mask_file is not None:
55+
mask = nb.load(self.inputs.mask_file).get_data()
56+
else:
57+
mask=None
58+
59+
# Load information about the gradients:
60+
gtab = grad.gradient_table(self.inputs.bvals, self.inputs.bvecs)
61+
62+
# Fit it
63+
tenmodel = dti.TensorModel(gtab)
64+
return tenmodel.fit(data, mask), affine
65+
66+
67+
class DTIInputSpec(TraitedSpec):
68+
in_file = File(exists=True, mandatory=True,
69+
desc='The input 4D diffusion-weighted image file')
70+
bvecs = File(exists=True, mandatory=True,
71+
desc='The input b-vector text file')
72+
bvals = File(exists=True, mandatory=True,
73+
desc='The input b-value text file')
74+
mask_file = File(exists=True, mandatory=False,
75+
desc='An optional white matter mask')
76+
out_filename = File(
77+
genfile=True, desc='The output filename for the DTI parameters image')
78+
79+
80+
class DTIOutputSpec(TraitedSpec):
81+
out_file = File(exists=True)
82+
83+
84+
class DTI(BaseInterface):
85+
"""
86+
Calculates the diffusion tensor model parameters
87+
88+
Example
89+
-------
90+
91+
>>> import nipype.interfaces.dipy as dipy
92+
>>> dti = dipy.DTI()
93+
>>> dti.inputs.in_file = 'diffusion.nii'
94+
>>> dti.inputs.bvecs = 'bvecs'
95+
>>> dti.inputs.bvals = 'bvals'
96+
>>> dti.run() # doctest: +SKIP
97+
"""
98+
input_spec = DTIInputSpec
99+
output_spec = DTIOutputSpec
100+
101+
def _run_interface(self, runtime):
102+
ten_fit, affine = tensor_fitting(self.inputs.in_file,
103+
self.inputs.bvals,
104+
self.inputs.bvecs,
105+
self.inputs.mask_file)
106+
lower_triangular = tenfit.lower_triangular()
107+
img = nifti1_symmat(lower_triangular, affine)
108+
out_file = op.abspath(self._gen_outfilename())
109+
nb.save(img, out_file)
110+
iflogger.info('DTI parameters image saved as {i}'.format(i=out_file))
111+
return runtime
112+
113+
def _list_outputs(self):
114+
outputs = self._outputs().get()
115+
outputs['out_file'] = op.abspath(self._gen_outfilename())
116+
return outputs
117+
118+
def _gen_filename(self, name):
119+
if name is 'out_filename':
120+
return self._gen_outfilename()
121+
else:
122+
return None
123+
124+
def _gen_outfilename(self):
125+
_, name, _ = split_filename(self.inputs.in_file)
126+
return name + '_dti.nii'
127+
128+
31129
class TensorModeInputSpec(TraitedSpec):
32130
in_file = File(exists=True, mandatory=True,
33131
desc='The input 4D diffusion-weighted image file')
34132
bvecs = File(exists=True, mandatory=True,
35133
desc='The input b-vector text file')
36134
bvals = File(exists=True, mandatory=True,
37135
desc='The input b-value text file')
136+
mask_file = File(exists=True, mandatory=False,
137+
desc='An optional white matter mask')
38138
out_filename = File(
39139
genfile=True, desc='The output filename for the Tensor mode image')
40140

@@ -69,32 +169,11 @@ class TensorMode(BaseInterface):
69169
output_spec = TensorModeOutputSpec
70170

71171
def _run_interface(self, runtime):
72-
## Load the 4D image files
73-
img = nb.load(self.inputs.in_file)
74-
data = img.get_data()
75-
affine = img.get_affine()
76-
77-
## Load the gradient strengths and directions
78-
bvals = np.loadtxt(self.inputs.bvals)
79-
gradients = np.loadtxt(self.inputs.bvecs).T
80-
81-
## Place in Dipy's preferred format
82-
gtab = GradientTable(gradients)
83-
gtab.bvals = bvals
84-
85-
## Mask the data so that tensors are not fit for
86-
## unnecessary voxels
87-
mask = data[..., 0] > 50
88-
89-
## Fit the tensors to the data
90-
tenmodel = dti.TensorModel(gtab)
91-
tenfit = tenmodel.fit(data, mask)
92-
93-
## Calculate the mode of each voxel's tensor
94-
mode_data = tenfit.mode
172+
ten_fit = tensor_fitting(self.inputs.in_file, self.inputs.bvals, self.inputs.bvecs,
173+
self.inputs.mask_file)
95174

96175
## Write as a 3D Nifti image with the original affine
97-
img = nb.Nifti1Image(mode_data, affine)
176+
img = nb.Nifti1Image(tenfit.mode, affine)
98177
out_file = op.abspath(self._gen_outfilename())
99178
nb.save(img, out_file)
100179
iflogger.info('Tensor mode image saved as {i}'.format(i=out_file))
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from nipype.testing import assert_equal
3+
from nipype.interfaces.dipy.tensors import DTI
4+
5+
def test_DTI_inputs():
6+
input_map = dict(bvals=dict(mandatory=True,
7+
),
8+
bvecs=dict(mandatory=True,
9+
),
10+
in_file=dict(mandatory=True,
11+
),
12+
mask_file=dict(mandatory=False,
13+
),
14+
out_filename=dict(genfile=True,
15+
),
16+
)
17+
inputs = DTI.input_spec()
18+
19+
for key, metadata in input_map.items():
20+
for metakey, value in metadata.items():
21+
yield assert_equal, getattr(inputs.traits()[key], metakey), value
22+
23+
def test_DTI_outputs():
24+
output_map = dict(out_file=dict(),
25+
)
26+
outputs = DTI.output_spec()
27+
28+
for key, metadata in output_map.items():
29+
for metakey, value in metadata.items():
30+
yield assert_equal, getattr(outputs.traits()[key], metakey), value
31+

nipype/interfaces/dipy/tests/test_auto_TensorMode.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ def test_TensorMode_inputs():
99
),
1010
in_file=dict(mandatory=True,
1111
),
12+
mask_file=dict(mandatory=False,
13+
),
1214
out_filename=dict(genfile=True,
1315
),
1416
)

0 commit comments

Comments
 (0)