Skip to content

ENH: Wrapper for DenoiseImage #1291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Dec 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Next release
============

* ENH: Provides a Nipype wrapper for ANTs DenoiseImage (https://github.com/nipy/nipype/pull/1291)
* FIX: Minor bugfix logging hash differences (https://github.com/nipy/nipype/pull/1298)
* FIX: Use released Prov python library (https://github.com/nipy/nipype/pull/1279)
* ENH: Support for Python 3 (https://github.com/nipy/nipype/pull/1221)
Expand Down
8 changes: 5 additions & 3 deletions doc/devel/cmd_interface_devel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,13 @@ output_name (optional)
name of the output (if this is not set same name as the input will be
assumed)

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

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

.. testcode::

Expand Down
2 changes: 1 addition & 1 deletion nipype/interfaces/ants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

# Segmentation Programs
from .segmentation import (Atropos, LaplacianThickness, N4BiasFieldCorrection, JointFusion, CorticalThickness,
BrainExtraction)
BrainExtraction, DenoiseImage)

# Visualization Programs
from .visualization import ConvertScalarImageToRGB, CreateTiledMosaic
Expand Down
74 changes: 74 additions & 0 deletions nipype/interfaces/ants/segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,3 +795,77 @@ def _list_outputs(self):
outputs['output_label_image'] = os.path.abspath(
self.inputs.output_label_image)
return outputs


class DenoiseImageInputSpec(ANTSCommandInputSpec):
dimension = traits.Enum(2, 3, 4, argstr='-d %d', usedefault=False,
desc='This option forces the image to be treated '
'as a specified-dimensional image. If not '
'specified, the program tries to infer the '
'dimensionality from the input image.')
input_image = File(exists=True, argstr="-i %s", mandatory=True,
desc='A scalar image is expected as input for noise correction.')
noise_model = traits.Enum('Gaussian', 'Rician', argstr='-n %s', usedefault=True,
desc=('Employ a Rician or Gaussian noise model.'))
shrink_factor = traits.Int(default_value=1, usedefault=True, argstr='-s %s',
desc=('Running noise correction on large images can '
'be time consuming. To lessen computation time, '
'the input image can be resampled. The shrink '
'factor, specified as a single integer, describes '
'this resampling. Shrink factor = 1 is the default.'))
output_image = File(argstr="-o %s", name_source=['input_image'], hash_files=False,
keep_extension=True, name_template='%s_noise_corrected',
desc='The output consists of the noise corrected '
'version of the input image.')
save_noise = traits.Bool(False, mandatory=True, usedefault=True,
desc=('True if the estimated noise should be saved '
'to file.'), xor=['noise_image'])
noise_image = File(name_source=['input_image'], hash_files=False,
keep_extension=True, name_template='%s_noise',
desc='Filename for the estimated noise.')
verbose = traits.Bool(False, argstr="-v", desc=('Verbose output.'))


class DenoiseImageOutputSpec(TraitedSpec):
output_image = File(exists=True)
noise_image = File(exists=True)


class DenoiseImage(ANTSCommand):
"""
Examples
--------
>>> import copy
>>> from nipype.interfaces.ants import DenoiseImage
>>> denoise = DenoiseImage()
>>> denoise.inputs.dimension = 3
>>> denoise.inputs.input_image = 'im1.nii'
>>> denoise.cmdline
'DenoiseImage -d 3 -i im1.nii -n Gaussian -o im1_noise_corrected.nii -s 1'

>>> denoise_2 = copy.deepcopy(denoise)
>>> denoise_2.inputs.output_image = 'output_corrected_image.nii.gz'
>>> denoise_2.inputs.noise_model = 'Rician'
>>> denoise_2.inputs.shrink_factor = 2
>>> denoise_2.cmdline
'DenoiseImage -d 3 -i im1.nii -n Rician -o output_corrected_image.nii.gz -s 2'

>>> denoise_3 = DenoiseImage()
>>> denoise_3.inputs.input_image = 'im1.nii'
>>> denoise_3.inputs.save_noise = True
>>> denoise_3.cmdline
'DenoiseImage -i im1.nii -n Gaussian -o [ im1_noise_corrected.nii, im1_noise.nii ] -s 1'
"""
input_spec = DenoiseImageInputSpec
output_spec = DenoiseImageOutputSpec
_cmd = 'DenoiseImage'

def _format_arg(self, name, trait_spec, value):
if ((name == 'output_image') and
(self.inputs.save_noise or isdefined(self.inputs.noise_image))):
newval = '[ %s, %s ]' % (self._filename_from_source('output_image'),
self._filename_from_source('noise_image'))
return trait_spec.argstr % newval

return super(DenoiseImage,
self)._format_arg(name, trait_spec, value)
65 changes: 65 additions & 0 deletions nipype/interfaces/ants/tests/test_auto_DenoiseImage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from ....testing import assert_equal
from ..segmentation import DenoiseImage


def test_DenoiseImage_inputs():
input_map = dict(args=dict(argstr='%s',
),
dimension=dict(argstr='-d %d',
usedefault=False,
),
environ=dict(nohash=True,
usedefault=True,
),
ignore_exception=dict(nohash=True,
usedefault=True,
),
input_image=dict(argstr='-i %s',
mandatory=True,
),
noise_image=dict(hash_files=False,
keep_extension=True,
name_source=['input_image'],
name_template='%s_noise',
),
noise_model=dict(argstr='-n %s',
usedefault=True,
),
num_threads=dict(nohash=True,
usedefault=True,
),
output_image=dict(argstr='-o %s',
hash_files=False,
keep_extension=True,
name_source=['input_image'],
name_template='%s_noise_corrected',
),
save_noise=dict(mandatory=True,
usedefault=True,
xor=['noise_image'],
),
shrink_factor=dict(argstr='-s %s',
usedefault=True,
),
terminal_output=dict(nohash=True,
),
verbose=dict(argstr='-v',
),
)
inputs = DenoiseImage.input_spec()

for key, metadata in list(input_map.items()):
for metakey, value in list(metadata.items()):
yield assert_equal, getattr(inputs.traits()[key], metakey), value


def test_DenoiseImage_outputs():
output_map = dict(noise_image=dict(),
output_image=dict(),
)
outputs = DenoiseImage.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
yield assert_equal, getattr(outputs.traits()[key], metakey), value
15 changes: 9 additions & 6 deletions nipype/interfaces/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1560,7 +1560,7 @@ def _filename_from_source(self, name, chain=None):

trait_spec = self.inputs.trait(name)
retval = getattr(self.inputs, name)

source_ext = None
if not isdefined(retval) or "%s" in retval:
if not trait_spec.name_source:
return retval
Expand Down Expand Up @@ -1589,7 +1589,7 @@ def _filename_from_source(self, name, chain=None):

# special treatment for files
try:
_, base, _ = split_filename(source)
_, base, source_ext = split_filename(source)
except AttributeError:
base = source
else:
Expand All @@ -1598,14 +1598,17 @@ def _filename_from_source(self, name, chain=None):

chain.append(name)
base = self._filename_from_source(ns, chain)
if isdefined(base):
_, _, source_ext = split_filename(base)

chain = None
retval = name_template % base
_, _, ext = split_filename(retval)
if trait_spec.keep_extension and ext:
return retval
return self._overload_extension(retval, name)

if trait_spec.keep_extension and (ext or source_ext):
if (ext is None or not ext) and source_ext:
retval = retval + source_ext
else:
retval = self._overload_extension(retval, name)
return retval

def _gen_filename(self, name):
Expand Down