-
Notifications
You must be signed in to change notification settings - Fork 12
/
__init__.py
374 lines (296 loc) · 12.8 KB
/
__init__.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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
"""
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWNNNNNNNWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMWWNNNNXXXXXXXXXXXXXXXKKKXNNWWMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMWWNNXKK0OOO0XNXK0KKXXXXKXNNX0OOOO0KKXXNNWMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMWNXXK0kddxOkxO0KXXKXXXXXXXXXXXNXXXKOxdxkOO00KXWMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMWWNKOxdxxxxxxxO0KNXKXNXKKK0KKKXKKKKKK0kkOkxolodxkk0XWMMMMMMMMMMMMMMM
MMMMMMMMMWNXXKOOkkxxxkOddOXNXKOOO0KKXXXNNWWNXK00OxdkXNX0kdc:coox0WMMMMMMMMMMMMMM
MMMMMMMWNKOOkxxxxxxkOOOOO00kkO0KNNWWNX0OkO0OxxO000KXXNNNWNXOdc:ldkNMMMMMMMMMMMMM
MMMMMMWKOxoc:lddxxxxkO0KKK0OkxkO00Okxlccclooooddooodxxk0KXXNWXkxddxKWMMMMMMMMMMM
MMMMMNklc:;:clooxxxkkkkO00OOOxc,,;;,,,,;;;;::looddxxkdoloxk0KXXNX0xx0WMMMMMMMMMM
MMMMNx:,;coxOXNNWWNNXXXXKOkxdl:,...';:clodkOKXNNNNNNWNXOdoodkkOKNWN0kONMMMMMMMMM
MMMNx:coxO0KKK0OkkkO0KXXKOkxoc;'.',;coxkO0KKKKKKKKKXNWWWX0kdddxkOKNWWKKXWMMMMMMM
MMW0odkOkkkxddxO0XXXXXKK0Odoooc'...,:ccccccccloodxxkOO0KXNNKkxxxxkOKXNXXXNMMMMMM
MNOdxxxdodxk0XNNXKKKXXX0OO00kc......,coxkkOkxxddoooddxxkk0XNNXOxddxkOO0KXXXWMMMM
XxloddoodOXNNXKKKXNWXOkOKX0l'.........;clldkO0KKKKKK0OOkkkO0KNNXOxddxkkO0KXKNWMM
OolllldOKXX0O0XWMWKxoxKNXk;............',;llcldxk0KNWWWWNK0OOOKXNXOxddxkkOKXKXWM
dc:cldk0OkkKNWWWXkox0XXKd'................;oxdlccldxOXNWMMWNX0OOKXNXOxddxxk0XKXM
kc:oxdxddOXWNXKOdx0NNXKl....................lKXKOkdoodxOKXNWMWX0OOKXNKOkxdxOXNXN
Kloxllld0K0KK0doONWNNKl......................:ONWWWNXKOkk0KKXWMNKOOOKNX0kxxk0NXX
W0o:,:xOxdk00xxKWWWWXl........................,o0XNWWMWNXXXKKNWMWKOkkOKNX0Ok0NXX
MWKl:ldlcx0OkOXWMWMXl....,:loxkxdlcc:::::;,'....,lkKXNWNNNXKKXNWMN0kxkOXWX00KNXX
MMMXxc;;cxkxkKWMMMXl..',:okOO0KKKKK00KXXXXK0Okdc,.'lkKKKKXNXKKXNMMXOxxkOXXOO0NXX
MMMMXl,;codx0WMMMXl'',:ldO0KXXNWWNNNNNNXXNNNNNNX0kl,,lk0OO0XXKKXWMWKkxxk0KOOOKXX
MMMMWO:;lookXMWWNd,;loxOXNNNNNKKNWWNXKKXXXNNWWNNNX0d,.'lxkkOKK0KNWMXkxkkOOkOOKKX
MMMMMXoclcxKWWNNk;';:clodk0KNWNXNWNK000kkdoddkKXXXOoc,..,lkkO000XNWKkxkkOkkOO00K
MMMMMXocclOXWWNO:'.',,,'.',:xXWNXOkkko:,''.'':ooloxxol:...cxkOOOKNW0dxxkOkxkO00K
MMMMMXdc:oO0XNXo.,:ccldoc,..,d0KOdodko:;::::;:lxklcdOOkc...:xkkkOXN0dxxkOxxkO0OK
MMMMMWOc;ok0KXKl'::;:oddol;..cOOkdokOd:cccclooooxd:;dKX0l'..:dxdkKX0ddxxkdxkOOON
MMMMMMNx;;oxk00l',;;,..;ddl,.;xOkdodOkdc;,,locxOdldOKWWNO:...:ol:oOkodkxddxOOOKW
MMMMMMMNx;;loxkd;,;;:cldkdc;'lKNN0xOXKOkkk0K000K0OKWNNXX0c.. 'c:';xxlododk0OO0WM
MMMMMMMMNOo:;:colodc:lddl;''cKWWNKkxO0OxxkKXNNNXKXNNNNNXkc.. .,;.'cc::cok0OkONMM
MMMMMMMMMMNOo;,;:xXKOxdoc;,c0WWWNX000KKK0KXXNXXXNWNNXXN0c.....,,..'..;lxxdoONMMM
MMMMMMMMMMMMW0d:;dKXNX0kdll0WMWWNXKXXXXXXXKKKXNNWWWWNXK0o...........;:c::dKWMMMM
MMMMMMMMMMMMMMW0:cOKNWXOllONWWNKK0KXXNNNNNNNXXKKXXXXXKKx:........',,;;:dKWMMMMMM
MMMMMMMMMMMMMMMWx:oO0Kk:,oKNWWXOkO00O0XNWWWWNXK000000Od:'.......'';ld0NMMMMMMMMM
MMMMMMMMMMMMMMMM0;,lxxc',o0XNNKdlOKOxxxOKXNNNNX0Okolxo,....',''cdkKWMMMMMMMMMMMM
MMMMMMMMMMMMMMMXo..:ddc,',lkkxl;,:lldK0xxk0XXXK0Odc',;'''...'',xWMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMNk:.'dko:'',;;;:;',cloxO0K0OO000Odcc:....'....''oNMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMNo..coc;''',;;;:,,,,,,;lxxolodxoc;;c,..;,'.''..oNMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMWO;.';:;:;;:od:cc:;;::,',,;,;;;;,,,c;.'::;'.,,..ckXMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMM0:..,:;:clccl;,:,,,,''...':lc::loclc',::,..'.. .,oKWMMMMMMMMMM
MMMMMMMMMMMMMMMMWk'':lc,,,,coooooddl;......:occlllld:.,:,...... 'l0WMMMMMMMM
MMMMMMMMMMMMMMMMNd';loc:;'',:cccclc:,.......:;,cc:cl,'cc;....... .lONMMMMMM
MMMMMMMMMMMMMMMMXl';cccc;;:,'.. ..''''.....,c;:l:cc,,:c:,'.''..... .;d0NMMM
MMMMMMMMMMMMMMMMKc'',:loc::;,...'c;''...'',:;';lcc;,c:,,,.''.. .... ....;d0W
MMMMMMMMMMMMMMWXxlc;',ll:;;:;...;l;''',;:c:,..':;,;;;'.'..''. ............ .:
MMMMMMMMMMMMN0o;:do;',;,;ooc:,',,:;',:::cc;.'',:,.,,,'..................... .
MMMMMMMMMN0d;...,lc,'.':odl::;;:,';;;clc:;,'..,,''''.'...................... .
MMMMMMWKd;. ....;cc'.:odollc;::'.';,;l:,,;,.',.'',,.',,,'......................
MMMMWKd,..........;;..,:ooloccol;',:;,lc,,',';:,;:'...''.........''''''''''..'''
MMMW0d:'..........',..';cclooxxdooll::oc;,...:c',:,.,:;'.''''..''',;;,,,,,,,;ccl
MMMWN0d:;,'............';:clodxxxxlc:;:;:,..;;'''::':c,,,;;,',,,;;,;;,'',;ldk00K
MMMMMWN0koc:,,'''..''''.';;;lldkxo::;;;,''.','..,:,''',,,,',,,:::::;;;;;cdKWWMMM
MMMMMMMMMWXKOxlc;,...','...,:;coc;;;;'''''.....,:,...''''',;::clcclddxk0XWMMMMMM
MMMMMMMMMMMMMWNN0d:,........''';;..,,........';;,'','',;;:codxkOO0KNWWMMMMMMMMMM
MMMMMMMMMMMMMMMMMWX0xl;,...........',...,...':;;;;;::codk0XNWWMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMWXOdc,'..',,,,,;;,,,'.,;ccldkOOKXNWMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMWN0xoc:cccccldkxxkOOO0KXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMN0xc;;::cxXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
`7MMF' `7MMF'
MM MM
MM MM ,6"Yb. M""MMV .gP"Ya 7MMpMMMb.
MMmmmmmmMM 8) MM ' AMV ,M' Yb MM MM
MM MM ,pm9MM AMV 8M~~~~~' MM MM
MM MM 8M MM AMV ,YM. , MM MM
.JMML. .JMML.`Moo9^Yo.AMMmmmM `Mbmmd'.JMML JMML.
Welcome to the Hazen Command Line Interface
Usage:
hazen <task> <folder> [--measured_slice_width=<mm>] [--report] [--calc_t1 | --calc_t2] [--plate_number=<n>] [--show_template_fit]
[--show_relax_fits] [--show_rois] [--log=<lvl>] [--verbose]
hazen -h|--help
hazen -v|--version
Options:
<task> snr | slice_position | slice_width | spatial_resolution | uniformity | ghosting | relaxometry | snr_map
<folder>
--report
"""
import logging
import os
import pprint
import importlib
import pydicom
from docopt import docopt
import numpy as np
import cv2
from hazenlib.tools import is_dicom_file
__version__ = '0.5.2'
import hazenlib.exceptions
EXCLUDED_FILES = ['.DS_Store']
def rescale_to_byte(array):
image_histogram, bins = np.histogram(array.flatten(), 255)
cdf = image_histogram.cumsum() # cumulative distribution function
cdf = 255 * cdf / cdf[-1] # normalize
# use linear interpolation of cdf to find new pixel values
image_equalized = np.interp(array.flatten(), bins[:-1], cdf)
return image_equalized.reshape(array.shape).astype('uint8')
#
def is_enhanced_dicom(dcm: pydicom.Dataset) -> bool:
"""
Parameters
----------
dcm
Returns
-------
bool
Raises
------
Exception
Unrecognised SOPClassUID
"""
if dcm.SOPClassUID == '1.2.840.10008.5.1.4.1.1.4.1':
return True
elif dcm.SOPClassUID == '1.2.840.10008.5.1.4.1.1.4':
return False
else:
raise Exception('Unrecognised SOPClassUID')
def get_manufacturer(dcm: pydicom.Dataset) -> str:
supported = ['ge', 'siemens', 'philips', 'toshiba', 'canon']
manufacturer = dcm.Manufacturer.lower()
for item in supported:
if item in manufacturer:
return item
raise Exception(f'{manufacturer} not recognised manufacturer')
def get_average(dcm: pydicom.Dataset) -> float:
if is_enhanced_dicom(dcm):
averages = dcm.SharedFunctionalGroupsSequence[0].MRAveragesSequence[0].NumberOfAverages
else:
averages = dcm.NumberOfAverages
return averages
def get_bandwidth(dcm: pydicom.Dataset) -> float:
"""
Returns PixelBandwidth
Parameters
----------
dcm: pydicom.Dataset
Returns
-------
bandwidth: float
"""
bandwidth = dcm.PixelBandwidth
return bandwidth
def get_num_of_frames(dcm: pydicom.Dataset) -> int:
"""
Returns number of frames of dicom object
Parameters
----------
dcm: pydicom.Dataset
DICOM object
Returns
-------
"""
if len(dcm.pixel_array.shape) > 2:
return dcm.pixel_array.shape[0]
elif len(dcm.pixel_array.shape) == 2:
return 1
def get_slice_thickness(dcm: pydicom.Dataset) -> float:
if is_enhanced_dicom(dcm):
try:
slice_thickness = dcm.PerFrameFunctionalGroupsSequence[0].PixelMeasuresSequence[0].SliceThickness
except AttributeError:
slice_thickness = dcm.PerFrameFunctionalGroupsSequence[0].Private_2005_140f[0].SliceThickness
except Exception:
raise Exception('Unrecognised metadata Field for Slice Thickness')
else:
slice_thickness = dcm.SliceThickness
return slice_thickness
def get_pixel_size(dcm: pydicom.Dataset) -> (float, float):
manufacturer = get_manufacturer(dcm)
try:
if is_enhanced_dicom(dcm):
dx, dy = dcm.PerFrameFunctionalGroupsSequence[0].PixelMeasuresSequence[0].PixelSpacing
else:
dx, dy = dcm.PixelSpacing
except:
print('Warning: Could not find PixelSpacing..')
if 'ge' in manufacturer:
fov = get_field_of_view(dcm)
dx = fov / dcm.Columns
dy = fov / dcm.Rows
else:
raise Exception('Manufacturer not recognised')
return dx, dy
def get_TR(dcm: pydicom.Dataset) -> (float):
"""
Returns Repetition Time (TR)
Parameters
----------
dcm: pydicom.Dataset
Returns
-------
TR: float
"""
try:
TR = dcm.RepetitionTime
except:
print('Warning: Could not find Repetition Time. Using default value of 1000 ms')
TR = 1000
return TR
def get_rows(dcm: pydicom.Dataset) -> (float):
"""
Returns number of image rows (rows)
Parameters
----------
dcm: pydicom.Dataset
Returns
-------
rows: float
"""
try:
rows = dcm.Rows
except:
print('Warning: Could not find Number of matrix rows. Using default value of 256')
rows = 256
return rows
def get_columns(dcm: pydicom.Dataset) -> (float):
"""
Returns number of image columns (columns)
Parameters
----------
dcm: pydicom.Dataset
Returns
-------
columns: float
"""
try:
columns = dcm.Columns
except:
print('Warning: Could not find matrix size (columns). Using default value of 256.')
columns = 256
return columns
def get_field_of_view(dcm: pydicom.Dataset):
# assumes square pixels
manufacturer = get_manufacturer(dcm)
if 'ge' in manufacturer:
fov = dcm[0x19, 0x101e].value
elif 'siemens' in manufacturer:
fov = dcm.Columns * dcm.PixelSpacing[0]
elif 'philips' in manufacturer:
if is_enhanced_dicom(dcm):
fov = dcm.Columns * dcm.PerFrameFunctionalGroupsSequence[0].PixelMeasuresSequence[0].PixelSpacing[0]
else:
fov = dcm.Columns * dcm.PixelSpacing[0]
elif 'toshiba' in manufacturer:
fov = dcm.Columns * dcm.PixelSpacing[0]
else:
raise NotImplementedError('Manufacturer not ge,siemens, toshiba or philips so FOV cannot be calculated.')
return fov
def parse_relaxometry_data(task, arguments, dicom_objects, report): #def parse_relaxometry_data(arguments, dicom_objects, report): #
# Relaxometry arguments
relaxometry_cli_args = {'--calc_t1', '--calc_t2', '--plate_number',
'--show_template_fit', '--show_relax_fits',
'--show_rois', '--verbose'}
# Pass arguments with dictionary, stripping initial double dash ('--')
relaxometry_args = {}
for key in relaxometry_cli_args:
relaxometry_args[key[2:]] = arguments[key]
return task.main(dicom_objects, report_path = report,
**relaxometry_args)
def main():
arguments = docopt(__doc__, version=__version__)
task = importlib.import_module(f"hazenlib.{arguments['<task>']}")
folder = arguments['<folder>']
files = [os.path.join(folder, x) for x in os.listdir(folder) if x not in EXCLUDED_FILES]
dicom_objects = [pydicom.read_file(x, force=True) for x in files if is_dicom_file(x)]
pp = pprint.PrettyPrinter(indent=4, depth=1, width=1)
if arguments['--report']:
report = True
else:
report = False
log_levels = {
"critical": logging.CRITICAL,
"debug": logging.DEBUG,
"info": logging.INFO,
"warning": logging.WARNING,
"error": logging.ERROR
}
if arguments['--log'] in log_levels.keys():
level = log_levels[arguments['--log']]
logging.getLogger().setLevel(level)
else:
# logging.basicConfig()
logging.getLogger().setLevel(logging.INFO)
if not arguments['<task>'] == 'snr' and arguments['--measured_slice_width']:
raise Exception("the (--measured_slice_width) option can only be used with snr")
elif arguments['<task>'] == 'snr' and arguments['--measured_slice_width']:
measured_slice_width = float(arguments['--measured_slice_width'])
result = task.main(dicom_objects, measured_slice_width, report_path=report)
elif arguments['<task>'] == 'relaxometry':
result = parse_relaxometry_data(task, arguments, dicom_objects, report)
else:
result = task.main(dicom_objects, report_path=report)
return pp.pformat(result)
def entry_point():
result = main()
print(result)