-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmri2mrs.py
executable file
·244 lines (167 loc) · 7.27 KB
/
mri2mrs.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/env python
from plumbum import cli, FG
from plumbum.cmd import rm, bet, fslmaths, fast, fslswapdim, fslstats, \
ConvertBetweenFileFormats, matlab, ImageMath, antsApplyTransforms
import os
def run_command(command, arguments):
command_line= '{}.run({}, retcode=None)'.format(command, arguments)
# log the command
log(command_line)
# execute the command
retcode, stdout, stderr= eval(command_line)
if retcode==0:
log(stdout)
else:
log('{} failed.'.format(command_line))
log(stderr)
exit(1)
def log(msg):
print(msg)
f.write(msg+'\n')
def warpDim(mri):
try:
fslswapdim[mri, 'LR', 'PA', 'IS', mri] & FG
except:
fslswapdim[mri, 'RL', 'PA', 'IS', mri] & FG
def createMRSmask(segment, MRSregion, MRSmask):
fslmaths[segment, '-mul', MRSregion, MRSmask] & FG
def calcVol(region):
vols= fslstats(region, '-V')
return float(vols.strip(' ').split(' ')[1])
class MRS(cli.Application):
"""Calculates brain and white matter volumes given an MRS (Magnetic Resonance Spectroscopy)
label map and a T1 image registered in the space MRS.
Dependencies on bet, fslmaths, fast, fslswapdim, fslstats, ConvertBetweenFileFormats, matlab, Slice.
Uses Ofer's MATLAB code (SIEMENS machine) for creating MRS region mask.
"""
img = cli.SwitchAttr(
['-i', '--input'],
cli.ExistingFile,
help='a T1 image registered in the space of magnetic resonance spectroscopy (nrrd or nifti)',
mandatory=True)
case= cli.SwitchAttr(
['-c', '--caseID'],
help='case ID',
mandatory=True)
mask = cli.Flag(
['-m', '--mask'],
help='''turn on this flag to mask the T1 image before running the pipeline.
When turned on, a mask is created and the T1 image is multiplied by the mask
before running further steps''',
mandatory= False,
default= False)
region = cli.SwitchAttr(
['--roi'],
help='region of interest (ROI) name (acg, agm, ltemp, pcg, pwm etc.)',
mandatory=True)
labelMap= cli.SwitchAttr(
['--rdaFile'],
cli.ExistingFile,
help='an rda file from the scanner defining coordinates of the ROI (acg, agm, ltemp, pcg, pwm etc.)',
mandatory=True)
outDir = cli.SwitchAttr(
['-o', '--out'],
help='''output directory where files created during the pipeline are saved
(if already exist, it will be deleted and recreated)''',
mandatory=True)
betThreshold= cli.SwitchAttr(
['-b', '--betThreshold'],
help='threshold for bet mask creation',
mandatory=False,
default='0.3')
def main(self):
if not (self.img.endswith('.nrrd') or self.img.endswith('.nrrd') or
self.img.endswith('.nii') or self.img.endswith('.nii.gz')):
print("Invalid T1 image format, exiting ...")
if not self.labelMap.endswith('.rda'):
print("Invalid label map format, exiting ...")
# if output directory exists, delete and re-create
if os.path.exists(self.outDir):
rm.run(['-r', self.outDir])
os.makedirs(self.outDir)
scriptDir= os.path.dirname(os.path.abspath(__file__))
os.chdir(self.outDir)
global f, logFile
logFile= 'log-{}.txt'.format(self.case)
f= open(logFile, 'w')
# fsl requires nifti
if self.img.endswith('.nrrd') or self.img.endswith('.nhdr'):
convertedImg= self.case+'-t1w.nii.gz'
((ConvertBetweenFileFormats[self.img, convertedImg] ) >> logFile)()
else:
convertedImg= self.img
if self.mask:
log('Creating mask and multiplying the input image by mask ...')
bet_mask= self.case+'_mask.nii.gz' # bet names the mask like this
bet[convertedImg, self.case, '-m', '-n', '-f', self.betThreshold] & FG
processedImg = self.case + '-t1w-bet.nii.gz'
fslmaths[convertedImg, '-mul', bet_mask, processedImg] & FG
else:
processedImg= convertedImg
warpDim(processedImg)
log('Segmenting T1 image to white/gray/CSF ...')
((fast['-o', 'fast_out', processedImg] ) >> logFile)()
log('Creating white, gray, csf separate images ...')
white = 'fast_out_seg.nii.gz'
gray = 'fast_out_seg2.nii.gz'
csf = 'fast_out_seg3.nii.gz'
fslmaths[white, '-uthr', '1', gray] & FG
fslmaths[gray, '-add', '1', gray] & FG
fslmaths[gray, '-uthr', '1', gray] & FG
fslmaths[white, '-thr', '3', csf] & FG
fslmaths[csf, '-div', '3', csf] & FG
# Ofer's MATLAB code reads only .nhdr file
compatibleImg= self.case+'-t1w.nhdr'
# TODO: Check with Ofer if he wants unprocessed image as the first argument
((ConvertBetweenFileFormats[processedImg, compatibleImg] ) >> logFile)()
regionPrefix = self.region
log('Defining MRS on T1 image using MATLAB ...')
command= 'matlab'
arguments= ['-singleCompThread', '-nojvm', '-nodisplay', '-nosplash', '-r',
'diary(\'{}\'); diary on; addpath {}; MRStoAnatomy(\'{}\', \'{}\', \'{}\', \'{}_mask\'); diary off; exit'
.format(logFile, scriptDir, 'tmp-sb.nhdr', compatibleImg, self.labelMap, regionPrefix)]
run_command(command, arguments)
'''
# Ofer's original command
command= 'Slicer'
arguments= ['--launch', 'AddScalarVolumes',
self.region+'_mask_zr.nhdr',
self.region + '_mask_jm.nhdr',
'--order', '0',
self.region + '_mask.nhdr']
run_command(command, arguments)
log("Converting MRS mask to nii ...")
oldMRSmask= regionPrefix+'_mask.nhdr'
newMRSmask= regionPrefix+'_mask.nii.gz'
((ConvertBetweenFileFormats[oldMRSmask, newMRSmask] ) >> logFile)()
'''
# Isaiah proposed command to replace Slicer
resampled= self.region+'_mask_aapt.nhdr'
antsApplyTransforms['-i', self.region + '_mask_jm.nhdr', '-r',
self.region+'_mask_zr.nhdr', '-o', resampled] & FG
newMRSmask = regionPrefix + '_mask.nii.gz'
ImageMath['3', newMRSmask, '+', self.region + '_mask_zr.nhdr', resampled] & FG
warpDim(newMRSmask)
log('Creating MRS masks ...')
MRSmaskBrain= regionPrefix+'_MRS_mask_brain.nii.gz'
MRSmaskWM = regionPrefix + '_MRS_mask_wm.nii.gz'
createMRSmask(gray, newMRSmask, MRSmaskBrain)
createMRSmask(csf, newMRSmask, MRSmaskWM)
brainVol= calcVol(MRSmaskBrain)
# print ROI volume
totVol= calcVol(newMRSmask)
log('ROI volume:{}'.format(totVol))
# print CSF volume
csfVol= totVol - brainVol
log('CSF volume:{}'.format(csfVol))
# print WM volume
wmVol= calcVol(MRSmaskWM)
log('WM volume:{}'.format(wmVol))
# print GM volume
gmVol= brainVol - wmVol
log('GM volume:{}'.format(gmVol))
print('Program finished, see {} for details'.format(os.path.join(self.outDir, logFile)))
os.chdir(scriptDir)
f.close()
if __name__ == '__main__':
MRS.run()