%% (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