ECG-Kit 1.0
(19,752 bytes)
classdef featureMatrices < handle
% featureMatrices object for ECGwrapper
% -------------------------------------
%
% Description:
%
%
% Arguments: (specified as ECGwrapper('arg_name1', arg_val1, ... , 'arg_nameN', arg_valN) )
%
% a. ECG specification:
%
% a.1 Recording filename where ECG is stored in a valid format.
%
% + recording_name: ECG recording to be classified.
% + recording_format : Valid ECG format. (MIT, ISHNE, AHA, HES, MAT)
%
% b. Operating modes
%
%
% c. Modifiers
%
%
% Output:
%
% Examples:
%
%
% Limits and Known bugs:
% Probably a lot :( ... but dont panic! send me feedback if you need
% help.
%
% Author: Mariano Llamedo Soria (llamedom at {electron.frba.utn.edu.ar; unizar.es}
% Version: 0.1 beta
% Birthdate : 20/3/2012
% Last update: 20/3/2012
properties(GetAccess = 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
autovec
q_filters
LP
LPsize
scale_idx
scales
global_struct
end
properties (SetAccess = private, GetAccess = public)
%read-only
name
class_labeling
true_labels
%auxiliary indexes
file_count = 1;
File_list = [];
File_idx = [];
QRS_idx = [];
end
properties
%read-write
recording_name
recording_format
progress_hdl
tmp_path
bCache
cant_pids
this_pid
cant_iteration
this_iteration
end
methods
function obj = featureMatrices(class_labeling)
%% Constants and definitions
obj.name = 'feat_matrix_construction';
obj.class_labeling = class_labeling;
end
function Start(obj)
%% 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;
clear LP
% Wavelet transform filter bank design
obj.scales = 1:6;
CantScales = length(obj.scales);
MaxScales = max(obj.scales);
obj.scale_idx= nan(MaxScales,1);
filters_cache_filename = ['wt_filters_' num2str(MaxScales) ' scales_' num2str(obj.sampling_rate) ' Hz.mat' ];
if( exist(filters_cache_filename, 'file') )
aux = load( filters_cache_filename );
obj.q_filters = aux.q_filters;
clear aux
else
obj.q_filters = qs_filter_design(MaxScales, 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
% Update point
obj.progress_hdl.checkpoint('PCA projection');
% obj.autovec = PCA_proj_basis(obj.recording_name, obj.recording_format, obj.q_filters);
obj.autovec = [-0.696609733483207 -0.403719836632163 -0.593081084444745;-0.487887339191286 -0.339521516938205 0.804171053814316;-0.526023595928265 0.849550135686930 0.0395441965529324;];
% data structure to keep data through all iterations.
obj.global_struct = [];
end
function payload = Process(obj, ECG, this_header, this_iter_ECG_relative_start_end_idx, this_iter_annotations)
obj.progress_hdl.checkpoint('Label parsing');
[QRS_locations, obj.true_labels ] = Annotation_process(this_iter_annotations, obj.recording_format, obj.class_labeling);
% 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.
RR_intervals = RR_intervals * obj.sampling_rate / this_header.freq;
dRR_intervals = dRR_intervals * obj.sampling_rate / this_header.freq;
%% Preprocessing
% Update point
obj.progress_hdl.checkpoint('Resampling');
cant_QRS_locations = length(QRS_locations);
this_iter_QRS_seq_idx = 1:cant_QRS_locations;
%resample to obj.sampling_rate Hz
ECG = resample(ECG, obj.sampling_rate, this_header.freq);
QRS_locations = colvec(round(QRS_locations * obj.sampling_rate / this_header.freq));
% this_iter_true_labels = true_labels(this_iter_QRS_seq_idx);
this_iter_ECG_resampled_size = size(ECG,1);
if( this_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_hdl.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_hdl.checkpoint('Wavelet transform calculation');
wtECG = qs_wt(ECG, obj.scales, obj.sampling_rate, obj.q_filters);
%% Features Calculation
% Update point
obj.progress_hdl.checkpoint('Features calculation');
this_iter_seq_idx = 1:length(QRS_locations);
% log(RRactual)
%%%%%%%%%%%%%%%
featMat_ldc = log(colvec(RR_intervals(this_iter_QRS_seq_idx))/obj.sampling_rate);
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% log(RRpost)
%%%%%%%%%%%%%%%
featMat_ldc = [ featMat_ldc log(colvec(RR_intervals(min(cant_QRS_locations, this_iter_QRS_seq_idx+1)))/obj.sampling_rate)];
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% log(RRmean1)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)( find( QRS_locations >= (QRS_locations(a) - 1*60*obj.sampling_rate) & ...
QRS_locations <= QRS_locations(a) )), ...
this_iter_seq_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a)(mean(RR_intervals(a))/obj.sampling_rate), aux_idx);
featMat_ldc = [ featMat_ldc colvec(log(aux_featval)) ];
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% log(RRmean20)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)( find( QRS_locations >= (QRS_locations(a) - 20*60*obj.sampling_rate) & ...
QRS_locations <= QRS_locations(a) )), ...
this_iter_QRS_seq_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a)(mean(RR_intervals(a))/obj.sampling_rate), aux_idx);
featMat_ldc = [ featMat_ldc colvec(log(aux_featval)) ];
% Update point
obj.progress_hdl.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);
aux_featval = cellfun(@(a)(squeeze(wtECG(a,:,obj.scale_idx(4)))), aux_idx, 'UniformOutput', false);
%calculate PCA matrix in this slices for feature
%AutoCorr1PlanoWTModMaxPos
autovec_slices = cellfun(@(a)(autovec_calculation(a)), aux_featval, 'UniformOutput', false );
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.2*obj.sampling_rate))) , ...
this_iter_seq_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a,b)(squeeze(wtECG(a,:,obj.scale_idx(4))) * b), aux_idx, autovec_slices, '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_hdl.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_hdl.checkpoint('Features calculation');
%clear some auxiliar variables.
clear aux*
%calculate clustering features.
% log(RRactual)
%%%%%%%%%%%%%%%
featMat_clust = featMat_ldc(:,1);
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% log(RRant)
%%%%%%%%%%%%%%%
featMat_clust = [ featMat_clust log(colvec(RR_intervals(max(1, this_iter_QRS_seq_idx-1)))/obj.sampling_rate)];
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% log(Prematuridad_loc)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)(max(1,a-1):min(cant_QRS_locations,a+1)), this_iter_QRS_seq_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} cant_QRS_locations];
end
aux_featval = cellfun(@(a)( RR_intervals(a(2))/sum(RR_intervals(a)) ), aux_idx);
featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% log(AbsRRlocalVar)
%%%%%%%%%%%%%%%
aux_idx = arrayfun(@(a)(max(1,a-1):min(cant_QRS_locations,a+1)), this_iter_QRS_seq_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} cant_QRS_locations];
end
aux_featval = cellfun(@(a)(obj.SmallValue+sum(abs(dRR_intervals(a)))/obj.sampling_rate), aux_idx);
featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% log(RRmean20)
%%%%%%%%%%%%%%%
featMat_clust = [ featMat_clust featMat_ldc(:,4) ];
% Update point
obj.progress_hdl.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)(wtECG(a,:,:)), aux_idx, 'UniformOutput', false);
% Update point
obj.progress_hdl.checkpoint('Features calculation');
aux_featval = cellfun(@(a)(squeeze(sum(abs(a(:,1,:))))'), aux_featval, 'UniformOutput', false );
aux_featval = cellfun(@(a)(obj.scales * colvec(a) ./ sum(a)), aux_featval );
% iAreaAbs = squeeze(sum(abs(wtAux)))';
% MaxProjArea = obj.scales * iAreaAbs ./ sum(iAreaAbs);
featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% AutoCorr1PlanoWTModMaxPos
%%%%%%%%%%%%%%%
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.2*obj.sampling_rate))) , ...
this_iter_seq_idx, 'UniformOutput', false);
aux_featval = cellfun(@(a,b)(squeeze(wtECG(a,:,obj.scale_idx(4))) * b), aux_idx, autovec_slices, 'UniformOutput', false);
aux_featval = cellfun(@(a)(CalcModMaxPos(a(:,1))), aux_featval);
featMat_clust = [ featMat_clust colvec(aux_featval) ];
% Update point
obj.progress_hdl.checkpoint('Features calculation');
% 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( this_header.nsig > 2 )
% wtECG already projected in obj.autovec.
aux_featval = cellfun(@(a)( CalcModMaxVal( squeeze(wtECG(a,:,obj.scale_idx(3))) ) ), aux_idx);
else
aux_featval = cellfun(@(a)( CalcModMaxVal( squeeze(wtECG(a,:,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 allPayloads = Concatenate(obj, allPayloads, payload )
if( isempty(allPayloads) )
for ii = 1:length(obj.fnFMfile)
allPayloads.(obj.fnFMfile{ii}) = payload.(obj.fnFMfile{ii});
end
else
for ii = 1:length(obj.fnFMfile)
allPayloads.(obj.fnFMfile{ii}) = [allPayloads.(obj.fnFMfile{ii}); payload.(obj.fnFMfile{ii})];
end
end
[~, rec_filename] = fileparts(obj.recording_name);
for ii = 1:length(obj.fnECGfile)
aux_payload.(obj.fnECGfile{ii}) = payload.(obj.fnECGfile{ii});
end
save([obj.tmp_path 'tmpfile_' rec_filename '_ECG_cantpids_' num2str(obj.cant_pids) '_' num2str(obj.this_pid) '_' num2str(obj.this_iteration) '_' num2str(obj.cant_iteration) '.mat'], '-struct', 'aux_payload' );
aux_ECG_filename = [obj.tmp_path 'tmpfile_' rec_filename '_ECG_cantpids_' num2str(obj.cant_pids) '_thispid_' num2str(obj.this_pid) '_iteration_' num2str(obj.this_iteration) '_of_' num2str(obj.cant_iteration) '.mat'];
%do this for cluster supercomputers reasons
movefile( [obj.tmp_path 'tmpfile_' rec_filename '_ECG_cantpids_' num2str(obj.cant_pids) '_' num2str(obj.this_pid) '_' num2str(obj.this_iteration) '_' num2str(obj.cant_iteration) '.mat'], ...
aux_ECG_filename, 'f' );
cant_QRS = length(payload.QRS_locations);
obj.QRS_idx = [obj.QRS_idx; uint16(colvec(1:cant_QRS))];
obj.File_idx = [obj.File_idx; repmat(uint16(obj.file_count), cant_QRS, 1)];
obj.File_list = [obj.File_list; cellstr(aux_ECG_filename)];
obj.file_count = obj.file_count + 1;
end
end
end
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