-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmri_qa.py
319 lines (234 loc) · 10.2 KB
/
mri_qa.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#!/usr/bin/env python3
import os
import subprocess as sp
import logging
import matplotlib.pyplot as pl
import matplotlib.image as mpm
pl.switch_backend('agg')
fsldir='/usr/lib/fsl/5.0'
log = logging.getLogger()
def bet(image,workdir,shell=False):
"""
Runs fsl's bet2 on an image and saves the results in workdir
Args:
image (str): path to the image to run bet2 on
workdir (str): path for bet2 output (used to generate <output_fileroot> option in bet2
shell (bool): pass the shell into the bet2 subprocess command
Returns:
bet_out (str): path to bet2's <output_fileroot> (currently "workdir"/bet
"""
com_cmd = ['{}/fslstats'.format(fsldir), image, '-C']
print(' '.join(com_cmd))
com_cmd = ' '.join(com_cmd)
result = sp.Popen(com_cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
center_of_mass = out.rstrip()
bet_out = os.path.join(workdir, 'bet')
bet_cmd = ['{}/bet2'.format(fsldir), image, bet_out, '-o', '-m', '-t', '-f', '0.5', '-w', '0.4', '-c', center_of_mass]
print(' '.join(bet_cmd))
bet_cmd = ' '.join(bet_cmd)
result = sp.Popen(bet_cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
return(bet_out)
def bet_2_outline(original,bet_root,shell=False):
"""
Takes a bet2 extracted image and it's original, and creates a mask of the outline. Requires that the "-o" option
was used during bet2 to generate an overlay image.
Args:
original (str): path to the original image that bet2 was performed on
bet_root (str): path to the bet2 root (the <output_fileroot> option used in bet2)
shell (bool): use the shell in the subprocess commands
Returns:
bin_out (str): path to the binary bet2 outline mask.
"""
overlay = bet_root+'_overlay.nii.gz'
diff_out = bet_root+'_diff'
cmd = ['fslmaths', overlay,'-sub',original,diff_out]
print(' '.join(cmd))
result = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
cmd = ['fslstats', diff_out, '-p', '97']
print(' '.join(cmd))
result = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
thresh = out.rstrip()
thresh_out = bet_root+'_thresh'
cmd = ['fslmaths',diff_out,'-thr',thresh,thresh_out]
print(' '.join(cmd))
result = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
bin_out = bet_root+'_outline'
cmd = ['fslmaths', thresh_out, '-bin', bin_out]
print(' '.join(cmd))
result = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
os.remove(thresh_out+'.nii.gz')
os.remove(diff_out+'.nii.gz')
return(bin_out)
def overlay(image1,image2,output,shell=False):
"""
creates an overlay of image 2 over image1. Saves three .pngs of the overlay (one along each plane), and merges
them into one single .png file
Args:
image1 (str): path to image1
image2 (str): path to image2
output (str): path to save merged overlays to
shell (bool): include the shell in the subprocess commands or not
Returns:
"""
cmd=['overlay','0','0',image1,'-a',image2,'0.001','5',output]
print(' '.join(cmd))
result = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
wrkdir = os.path.split(output)[0]
cmd=['slicer',output,'-c','-s','3',
'-x','0.5',os.path.join(wrkdir,'x0v.png'),
'-y','0.5',os.path.join(wrkdir,'y0v.png'),
'-z','0.5',os.path.join(wrkdir,'z0v.png'),]
print(' '.join(cmd))
result = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
cmd=['{}/pngappend'.format(fsldir),os.path.join(wrkdir,'x0v.png'),'+','4',
os.path.join(wrkdir,'y0v.png'),'+','4',
os.path.join(wrkdir,'z0v.png'),output+'.png']
print(' '.join(cmd))
result = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE,
universal_newlines=True, shell=shell)
out, err = result.communicate()
print(out)
print(err)
def outline_overlay(background, outline, name=''):
"""
Generates a .png image of one image's BET extracted brain outline over another image. Each image containes three
overlay views: one along each plane (Cor, Sag, Tra).
Args:
background (str): path to the backround image
outline (str): path to the image who's BET extracted brain is outlined and overlayed on 'background'
name (str): The name to save the final image as
Returns:
"""
bg_dir, bg_base = os.path.split(background)
ol_dir, ol_base = os.path.split(outline)
if name == '':
bg_root = bg_base[:bg_base.find('.nii')]
ol_root = ol_base[:ol_base.find('.nii')]
name = '{}_on_{}'.format(ol_root, bg_root)
work_base = bg_dir
else:
work_base = os.path.split(name)[0]
workdir = os.path.join(work_base, 'outline_work')
os.makedirs(workdir, exist_ok=True)
bet_out = bet(outline, workdir, True)
mask_outline = bet_2_outline(outline, bet_out, shell=False)
overlay(background, mask_outline, name, shell=False)
def plot_overlays(files, titles, output):
"""
Takes any number N of .png files, each with an associated title, and plots them together in a Nx1 subplot.
Args:
files (list): list of paths to .png images to include in the plot (ordered)
titles (list): list of titles to assign to each plot
output (str): the output name to save the image as
Returns:
"""
if not len(files) == len(titles):
log.warning('Number of files different than number of provided titles')
return
f, ax = pl.subplots(len(files), 1)
for ai, a in enumerate(ax):
file = files[ai]
title = titles[ai]
image = mpm.imread(file+'.png')
a.imshow(image)
a.set_title(title)
a.set_xticks([])
a.set_yticks([])
pl.tight_layout()
pl.savefig(output)
pl.close()
def generate_topup_report(original_image, corrected_image, output_base=''):
"""
Taking an original and topup corrected image, this creates a QA report image by overlaying an outline of the topup
corrected image over the original, as well as overlaying an outline of the original image over the topup corrected
image. Three slices are taken from the center of the image, along each plane of acquistion (Sag, Cor, Tra)
Args:
original_image (str): path to original image
corrected_image (str): path to TOPUP fixed image
output_base (str): base directory for output files
Returns:
report_out (str): The path to the final QA image
"""
path, original_base = os.path.split(original_image)
if output_base == '':
output_base = path
original_base = original_base[:original_base.find('.nii.gz')]
log.info('overlay 1')
name1 = os.path.join(output_base, 'corrected_over_original')
outline_overlay(original_image, corrected_image, name1)
log.info('overlay 2')
name2 = os.path.join(output_base,'original_over_corrected')
outline_overlay(corrected_image, original_image, name2)
log.info('generating report')
report_out = os.path.join(output_base,'{}_QA_report.png'.format(original_base))
plot_overlays([name1, name2], ['topup (red) over original', 'original (red) over topup'], report_out)
return(report_out)
# def debug1():
# background = '/Users/davidparker/Documents/Flywheel/SSE/MyWork/Gears/Topup/Gear/work/Image1.nii.gz'
# outline = '/Users/davidparker/Documents/Flywheel/SSE/MyWork/Gears/Topup/Gear/output/topup_corrected_nodif.nii.gz'
# name1 = '/Users/davidparker/Documents/Flywheel/SSE/MyWork/Gears/Topup/Gear/work/topup_over_orig'
# outline_overlay(background, outline, name1)
#
# name2 = '/Users/davidparker/Documents/Flywheel/SSE/MyWork/Gears/Topup/Gear/work/orig_over_topup'
# outline_overlay(outline, background, name2)
#
# report_out = '/Users/davidparker/Documents/Flywheel/SSE/MyWork/Gears/Topup/Gear/work/report_out.png'
# plot_overlays([name1, name2], ['topup (red) over original', 'original (red) over topup'], report_out)
def debug2():
#cmd = "drun -v /Users/davidparker/Documents/Flywheel/SSE/MyWork/Gears/Topup/Gear/fsl-topup:/flywheel/v0 flywheel/fsl-topup:0.0.4"
import shutil
# file_comparison = [(apply_to_files[i][0], corrected_files[i]) for i in range(len(corrected_files))]
file_comparison = [('/flywheel/v0/work/Image1.nii.gz', '/flywheel/v0/output/topup-corrected-nodif.nii.gz'), ('/flywheel/v0/work/Image2.nii.gz', '/flywheel/v0/output/topup-corrected-nodif_PA.nii.gz')]
log.info('Running Topup QA')
work_dir = '/flywheel/v0/work'
output_dir = '/flywheel/v0/output'
for original, corrected in file_comparison:
report_out = generate_topup_report(original, corrected, work_dir)
report_dir, report_base = os.path.split(report_out)
shutil.move(report_out, os.path.join(output_dir, report_base))
# Move the config file used in the analysis to the output
config_path = '/flywheel/v0/output/config_file.txt'
config_out = '/flywheel/v0/output/config_file.txt'
if not config_path:
config_path = DEFAULT_CONFIG
if os.path.exists(config_path):
shutil.move(config_path, config_out)
else:
log.info(f'no path {config_path}')
exec_command(['ls', '-l', '/'])
exec_command(['ls', '-l', '/flywheel'])
exec_command(['ls', '-l', '/flywheel/v0'])
if __name__ == '__main__':
debug2()