Skip to content

Commit c0280ad

Browse files
committed
Merge remote-tracking branch 'origin/pr/910'
Conflicts: CHANGES
2 parents 88b08c0 + 6dab71d commit c0280ad

File tree

9 files changed

+417
-1
lines changed

9 files changed

+417
-1
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 interfaces: Tkregister2 (for conversion of fsl style matrices to freesurfer format),
56
MRIPretess
67
* ENH: New FSL interfaces: WarpPoints, WarpPointsToStd, EpiReg, ProbTrackX2, WarpUtils, ConvertWarp

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

nipype/workflows/dmri/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
import camino, mrtrix, fsl
1+
import camino, mrtrix, fsl, dipy
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 denoise import nlmeans_pipeline

0 commit comments

Comments
 (0)