-
Notifications
You must be signed in to change notification settings - Fork 0
/
rdann.m
266 lines (247 loc) · 7.98 KB
/
rdann.m
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
function varargout=rdann(varargin)
%
% [ann,anntype,subtype,chan,num,comments]=rdann(recordName, annotator, C, N, N0, AT)
%
% Wrapper to WFDB RDANN:
% http://www.physionet.org/physiotools/wag/rdann-1.htm
%
% NOTE: The WFDB Toolbox uses 0 based index, and MATLAB uses 1 based index.
% Due to this difference annotation values ('ann') are shifted inside
% this function in order to be compatible with the WFDB native
% library. The MATLAB user should leave the indexing conversion to
% the WFDB Toolbox.
%
% Reads a WFDB annotation and returns:
%
%
% ann
% Nx1 integer vector of the annotation locations in samples
% with respect to the beginning of the record.
% To convert this vector to a string of time stamps see WFDBTIME.
%
% anntype
% Nx1 character vector describing the annotation types.
% For a list of standard annotation codes used by PhyioNet, see:
% http://www.physionet.org/physiobank/annotations.shtml
%
% subtype
% Nx1 integer vector describing annotation subtype.
%
% chan
% Nx1 integer vector describing annotation channel.
%
% num
% Nx1 integer vector describing annotation NUM.
%
% comments
% Nx1 cell of strings describing annotation comments.
%
%
% Required Parameters:
%
% recorName
% String specifying the name of the record in the WFDB path or
% in the current directory.
%
% annotator
% String specifying the name of the annotation file in the WFDB path or
% in the current directory.
%
% Optional Parameters are:
%
% C
% An integer scalar. Return only the annotations with chan = C.
% N
% An integer specifying the sample number at which to stop reading the
% record file. Default read all.
% N0
% A 1x1 integer specifying the sample number at which to start reading the
% annotion file. Default = 1, begining of the record.
%
% AT
% The anntype character. Return only annotations with subtype = S.
% Default is empty, which returns all annotations.
%
%
%
% **OPTIMIZATION NOTE: This function has been optimized for cases in which
% only the annotation sample vector is required, ie for function calls of
% the type:
%
% ann=rdann(recordName, annotator, C, N, N0, AT);
%
% For these cases, no string parsing is required and the formatting is
% done at the Java level, increasing substantially the processing speed.
%
%
%
% Written by Ikaro Silva, 2013
% Last Modified: January 4, 2017
% Version 2.1
% Since 0.0.1
%
% %Example 1- Read a signal and annotation from PhysioNet's Remote server:
%[signal,Fs,tm]=rdsamp('challenge/2013/set-a/a01');
%[ann]=rdann('challenge/2013/set-a/a01','fqrs');
%plot(tm,signal(:,1));hold on;grid on
%plot(tm(ann),signal(ann,1),'ro','MarkerSize',4)
%
%%Example 2- Read annotation from the first 500 samples only
% ann=rdann('mitdb/100','atr',[],500);
%
%
%%Example 3- Read annotations with anntype = 'V' only.
% annV=rdann('mitdb/100', 'atr', [],[],[],'V');
%
%
% See also wfdbtime, wrann
%endOfHelp
persistent javaWfdbExec config
if(isempty(javaWfdbExec))
javaWfdbExec=getWfdbClass('rdann');
[~,config]=wfdbloadlib;
end
%Set default pararamter values
% [ann, anntype, subtype, chan, num, comments] = rdann(recordName, annotator, C, N, N0, AT)
inputs={'recordName','annotator','C','N','N0','AT'};
outputs={'ann','anntype','subtype','chan','num','comments'};
N=[];
N0=[];
C=[];
AT=[];
for n=1:nargin
if(~isempty(varargin{n}))
eval([inputs{n} '=varargin{n};'])
end
end
%Remove file extension if present
if(length(recordName)>4 && strcmp(recordName(end-3:end),'.dat'))
recordName=recordName(1:end-4);
end
wfdb_argument={'-r',recordName,'-a',annotator};
if(~isempty(N0) && N0>1)
%-1 is necessary because WFDB is 0 based indexed.
%RDANN expects timestamp, so convert from sample to timestamp
start_time=wfdbtime(recordName,N0-1);
if(~isempty(start_time{end}))
wfdb_argument{end+1}='-f';
wfdb_argument{end+1}=[start_time{1}];
else
error(['Could not get record header information to find start time.'])
end
end
if(~isempty(N))
%-1 is necessary because WFDB is 0 based indexed.
%RDANN expects timestamp, so convert from sample to timestamp
end_time=wfdbtime(recordName,N-1);
if(~isempty(end_time{end}))
wfdb_argument{end+1}='-t';
wfdb_argument{end+1}=[end_time{1}];
else
error(['Could not get record header information to find stop time.'])
end
end
if(~isempty(AT))
wfdb_argument{end+1}='-p';
%-1 is necessary because WFDB is 0 based indexed.
wfdb_argument{end+1}=AT;
end
if(~isempty(C))
wfdb_argument{end+1}='-c ';
%-1 is necessary because WFDB is 0 based indexed.
wfdb_argument{end+1}=[num2str(C-1)];
end
if(nargout ==1)
%Optmize the parsing for cases in which we are interested only in the sample number
%annotation values
wfdb_argument{end+1}='-e'; % Ensure first column is just elapsed time so it can be skipped.
ann=javaWfdbExec.execToDoubleArray(wfdb_argument);
if(config.inOctave)
ann=java2mat(ann);
end
else
%TODO: Improve the parsing of data. To avoid doing this at the ML wrapper
%level! The parsing assumes each line starts with a "[" and that not "["
%occurs at the comment.
%outputs={ann,anntype,subtype,chan,num,comments};
dataJava=javaWfdbExec.execToStringList(wfdb_argument);
data=dataJava.toArray();
N=length(data);
ann=zeros(N,1);
anntype=[]; % Size to be defined at runtime
subtype=zeros(N,1);
chan=zeros(N,1);
num=zeros(N,1);
comments=cell(N,1);
str=char(data(1));
if(~isempty(strfind(str,'init: can''t open header for record')))
error(str)
end
if(~isempty(str) && strcmp(str(1),'['))
% Absolute time stamp. Also possibly a date stamp
% right after the timestamp such as:
% [00:11:30.628 09/11/1989] 157 N 0 1 0
% but not always. The following case is also possible:
% [00:11:30.628] 157 N 0 1 0
%
% So we remove the everything between [ * ] prior to parsing
for n=1:N
str=char(data(n));
if(~isempty(str))
del_str=findstr(str,']');
str(1:del_str)=[];
C=textscan(str,'%d %s %d %d %d',1);
ann(n)=C{1};
if(isempty(anntype))
T=size(C{2},2);
anntype=zeros(N,T);
end
CN=length(char(C{2}));
anntype(n,1:CN)=char(C{2});
subtype(n)=C{3};
chan(n)=C{4}+1;%Convert to MATLAB indexing
if(~isempty(C{5}))
num(n)=C{5};
end
tabpos=findstr(str,char(9));
if(tabpos)
comments{n}=str(tabpos(1)+1:end);
end
end
end
else
%In this case there is only a relative timestamp such as:
% 0:00.355 355 N 0 0 0
str=data(1);
if(~isempty(strfind(str,['annopen: can''t read annotator'])))
error(str)
end
for n=1:N
str=char(data(n));
if(~isempty(str))
C=textscan(str,'%s %d %s %d %d %d',1);
ann(n)=C{2};
if(isempty(anntype))
T=size(C{3}{:},2);
anntype=zeros(N,T);
end
CN=length(C{3}{:});
anntype(n,1:CN)=C{3}{:};
subtype(n)=C{4};
chan(n)=C{5}+1;%Convert to MATLAB indexing
if(~isempty(C{6}))
num(n)=C{6};
end
tabpos=findstr(str,char(9));
if(tabpos)
comments{n}=str(tabpos(1)+1:end);
end
end
end
end
anntype=char(anntype);
end
ann=ann+1; %Convert to MATLAB indexing
for n=1:nargout
eval(['varargout{n}=' outputs{n} ';'])
end