Skip to content

Commit 7d8fdfe

Browse files
committed
ENH: Add --rescale-intensities and name_source to N4BiasFieldCorrection
1 parent 23300e9 commit 7d8fdfe

File tree

2 files changed

+47
-41
lines changed

2 files changed

+47
-41
lines changed

nipype/interfaces/ants/segmentation.py

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import os
99
from ...external.due import BibTeX
10-
from ...utils.filemanip import split_filename, copyfile, which
10+
from ...utils.filemanip import split_filename, copyfile, which, fname_presuffix
1111
from ..base import TraitedSpec, File, traits, InputMultiPath, OutputMultiPath, isdefined
1212
from .base import ANTSCommand, ANTSCommandInputSpec
1313

@@ -274,18 +274,20 @@ class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec):
274274
argstr='--input-image %s',
275275
mandatory=True,
276276
desc=('input for bias correction. Negative values or values close to '
277-
'zero should be processed prior to correction'))
277+
'zero should be processed prior to correction'))
278278
mask_image = File(
279279
argstr='--mask-image %s',
280280
desc=('image to specify region to perform final bias correction in'))
281281
weight_image = File(
282282
argstr='--weight-image %s',
283283
desc=('image for relative weighting (e.g. probability map of the white '
284-
'matter) of voxels during the B-spline fitting. '))
284+
'matter) of voxels during the B-spline fitting. '))
285285
output_image = traits.Str(
286286
argstr='--output %s',
287287
desc='output file name',
288-
genfile=True,
288+
name_source=['input_image'],
289+
name_template='%s_corrected',
290+
keep_extension=True,
289291
hash_files=False)
290292
bspline_fitting_distance = traits.Float(argstr="--bspline-fitting %s")
291293
bspline_order = traits.Int(requires=['bspline_fitting_distance'])
@@ -306,6 +308,14 @@ class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec):
306308
usedefault=True,
307309
desc='copy headers of the original image into the '
308310
'output (corrected) file')
311+
rescale_intensities = traits.Bool(
312+
False, usedefault=True, argstr='-r',
313+
desc="""\
314+
At each iteration, a new intensity mapping is calculated and applied but there
315+
is nothing which constrains the new intensity range to be within certain values.
316+
The result is that the range can "drift" from the original at each iteration.
317+
This option rescales to the [min,max] range of the original image intensities
318+
within the user-specified mask.""")
309319

310320

311321
class N4BiasFieldCorrectionOutputSpec(TraitedSpec):
@@ -314,7 +324,10 @@ class N4BiasFieldCorrectionOutputSpec(TraitedSpec):
314324

315325

316326
class N4BiasFieldCorrection(ANTSCommand):
317-
"""N4 is a variant of the popular N3 (nonparameteric nonuniform normalization)
327+
"""
328+
Bias field correction.
329+
330+
N4 is a variant of the popular N3 (nonparameteric nonuniform normalization)
318331
retrospective bias correction algorithm. Based on the assumption that the
319332
corruption of the low frequency bias field can be modeled as a convolution of
320333
the intensity histogram by a Gaussian, the basic algorithmic protocol is to
@@ -373,28 +386,9 @@ class N4BiasFieldCorrection(ANTSCommand):
373386
input_spec = N4BiasFieldCorrectionInputSpec
374387
output_spec = N4BiasFieldCorrectionOutputSpec
375388

376-
def _gen_filename(self, name):
377-
if name == 'output_image':
378-
output = self.inputs.output_image
379-
if not isdefined(output):
380-
_, name, ext = split_filename(self.inputs.input_image)
381-
output = name + '_corrected' + ext
382-
return output
383-
384-
if name == 'bias_image':
385-
output = self.inputs.bias_image
386-
if not isdefined(output):
387-
_, name, ext = split_filename(self.inputs.input_image)
388-
output = name + '_bias' + ext
389-
return output
390-
return None
391-
392389
def _format_arg(self, name, trait_spec, value):
393-
if ((name == 'output_image') and
394-
(self.inputs.save_bias or isdefined(self.inputs.bias_image))):
395-
bias_image = self._gen_filename('bias_image')
396-
output = self._gen_filename('output_image')
397-
newval = '[ %s, %s ]' % (output, bias_image)
390+
if name == 'output_image' and getattr(self, '_out_bias_file', None):
391+
newval = '[ %s, %s ]' % (value, getattr(self, '_out_bias_file'))
398392
return trait_spec.argstr % newval
399393

400394
if name == 'bspline_fitting_distance':
@@ -418,34 +412,40 @@ def _format_arg(self, name, trait_spec, value):
418412
name, trait_spec, value)
419413

420414
def _parse_inputs(self, skip=None):
421-
if skip is None:
422-
skip = []
423-
skip += ['save_bias', 'bias_image']
415+
skip = (skip or []) + ['save_bias', 'bias_image']
416+
if self.inputs.save_bias or isdefined(self.inputs.bias_image):
417+
bias_image = self.inputs.bias_image
418+
if not isdefined(bias_image):
419+
bias_image = fname_presuffix(os.path.basename(self.inputs.input_image),
420+
suffix='_bias')
421+
setattr(self, '_out_bias_file', bias_image)
422+
else:
423+
try:
424+
delattr(self, '_out_bias_file')
425+
except AttributeError:
426+
pass
424427
return super(N4BiasFieldCorrection, self)._parse_inputs(skip=skip)
425428

426429
def _list_outputs(self):
427-
outputs = self._outputs().get()
428-
outputs['output_image'] = os.path.abspath(
429-
self._gen_filename('output_image'))
430-
431-
if self.inputs.save_bias or isdefined(self.inputs.bias_image):
432-
outputs['bias_image'] = os.path.abspath(
433-
self._gen_filename('bias_image'))
430+
outputs = super(N4BiasFieldCorrection, self)._list_outputs()
431+
bias_image = getattr(self, '_out_bias_file', None)
432+
if bias_image:
433+
outputs['bias_image'] = bias_image
434434
return outputs
435435

436436
def _run_interface(self, runtime, correct_return_codes=(0, )):
437437
runtime = super(N4BiasFieldCorrection, self)._run_interface(
438438
runtime, correct_return_codes)
439439

440440
if self.inputs.copy_header and runtime.returncode in correct_return_codes:
441-
self._copy_header(self._gen_filename('output_image'))
442-
if self.inputs.save_bias or isdefined(self.inputs.bias_image):
443-
self._copy_header(self._gen_filename('bias_image'))
441+
self._copy_header(self.inputs.output_image)
442+
if getattr(self, '_out_bias_file', None):
443+
self._copy_header(getattr(self, '_out_bias_file'))
444444

445445
return runtime
446446

447447
def _copy_header(self, fname):
448-
"""Copy header from input image to an output image"""
448+
"""Copy header from input image to an output image."""
449449
import nibabel as nb
450450
in_img = nb.load(self.inputs.input_image)
451451
out_img = nb.load(fname, mmap=False)

nipype/interfaces/ants/tests/test_auto_N4BiasFieldCorrection.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,14 @@ def test_N4BiasFieldCorrection_inputs():
4141
),
4242
output_image=dict(
4343
argstr='--output %s',
44-
genfile=True,
4544
hash_files=False,
45+
keep_extension=True,
46+
name_source=['input_image'],
47+
name_template='%s_corrected',
48+
),
49+
rescale_intensities=dict(
50+
argstr='-r',
51+
usedefault=True,
4652
),
4753
save_bias=dict(
4854
mandatory=True,

0 commit comments

Comments
 (0)