ECG-Kit 1.0
(7,276 bytes)
%% (Internal) Woody algorithm for heartbeat allignment
%
% [ best_esemble_avg,
% min_anns_refined,
% all_anns_matched_with_BEA] = woody_method(signal,
% annotations, realization_limit,
% outliers_proportion, bRobust )
%
% Arguments:
%
% + signal:
%
% + annotations: heartbeat sample location.
%
% + realization_limit: limits of the heartbeat with respect to
% annotations(i) - realization_limit(1) to annotations(i) +
% realization_limit(2).
%
% + outliers_proportion: Proportions of outliers to assume, and discard
% from the template calculation.
%
% + bRobust: Calculate the template with median instead of mean.
%
% Output:
%
% + best_esemble_avg: The best template found.
%
% + min_anns_refined: The refined location of the selected heartbeats,
% without outliers.
%
% + all_anns_matched_with_BEA: The refined location of the selected heartbeats,
% with outliers.
%
% See also
%
% Author: Mariano Llamedo Soria (llamedom at {electron.frba.utn.edu.ar; unizar.es}
% Version: 0.1 beta
% Birthdate: 21/7/2010
% Last update: 20/02/2013
% Copyright 2008-2015
%
function [best_esemble_avg, min_anns_refined, all_anns_matched_with_BEA] = woody_method(signal, annotations, realization_limit, outliers_proportion, bRobust )
if( nargin < 5 || isempty(bRobust ) )
bRobust = false;
end
MAX_LOOPS = 20;
[nsamp, nsig] = size(signal);
cant_anns = length(annotations);
anns_refined = colvec(annotations);
discarded_anns_idx = [];
anns_aux_idx = colvec(1:cant_anns);
loop_count = 1;
crit = [];
crit(1) = inf;
if( nargin < 4 || isempty(outliers_proportion) )
outliers_proportion = 0.9;
end
min_cant_anns = round(outliers_proportion * cant_anns);
ensemble_size = sum(realization_limit);
min_anns_refined = [];
min_crit = inf;
while( loop_count < MAX_LOOPS && crit(max(loop_count-1,1)) > 0.001 )
[avg_pack, aux_idx, anns_idx ] = pack_signal(signal, anns_refined, realization_limit);
if( bRobust )
esemble_avg = flipud(squeeze(median(avg_pack,3)));
else
esemble_avg = flipud(squeeze(mean(avg_pack,3)));
end
if(loop_count == 1)
esemble_avg_initial = esemble_avg;
end
realizations = cellfun(@(a)( cell2mat(arrayfun( @(aa)( conv(signal(a,aa), esemble_avg(:,aa) ) ), 1:nsig, 'UniformOutput', false )) ), aux_idx, 'UniformOutput', false);
cross_corr_idx = cellfun(@(a)( arrayfun( @(aa)( max_index( a(:,aa) ) ), 1:nsig)), realizations, 'UniformOutput', false);
cross_corr_idx = cell2mat( cross_corr_idx(:) ) ;
%just calculate the median change in time
% error_sig = round(median( cross_corr_idx, 2 )) - ensemble_size ;
%do a weighted avg favoring small changes in time
cross_corr_idx = cross_corr_idx - ensemble_size;
err_weight = pdf('norm',cross_corr_idx,0,5); % 5 samples of refinement
error_sig = round( sum(cross_corr_idx .* err_weight,2) ./ (sum(err_weight,2)+1e-6) );
% % debugging lines
% plot_ecg_mosaic(shiftdim(avg_pack,1), [], [], [], [], [], 3);
% plot_ecg_mosaic(flipud(esemble_avg), [], [], [], [], [], 4);
% realizations_discarded = pack_signal(signal, annotations(discarded_anns_idx), realization_limit);
% plot_ecg_mosaic(shiftdim(realizations_discarded,1), [], [], [], [], [], 5);
% [~, greater_change_idx] = sort(abs(error_sig), 'descend');
% realizations_greater_change = pack_signal(signal, anns_refined(greater_change_idx(1:10)), realization_limit);
% plot_ecg_mosaic(shiftdim(realizations_greater_change,1), [], [], [], [], [], 6);
% figure(7)
% hist(realizations_out)
% time correction
anns_refined(anns_idx) = anns_refined(anns_idx) + error_sig;
crit(loop_count) = sqrt(mean(error_sig.^2));
if(crit(loop_count) < min_crit)
min_crit = crit(loop_count);
min_anns_refined = anns_refined;
min_discarded_anns_idx = discarded_anns_idx;
best_esemble_avg = flipud(esemble_avg);
end
if( cant_anns > min_cant_anns )
%try to refine the realizations with the most similar examples
aux_val = cell2mat(reshape(realizations, 1, 1, length(realizations)));
realizations_lims = prctile( aux_val, [2.5 97.5], 3);
realizations_out = bsxfun( @lt, aux_val, realizations_lims(:,:,1)) | bsxfun( @ge, aux_val, realizations_lims(:,:,2));
realizations_out = squeeze(sum(sum(realizations_out, 1),2));
[~, realizations_idx] = sort( realizations_out, 'Descend' );
%discard most outlying examples
aux_idx = colvec(find(anns_refined < realization_limit(1) | anns_refined > (nsamp - realization_limit(2))));
aux_idx = unique([aux_idx; realizations_idx(1:round(0.05*cant_anns) )]);
discarded_anns_idx = [discarded_anns_idx; anns_aux_idx( aux_idx ) ];
anns_refined(aux_idx ) = [];
anns_aux_idx(aux_idx ) = [];
cant_anns = length(anns_refined);
else
% watch refinements not move to the extreme of the segment
aux_idx = colvec(find(anns_refined < realization_limit(1) | anns_refined > (nsamp - realization_limit(2))));
discarded_anns_idx = [discarded_anns_idx; anns_aux_idx( aux_idx ) ];
anns_refined(aux_idx ) = [];
anns_aux_idx(aux_idx ) = [];
cant_anns = length(anns_refined);
end
loop_count = loop_count + 1;
end
difference_between_pattern = cell2mat(arrayfun( @(aa)( conv(esemble_avg_initial(:,aa), best_esemble_avg(:,aa) )/(size(esemble_avg_initial,1)*std(best_esemble_avg(:,aa))* std(esemble_avg_initial(:,aa)) ) ), 1:nsig, 'UniformOutput', false ));
cross_corr_val = mean(difference_between_pattern(ensemble_size,:));
if( loop_count == MAX_LOOPS && cross_corr_val < 0.8 )
if( usejava('desktop') )
%warn humans.
warning('woody_method_BAD_CONVERGENCE', 'Woody method reach the last iteration with a poor result. Check results manually.')
% else
% best_esemble_avg = [];
% min_anns_refined = annotations;
% all_anns_matched_with_BEA = annotations;
% return
end
end
if( nargout > 2 )
all_anns_discarded = sort([ annotations(min_discarded_anns_idx)] );
cant_anns = length(all_anns_discarded);
seq_idx = 1:cant_anns;
aux_idx = arrayfun(@(a)(all_anns_discarded(a) - realization_limit(1):all_anns_discarded(a) + realization_limit(2)), ...
seq_idx, 'UniformOutput', false);
realizations = cellfun(@(a)( cell2mat(arrayfun( @(aa)( conv(signal(max(1,min(nsamp,a)),aa), flipud(best_esemble_avg(:,aa)) ) ), 1:nsig, 'UniformOutput', false )) ), aux_idx, 'UniformOutput', false);
cross_corr_idx = cellfun(@(a)( arrayfun( @(aa)( max_index( a(:,aa) ) ), 1:nsig)), realizations, 'UniformOutput', false);
cross_corr_idx = cell2mat( cross_corr_idx(:) ) ;
error_sig = round(median( cross_corr_idx, 2 )) - ensemble_size ;
all_anns_discarded = colvec(all_anns_discarded) + colvec(error_sig);
all_anns_matched_with_BEA = sort([ min_anns_refined; all_anns_discarded] );
end