ECG-Kit 1.0
(17,726 bytes)
classdef ECGtask_classification_features_calc < 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/2/2013
% Last update: 18/2/2013
properties(GetAccess = public, Constant)
name = 'ECGclassificationTask';
target_units = 'uV';
doPayload = true;
% if user = memory;
% memory_constant is the fraction respect to user.MaxPossibleArrayBytes
% which determines the maximum input data size.
end
properties(GetAccess = public, SetAccess = private)
true_labels
memory_constant = 0.15;
started
end
properties( Access = private, Constant)
sampling_rate = 360; %Hz
fnPayload = {'featMat_clust' 'featMat_ldc' 'ECG' 'QRS_locations' 'RR_intervals'};
fnFMfile = {'featMat_clust' 'featMat_ldc'};
fnECGfile = {'ECG' 'QRS_locations' 'RR_intervals'};
SmallValue = 0.0001;
end
properties( Access = private)
delayLP
q_filters
LP
LPsize
scale_idx
scales
series
cant_QRS_locations
end
properties
progress_handle
recording_format
class_labeling
autovec
tmp_path
end
methods
function obj = ECGclassificationTask(obj)
% not implemented
end
function Start(obj, ECG_header, ECG_annotations)
% the dimension of the input block should be affected for the
% internal sampling rate, since a resampling is performed
% internally.
obj.memory_constant = obj.memory_constant * ECG_header.freq / 360;
%% Calculate accesories signals
%load low-pass preprocessing filter.
load( ['LP Fs ' num2str(obj.sampling_rate) 'Hz - Fc 35Hz - 80db stop.mat' ]);
obj.LPsize = length(LP.numerator);
obj.delayLP = round((obj.LPsize-1)/2);
obj.LP = LP;
% Wavelet transform filter bank design
obj.scales = 3:6;
CantScales = length(obj.scales);
MaxScales = max(obj.scales);
obj.scale_idx= nan(MaxScales,1);
filters_cache_filename = ['wt_filters_' num2str(obj.scales) ' scales_' num2str(obj.sampling_rate) ' Hz.mat' ];
if( exist(filters_cache_filename, 'file') )
aux = load( filters_cache_filename );
obj.q_filters = aux.q_filters;
else
obj.q_filters = qs_filter_design(obj.scales, obj.sampling_rate);
end
for ii = 1:CantScales
%indice para saber que escala corresponde a cada columna de MyWavelets
obj.scale_idx(obj.scales(ii)) = ii;
end
if( isfield( ECG_annotations, 'anntyp'))
obj.true_labels = ECG_annotations.anntyp;
end
QRS_locations = ECG_annotations.time;
% RR interval sequence
RR_intervals = diff(QRS_locations, 1);
RR_intervals = colvec([ RR_intervals(1); RR_intervals ]);
dRR_intervals = diff(RR_intervals, 1);
dRR_intervals = colvec([ dRR_intervals(2); dRR_intervals(2); dRR_intervals(2:end) ]);
% interval resampling.
obj.series.RR_intervals = round(RR_intervals * obj.sampling_rate / ECG_header.freq);
obj.series.dRR_intervals = round(dRR_intervals * obj.sampling_rate / ECG_header.freq);
obj.series.QRS_locations = QRS_locations;
obj.cant_QRS_locations = length(obj.series.RR_intervals);
end
function payload = Process(obj, ECG, ECG_sample_start_end_idx, ECG_header, ECG_annotations, ECG_annotations_start_end_idx )
payload = [];
%refered to the whole ECG recording
this_iter_QRS_idx = ECG_annotations_start_end_idx(1):ECG_annotations_start_end_idx(2);
RR_intervals = obj.series.RR_intervals(this_iter_QRS_idx);
%refered to this ECG strip
QRS_locations = ECG_annotations.time;
%% Preprocessing
% Update point
obj.progress_handle.checkpoint('Resampling');
%resample to obj.sampling_rate Hz
ECG = resample(ECG, obj.sampling_rate, ECG_header.freq);
QRS_locations = colvec(round(QRS_locations * obj.sampling_rate / ECG_header.freq));
% this_iter_true_labels = true_labels(this_iter_QRS_idx);
this_iter_ECG_resampled_size = size(ECG,1);
if( ECG_header.nsig > 2 )
%multilead approach.
% project ECG and wtECG to obtain PCA components
ECG = ECG * obj.autovec(:,1:2);
% wtECG = cellfun(@(a)( a * obj.autovec(:,1:2) ), mat2cell(wtECG, this_iter_ECG_resampled_size, obj.ECG_header.nsig, ones(1,CantScales) ), 'UniformOutput', false);
% wtECG = cell2mat(wtECG);
end
% Update point
obj.progress_handle.checkpoint('Filtering');
%Low pass filtering @ 35Hz => ECG recovery
ECG = filter(obj.LP, ECG);
%delay compensation.
ECG = [ zeros(obj.delayLP, size(ECG,2) ) ;ECG(obj.LPsize:end,:) ; zeros(obj.delayLP, size(ECG,2))];
%Quito la linea de base.
% ECG = BaselineWanderRemovalMedian( ECG, obj.sampling_rate);
ECG = BaselineWanderRemovalSplines( ECG, QRS_locations, obj.sampling_rate);
% Update point
obj.progress_handle.checkpoint('Wavelet transform calculation');
wtECG = qs_wt(ECG, obj.scales, obj.sampling_rate, obj.q_filters);
%% Features Calculation
% Update point
obj.progress_handle.checkpoint('Features calculation');
this_iter_cant_QRS = length(QRS_locations);
this_iter_seq_idx = 1:this_iter_cant_QRS;
% log(RRactual)
%%%%%%%%%%%%%%%
featMat_ldc = log(colvec(RR_intervals)/obj.sampling_rate);
obj.progress_handle.checkpoint('Features calculation');
% log(RRpost)
%%%%%%%%%%%%%%%
featMat_ldc = [ featMat_ldc log(colvec(obj.series.RR_intervals(min(obj.cant_QRS_locations, this_iter_QRS_idx+1)))/obj.sampling_rate)];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% log(RRmean1)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)( findStartEnd( obj.series.QRS_locations >= (obj.series.QRS_locations(a) - 1*60*obj.sampling_rate) & ...
obj.series.QRS_locations <= obj.series.QRS_locations(a) )), ...
this_iter_QRS_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a)(mean(obj.series.RR_intervals(a(1):a(2)))/obj.sampling_rate), aux_idx);
featMat_ldc = [ featMat_ldc colvec(log(aux_featval)) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% log(RRmean20)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)( findStartEnd( obj.series.RR_intervals >= (obj.series.RR_intervals(a) - 20*60*obj.sampling_rate) & ...
obj.series.RR_intervals <= obj.series.RR_intervals(a) )), ...
this_iter_QRS_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a)(mean(obj.series.RR_intervals(a(1):a(2)))/obj.sampling_rate), aux_idx);
featMat_ldc = [ featMat_ldc colvec(log(aux_featval)) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% AutoCorr1PlanoWTZeroCross
%%%%%%%%%%%%%%%
% AutoCorr1PlanoWTModMaxPos
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)( [ max(1, QRS_locations(a) - round(0.08*obj.sampling_rate)) ...
min(this_iter_ECG_resampled_size, QRS_locations(a) + round(0.08*obj.sampling_rate)) ]) , ...
this_iter_seq_idx , 'UniformOutput', false);
%calculate PCA matrix in this slices for feature
%AutoCorr1PlanoWTModMaxPos
aux_idx2 = arrayfun(@(a)( [ max(1, QRS_locations(a) - round(0.13*obj.sampling_rate)) ...
min(this_iter_ECG_resampled_size, QRS_locations(a) + round(0.2*obj.sampling_rate)) ]) , ...
this_iter_seq_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a,b)(squeeze(wtECG(b(1):b(2),:,obj.scale_idx(4))) * autovec_calculation(squeeze(wtECG(a(1):a(2),:,obj.scale_idx(4)))) ), aux_idx, aux_idx2, 'UniformOutput', false);
[ aux_mp aux_zc ] = cellfun(@(a)(CalcModMaxPos(a(:,1))), aux_featval );
featMat_ldc = [ featMat_ldc colvec(aux_zc) colvec(aux_mp) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% AutoCorr2PlanoWTZeroCross
%%%%%%%%%%%%%%%
% AutoCorr2PlanoWTModMaxPos
%%%%%%%%%%%%%%%
[ aux_mp aux_zc ] = cellfun(@(a)(CalcModMaxPos(a(:,2))), aux_featval );
featMat_ldc = [ featMat_ldc colvec(aux_zc) colvec(aux_mp) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
%calculate clustering features.
% log(RRactual)
%%%%%%%%%%%%%%%
featMat_clust = featMat_ldc(:,1);
% Update point
obj.progress_handle.checkpoint('Features calculation');
% log(RRant)
%%%%%%%%%%%%%%%
featMat_clust = [ featMat_clust log(colvec(obj.series.RR_intervals(max(1, this_iter_QRS_idx-1)))/obj.sampling_rate)];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% log(Prematuridad_loc)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)(max(1,a-1):min(obj.cant_QRS_locations,a+1)), this_iter_QRS_idx, 'UniformOutput', false);
%tengo que contemplar los casos extremos
if( length(aux_idx{1}) < 3 )
aux_idx{1} = [1 aux_idx{1}];
end
if( length(aux_idx{end}) < 3 )
aux_idx{end} = [aux_idx{end} obj.cant_QRS_locations];
end
aux_featval = cellfun(@(a)( obj.series.RR_intervals(a(2))/sum(obj.series.RR_intervals(a)) ), aux_idx);
featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% log(AbsRRlocalVar)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)(max(1,a-1):min(obj.cant_QRS_locations,a+1)), this_iter_QRS_idx, 'UniformOutput', false);
if( length(aux_idx{1}) < 3 )
aux_idx{1} = [1 aux_idx{1}];
end
if( length(aux_idx{end}) < 3 )
aux_idx{end} = [aux_idx{end} obj.cant_QRS_locations];
end
aux_featval = cellfun(@(a)(obj.SmallValue+sum(abs(obj.series.dRR_intervals(a)))/obj.sampling_rate), aux_idx);
featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% log(RRmean20)
%%%%%%%%%%%%%%%
featMat_clust = [ featMat_clust featMat_ldc(:,4) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% log(QRS_max_scale_proj12)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)( [ max(1, QRS_locations(a) - round(0.08*obj.sampling_rate)) ...
min(this_iter_ECG_resampled_size, QRS_locations(a) + round(0.08*obj.sampling_rate)) ]) , ...
this_iter_seq_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a)( Calc_QRS_max_scale_proj( wtECG(a(1):a(2),:,:), obj.scales) ), aux_idx);
% iAreaAbs = squeeze(sum(abs(wtAux)))';
% MaxProjArea = scales * iAreaAbs ./ sum(iAreaAbs);
featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];
% Update point
obj.progress_handle.checkpoint('Features calculation');
% AutoCorr1PlanoWTModMaxPos
%%%%%%%%%%%%%%%
featMat_clust = [ featMat_clust featMat_ldc(:,6) ];
% CrossCorr_QRST_ModMaxVal13
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)( [max(1, QRS_locations(a) - round(0.13*obj.sampling_rate)) ...
min( [ this_iter_ECG_resampled_size, ...
QRS_locations(a) + round(0.4*obj.sampling_rate), ...
QRS_locations(a) + RR_intervals(a) - round(0.13*obj.sampling_rate) ] ) ]), ...
this_iter_seq_idx, 'UniformOutput', false);
if( ECG_header.nsig > 2 )
% wtECG already projected in autovec.
aux_featval = cellfun(@(a)( CalcModMaxVal( squeeze(wtECG(a(1):a(2),:,obj.scale_idx(3))) ) ), aux_idx);
else
aux_featval = cellfun(@(a)( CalcModMaxVal( squeeze(wtECG(a(1):a(2),:,obj.scale_idx(3))) * obj.autovec ) ), aux_idx);
end
featMat_clust = [ featMat_clust colvec(aux_featval) ];
%save ECG for user interface and cluster analysis
for ii = 1:length(obj.fnPayload)
payload.(obj.fnPayload{ii}) = eval(obj.fnPayload{ii});
end
end
function payload = Finish(obj, payload, ECG_header)
% not implemented
end
function plA = Concatenate(obj, plA, plB)
if( isempty(plA) )
for ii = 1:length(obj.fnFMfile)
plA.(obj.fnFMfile{ii}) = plB.(obj.fnFMfile{ii});
end
else
for ii = 1:length(obj.fnFMfile)
plA.(obj.fnFMfile{ii}) = [plA.(obj.fnFMfile{ii}); plB.(obj.fnFMfile{ii})];
end
end
end
end
end
%%%%%%%%%%%%%%%%%%
% Other functions
%%%%%%%%%%%%%%%%%%
function autovec = autovec_calculation(wtECGslice)
mean_wtECGslice = mean(wtECGslice);
wtECGslice_cov = cov( bsxfun( @minus, wtECGslice, mean_wtECGslice ));
[autovec autoval] = eig(wtECGslice_cov);
[~, autoval_idx] = sort(diag(autoval), 'descend');
autovec = autovec(:,autoval_idx);
end
function [ ModMaxPos ZeroCross ] = CalcModMaxPos( wtSignal )
ModMaxPos = nan;
ZeroCross = nan;
lwtSignal = size(wtSignal, 1);
if( all( wtSignal == 0 ) )
return
end
ac1 = conv(wtSignal(:,1), flipud(wtSignal(:,1)));
ac1 = ac1(lwtSignal:end);
ac1 = 1/ac1(1)*ac1;
interp_idx = (1:0.1:lwtSignal)';
ac1_interp = spline( 1:lwtSignal, ac1, interp_idx );
iZeroCrossPos_ac1 = myzerocros(ac1);
if( isempty(iZeroCrossPos_ac1))
return
else
if( iZeroCrossPos_ac1(1) < lwtSignal )
if( sign(ac1(iZeroCrossPos_ac1(1))) ~= sign(ac1(iZeroCrossPos_ac1(1)+1)) )
ZeroCross = iZeroCrossPos_ac1(1) - ac1(iZeroCrossPos_ac1(1)) / ( ac1(iZeroCrossPos_ac1(1)+1) - ac1(iZeroCrossPos_ac1(1)) );
else
ZeroCross = (iZeroCrossPos_ac1(1)-1) - ac1(iZeroCrossPos_ac1(1)-1) / ( ac1(iZeroCrossPos_ac1(1)) - ac1(iZeroCrossPos_ac1(1)-1) );
end
else
return
end
end
ModMaxPos_ac1 = modmax( ac1_interp, 2, 0, 0 );
if( ~isempty(ModMaxPos_ac1) )
valid_ac1_max_idx = find(interp_idx(ModMaxPos_ac1) > ZeroCross);
if( ~isempty(valid_ac1_max_idx) )
ModMaxPos = interp_idx(ModMaxPos_ac1(valid_ac1_max_idx(1)));
else
return
end
else
return
end
end
function ModMaxVal = CalcModMaxVal( wtSignal )
% std_wtSignal = std(wtSignal);
% wtSignal = bsxfun( @times, wtSignal, 1./std_wtSignal);
cc = conv(wtSignal(:,1), flipud(wtSignal(:,2)));
[~, max_pos] = max(abs(cc));
ModMaxVal = cc(max_pos);
end
function start_end_aux = findStartEnd( bAux )
start_aux = find( bAux, 1, 'first' );
end_aux = find( bAux, 1, 'last' );
start_end_aux = [start_aux end_aux];
end
function aux_slice = Calc_QRS_max_scale_proj(aux_slice, scales)
aux_slice = squeeze(sum(abs(aux_slice(:,1,:))))';
aux_slice = scales * colvec(aux_slice) ./ sum(aux_slice);
end