@@ -20,6 +20,7 @@ def read_edf(
20
20
header_only = False ,
21
21
verbose = False ,
22
22
rdedfann_flag = False ,
23
+ encoding = "iso8859-1" ,
23
24
):
24
25
"""
25
26
Read a EDF format file into a WFDB Record.
@@ -61,6 +62,9 @@ def read_edf(
61
62
is being called by the user and the file has annotations, then warn
62
63
them that the EDF file has annotations and that they should use
63
64
`rdedfann` instead.
65
+ encoding : str, optional
66
+ The encoding to use for strings in the header. Although the edf
67
+ specification requires ascii strings, some files do not adhere to it.
64
68
65
69
Returns
66
70
-------
@@ -139,7 +143,7 @@ def read_edf(
139
143
edf_file = open (record_name , mode = "rb" )
140
144
141
145
# Version of this data format (8 bytes)
142
- version = struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ()
146
+ version = struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding )
143
147
144
148
# Check to see that the input is an EDF file. (This check will detect
145
149
# most but not all other types of files.)
@@ -152,7 +156,7 @@ def read_edf(
152
156
print ("EDF version number: {}" .format (version .strip ()))
153
157
154
158
# Local patient identification (80 bytes)
155
- patient_id = struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode ()
159
+ patient_id = struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode (encoding )
156
160
if verbose :
157
161
print ("Patient ID: {}" .format (patient_id ))
158
162
@@ -161,12 +165,12 @@ def read_edf(
161
165
# including an abbreviated month name in English and a full (4-digit)
162
166
# year, as is done here if this information is available in the input
163
167
# record. EDF+ requires this.
164
- record_id = struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode ()
168
+ record_id = struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode (encoding )
165
169
if verbose :
166
170
print ("Recording ID: {}" .format (record_id ))
167
171
168
172
# Start date of recording (dd.mm.yy) (8 bytes)
169
- start_date = struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ()
173
+ start_date = struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding )
170
174
if verbose :
171
175
print ("Recording Date: {}" .format (start_date ))
172
176
start_day , start_month , start_year = [int (i ) for i in start_date .split ("." )]
@@ -177,21 +181,21 @@ def read_edf(
177
181
start_year += 100
178
182
179
183
# Start time of recording (hh.mm.ss) (8 bytes)
180
- start_time = struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ()
184
+ start_time = struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding )
181
185
if verbose :
182
186
print ("Recording Time: {}" .format (start_time ))
183
187
start_hour , start_minute , start_second = [
184
188
int (i ) for i in start_time .split ("." )
185
189
]
186
190
187
191
# Number of bytes in header (8 bytes)
188
- header_bytes = int (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ())
192
+ header_bytes = int (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding ))
189
193
if verbose :
190
194
print ("Number of bytes in header record: {}" .format (header_bytes ))
191
195
192
196
# Reserved (44 bytes)
193
197
reserved_notes = (
194
- struct .unpack ("<44s" , edf_file .read (44 ))[0 ].decode ().strip ()
198
+ struct .unpack ("<44s" , edf_file .read (44 ))[0 ].decode (encoding ).strip ()
195
199
)
196
200
if reserved_notes [:5 ] == "EDF+C" :
197
201
# The file is EDF compatible and will work without issue
@@ -209,7 +213,7 @@ def read_edf(
209
213
print ("Free Space: {}" .format (reserved_notes ))
210
214
211
215
# Number of blocks (-1 if unknown) (8 bytes)
212
- num_blocks = int (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ())
216
+ num_blocks = int (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding ))
213
217
if verbose :
214
218
print ("Number of data records: {}" .format (num_blocks ))
215
219
if num_blocks == - 1 :
@@ -218,7 +222,7 @@ def read_edf(
218
222
)
219
223
220
224
# Duration of a block, in seconds (8 bytes)
221
- block_duration = float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ())
225
+ block_duration = float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding ))
222
226
if verbose :
223
227
print (
224
228
"Duration of each data record in seconds: {}" .format (block_duration )
@@ -227,7 +231,7 @@ def read_edf(
227
231
block_duration = 1.0
228
232
229
233
# Number of signals (4 bytes)
230
- n_sig = int (struct .unpack ("<4s" , edf_file .read (4 ))[0 ].decode ())
234
+ n_sig = int (struct .unpack ("<4s" , edf_file .read (4 ))[0 ].decode (encoding ))
231
235
if verbose :
232
236
print ("Number of signals: {}" .format (n_sig ))
233
237
if n_sig < 1 :
@@ -236,7 +240,7 @@ def read_edf(
236
240
# Label (e.g., EEG FpzCz or Body temp) (16 bytes each)
237
241
sig_name = []
238
242
for _ in range (n_sig ):
239
- temp_sig = struct .unpack ("<16s" , edf_file .read (16 ))[0 ].decode ().strip ()
243
+ temp_sig = struct .unpack ("<16s" , edf_file .read (16 ))[0 ].decode (encoding ).strip ()
240
244
if temp_sig == "EDF Annotations" and not rdedfann_flag :
241
245
print (
242
246
"*** This may be an EDF+ Annotation file instead, please see "
@@ -250,7 +254,7 @@ def read_edf(
250
254
transducer_types = []
251
255
for _ in range (n_sig ):
252
256
transducer_types .append (
253
- struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode ().strip ()
257
+ struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode (encoding ).strip ()
254
258
)
255
259
if verbose :
256
260
print ("Transducer Types: {}" .format (transducer_types ))
@@ -259,7 +263,7 @@ def read_edf(
259
263
physical_dims = []
260
264
for _ in range (n_sig ):
261
265
physical_dims .append (
262
- struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ().strip ()
266
+ struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding ).strip ()
263
267
)
264
268
if verbose :
265
269
print ("Physical Dimensions: {}" .format (physical_dims ))
@@ -269,7 +273,7 @@ def read_edf(
269
273
for _ in range (n_sig ):
270
274
physical_min = np .append (
271
275
physical_min ,
272
- float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ()),
276
+ float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding )),
273
277
)
274
278
if verbose :
275
279
print ("Physical Minimums: {}" .format (physical_min ))
@@ -279,7 +283,7 @@ def read_edf(
279
283
for _ in range (n_sig ):
280
284
physical_max = np .append (
281
285
physical_max ,
282
- float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ()),
286
+ float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding )),
283
287
)
284
288
if verbose :
285
289
print ("Physical Maximums: {}" .format (physical_max ))
@@ -289,7 +293,7 @@ def read_edf(
289
293
for _ in range (n_sig ):
290
294
digital_min = np .append (
291
295
digital_min ,
292
- float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ()),
296
+ float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding )),
293
297
)
294
298
if verbose :
295
299
print ("Digital Minimums: {}" .format (digital_min ))
@@ -299,7 +303,7 @@ def read_edf(
299
303
for _ in range (n_sig ):
300
304
digital_max = np .append (
301
305
digital_max ,
302
- float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ()),
306
+ float (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding )),
303
307
)
304
308
if verbose :
305
309
print ("Digital Maximums: {}" .format (digital_max ))
@@ -308,7 +312,7 @@ def read_edf(
308
312
prefilter_info = []
309
313
for _ in range (n_sig ):
310
314
prefilter_info .append (
311
- struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode ().strip ()
315
+ struct .unpack ("<80s" , edf_file .read (80 ))[0 ].decode (encoding ).strip ()
312
316
)
313
317
if verbose :
314
318
print ("Prefiltering Information: {}" .format (prefilter_info ))
@@ -317,14 +321,14 @@ def read_edf(
317
321
samps_per_block = []
318
322
for _ in range (n_sig ):
319
323
samps_per_block .append (
320
- int (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode ())
324
+ int (struct .unpack ("<8s" , edf_file .read (8 ))[0 ].decode (encoding ))
321
325
)
322
326
if verbose :
323
327
print ("Number of Samples per Record: {}" .format (samps_per_block ))
324
328
325
329
# The last 32*nsig bytes in the header are unused
326
330
for _ in range (n_sig ):
327
- struct .unpack ("<32s" , edf_file .read (32 ))[0 ].decode ()
331
+ struct .unpack ("<32s" , edf_file .read (32 ))[0 ].decode (encoding )
328
332
329
333
# Pre-process the acquired data before creating the record
330
334
record_name_out = (
@@ -997,6 +1001,7 @@ def rdedfann(
997
1001
info_only = True ,
998
1002
record_only = False ,
999
1003
verbose = False ,
1004
+ encoding = "iso8859-1" ,
1000
1005
):
1001
1006
"""
1002
1007
This program returns the annotation information from an EDF+ file
@@ -1038,6 +1043,9 @@ def rdedfann(
1038
1043
verbose : bool, optional
1039
1044
Whether to print all the information read about the file (True) or
1040
1045
not (False).
1046
+ encoding : str, optional
1047
+ The encoding to use for strings in the header. Although the edf
1048
+ specification requires ascii strings, some files do not adhere to it.
1041
1049
1042
1050
Returns
1043
1051
-------
@@ -1110,7 +1118,7 @@ def rdedfann(
1110
1118
adjusted_hex = hex (
1111
1119
struct .unpack ("<H" , struct .pack (">H" , chunk + 1 ))[0 ]
1112
1120
)
1113
- annotation_string += bytes .fromhex (adjusted_hex [2 :]).decode ("ascii" )
1121
+ annotation_string += bytes .fromhex (adjusted_hex [2 :]).decode (encoding )
1114
1122
# Remove all of the whitespace
1115
1123
for rep in ["\x00 " , "\x14 " , "\x15 " ]:
1116
1124
annotation_string = annotation_string .replace (rep , " " )
0 commit comments