Skip to content

Commit 5b5ef20

Browse files
authored
Merge pull request #785 from MRtrix3/tag_0.3.16
Tag 0.3.16
2 parents bea092c + bfd82fe commit 5b5ef20

File tree

1,123 files changed

+49587
-47550
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,123 files changed

+49587
-47550
lines changed

.gitignore

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,21 @@
1717
*.hdr
1818
*.qrc
1919
*.rste
20-
icons.cpp
21-
/config.*
20+
config
21+
build.*
2222
/configure.log
2323
/build.log
24-
/test/build.log
25-
/lib/version.cpp
24+
/core/version.cpp
2625
/src/project_version.h
26+
/lib/mrtrix3/_version.py
2727
/test/src/project_version.h
2828
/scripts/mrtrix_bash_completion
2929
/release/
30-
/test/bin/
3130
/dev/
31+
/bin/
32+
/tmp/
3233
.cproject
34+
.idea
3335
.project
3436
.settings
3537
.pydevproject
@@ -44,6 +46,6 @@ icons.cpp
4446
/assert/
4547
testing.log
4648
testing/build.log
47-
testing/release/
48-
testing/src/
49+
testing/bin/
50+
testing/src/project_version.h
4951
.__tmp.log

.travis.yml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
11
sudo: false
22
language: cpp
3-
os:
4-
- linux
53
addons:
64
apt:
75
sources:
86
- ubuntu-toolchain-r-test
97
packages:
108
- zlib1g-dev
11-
- libgsl0-dev
129
- libqt4-opengl-dev
1310
- g++-4.8
1411
- python3
1512
compiler:
1613
- clang
1714
env:
18-
- python_version=2
19-
- python_version=3
15+
- py=python2
16+
- py=python3
2017
install:
2118
- export NUMBER_OF_PROCESSORS=4
2219
- export PATH=`pwd`/release/bin:`pwd`/scripts:${PATH}
23-
- export EIGEN_CFLAGS=-I`pwd`/eigen
24-
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi
25-
- (hg clone https://bitbucket.org/eigen/eigen/; cd eigen; hg update 3.2)
20+
- export EIGEN_CFLAGS=-I`pwd`/../eigen
21+
- (cd ..; hg clone https://bitbucket.org/eigen/eigen/; cd eigen; hg update 3.3)
2622
script:
27-
- if [ ${python_version} = "3" ]; then python3 ./configure -assert && python3 ./build -nowarnings && ./run_tests; else ./configure -assert && ./build -nowarnings && ./run_tests; fi
23+
- ./check_memalign && $py ./configure -assert && $py ./build -nowarnings && ./run_tests
24+
2825
after_failure:
26+
- cat memalign.log
2927
- cat configure.log
3028
- cat build.log
3129
- cat testing.log

bin/5ttgen

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python
2+
3+
# Script that generates a five-tissue-type (5TT) segmented image: the format appropriate for ACT
4+
#
5+
# In this script, major stages of processing can be performed in one of two ways:
6+
# - Using FSL tools: BET for brain extraction, FAST for tissue segmentation, FIRST for sub-cortical grey matter segmentation
7+
# - Using segmentations from FreeSurfer
8+
# Alternative algorithms for performing this conversion can be added by creating a new file in lib/mrtrix3/_5ttgen/ and
9+
# defining the appropriate functions; 5ttgen will automatically make that algorithm available at the command-line
10+
11+
12+
# Make the corresponding MRtrix3 Python libraries available
13+
import inspect, os, sys
14+
lib_folder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile(inspect.currentframe()))[0], os.pardir, 'lib')))
15+
if not os.path.isdir(lib_folder):
16+
sys.stderr.write('Unable to locate MRtrix3 Python libraries')
17+
sys.exit(1)
18+
sys.path.insert(0, lib_folder)
19+
20+
21+
from mrtrix3 import algorithm, app, path, run
22+
23+
app.init('Robert E. Smith (robert.smith@florey.edu.au)', 'Generate a 5TT image suitable for ACT')
24+
app.cmdline.addCitation('', 'Smith, R. E.; Tournier, J.-D.; Calamante, F. & Connelly, A. Anatomically-constrained tractography: Improved diffusion MRI streamlines tractography through effective use of anatomical information. NeuroImage, 2012, 62, 1924-1938', False)
25+
app.cmdline.addDescription('5ttgen acts as a \'master\' script for generating a five-tissue-type (5TT) segmented tissue image suitable for use in Anatomically-Constrained Tractography (ACT). A range of different algorithms are available for completing this task. When using this script, the name of the algorithm to be used must appear as the first argument on the command-line after \'5ttgen\'. The subsequent compulsory arguments and options available depend on the particular algorithm being invoked.')
26+
app.cmdline.addDescription('Each algorithm available also has its own help page, including necessary references; e.g. to see the help page of the \'fsl\' algorithm, type \'5ttgen fsl\'.')
27+
28+
common_options = app.cmdline.add_argument_group('Options common to all 5ttgen algorithms')
29+
common_options.add_argument('-nocrop', action='store_true', default=False, help='Do NOT crop the resulting 5TT image to reduce its size (keep the same dimensions as the input image)')
30+
common_options.add_argument('-sgm_amyg_hipp', action='store_true', default=False, help='Represent the amygdalae and hippocampi as sub-cortical grey matter in the 5TT image')
31+
32+
# Import the command-line settings for all algorithms found in the relevant directory
33+
algorithm.initialise()
34+
35+
app.parse()
36+
37+
# Find out which algorithm the user has requested
38+
alg = algorithm.getModule(app.args.algorithm)
39+
40+
app.checkOutputPath(app.args.output)
41+
alg.checkOutputPaths()
42+
43+
app.makeTempDir()
44+
run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('input.mif', True))
45+
alg.getInputs()
46+
47+
app.gotoTempDir()
48+
49+
alg.execute()
50+
51+
(stdout,stderr) = run.command('5ttcheck result.mif', False)
52+
if len(stderr) and 'ERROR' in stderr:
53+
app.warn('Generated image does not perfectly conform to 5TT format')
54+
55+
run.command('mrconvert result.mif ' + path.fromUser(app.args.output, True) + (' -force' if app.force else ''))
56+
app.complete()
57+
File renamed without changes.

bin/dwi2response

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env python
2+
3+
# Script for estimating response functions for spherical deconvolution
4+
# A number of different approaches are available within this script for performing response function estimation.
5+
6+
7+
# Make the corresponding MRtrix3 Python libraries available
8+
import inspect, os, sys
9+
lib_folder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile(inspect.currentframe()))[0], os.pardir, 'lib')))
10+
if not os.path.isdir(lib_folder):
11+
sys.stderr.write('Unable to locate MRtrix3 Python libraries')
12+
sys.exit(1)
13+
sys.path.insert(0, lib_folder)
14+
15+
16+
from mrtrix3 import algorithm, app, image, path, run
17+
18+
19+
app.init('Robert E. Smith (robert.smith@florey.edu.au) and Thijs Dhollander (thijs.dhollander@gmail.com)',
20+
'Estimate response function(s) for spherical deconvolution')
21+
app.cmdline.addDescription('dwi2response acts as a \'master\' script for performing various types of response function estimation; a range of different algorithms are available for completing this task. When using this script, the name of the algorithm to be used must appear as the first argument on the command-line after \'dwi2response\'. The subsequent compulsory arguments and options available depend on the particular algorithm being invoked.')
22+
app.cmdline.addDescription('Each algorithm available also has its own help page, including necessary references; e.g. to see the help page of the \'fa\' algorithm, type \'dwi2response fa\'.')
23+
24+
# General options
25+
common_options = app.cmdline.add_argument_group('Options common to all dwi2response algorithms')
26+
common_options.add_argument('-shell', help='The b-value shell(s) to use in response function estimation (single value for single-shell response, comma-separated list for multi-shell response)')
27+
common_options.add_argument('-lmax', help='The maximum harmonic degree(s) of response function estimation (single value for single-shell response, comma-separated list for multi-shell response)')
28+
common_options.add_argument('-mask', help='Provide an initial mask for response voxel selection')
29+
common_options.add_argument('-voxels', help='Output an image showing the final voxel selection(s)')
30+
common_options.add_argument('-grad', help='Pass the diffusion gradient table in MRtrix format')
31+
common_options.add_argument('-fslgrad', nargs=2, metavar=('bvecs', 'bvals'), help='Pass the diffusion gradient table in FSL bvecs/bvals format')
32+
app.cmdline.flagMutuallyExclusiveOptions( [ 'grad', 'fslgrad' ] )
33+
34+
# Import the command-line settings for all algorithms found in the relevant directory
35+
algorithm.initialise()
36+
37+
38+
app.parse()
39+
40+
41+
# Find out which algorithm the user has requested
42+
alg = algorithm.getModule(app.args.algorithm)
43+
44+
45+
# Check for prior existence of output files, and grab any input files, used by the particular algorithm
46+
if app.args.voxels:
47+
app.checkOutputPath(app.args.voxels)
48+
alg.checkOutputPaths()
49+
50+
51+
# Sanitise some inputs, and get ready for data import
52+
if app.args.lmax:
53+
try:
54+
lmax = [ int(x) for x in app.args.lmax.split(',') ]
55+
if any([lmax_value%2 for lmax_value in lmax]):
56+
app.error('Value of lmax must be even')
57+
except:
58+
app.error('Parameter lmax must be a number')
59+
if alg.needsSingleShell() and not len(lmax) == 1:
60+
app.error('Can only specify a single lmax value for single-shell algorithms')
61+
shell_option = ''
62+
if app.args.shell:
63+
try:
64+
shell_values = [ int(x) for x in app.args.shell.split(',') ]
65+
except:
66+
app.error('-shell option should provide a comma-separated list of b-values')
67+
if alg.needsSingleShell() and not len(shell_values) == 1:
68+
app.error('Can only specify a single b-value shell for single-shell algorithms')
69+
shell_option = ' -shell ' + app.args.shell
70+
singleshell_option = ''
71+
if alg.needsSingleShell():
72+
singleshell_option = ' -singleshell -no_bzero'
73+
74+
grad_import_option = ''
75+
if app.args.grad:
76+
grad_import_option = ' -grad ' + path.fromUser(app.args.grad, True)
77+
elif app.args.fslgrad:
78+
grad_import_option = ' -fslgrad ' + path.fromUser(app.args.fslgrad[0], True) + ' ' + path.fromUser(app.args.fslgrad[1], True)
79+
elif not image.headerField(path.fromUser(app.args.input, False), 'dwgrad'):
80+
app.error('Script requires diffusion gradient table: either in image header, or using -grad / -fslgrad option')
81+
82+
app.makeTempDir()
83+
84+
# Get standard input data into the temporary directory
85+
if alg.needsSingleShell() or shell_option:
86+
run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' - -stride 0,0,0,1' + grad_import_option + ' | dwiextract - ' + path.toTemp('dwi.mif', True) + shell_option + singleshell_option)
87+
else: # Don't discard b=0 in multi-shell algorithms
88+
run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('dwi.mif', True) + ' -stride 0,0,0,1' + grad_import_option)
89+
if app.args.mask:
90+
run.command('mrconvert ' + path.fromUser(app.args.mask, True) + ' ' + path.toTemp('mask.mif', True) + ' -datatype bit')
91+
92+
alg.getInputs()
93+
94+
app.gotoTempDir()
95+
96+
97+
# Generate a brain mask (if necessary)
98+
# Otherwise, check that the mask provided is appropriate
99+
if os.path.exists('mask.mif'):
100+
dwi_size = [ int(x) for x in image.headerField('dwi.mif', 'size').split() ]
101+
mask_size = [ int(x) for x in image.headerField('mask.mif', 'size').split() ]
102+
if not mask_size[:3] == dwi_size[:3]:
103+
app.error('Dimensions of provided mask image do not match DWI')
104+
if int(image.statistic('mask.mif', 'count', 'mask.mif')) == 0:
105+
app.error('Input mask does not contain any voxels')
106+
else:
107+
run.command('dwi2mask dwi.mif mask.mif')
108+
109+
110+
# From here, the script splits depending on what estimation algorithm is being used
111+
alg.execute()
112+
113+
114+
# Finalize for all algorithms
115+
if app.args.voxels:
116+
run.command('mrconvert voxels.mif ' + path.fromUser(app.args.voxels, True) + (' -force' if app.force else ''))
117+
app.complete()
118+

bin/dwibiascorrect

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env python
2+
3+
# Script that performs B1 field inhomogeneity correction for a DWI volume series
4+
# Bias field is estimated using the mean b=0 image, and subsequently used to correct all volumes
5+
6+
7+
# Make the corresponding MRtrix3 Python libraries available
8+
import inspect, os, sys
9+
lib_folder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile(inspect.currentframe()))[0], os.pardir, 'lib')))
10+
if not os.path.isdir(lib_folder):
11+
sys.stderr.write('Unable to locate MRtrix3 Python libraries')
12+
sys.exit(1)
13+
sys.path.insert(0, lib_folder)
14+
15+
16+
from distutils.spawn import find_executable
17+
from mrtrix3 import app, fsl, image, path, run
18+
19+
20+
app.init('Robert E. Smith (robert.smith@florey.edu.au)',
21+
'Perform B1 field inhomogeneity correction for a DWI volume series')
22+
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)
23+
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)
24+
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)
25+
app.cmdline.add_argument('input', help='The input image series to be corrected')
26+
app.cmdline.add_argument('output', help='The output corrected image series')
27+
options = app.cmdline.add_argument_group('Options for the dwibiascorrect script')
28+
options.add_argument('-mask', help='Manually provide a mask image for bias field estimation')
29+
options.add_argument('-bias', help='Output the estimated bias field')
30+
options.add_argument('-ants', action='store_true', help='Use ANTS N4 to estimate the inhomogeneity field')
31+
options.add_argument('-fsl', action='store_true', help='Use FSL FAST to estimate the inhomogeneity field')
32+
app.cmdline.flagMutuallyExclusiveOptions( [ 'ants', 'fsl' ] )
33+
options.add_argument('-grad', help='Pass the diffusion gradient table in MRtrix format')
34+
options.add_argument('-fslgrad', nargs=2, metavar=('bvecs', 'bvals'), help='Pass the diffusion gradient table in FSL bvecs/bvals format')
35+
app.cmdline.flagMutuallyExclusiveOptions( [ 'grad', 'fslgrad' ] )
36+
app.parse()
37+
38+
if app.args.fsl:
39+
40+
if app.isWindows():
41+
app.error('Script cannot run using FSL on Windows due to FSL dependency')
42+
43+
fsl_path = os.environ.get('FSLDIR', '')
44+
if not fsl_path:
45+
app.error('Environment variable FSLDIR is not set; please run appropriate FSL configuration script')
46+
47+
fast_cmd = 'fast'
48+
if not find_executable(fast_cmd):
49+
fast_cmd = 'fsl5.0-fast'
50+
if not find_executable(fast_cmd):
51+
app.error('Could not find FSL program fast; please verify FSL install')
52+
53+
fsl_suffix = fsl.suffix()
54+
if fast_cmd == 'fast':
55+
fast_suffix = fsl_suffix
56+
else:
57+
fast_suffix = '.nii.gz'
58+
59+
elif app.args.ants:
60+
61+
if not find_executable('N4BiasFieldCorrection'):
62+
app.error('Could not find ANTS program N4BiasFieldCorrection; please check installation')
63+
64+
else:
65+
app.error('No bias field estimation algorithm specified')
66+
67+
grad_import_option = ''
68+
if app.args.grad:
69+
grad_import_option = ' -grad ' + path.fromUser(app.args.grad, True)
70+
elif app.args.fslgrad:
71+
grad_import_option = ' -fslgrad ' + path.fromUser(app.args.fslgrad[0], True) + ' ' + path.fromUser(app.args.fslgrad[1], True)
72+
73+
app.checkOutputPath(app.args.output)
74+
app.checkOutputPath(app.args.bias)
75+
76+
app.makeTempDir()
77+
78+
run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('in.mif', True) + grad_import_option)
79+
if app.args.mask:
80+
run.command('mrconvert ' + path.fromUser(app.args.mask, True) + ' ' + path.toTemp('mask.mif', True))
81+
82+
app.gotoTempDir()
83+
84+
# Make sure it's actually a DWI that's been passed
85+
dwi_sizes = image.headerField('in.mif', 'size').split()
86+
if len(dwi_sizes) != 4:
87+
app.error('Input image must be a 4D image')
88+
DW_scheme = image.headerField('in.mif', 'dwgrad').split('\n')
89+
if len(DW_scheme) != int(dwi_sizes[3]):
90+
app.error('Input image does not contain valid DW gradient scheme')
91+
92+
# Generate a brain mask if required, or check the mask if provided
93+
if app.args.mask:
94+
mask_sizes = image.headerField('mask.mif', 'size').split()
95+
if not mask_sizes[:3] == dwi_sizes[:3]:
96+
app.error('Provided mask image does not match input DWI')
97+
else:
98+
run.command('dwi2mask in.mif mask.mif')
99+
100+
# Generate a mean b=0 image
101+
run.command('dwiextract in.mif - -bzero | mrmath - mean mean_bzero.mif -axis 3')
102+
103+
if app.args.fsl:
104+
# FAST doesn't accept a mask input; therefore need to explicitly mask the input image
105+
run.command('mrcalc mean_bzero.mif mask.mif -mult - | mrconvert - mean_bzero_masked.nii -stride -1,+2,+3')
106+
run.command(fast_cmd + ' -t 2 -o fast -n 3 -b mean_bzero_masked.nii')
107+
bias_path = 'fast_bias' + fast_suffix
108+
elif app.args.ants:
109+
110+
# If the mask image was provided manually, and doesn't match the input image perfectly
111+
# (i.e. also transform and voxel sizes), N4 will fail
112+
if app.args.mask:
113+
if not image.match('mean_bzero.mif', 'mask.mif'):
114+
app.error('Input mask header does not perfectly match DWI as required by N4')
115+
116+
# Use the brain mask as a weights image rather than a mask; means that voxels at the edge of the mask
117+
# will have a smoothly-varying bias field correction applied, rather than multiplying by 1.0 outside the mask
118+
run.command('mrconvert mean_bzero.mif mean_bzero.nii -stride +1,+2,+3')
119+
run.command('mrconvert mask.mif mask.nii -stride +1,+2,+3')
120+
bias_path = 'bias.nii'
121+
run.command('N4BiasFieldCorrection -d 3 -i mean_bzero.nii -w mask.nii -o [corrected.nii,' + bias_path + '] -s 2 -b [150] -c [200x200,0.0]')
122+
123+
run.command('mrcalc in.mif ' + bias_path + ' -div result.mif')
124+
run.command('mrconvert result.mif ' + path.fromUser(app.args.output, True) + (' -force' if app.force else ''))
125+
if app.args.bias:
126+
run.command('mrconvert ' + bias_path + ' ' + path.fromUser(app.args.bias, True) + (' -force' if app.force else ''))
127+
app.complete()
128+

0 commit comments

Comments
 (0)