Waveform Database Software Package (WFDB) for MATLAB and Octave 0.10.0
(8,179 bytes)
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