forked from scilus/scilpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscil_dwi_powder_average.py
executable file
·135 lines (102 loc) · 4.64 KB
/
scil_dwi_powder_average.py
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script to compute powder average (mean diffusion weighted image) from set of
diffusion images.
By default will output an average image calculated from all images with
non-zero bvalue.
Specify --bvalue to output an image for a single shell
Script currently does not take into account the diffusion gradient directions
being averaged.
Formerly: scil_compute_powder_average.py
"""
import argparse
import logging
import nibabel as nib
import numpy as np
from dipy.core.gradients import get_bval_indices
from dipy.io.gradients import read_bvals_bvecs
from scilpy.io.image import get_data_as_mask
from scilpy.io.utils import (add_overwrite_arg, assert_inputs_exist,
assert_outputs_exist, add_verbose_arg,
assert_headers_compatible)
def _build_arg_parser():
p = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawTextHelpFormatter)
p.add_argument('in_dwi',
help='Path of the input diffusion volume.')
p.add_argument('in_bval',
help='Path of the bvals file, in FSL format.')
p.add_argument('out_avg',
help='Path of the output file.')
add_overwrite_arg(p)
p.add_argument('--mask', metavar='file',
help='Path to a binary mask.\nOnly data inside the'
' mask will be used for powder avg. '
'(Default: %(default)s)')
p.add_argument('--b0_thr', type=int, default='50',
help='Exclude b0 volumes from powder average with'
' bvalue less than specified threshold.\n'
'(Default: remove volumes with bvalue < %(default)s')
p.add_argument('--shells', nargs='+', type=int, default=None,
help='bvalue (shells) to include in powder average'
' passed as a list \n(e.g. --shells 1000 2000). '
'If not specified will include all volumes with'
' a non-zero bvalue.')
p.add_argument('--shell_thr', type=int, default='50',
help='Include volumes with bvalue +- the specified'
' threshold.\n(Default: [%(default)s]')
add_verbose_arg(p)
return p
def main():
parser = _build_arg_parser()
args = parser.parse_args()
logging.getLogger().setLevel(logging.getLevelName(args.verbose))
assert_inputs_exist(parser, [args.in_dwi, args.in_bval], args.mask)
assert_outputs_exist(parser, args, args.out_avg)
assert_headers_compatible(parser, args.in_dwi, args.mask)
img = nib.load(args.in_dwi)
data = img.get_fdata(dtype=np.float32)
affine = img.affine
mask = get_data_as_mask(nib.load(args.mask),
dtype='uint8') if args.mask else None
# Read bvals (bvecs not needed at this point)
logging.info('Performing powder average')
bvals, _ = read_bvals_bvecs(args.in_bval, None)
# Select diffusion volumes to average
if not (args.shells):
# If no shell given, average all diffusion weighted images
pwd_avg_idx = np.squeeze(np.where(bvals > 0 + args.b0_thr))
logging.debug('Calculating powder average from all diffusion'
'-weighted volumes, {} volumes '
'included.'.format(len(pwd_avg_idx)))
else:
pwd_avg_idx = []
logging.debug('Calculating powder average from {} '
'shells {}'.format(len(args.shells), args.shells))
for shell in args.shells:
pwd_avg_idx = np.int64(
np.concatenate((pwd_avg_idx,
get_bval_indices(bvals,
shell,
tol=args.shell_thr))))
logging.debug('{} b{} volumes detected and included'.format(
len(pwd_avg_idx), shell))
# remove b0 indices
b0_idx = get_bval_indices(bvals, 0, args.b0_thr)
logging.debug('{} b0 volumes detected and not included'.format(
len(b0_idx)))
for val in b0_idx:
pwd_avg_idx = pwd_avg_idx[pwd_avg_idx != val]
if len(pwd_avg_idx) == 0:
raise ValueError('No shells selected for powder average, ensure '
'shell, shell_thr and b0_thr are set '
'appropriately')
powder_avg = np.squeeze(np.mean(data[:, :, :, pwd_avg_idx], axis=3))
if args.mask:
powder_avg = powder_avg * mask
powder_avg_img = nib.Nifti1Image(powder_avg.astype(np.float32), affine)
nib.save(powder_avg_img, args.out_avg)
if __name__ == "__main__":
main()