classdef ECGtask_QRS_detections_post_process < ECGtask % ECGtask for ECGwrapper (for Matlab) % --------------------------------- % % Description: % % Abstract class for defining ECGtask interface % % Adding user-defined QRS detectors: % A QRS detector that has the following interface can be added to the task: % % [positions_single_lead, position_multilead] = your_ECG_delineator( ECG_matrix, ECG_header, progress_handle, payload_in); % % where the arguments are: % + ECG_matrix, is a matrix size [ECG_header.nsamp ECG_header.nsig] % + ECG_header, is a struct with info about the ECG signal, such as: % .freq, the sampling frequency % .desc, description about the signals. % + progress_handle, is a handle to a waitbar object, that can be used % to track the progress within your function. See the % documentation about this class in this kit. % + payload_in, is a user data variable allowed to be sent each call to % your function. It is sent, via the payload property of this % class, for example: % % this_ECG_wrappers.ECGtaskHandle.payload = your_variable; % this_ECG_wrappers.ECGtaskHandle.payload = {your_var1 your_var2}; % this_ECG_wrappers.ECGtaskHandle.payload = load(cached_filenames); % % In the context of delineation, it is thought to be a user % corrected, or "gold quality" QRS location, in order to % improve the wave delineation quality. If "payload_in" is a % struct, this function will automatically filter and time-shift % all QRS detection fields started with the string "corrected_". % For this purpose, QRScorrector task, automatically appends this % string to eachm anually reviewed QRS location series. % % the output of your function must be: % + positions_single_lead, a cell array size ECG_header.nsig with the % QRS sample locations found in each lead. % + position_multilead, a numeric vector with the QRS locations % calculated using multilead rules. % % % Author: Mariano Llamedo Soria (llamedom at {electron.frba.utn.edu.ar; unizar.es} % Version: 0.1 beta % Birthdate : 18/2/2013 % Last update: 18/2/2013 properties(GetAccess = public, Constant) name = 'QRS_detections_post_process'; 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) end properties( Access = private ) tmp_path_local end properties post_proc_func progress_handle payload tmp_path CalculatePerformance = false; end methods function obj = ECGtask_QRS_detections_post_process (obj) end function Start(obj, ECG_header, ECG_annotations) obj.started = true; end function payload_out = Process(obj, ECG, ECG_start_offset, ECG_sample_start_end_idx, ECG_header, ECG_annotations, ECG_annotations_start_end_idx ) payload_out = []; 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 % payload property is used in this task to input an external QRS % detector, or manually corrected detections. if( isstruct(obj.payload) ) AnnNames = obj.payload.series_quality.AnnNames(:,1); aux_struct = obj.payload; for fn = rowvec(AnnNames) aux_val = obj.payload.(fn{1}).time - ECG_start_offset + 1; aux_val = aux_val( aux_val >= ECG_sample_start_end_idx(1) & aux_val < ECG_sample_start_end_idx(2) ); aux_struct.(fn{1}).time = aux_val; end else return end for this_func = rowvec(obj.post_proc_func) try obj.progress_handle.checkpoint([ 'User defined function: ' this_func{1}]) % this_func_ptr = eval(['@' this_func]); this_func_ptr = str2func(this_func{1}); this_payload = this_func_ptr( aux_struct, ECG_header, ECG_sample_start_end_idx ); for fn = rowvec(fieldnames(this_payload)) payload_out.(fn{1}) = this_payload.(fn{1}); end catch aux_ME disp_string_framed(2, sprintf('User-function "%s" failed in recording %s', this_func{1}, ECG_header.recname ) ); report = getReport(aux_ME); fprintf(2, 'Error report:\n%s', report); end end obj.progress_handle.checkpoint('Adding quality metrics') % Add QRS detections quality metrics, Names, etc. payload_out = calculateSeriesQuality(payload_out, ECG_header, ECG_sample_start_end_idx ); % offset in time AnnNames = payload_out.series_quality.AnnNames(:,1); for fn = rowvec(AnnNames) aux_val = payload_out.(fn{1}).time + ECG_start_offset - 1; payload_out.(fn{1}).time = aux_val; end % calculate performance if( obj.CalculatePerformance ) AnnNames = payload_out.series_quality.AnnNames(:,1); cant_lead_name = size(AnnNames,1); payload_out.series_performance.conf_mat = zeros(2,2,cant_lead_name); if(isempty(ECG_annotations)) disp_string_framed(2, sprintf('Trusted references not found for %s', ECG_header.recname) ); else % offset refs, produced anns were already shifted ECG_annotations.time = ECG_annotations.time + ECG_start_offset - 1; for kk = 1:cant_lead_name payload_out.series_performance.conf_mat(:,:,kk) = bxb(ECG_annotations, payload_out.(AnnNames{kk}).time, ECG_header ); end end end obj.progress_handle.checkpoint('Done') 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) payload = ConcatenateQRSdetectionPayloads(obj, plA, plB); end %% property restriction functions function set.post_proc_func(obj, x) if( ischar(x) ) if( exist(x) == 2 ) obj.post_proc_func = cellstr(x); else disp_string_framed(2, sprintf('Function "%s" is not reachable in path.', x)); fprintf(1, 'Make sure that exist(%s) == 2\n',x); end elseif( iscellstr(x) ) if( any( cellfun( @(a)(exist(a)), x) == 2) ) obj.post_proc_func = x; else disp_string_framed(2, sprintf('Function "%s" is not reachable in path.', x)); fprintf(1, 'Make sure that exist(%s) == 2\n',x); end else warning('ECGtask_QRS_detections_post_process:BadArg', 'post_proc_func must be a string.'); 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_detections_post_process:BadArg', ['Could not create ' x ]); end end else warning('ECGtask_QRS_detections_post_process: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_detections_post_process:BadArg', ['Could not create ' x ]); end end else warning('ECGtask_QRS_detections_post_process:BadArg', 'tmp_path_local must be a string.'); end end end methods ( Access = private ) end end