ECG-Kit 1.0
(15,039 bytes)
classdef ECGtask_PPG_ABP_detector < ECGtask
% ECGtask for ECGwrapper (for Matlab)
% ---------------------------------
%
% Description:
%
% Abstract class for defining ECGtask interface
%
%
% Author: Mariano Llamedo Soria (llamedom at {electron.frba.utn.edu.ar; unizar.es}
% Version: 0.1 beta
% Birthdate : 18/7/2014
% Last update: 18/7/2014
properties(GetAccess = public, Constant)
name = 'PPG_ABP_detector';
target_units = 'ADCu';
doPayload = true;
end
properties( GetAccess = public, SetAccess = private)
% if user = memory;
% memory_constant is the fraction respect to user.MaxPossibleArrayBytes
% which determines the maximum input data size.
memory_constant = 0.3;
started = false;
end
properties( Access = private, Constant)
cPPGdetectors = {'all-detectors' 'wavePPG' 'wabp' };
cLeadConfiguration = {'all-leads' 'PPG-ABP-only' 'User-defined-leads' };
end
properties( Access = private )
detectors2do
bWFDBdetectors
tmp_path_local
WFDB_bin_path
WFDB_cmd_prefix_str
end
properties
progress_handle
tmp_path
detectors = 'all-detectors';
lead_config = 'PPG-ABP-only';
PPG_ABP_idx = [];
end
methods
function obj = ECGtask_PPG_ABP_detector(obj)
end
function Start(obj, ECG_header, ECG_annotations)
if( strcmpi('all-leads', obj.lead_config) )
obj.lead_config = 'all-leads';
obj.PPG_ABP_idx = 1:ECG_header.nsig;
elseif( strcmpi('PPG-ABP-only', obj.lead_config) )
obj.PPG_ABP_idx = get_PPG_ABP_idx_from_header(ECG_header);
else
if( isempty(obj.PPG_ABP_idx) || ~isnumeric(obj.PPG_ABP_idx) || ~(all(obj.PPG_ABP_idx > 0) && all(obj.PPG_ABP_idx <= ECG_header.nsig)) )
warning('ECGtask_QRS_detection:BadArg', 'Invalid lead indexes. Indexes between 1 and %d\n', ECG_header.nsig);
obj.lead_config = 'all-leads';
obj.PPG_ABP_idx = 1:ECG_header.nsig;
else
obj.lead_config = 'User-defined-leads';
end
end
if( isempty(obj.PPG_ABP_idx) )
return
end
if( strcmpi('all-detectors', obj.detectors) )
obj.detectors2do = obj.cPPGdetectors(2:end);
else
if( ischar(obj.detectors) )
obj.detectors2do = {obj.detectors};
else
obj.detectors2do = obj.detectors;
end
end
obj.bWFDBdetectors = any(strcmpi('wabp', obj.cPPGdetectors));
% local path required to avoid network bottlenecks in distributed filesystems
if( isunix() && exist('/scratch/', 'dir') )
str_username = getenv('USER');
obj.tmp_path_local = ['/scratch/' str_username filesep];
if( ~exist(obj.tmp_path_local, 'dir') )
if(~mkdir(obj.tmp_path_local))
obj.tmp_path_local = '/scratch/';
end
end
obj.tmp_path = tempdir;
else
if( isempty(obj.tmp_path) )
obj.tmp_path = tempdir;
end
obj.tmp_path_local = obj.tmp_path;
end
obj.WFDB_bin_path = init_WFDB_library(obj.tmp_path_local);
obj.WFDB_cmd_prefix_str = WFDB_command_prefix(obj.tmp_path_local);
obj.started = true;
end
function payload = Process(obj, PPG_ABP, ECG_start_offset, ECG_sample_start_end_idx, ECG_header, ECG_annotations, ECG_annotations_start_end_idx, payload_in )
payload = [];
if( ~obj.started )
obj.Start(ECG_header);
if( ~obj.started )
cprintf('*[1,0.5,0]', 'Task %s unable to be started for %s.\n', obj.name, ECG_header.recname);
return
end
end
if(obj.bWFDBdetectors)
% MIT conversion is needed for WFDB detectors.
ECG_header.recname(ECG_header.recname == ' ') = '_';
MIT_filename = [ECG_header.recname '_' num2str(ECG_start_offset) '_' num2str(ECG_header.nsamp+ECG_start_offset-1) ];
ECG_header.recname = MIT_filename;
MIT_filename = [obj.tmp_path_local MIT_filename '.dat'];
fidECG = fopen(MIT_filename, 'w');
try
fwrite(fidECG, PPG_ABP', 'int16', 0 );
fclose(fidECG);
catch MEE
fclose(fidECG);
rethrow(MEE);
end
writeheader(obj.tmp_path_local, ECG_header);
end
cant_PPG_ABP_detectors = length(obj.detectors2do);
for ii = 1:cant_PPG_ABP_detectors
this_detector = obj.detectors2do{ii};
cprintf( 'Blue', [ 'Processing PPG/ABP detector ' this_detector '\n' ] );
%% perform pulse detection
switch( this_detector )
case 'wavePPG'
%% wavePPG delineation
for jj = rowvec(obj.PPG_ABP_idx)
try
PPG_ABP_peak_idx = PPG_pulses_detector( double(PPG_ABP(:,jj)), ECG_header.freq, obj.progress_handle );
PPG_ABP_peak_idx = PPG_ABP_peak_idx( PPG_ABP_peak_idx >= ECG_sample_start_end_idx(1) & PPG_ABP_peak_idx <= ECG_sample_start_end_idx(2)) + ECG_start_offset - 1 ;
% QRS detections in milliseconds
str_aux = strrep(strtrim(ECG_header.desc(jj,:)), ' ', '_' );
payload.(['wavePPG_' str_aux]).time = colvec(PPG_ABP_peak_idx);
catch aux_ME
strAux = sprintf('WavePPG failed in recording %s\n', ECG_header.recname);
strAux = sprintf('%s\n', strAux);
report = getReport(aux_ME);
fprintf(2, '%s\nError report:\n%s', strAux, report);
end
end
case 'wabp'
%% wabp WFDB interface
file_name_orig = [obj.tmp_path_local ECG_header.recname '.wabp' ];
% QRS detection.
for jj = rowvec(obj.PPG_ABP_idx)
if( exist(file_name_orig, 'file') )
delete(file_name_orig)
end
try
% run wabp
[status, ~] = system([ obj.WFDB_cmd_prefix_str 'wabp -r ' ECG_header.recname ' -s ' num2str(jj-1)]);
if( status ~= 0 ); error('ECGtask_PPG_detector:WFDB_error', 'Error calling WFDB function.'); end
catch aux_ME
strAux = sprintf( '%s failed in recording %s in %s\n', this_detector, ECG_header.recname, ECG_header.desc(jj,:) );
report = getReport(aux_ME);
fprintf(2, '%s\nError report:\n%s', strAux, report);
end
if( exist(file_name_orig, 'file') )
% reference comparison
anns_test = [];
try
anns_test = readannot(file_name_orig);
if( isempty(anns_test) )
str_aux = strrep(strtrim(ECG_header.desc(jj,:)), ' ', '_' );
payload.([this_detector '_' str_aux ]).time = [];
else
anns_test = AnnotationFilterConvert(anns_test, 'MIT', 'AAMI');
PPG_ABP_peak_idx = anns_test.time;
PPG_ABP_peak_idx = PPG_ABP_peak_idx( PPG_ABP_peak_idx >= ECG_sample_start_end_idx(1) & PPG_ABP_peak_idx <= ECG_sample_start_end_idx(2)) + ECG_start_offset - 1 ;
str_aux = strrep(strtrim(ECG_header.desc(jj,:)), ' ', '_' );
payload.([this_detector '_' str_aux ]).time = colvec(PPG_ABP_peak_idx);
end
catch aux_ME
if( strcmpi(aux_ME.identifier, 'MATLAB:nomem') )
payload.([this_detector '_' ECG_header.desc(jj,:)]).time = [];
else
strAux = sprintf( '%s failed in recording %s in %s\n', this_detector, ECG_header.recname, ECG_header.desc(jj,:) );
report = getReport(aux_ME);
fprintf(2, '%s\nError report:\n%s', strAux, report);
end
end
end
end
end
end
% Add QRS detections quality metrics, Names, etc.
payload = calculateSeriesQuality(payload, ECG_header, [1 ECG_header.nsamp] + ECG_start_offset - 1 );
% delete intermediate tmp files
if(obj.bWFDBdetectors)
delete([obj.tmp_path_local ECG_header.recname '.*' ]);
end
end
function payload = Finish(obj, payload, ECG_header)
if( isfield(payload, 'series_quality') && isfield(payload.series_quality, 'ratios') )
payload.series_quality.ratios = mean(payload.series_quality.ratios, 2);
end
end
function payload = Concatenate(obj, plA, plB)
if( isempty(plA) )
payload = plB;
else
fields = rowvec(unique( [rowvec(fieldnames(plA)) rowvec(fieldnames(plB))] ));
diff_fields = setdiff(fields, {'series_quality'});
for fn = diff_fields
if( isfield(plA, fn{1}) && isfield(plB, fn{1}) )
payload.(fn{1}).time = [ colvec(plA.(fn{1}).time); colvec(plB.(fn{1}).time) ];
elseif( ~isfield(plA, fn{1}) && isfield(plB, fn{1}) )
payload.(fn{1}).time = colvec(plB.(fn{1}).time);
end
end
aux_idx = find(strcmpi(fields, 'series_quality'));
if( ~isempty(aux_idx) )
if( isfield(plA, 'series_quality') && isfield(plB, 'series_quality') )
if( size(plA.series_quality.ratios,1) == size(plB.series_quality.ratios,1) )
payload.series_quality.ratios = [plA.series_quality.ratios plB.series_quality.ratios];
payload.series_quality.estimated_labs = cellfun(@(a,b)( [colvec(a);colvec(b)] ) , plA.series_quality.estimated_labs, plB.series_quality.estimated_labs, 'UniformOutput', false);
payload.series_quality.AnnNames = plA.series_quality.AnnNames;
else
payload.series_quality.ratios = plA.series_quality.ratios;
payload.series_quality.estimated_labs = plA.series_quality.estimated_labs;
payload.series_quality.AnnNames = plA.series_quality.AnnNames;
end
end
end
end
end
%% property restriction functions
function set.detectors(obj,x)
if( (ischar(x) || iscellstr(x)) && ~isempty(intersect(obj.cPPGdetectors, cellstr(x))) )
obj.detectors = x;
else
warning('ECGtask_QRS_detection:BadArg', 'Invalid detectors.');
end
end
function set.tmp_path_local(obj,x)
if( ischar(x) )
if(exist(x, 'dir'))
obj.tmp_path_local = x;
else
if(mkdir(x))
obj.tmp_path_local = x;
else
warning('ECGtask_QRS_detection:BadArg', ['Could not create ' x ]);
end
end
else
warning('ECGtask_QRS_detection:BadArg', 'tmp_path_local must be a string.');
end
end
function set.tmp_path(obj,x)
if( ischar(x) )
if(exist(x, 'dir'))
obj.tmp_path = x;
else
if(mkdir(x))
obj.tmp_path = x;
else
warning('ECGtask_QRS_detection:BadArg', ['Could not create ' x ]);
end
end
else
warning('ECGtask_QRS_detection:BadArg', 'tmp_path_local must be a string.');
end
end
end
methods ( Access = private )
end
end