Skip to content

Commit c7a8b84

Browse files
committed
ENH:new preprocess interfaces in dipy (Denoise, Resample)
1 parent ba986a9 commit c7a8b84

File tree

5 files changed

+264
-0
lines changed

5 files changed

+264
-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 dipy interfaces: Denoise, Resample
45
* ENH: New Freesurfer interface: MRIPretess
56
* ENH: New miscelaneous interface: AddCSVRow
67
* ENH: FUGUE interface has been refactored to use the name_template system, 3 examples

nipype/interfaces/dipy/__init__.py

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

nipype/interfaces/dipy/preprocess.py

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# @Author: oesteban
4+
# @Date: 2014-09-01 10:33:35
5+
# @Last Modified by: oesteban
6+
# @Last Modified time: 2014-09-01 11:27:55
7+
from nipype.interfaces.base import (traits, TraitedSpec, BaseInterface,
8+
File, isdefined)
9+
from nipype.utils.filemanip import split_filename
10+
import os.path as op
11+
import nibabel as nb
12+
import numpy as np
13+
from nipype.utils.misc import package_check
14+
import warnings
15+
from ... import logging
16+
iflogger = logging.getLogger('interface')
17+
18+
have_dipy = True
19+
try:
20+
package_check('dipy', version='0.6.0')
21+
except Exception, e:
22+
have_dipy = False
23+
else:
24+
from dipy.align.aniso2iso import resample
25+
from dipy.core.gradients import GradientTable
26+
from dipy.denoise.nlmeans import nlmeans
27+
28+
29+
class ResampleInputSpec(TraitedSpec):
30+
in_file = File(exists=True, mandatory=True,
31+
desc='The input 4D diffusion-weighted image file')
32+
vox_size = traits.Tuple(traits.Float, traits.Float, traits.Float, desc=('specify the new '
33+
'voxel zooms. If no vox_size is set, then isotropic regridding will '
34+
'be performed, with spacing equal to the smallest current zoom.'))
35+
interp = traits.Int(1, mandatory=True, usedefault=True, desc=('order of the interpolator'
36+
'(0 = nearest, 1 = linear, etc.'))
37+
38+
39+
class ResampleOutputSpec(TraitedSpec):
40+
out_file = File(exists=True)
41+
42+
43+
class Resample(BaseInterface):
44+
"""
45+
An interface to reslicing diffusion datasets.
46+
See http://nipy.org/dipy/examples_built/reslice_datasets.html#example-reslice-datasets.
47+
48+
Example
49+
-------
50+
51+
>>> import nipype.interfaces.dipy as dipy
52+
>>> reslice = dipy.Resample()
53+
>>> reslice.inputs.in_file = 'diffusion.nii'
54+
>>> reslice.run() # doctest: +SKIP
55+
"""
56+
input_spec = ResampleInputSpec
57+
output_spec = ResampleOutputSpec
58+
59+
def _run_interface(self, runtime):
60+
order = self.inputs.interp
61+
vox_size = None
62+
63+
if isdefined(self.inputs.vox_size):
64+
vox_size = self.inputs.vox_size
65+
66+
out_file = op.abspath(self._gen_outfilename())
67+
resample_proxy(self.inputs.in_file, order=order,
68+
new_zooms=vox_size, out_file=out_file)
69+
70+
iflogger.info('Resliced image saved as {i}'.format(i=out_file))
71+
return runtime
72+
73+
def _list_outputs(self):
74+
outputs = self._outputs().get()
75+
outputs['out_file'] = op.abspath(self._gen_outfilename())
76+
return outputs
77+
78+
def _gen_outfilename(self):
79+
fname, fext = op.splitext(op.basename(self.inputs.in_file))
80+
if fext == '.gz':
81+
fname, fext2 = op.splitext(fname)
82+
fext = fext2 + fext
83+
return op.abspath('%s_reslice%s' % (fname, fext))
84+
85+
86+
class DenoiseInputSpec(TraitedSpec):
87+
in_file = File(exists=True, mandatory=True,
88+
desc='The input 4D diffusion-weighted image file')
89+
in_mask = File(exists=True, desc='brain mask')
90+
noise_model = traits.Enum('rician', 'gaussian', mandatory=True, usedefault=True,
91+
desc=('noise distribution model'))
92+
93+
94+
class DenoiseOutputSpec(TraitedSpec):
95+
out_file = File(exists=True)
96+
97+
98+
class Denoise(BaseInterface):
99+
"""
100+
An interface to denoising diffusion datasets [Coupe2008]_.
101+
See http://nipy.org/dipy/examples_built/denoise_nlmeans.html#example-denoise-nlmeans.
102+
103+
.. [Coupe2008] P. Coupe, P. Yger, S. Prima, P. Hellier, C. Kervrann, C. Barillot,
104+
`An Optimized Blockwise Non Local Means Denoising Filter for 3D Magnetic Resonance Images
105+
<http://dx.doi.org/10.1109%2FTMI.2007.906087>`_,
106+
IEEE Transactions on Medical Imaging, 27(4):425-441, 2008.
107+
108+
Example
109+
-------
110+
111+
>>> import nipype.interfaces.dipy as dipy
112+
>>> denoise = dipy.Denoise()
113+
>>> denoise.inputs.in_file = 'diffusion.nii'
114+
>>> denoise.run() # doctest: +SKIP
115+
"""
116+
input_spec = DenoiseInputSpec
117+
output_spec = DenoiseOutputSpec
118+
119+
def _run_interface(self, runtime):
120+
out_file = op.abspath(self._gen_outfilename())
121+
122+
mask = None
123+
if isdefined(self.inputs.in_mask):
124+
mask = nb.load(self.inputs.in_mask).get_data()
125+
126+
nlmeans_proxy(self.inputs.in_file, in_mask=mask,
127+
rician=(self.inputs.noise_model=='rician'),
128+
out_file=out_file)
129+
iflogger.info('Denoised image saved as {i}'.format(i=out_file))
130+
return runtime
131+
132+
def _list_outputs(self):
133+
outputs = self._outputs().get()
134+
outputs['out_file'] = op.abspath(self._gen_outfilename())
135+
return outputs
136+
137+
def _gen_outfilename(self):
138+
fname, fext = op.splitext(op.basename(self.inputs.in_file))
139+
if fext == '.gz':
140+
fname, fext2 = op.splitext(fname)
141+
fext = fext2 + fext
142+
return op.abspath('%s_denoise%s' % (fname, fext))
143+
144+
def resample_proxy(in_file, order=3, new_zooms=None, out_file=None):
145+
"""
146+
Performs regridding of an image to set isotropic voxel sizes using dipy.
147+
"""
148+
149+
if out_file is None:
150+
fname, fext = op.splitext(op.basename(in_file))
151+
if fext == '.gz':
152+
fname, fext2 = op.splitext(fname)
153+
fext = fext2 + fext
154+
out_file = op.abspath('./%s_reslice%s' % (fname, fext))
155+
156+
img = nb.load(in_file)
157+
hdr = img.get_header().copy()
158+
data = img.get_data().astype(np.float32)
159+
affine = img.get_affine()
160+
im_zooms = hdr.get_zooms()[:3]
161+
162+
if new_zooms is None:
163+
minzoom = np.array(im_zooms).min()
164+
new_zooms = tuple(np.ones((3,)) * minzoom)
165+
166+
if np.all(im_zooms == new_zooms):
167+
return in_file
168+
169+
data2, affine2 = resample(data, affine, im_zooms, new_zooms, order=order)
170+
tmp_zooms = np.array(hdr.get_zooms())
171+
tmp_zooms[:3] = new_zooms[0]
172+
hdr.set_zooms(tuple(tmp_zooms))
173+
hdr.set_data_shape(data2.shape)
174+
hdr.set_xyzt_units('mm')
175+
nb.Nifti1Image(data2.astype(hdr.get_data_dtype()),
176+
affine2, hdr).to_filename(out_file)
177+
return out_file, new_zooms
178+
179+
180+
def nlmeans_proxy(in_file, in_mask=None, rician=True, out_file=None):
181+
"""
182+
Uses non-local means to denoise 4D datasets
183+
"""
184+
185+
if out_file is None:
186+
fname, fext = op.splitext(op.basename(in_file))
187+
if fext == '.gz':
188+
fname, fext2 = op.splitext(fname)
189+
fext = fext2 + fext
190+
out_file = op.abspath('./%s_denoise%s' % (fname, fext))
191+
192+
img = nb.load(in_file)
193+
hdr = img.get_header()
194+
data = img.get_data()
195+
aff = img.get_affine()
196+
197+
if in_mask is None:
198+
mask = data[..., 0] > 80
199+
else:
200+
mask = in_mask > 0
201+
202+
sigma = np.std(data[~mask])
203+
den = nlmeans(data, sigma=sigma, mask=mask)
204+
205+
nb.Nifti1Image(den.astype(hdr.get_data_dtype()), aff,
206+
hdr).to_filename(out_file)
207+
return out_file
208+
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 nipype.testing import assert_equal
3+
from nipype.interfaces.dipy.preprocess import Denoise
4+
5+
def test_Denoise_inputs():
6+
input_map = dict(in_file=dict(mandatory=True,
7+
),
8+
in_mask=dict(),
9+
noise_model=dict(mandatory=True,
10+
usedefault=True,
11+
),
12+
)
13+
inputs = Denoise.input_spec()
14+
15+
for key, metadata in input_map.items():
16+
for metakey, value in metadata.items():
17+
yield assert_equal, getattr(inputs.traits()[key], metakey), value
18+
19+
def test_Denoise_outputs():
20+
output_map = dict(out_file=dict(),
21+
)
22+
outputs = Denoise.output_spec()
23+
24+
for key, metadata in output_map.items():
25+
for metakey, value in metadata.items():
26+
yield assert_equal, getattr(outputs.traits()[key], metakey), value
27+
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 nipype.testing import assert_equal
3+
from nipype.interfaces.dipy.preprocess import Resample
4+
5+
def test_Resample_inputs():
6+
input_map = dict(in_file=dict(mandatory=True,
7+
),
8+
interp=dict(mandatory=True,
9+
usedefault=True,
10+
),
11+
vox_size=dict(),
12+
)
13+
inputs = Resample.input_spec()
14+
15+
for key, metadata in input_map.items():
16+
for metakey, value in metadata.items():
17+
yield assert_equal, getattr(inputs.traits()[key], metakey), value
18+
19+
def test_Resample_outputs():
20+
output_map = dict(out_file=dict(),
21+
)
22+
outputs = Resample.output_spec()
23+
24+
for key, metadata in output_map.items():
25+
for metakey, value in metadata.items():
26+
yield assert_equal, getattr(outputs.traits()[key], metakey), value
27+

0 commit comments

Comments
 (0)