-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathmrdhelper.py
executable file
·278 lines (229 loc) · 9.94 KB
/
mrdhelper.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
# MRD Helper functions
import ismrmrd
import re
import base64
def update_img_header_from_raw(imgHead, rawHead):
"""Populate ImageHeader fields from AcquisitionHeader"""
if rawHead is None:
return imgHead
# # These fields are not translated from the raw header, but filled in
# # during image creation by from_array
# imgHead.data_type =
# imgHead.matrix_size =
# imgHead.channels =
# # This is mandatory, but must be filled in from the XML header,
# # not from the acquisition header
# imgHead.field_of_view =
imgHead.version = rawHead.version
imgHead.flags = rawHead.flags
imgHead.measurement_uid = rawHead.measurement_uid
imgHead.position = rawHead.position
imgHead.read_dir = rawHead.read_dir
imgHead.phase_dir = rawHead.phase_dir
imgHead.slice_dir = rawHead.slice_dir
imgHead.patient_table_position = rawHead.patient_table_position
imgHead.average = rawHead.idx.average
imgHead.slice = rawHead.idx.slice
imgHead.contrast = rawHead.idx.contrast
imgHead.phase = rawHead.idx.phase
imgHead.repetition = rawHead.idx.repetition
imgHead.set = rawHead.idx.set
imgHead.acquisition_time_stamp = rawHead.acquisition_time_stamp
imgHead.physiology_time_stamp = rawHead.physiology_time_stamp
# Defaults, to be updated by the user
imgHead.image_type = ismrmrd.IMTYPE_MAGNITUDE
imgHead.image_index = 1
imgHead.image_series_index = 0
imgHead.user_float = rawHead.user_float
imgHead.user_int = rawHead.user_int
return imgHead
def get_userParameterLong_value(metadata, name):
"""Get a value from MRD Header userParameterLong (returns None if key not found)"""
if metadata.userParameters is not None:
for param in metadata.userParameters.userParameterLong:
if param.name == name:
return int(param.value)
return None
def get_userParameterDouble_value(metadata, name):
"""Get a value from MRD Header userParameterDouble (returns None if key not found)"""
if metadata.userParameters is not None:
for param in metadata.userParameters.userParameterDouble:
if param.name == name:
return float(param.value)
return None
def get_userParameterString_value(metadata, name):
"""Get a value from MRD Header userParameterDouble (returns None if key not found)"""
if metadata.userParameters is not None:
for param in metadata.userParameters.userParameterDouble:
if param.name == name:
return float(param.value)
return None
def get_userParameterBase64_value(metadata, name):
"""Get a value from MRD Header userParameterBase64 (returns None if key not found)"""
if metadata.userParameters is not None:
for param in metadata.userParameters.userParameterBase64:
if param.name == name:
return base64.b64decode(param.value).decode('utf-8')
return None
def get_meta_value(meta, key):
"""Get a value from MRD Meta Attributes (returns None if key not found)"""
if key in meta.keys():
return meta[key]
else:
return None
def extract_minihead_bool_param(miniHead, name):
"""Extract a bool parameter from the serialized text of the ICE MiniHeader"""
val = extract_minihead_param(miniHead, name, 'ParamBool')
if val is None:
return False
elif val.strip('" ').lower() == 'true'.lower():
return True
else:
return False
def extract_minihead_long_param(miniHead, name):
"""Extract a long parameter from the serialized text of the ICE MiniHeader"""
val = extract_minihead_param(miniHead, name, 'ParamLong')
if val is None:
return int(0)
else:
return int(val)
def extract_minihead_double_param(miniHead, name):
"""Extract a double parameter from the serialized text of the ICE MiniHeader"""
val = extract_minihead_param(miniHead, name, 'ParamDouble')
if val is None:
return float(0)
else:
return float(val)
def extract_minihead_string_param(miniHead, name):
"""Extract a string parameter from the serialized text of the ICE MiniHeader"""
val = extract_minihead_param(miniHead, name, 'ParamString')
return val.strip(' "')
def extract_minihead_param(miniHead, name, strType):
"""Extract a string parameter from the serialized text of the ICE MiniHeader"""
expr = r'(?<=<' + strType + r'."' + name + r'">)\s*[^}]*\s*'
res = re.search(expr, miniHead)
if res is None:
return None
# Strip off beginning '{' and whitespace, then split on newlines
values = res.group(0).strip('{\n ').split('\n')
# Lines beginning with <> are properties -- ignore them
values = [val for val in values if bool(re.search(r'^\s*<\w+>', val)) is False]
if len(values) != 1:
return None
else:
return values[0]
def get_json_param(config, field, type=None):
"""Get a field from the JSON additional config with optional type casting"""
if not isinstance(config, dict):
return None
if not 'parameters' in config:
return None
if not field in config['parameters']:
return None
value = config['parameters'][field]
if type is None:
return value
elif type == 'int':
return int(value)
elif (type == 'float') or (type == 'double'):
return float(value)
elif (type == 'string') or (type == 'choice'):
return str(value)
elif (type == 'bool') or (type == 'boolean'):
if isinstance(value, bool):
return value
else:
return ('true' in value.lower())
else:
raise Exception("'type' must be None (no type conversion), int, float, string, or bool")
def create_roi(x, y, rgb = (1, 0, 0), thickness = 1, style: int = 0, visibility: int = 1):
"""
Create an MRD-formatted ROI
Parameters:
- x (1D ndarray) : x coordinates in units of pixels, with (0,0) at the top left
- y (1D ndarray) : y coordinates in units of pixels, matching the length of x
- rgb (3 item tuple) : Colour as an (red, green, blue) tuple normalized to 1
- thickness (float) : Line thickness
- style (int) : Line style (0 = solid, 1 = dashed)
- visibility (int) : Line visibility (0 = false, 1 = true)
Returns:
- roi (string list) : MRD-formatted ROI, intended to be stored as a MetaAttribute
with field name starting with "ROI_"
"""
xy = [(x[i], y[i]) for i in range(0, len(x))] # List of (x,y) tuples
roi = []
roi.append('%f' % rgb[0])
roi.append('%f' % rgb[1])
roi.append('%f' % rgb[2])
roi.append('%f' % thickness)
roi.append('%d' % style)
roi.append('%d' % visibility)
for i in range(0, len(xy)):
roi.append('%f' % xy[i][0])
roi.append('%f' % xy[i][1])
return roi
def parse_roi(roi):
"""
Parse an MRD-formatted ROI
Input:
- roi (string list) : MRD-formatted ROI from a MetaAttribute
Output:
- x (1D ndarray) : x coordinates in units of pixels, with (0,0) at the top left
- y (1D ndarray) : y coordinates in units of pixels, matching the length of x
- rgb (3 item tuple) : Colour as an (red, green, blue) tuple normalized to 1
- thickness (float) : Line thickness
- style (int) : Line style (0 = solid, 1 = dashed)
- visibility (int) : Line visibility (0 = false, 1 = true)
"""
if (not isinstance(roi, list)) or (len(roi) < 8) or (len(roi) % 2):
raise Exception("ROI must be a list, have 6 metadata values, at least one coordinate, and an even number of values (x,y pairs)")
fRoi = [float(x) for x in roi]
rgb = tuple(fRoi[0:3])
thickness = fRoi[3]
style = int(fRoi[4])
visibility = int(fRoi[5])
x = fRoi[6::2]
y = fRoi[7::2]
return x, y, rgb, thickness, style, visibility
def create_text(x, y, rgb = (1, 0, 0), visibility: int = 1, string = ''):
"""
Create an MRD-formatted text object
Parameters:
- x (float) : x coordinate in units of pixels, with (0,0) at the top left
- y (float) : y coordinate in units of pixels
- rgb (3 item tuple) : Colour as an (red, green, blue) tuple normalized to 1
- visibility (int) : Line visibility (0 = false, 1 = true)
- string (string) : Text string
Returns:
- txt (string list) : MRD-formatted text, intended to be stored as a MetaAttribute
with field name starting with "Text_"
"""
txt = []
txt.append('%f' % rgb[0])
txt.append('%f' % rgb[1])
txt.append('%f' % rgb[2])
txt.append('%f' % x)
txt.append('%f' % y)
txt.append('%d' % visibility)
txt.append('%s' % string)
return txt
def parse_text(txt):
"""
Parse an MRD-formatted text object
Input:
- txt (string list) : MRD-formatted text from a MetaAttribute
Output:
- x (float) : x coordinate in units of pixels, with (0,0) at the top left
- y (float) : y coordinate in units of pixels
- rgb (3 item tuple) : Colour as an (red, green, blue) tuple normalized to 1
- visibility (int) : Line visibility (0 = false, 1 = true)
- string (string) : Text string
"""
if (not isinstance(txt, list)) or (len(txt) != 7):
raise Exception("txt must be a list that has exactly 7 metadata values")
rgb = tuple([float(x) for x in txt[0:3]])
x = float(txt[3])
y = float(txt[4])
visibility = int(float(txt[5]))
string = txt[6]
return x, y, rgb, visibility, string