Skip to content

Commit a4b1867

Browse files
committed
Merge pull request #1291 from BRAINSia/wrapDenoiseImage
ENH: Wrapper for DenoiseImage
2 parents 4ae3ec1 + da38676 commit a4b1867

File tree

6 files changed

+155
-10
lines changed

6 files changed

+155
-10
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: Provides a Nipype wrapper for ANTs DenoiseImage (https://github.com/nipy/nipype/pull/1291)
45
* FIX: Minor bugfix logging hash differences (https://github.com/nipy/nipype/pull/1298)
56
* FIX: Use released Prov python library (https://github.com/nipy/nipype/pull/1279)
67
* ENH: Support for Python 3 (https://github.com/nipy/nipype/pull/1221)

doc/devel/cmd_interface_devel.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,13 @@ output_name (optional)
198198
name of the output (if this is not set same name as the input will be
199199
assumed)
200200

201-
keep_extension (optional - not used)
202-
if you want the extension from the input to be kept
201+
keep_extension (optional)
202+
if you want the extension from the input or name_template to be kept. The
203+
name_template extension always overrides the input extension.
203204

204205
In addition one can add functionality to your class or base class, to allow
205-
changing extensions specific to package or interface
206+
changing extensions specific to package or interface. This overload function is
207+
trigerred only if keep_extension is not defined.
206208

207209
.. testcode::
208210

nipype/interfaces/ants/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
# Segmentation Programs
1414
from .segmentation import (Atropos, LaplacianThickness, N4BiasFieldCorrection, JointFusion, CorticalThickness,
15-
BrainExtraction)
15+
BrainExtraction, DenoiseImage)
1616

1717
# Visualization Programs
1818
from .visualization import ConvertScalarImageToRGB, CreateTiledMosaic

nipype/interfaces/ants/segmentation.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,3 +795,77 @@ def _list_outputs(self):
795795
outputs['output_label_image'] = os.path.abspath(
796796
self.inputs.output_label_image)
797797
return outputs
798+
799+
800+
class DenoiseImageInputSpec(ANTSCommandInputSpec):
801+
dimension = traits.Enum(2, 3, 4, argstr='-d %d', usedefault=False,
802+
desc='This option forces the image to be treated '
803+
'as a specified-dimensional image. If not '
804+
'specified, the program tries to infer the '
805+
'dimensionality from the input image.')
806+
input_image = File(exists=True, argstr="-i %s", mandatory=True,
807+
desc='A scalar image is expected as input for noise correction.')
808+
noise_model = traits.Enum('Gaussian', 'Rician', argstr='-n %s', usedefault=True,
809+
desc=('Employ a Rician or Gaussian noise model.'))
810+
shrink_factor = traits.Int(default_value=1, usedefault=True, argstr='-s %s',
811+
desc=('Running noise correction on large images can '
812+
'be time consuming. To lessen computation time, '
813+
'the input image can be resampled. The shrink '
814+
'factor, specified as a single integer, describes '
815+
'this resampling. Shrink factor = 1 is the default.'))
816+
output_image = File(argstr="-o %s", name_source=['input_image'], hash_files=False,
817+
keep_extension=True, name_template='%s_noise_corrected',
818+
desc='The output consists of the noise corrected '
819+
'version of the input image.')
820+
save_noise = traits.Bool(False, mandatory=True, usedefault=True,
821+
desc=('True if the estimated noise should be saved '
822+
'to file.'), xor=['noise_image'])
823+
noise_image = File(name_source=['input_image'], hash_files=False,
824+
keep_extension=True, name_template='%s_noise',
825+
desc='Filename for the estimated noise.')
826+
verbose = traits.Bool(False, argstr="-v", desc=('Verbose output.'))
827+
828+
829+
class DenoiseImageOutputSpec(TraitedSpec):
830+
output_image = File(exists=True)
831+
noise_image = File(exists=True)
832+
833+
834+
class DenoiseImage(ANTSCommand):
835+
"""
836+
Examples
837+
--------
838+
>>> import copy
839+
>>> from nipype.interfaces.ants import DenoiseImage
840+
>>> denoise = DenoiseImage()
841+
>>> denoise.inputs.dimension = 3
842+
>>> denoise.inputs.input_image = 'im1.nii'
843+
>>> denoise.cmdline
844+
'DenoiseImage -d 3 -i im1.nii -n Gaussian -o im1_noise_corrected.nii -s 1'
845+
846+
>>> denoise_2 = copy.deepcopy(denoise)
847+
>>> denoise_2.inputs.output_image = 'output_corrected_image.nii.gz'
848+
>>> denoise_2.inputs.noise_model = 'Rician'
849+
>>> denoise_2.inputs.shrink_factor = 2
850+
>>> denoise_2.cmdline
851+
'DenoiseImage -d 3 -i im1.nii -n Rician -o output_corrected_image.nii.gz -s 2'
852+
853+
>>> denoise_3 = DenoiseImage()
854+
>>> denoise_3.inputs.input_image = 'im1.nii'
855+
>>> denoise_3.inputs.save_noise = True
856+
>>> denoise_3.cmdline
857+
'DenoiseImage -i im1.nii -n Gaussian -o [ im1_noise_corrected.nii, im1_noise.nii ] -s 1'
858+
"""
859+
input_spec = DenoiseImageInputSpec
860+
output_spec = DenoiseImageOutputSpec
861+
_cmd = 'DenoiseImage'
862+
863+
def _format_arg(self, name, trait_spec, value):
864+
if ((name == 'output_image') and
865+
(self.inputs.save_noise or isdefined(self.inputs.noise_image))):
866+
newval = '[ %s, %s ]' % (self._filename_from_source('output_image'),
867+
self._filename_from_source('noise_image'))
868+
return trait_spec.argstr % newval
869+
870+
return super(DenoiseImage,
871+
self)._format_arg(name, trait_spec, value)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from ....testing import assert_equal
3+
from ..segmentation import DenoiseImage
4+
5+
6+
def test_DenoiseImage_inputs():
7+
input_map = dict(args=dict(argstr='%s',
8+
),
9+
dimension=dict(argstr='-d %d',
10+
usedefault=False,
11+
),
12+
environ=dict(nohash=True,
13+
usedefault=True,
14+
),
15+
ignore_exception=dict(nohash=True,
16+
usedefault=True,
17+
),
18+
input_image=dict(argstr='-i %s',
19+
mandatory=True,
20+
),
21+
noise_image=dict(hash_files=False,
22+
keep_extension=True,
23+
name_source=['input_image'],
24+
name_template='%s_noise',
25+
),
26+
noise_model=dict(argstr='-n %s',
27+
usedefault=True,
28+
),
29+
num_threads=dict(nohash=True,
30+
usedefault=True,
31+
),
32+
output_image=dict(argstr='-o %s',
33+
hash_files=False,
34+
keep_extension=True,
35+
name_source=['input_image'],
36+
name_template='%s_noise_corrected',
37+
),
38+
save_noise=dict(mandatory=True,
39+
usedefault=True,
40+
xor=['noise_image'],
41+
),
42+
shrink_factor=dict(argstr='-s %s',
43+
usedefault=True,
44+
),
45+
terminal_output=dict(nohash=True,
46+
),
47+
verbose=dict(argstr='-v',
48+
),
49+
)
50+
inputs = DenoiseImage.input_spec()
51+
52+
for key, metadata in list(input_map.items()):
53+
for metakey, value in list(metadata.items()):
54+
yield assert_equal, getattr(inputs.traits()[key], metakey), value
55+
56+
57+
def test_DenoiseImage_outputs():
58+
output_map = dict(noise_image=dict(),
59+
output_image=dict(),
60+
)
61+
outputs = DenoiseImage.output_spec()
62+
63+
for key, metadata in list(output_map.items()):
64+
for metakey, value in list(metadata.items()):
65+
yield assert_equal, getattr(outputs.traits()[key], metakey), value

nipype/interfaces/base.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,7 @@ def _filename_from_source(self, name, chain=None):
15601560

15611561
trait_spec = self.inputs.trait(name)
15621562
retval = getattr(self.inputs, name)
1563-
1563+
source_ext = None
15641564
if not isdefined(retval) or "%s" in retval:
15651565
if not trait_spec.name_source:
15661566
return retval
@@ -1589,7 +1589,7 @@ def _filename_from_source(self, name, chain=None):
15891589

15901590
# special treatment for files
15911591
try:
1592-
_, base, _ = split_filename(source)
1592+
_, base, source_ext = split_filename(source)
15931593
except AttributeError:
15941594
base = source
15951595
else:
@@ -1598,14 +1598,17 @@ def _filename_from_source(self, name, chain=None):
15981598

15991599
chain.append(name)
16001600
base = self._filename_from_source(ns, chain)
1601+
if isdefined(base):
1602+
_, _, source_ext = split_filename(base)
16011603

16021604
chain = None
16031605
retval = name_template % base
16041606
_, _, ext = split_filename(retval)
1605-
if trait_spec.keep_extension and ext:
1606-
return retval
1607-
return self._overload_extension(retval, name)
1608-
1607+
if trait_spec.keep_extension and (ext or source_ext):
1608+
if (ext is None or not ext) and source_ext:
1609+
retval = retval + source_ext
1610+
else:
1611+
retval = self._overload_extension(retval, name)
16091612
return retval
16101613

16111614
def _gen_filename(self, name):

0 commit comments

Comments
 (0)