7
7
8
8
import os
9
9
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
11
11
from ..base import TraitedSpec , File , traits , InputMultiPath , OutputMultiPath , isdefined
12
12
from .base import ANTSCommand , ANTSCommandInputSpec
13
13
@@ -274,18 +274,20 @@ class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec):
274
274
argstr = '--input-image %s' ,
275
275
mandatory = True ,
276
276
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' ))
278
278
mask_image = File (
279
279
argstr = '--mask-image %s' ,
280
280
desc = ('image to specify region to perform final bias correction in' ))
281
281
weight_image = File (
282
282
argstr = '--weight-image %s' ,
283
283
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. ' ))
285
285
output_image = traits .Str (
286
286
argstr = '--output %s' ,
287
287
desc = 'output file name' ,
288
- genfile = True ,
288
+ name_source = ['input_image' ],
289
+ name_template = '%s_corrected' ,
290
+ keep_extension = True ,
289
291
hash_files = False )
290
292
bspline_fitting_distance = traits .Float (argstr = "--bspline-fitting %s" )
291
293
bspline_order = traits .Int (requires = ['bspline_fitting_distance' ])
@@ -306,6 +308,15 @@ class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec):
306
308
usedefault = True ,
307
309
desc = 'copy headers of the original image into the '
308
310
'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.""" )
309
320
310
321
311
322
class N4BiasFieldCorrectionOutputSpec (TraitedSpec ):
@@ -314,7 +325,10 @@ class N4BiasFieldCorrectionOutputSpec(TraitedSpec):
314
325
315
326
316
327
class 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)
318
332
retrospective bias correction algorithm. Based on the assumption that the
319
333
corruption of the low frequency bias field can be modeled as a convolution of
320
334
the intensity histogram by a Gaussian, the basic algorithmic protocol is to
@@ -373,28 +387,14 @@ class N4BiasFieldCorrection(ANTSCommand):
373
387
input_spec = N4BiasFieldCorrectionInputSpec
374
388
output_spec = N4BiasFieldCorrectionOutputSpec
375
389
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 )
391
394
392
395
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 )
398
398
return trait_spec .argstr % newval
399
399
400
400
if name == 'bspline_fitting_distance' :
@@ -418,38 +418,35 @@ def _format_arg(self, name, trait_spec, value):
418
418
name , trait_spec , value )
419
419
420
420
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
424
429
return super (N4BiasFieldCorrection , self )._parse_inputs (skip = skip )
425
430
426
431
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 ()
430
433
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' ])
435
437
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
446
443
447
444
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. """
449
446
import nibabel as nb
450
447
in_img = nb .load (self .inputs .input_image )
451
448
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 ,
453
450
in_img .header )
454
451
new_img .set_data_dtype (out_img .get_data_dtype ())
455
452
new_img .to_filename (fname )
0 commit comments