77
88import os
99from ...external .due import BibTeX
10- from ...utils .filemanip import split_filename , copyfile , which
10+ from ...utils .filemanip import split_filename , copyfile , which , fname_presuffix
1111from ..base import TraitedSpec , File , traits , InputMultiPath , OutputMultiPath , isdefined
1212from .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,15 @@ 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' , min_ver = '2.1.0' ,
313+ desc = """\
314+ [NOTE: Only ANTs>=2.1.0]
315+ At each iteration, a new intensity mapping is calculated and applied but there
316+ is nothing which constrains the new intensity range to be within certain values.
317+ The result is that the range can "drift" from the original at each iteration.
318+ This option rescales to the [min,max] range of the original image intensities
319+ within the user-specified mask.""" )
309320
310321
311322class N4BiasFieldCorrectionOutputSpec (TraitedSpec ):
@@ -314,7 +325,10 @@ class N4BiasFieldCorrectionOutputSpec(TraitedSpec):
314325
315326
316327class N4BiasFieldCorrection (ANTSCommand ):
317- """N4 is a variant of the popular N3 (nonparameteric nonuniform normalization)
328+ """
329+ Bias field correction.
330+
331+ N4 is a variant of the popular N3 (nonparameteric nonuniform normalization)
318332 retrospective bias correction algorithm. Based on the assumption that the
319333 corruption of the low frequency bias field can be modeled as a convolution of
320334 the intensity histogram by a Gaussian, the basic algorithmic protocol is to
@@ -373,28 +387,14 @@ class N4BiasFieldCorrection(ANTSCommand):
373387 input_spec = N4BiasFieldCorrectionInputSpec
374388 output_spec = N4BiasFieldCorrectionOutputSpec
375389
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
390+ def __init__ (self , * args , ** kwargs ):
391+ """Instantiate the N4BiasFieldCorrection interface."""
392+ self ._out_bias_file = None
393+ super (N4BiasFieldCorrection , self ).__init__ (* args , ** kwargs )
391394
392395 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 )
396+ if name == 'output_image' and self ._out_bias_file :
397+ newval = '[ %s, %s ]' % (value , self ._out_bias_file )
398398 return trait_spec .argstr % newval
399399
400400 if name == 'bspline_fitting_distance' :
@@ -418,38 +418,35 @@ def _format_arg(self, name, trait_spec, value):
418418 name , trait_spec , value )
419419
420420 def _parse_inputs (self , skip = None ):
421- if skip is None :
422- skip = []
423- skip += ['save_bias' , 'bias_image' ]
421+ skip = (skip or []) + ['save_bias' , 'bias_image' ]
422+ self ._out_bias_file = None
423+ if self .inputs .save_bias or isdefined (self .inputs .bias_image ):
424+ bias_image = self .inputs .bias_image
425+ if not isdefined (bias_image ):
426+ bias_image = fname_presuffix (os .path .basename (self .inputs .input_image ),
427+ suffix = '_bias' )
428+ self ._out_bias_file = bias_image
424429 return super (N4BiasFieldCorrection , self )._parse_inputs (skip = skip )
425430
426431 def _list_outputs (self ):
427- outputs = self ._outputs ().get ()
428- outputs ['output_image' ] = os .path .abspath (
429- self ._gen_filename ('output_image' ))
432+ outputs = super (N4BiasFieldCorrection , self )._list_outputs ()
430433
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' ))
434- return outputs
434+ # Fix headers
435+ if self .inputs .copy_header :
436+ self ._copy_header (outputs ['output_image' ])
435437
436- def _run_interface (self , runtime , correct_return_codes = (0 , )):
437- runtime = super (N4BiasFieldCorrection , self )._run_interface (
438- runtime , correct_return_codes )
439-
440- 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' ))
444-
445- return runtime
438+ if self ._out_bias_file :
439+ outputs ['bias_image' ] = os .path .abspath (self ._out_bias_file )
440+ if self .inputs .copy_header :
441+ self ._copy_header (outputs ['bias_image' ])
442+ return outputs
446443
447444 def _copy_header (self , fname ):
448- """Copy header from input image to an output image"""
445+ """Copy header from input image to an output image. """
449446 import nibabel as nb
450447 in_img = nb .load (self .inputs .input_image )
451448 out_img = nb .load (fname , mmap = False )
452- new_img = out_img .__class__ (out_img .get_data (), in_img .affine ,
449+ new_img = out_img .__class__ (out_img .get_fdata (), in_img .affine ,
453450 in_img .header )
454451 new_img .set_data_dtype (out_img .get_data_dtype ())
455452 new_img .to_filename (fname )
0 commit comments