File: dwibiascorrect

package info (click to toggle)
mrtrix3 3.0~rc3%2Bgit135-g2b8e7d0c2-5
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 34,548 kB
  • sloc: cpp: 117,101; python: 6,472; sh: 638; makefile: 231; xml: 39; ansic: 20
file content (146 lines) | stat: -rwxr-xr-x 8,397 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/python3

# Script that performs B1 field inhomogeneity correction for a DWI volume series
# Bias field is estimated using the mean b=0 image, and subsequently used to correct all volumes

# Make the corresponding MRtrix3 Python libraries available
import inspect, os, sys
lib_folder = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))), os.pardir, 'lib'))
if not os.path.isdir(lib_folder):
  sys.stderr.write('Unable to locate MRtrix3 Python libraries')
  sys.exit(1)
sys.path.insert(0, lib_folder)

from distutils.spawn import find_executable
from mrtrix3 import app, fsl, image, path, run

opt_N4BiasFieldCorrection = {
    's': ('4','shrink-factor applied to spatial dimensions'),
    'b':('[100,3]','[initial mesh resolution in mm, spline order] This value is optimised for human adult data and needs to be adjusted for rodent data.'),
    'c':('[1000,0.0]', '[numberOfIterations,convergenceThreshold]')}

app.init('Robert E. Smith (robert.smith@florey.edu.au)',
         'Perform B1 field inhomogeneity correction for a DWI volume series')
app.cmdline.addCitation('If using -fast option', 'Zhang, Y.; Brady, M. & Smith, S. Segmentation of brain MR images through a hidden Markov random field model and the expectation-maximization algorithm. IEEE Transactions on Medical Imaging, 2001, 20, 45-57', True)
app.cmdline.addCitation('If using -fast option', 'Smith, S. M.; Jenkinson, M.; Woolrich, M. W.; Beckmann, C. F.; Behrens, T. E.; Johansen-Berg, H.; Bannister, P. R.; De Luca, M.; Drobnjak, I.; Flitney, D. E.; Niazy, R. K.; Saunders, J.; Vickers, J.; Zhang, Y.; De Stefano, N.; Brady, J. M. & Matthews, P. M. Advances in functional and structural MR image analysis and implementation as FSL. NeuroImage, 2004, 23, S208-S219', True)
app.cmdline.addCitation('If using -ants option', 'Tustison, N.; Avants, B.; Cook, P.; Zheng, Y.; Egan, A.; Yushkevich, P. & Gee, J. N4ITK: Improved N3 Bias Correction. IEEE Transactions on Medical Imaging, 2010, 29, 1310-1320', True)
antsoptions = app.cmdline.add_argument_group('Options for ANTS N4BiasFieldCorrection')
for key in sorted(opt_N4BiasFieldCorrection):
  antsoptions.add_argument('-ants.'+key, metavar=opt_N4BiasFieldCorrection[key][0], help='N4BiasFieldCorrection option -%s. %s' % (key,opt_N4BiasFieldCorrection[key][1]))
  app.cmdline.flagMutuallyExclusiveOptions( [ '-ants.'+key, 'fsl' ] )
app.cmdline.add_argument('input',  help='The input image series to be corrected')
app.cmdline.add_argument('output', help='The output corrected image series')
options = app.cmdline.add_argument_group('Options for the dwibiascorrect script')
options.add_argument('-mask', help='Manually provide a mask image for bias field estimation')
options.add_argument('-bias', help='Output the estimated bias field')
options.add_argument('-ants', action='store_true', help='Use ANTS N4 to estimate the inhomogeneity field')
options.add_argument('-fsl', action='store_true', help='Use FSL FAST to estimate the inhomogeneity field')
app.cmdline.flagMutuallyExclusiveOptions( [ 'ants', 'fsl' ] )
options.add_argument('-grad', help='Pass the diffusion gradient table in MRtrix format')
options.add_argument('-fslgrad', nargs=2, metavar=('bvecs', 'bvals'), help='Pass the diffusion gradient table in FSL bvecs/bvals format')
app.cmdline.flagMutuallyExclusiveOptions( [ 'grad', 'fslgrad' ] )
app.parse()

if app.args.fsl:

  if app.isWindows():
    app.error('Script cannot run using FSL on Windows due to FSL dependency')

  fsl_path = os.environ.get('FSLDIR', '')
  if not fsl_path:
    app.error('Environment variable FSLDIR is not set; please run appropriate FSL configuration script')

  fast_cmd = fsl.exeName('fast')

  app.warn('Use of -fsl option in dwibiascorrect script is discouraged due to its strong dependence ' + \
           'on initial brain masking, and its inability to correct voxels outside of this mask.' + \
           'Use of the -ants option is recommended for quantitative DWI analyses.')

elif app.args.ants:

  if not find_executable('N4BiasFieldCorrection'):
    app.error('Could not find ANTS program N4BiasFieldCorrection; please check installation')

  for key in sorted(opt_N4BiasFieldCorrection):
    if hasattr(app.args, 'ants.'+key):
      val = getattr(app.args, 'ants.'+key)
      if val is not None:
        opt_N4BiasFieldCorrection[key] = (val, 'user defined')
  ants_options = ' '.join(['-%s %s' %(k, v[0]) for k, v in opt_N4BiasFieldCorrection.items()])
else:
  app.error('No bias field estimation algorithm specified')

grad_import_option = ''
if app.args.grad:
  grad_import_option = ' -grad ' + path.fromUser(app.args.grad, True)
elif app.args.fslgrad:
  grad_import_option = ' -fslgrad ' + path.fromUser(app.args.fslgrad[0], True) + ' ' + path.fromUser(app.args.fslgrad[1], True)

app.checkOutputPath(app.args.output)
app.checkOutputPath(app.args.bias)

app.makeTempDir()

run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('in.mif', True) + grad_import_option)
if app.args.mask:
  run.command('mrconvert ' + path.fromUser(app.args.mask, True) + ' ' + path.toTemp('mask.mif', True))

app.gotoTempDir()

# Make sure it's actually a DWI that's been passed
dwi_header = image.Header('in.mif')
if len(dwi_header.size()) != 4:
  app.error('Input image must be a 4D image')
if 'dw_scheme' not in dwi_header.keyval():
  app.error('No valid DW gradient scheme provided or present in image header')
if len(dwi_header.keyval()['dw_scheme']) != dwi_header.size()[3]:
  app.error('DW gradient scheme contains different number of entries (' + str(len(dwi_header.keyval()['dw_scheme'])) + ' to number of volumes in DWIs (' + dwi_header.size()[3] + ')')

# Generate a brain mask if required, or check the mask if provided
if app.args.mask:
  if image.Header('mask.mif').size()[:3] != dwi_header.size()[:3]:
    app.error('Provided mask image does not match input DWI')
else:
  run.command('dwi2mask in.mif mask.mif')

# Generate a mean b=0 image
run.command('dwiextract in.mif - -bzero | mrmath - mean mean_bzero.mif -axis 3')

if app.args.fsl:

  # FAST doesn't accept a mask input; therefore need to explicitly mask the input image
  run.command('mrcalc mean_bzero.mif mask.mif -mult - | mrconvert - mean_bzero_masked.nii -strides -1,+2,+3')
  run.command(fast_cmd + ' -t 2 -o fast -n 3 -b mean_bzero_masked.nii')
  bias_path = fsl.findImage('fast_bias')

elif app.args.ants:

  # If the mask image was provided manually, and doesn't match the input image perfectly
  #   (i.e. also transform and voxel sizes), N4 will fail
  if app.args.mask:
    if not image.match('mean_bzero.mif', 'mask.mif'):
      app.error('Input mask header does not perfectly match DWI as required by N4')

  # Use the brain mask as a weights image rather than a mask; means that voxels at the edge of the mask
  #   will have a smoothly-varying bias field correction applied, rather than multiplying by 1.0 outside the mask
  run.command('mrconvert mean_bzero.mif mean_bzero.nii -strides +1,+2,+3')
  run.command('mrconvert mask.mif mask.nii -strides +1,+2,+3')
  init_bias_path = 'init_bias.nii'
  corrected_path = 'corrected.nii'
  run.command('N4BiasFieldCorrection -d 3 -i mean_bzero.nii -w mask.nii -o [' + corrected_path + ',' + init_bias_path + '] ' + ants_options)

  # N4 can introduce large differences between subjects via a global scaling of the bias field
  # Estimate this scaling based on the total integral of the pre- and post-correction images within the brain mask
  input_integral  = float(run.command('mrcalc mean_bzero.mif mask.mif -mult - | mrmath - sum - -axis 0 | mrmath - sum - -axis 1 | mrmath - sum - -axis 2 | mrdump -')[0])
  output_integral = float(run.command('mrcalc ' + corrected_path + ' mask.mif -mult - | mrmath - sum - -axis 0 | mrmath - sum - -axis 1 | mrmath - sum - -axis 2 | mrdump -')[0])
  app.var(input_integral, output_integral)
  bias_path = 'bias.mif'
  run.command('mrcalc ' + init_bias_path + ' ' + str(output_integral / input_integral) + ' -mult ' + bias_path)



run.command('mrcalc in.mif ' + bias_path + ' -div result.mif')
run.command('mrconvert result.mif ' + path.fromUser(app.args.output, True) + (' -force' if app.forceOverwrite else ''))
if app.args.bias:
  run.command('mrconvert ' + bias_path + ' ' + path.fromUser(app.args.bias, True) + (' -force' if app.forceOverwrite else ''))
app.complete()