QT Interval Measurement: The PhysioNet/Computing in Cardiology Challenge 2006 1.0.0

File: <base>/sources/vaclav-chudacek/QT_detection_using_GradientRules/QT_detection_using_Gradient_Rules_CinC_2006.m (13,640 bytes)
% QT_gradient_detection.m - Find QT intervals based on the gradient rules
%        Copyright (C) 2006  Vaclav Chudacek
%        This software is released under the terms of the GNU General
%        Public License (http://www.gnu.org/copyleft/gpl.html).
     
function QT_detection_using_Gradient_Rules_CinC_2006(path,plot_enabled)

if (nargin~=2),
 disp(sprintf('This function requires two arguments. Setting to default'));
 path = 'C:\!Vasek\Work\!CINC 2006\!Data\ptbdb';
 plot_enabled = 0;
end

recordFile = 'RECORDS';           % Record names
load 'CINC_2006_Reference_results.txt';     % Official results for comparison  as of 07.09.2006
CINC_2006_results = zeros(549,2); % Our results
CINC_2006_QT_lengths = zeros(1,549);
CINC_2006_differences = zeros(1,549);     % Difference of our results to the official
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
recordFileFull = fullfile(path, recordFile);
fid_record = fopen(recordFileFull,'r');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% begin_at = 119;
% for i = 1:begin_at,
%     fileName = fgetl(fid_record);
% end
for counter = 1:549,
    fileName = fgetl(fid_record);
    headerFile = fullfile(path,[fileName '.hea']);
%     vcgFile = fullfile(path,[fileName '.xyz']);
    ecgFile = fullfile(path,[fileName '.dat']);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %------     LOAD HEADER     -------------------------------------------
    fprintf(strcat('Loading new record #',num2str(counter),': ',fileName));
    fprintf('\n');
    fid_header = fopen(headerFile,'r');
    z = fgetl(fid_header);
    fclose(fid_header);
    %------     LOAD DATA      --------------------------------------------
    fid_ecg = fopen(ecgFile,'r');
    ECG = fread(fid_ecg, [12, inf], 'int16');
    fclose(fid_ecg);
%     fid_vcg = fopen(vcgFile,'r');
%     VCG = fread(fid_vcg, [3, inf], 'int16');
%     fclose(fid_vcg);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    %%% Very, very basic filtration - just to get rid of the worst...
    for i = 1:12
        herz = 1000/length(ECG(i,:));
        A = fft(ECG(i,:));
        A(1:round(0.66/herz)) = 0;
        A(end-round(0.66/herz):end)=0;
        A((round(50/herz)-5):(round(50/herz)+5)) = 0;
        A(end-(round(50/herz)+5):end-(round(50/herz)-5)) = 0;
        ECGf(i,:) = (real(ifft(A)));
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%% R-peak detection based on PT_Hamilton algorithm %%%%%%%
    % Source code distributed under GNU license can be found on
    % http://www.eplimited.com/
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%% START %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    [nbr_heart_beats, r_peak_sample_list] = PanTomkinsHamilton(ECGf(5,:),1000);
    [nbr_heart_beatsI, r_peak_sample_listI] = PanTomkinsHamilton(ECGf(1,:),1000);
    [nbr_heart_beatsV4, r_peak_sample_listV4] = PanTomkinsHamilton(ECGf(10,:),1000);
    r_peak_sample_list = r_peak_sample_list*5;
    posR = r_peak_sample_list;
    posRI = r_peak_sample_listI;
    posRV4 = r_peak_sample_listV4;

    RR_dist = zeros(1,length(min(100,length(posR)-1)));
    selected_beat = 5; next_beat = 6;
    for i = 1:min(100,length(posR)-1),
        RR_dist(i) = (posR(i+1) - posR(i));
    end

    %%% Correction of detected peaks - R-T mess, wrong detection
    if ((RR_dist(selected_beat))>(1.2*RR_dist(selected_beat-1))||(RR_dist(selected_beat))<(0.8*RR_dist(selected_beat-1)))||...
            ((RR_dist(selected_beat))>(1.2*median(RR_dist))||(RR_dist(selected_beat))), %%% added because of file #543
        if ((RR_dist(selected_beat))>=1500), %%% added because of files #543, #315, #415
            selected_beat = selected_beat - 1; next_beat = next_beat +1;
        elseif (RR_dist(selected_beat+2))<=(1.2*RR_dist(selected_beat))&&...
                (RR_dist(selected_beat+2))>=(0.8*RR_dist(selected_beat))&&...
                (RR_dist(selected_beat))<425,
            if RR_dist(selected_beat)<320,
                if RR_dist(selected_beat)<500
                    next_beat = next_beat+1;
                else
                    selected_beat = selected_beat+1; next_beat = next_beat+2;
                end
            else
                selected_beat = selected_beat-1;
            end
        elseif (RR_dist(selected_beat+1))<=(1.2*RR_dist(selected_beat))&&(RR_dist(selected_beat+1))>=(0.8*RR_dist(selected_beat))
            selected_beat = selected_beat+1; next_beat = next_beat +1;
        end
    end
    posRpeak_out = posR(selected_beat);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%% R-peak detection by PanTomkins&Hamilton algortihm END %%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%% Q-on computation START %%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    Q_window_span_left = 150; Q_window_span_right = 50;
    R_window_span_left = 70; R_window_span_right = 10;
    T_window_span_right_1 = 200; T_window_span_right_2 = 200;
    Q_comps = 0; mistakeQ = 0; T_comps = 0; mistakeT = 0;
    Q_on_abs = zeros(1,12);

    for i = 1:12,
        %%% Correction of R-peak for each lead
        [temp correctionR_temp] = max((ECGf(i,posR(selected_beat)-R_window_span_left:posR(selected_beat)+R_window_span_right)));
        correctionR = correctionR_temp - R_window_span_left;
        posR_adjusted = posR(selected_beat) + correctionR;

        %%% 1st step of Q-on computation - see function: FIND_Q_ON
        try
            Q_on_temp = find_Q_on(ECGf(i,posR_adjusted - Q_window_span_left:posR_adjusted + Q_window_span_right));
        catch
            disp(strcat(['!!!!! MISTAKE of: Q-on computation on lead:', (num2str(i)),'!!!!!']));
            mistakeQ = 1;
            Q_on_temp = 0;
        end

        if mistakeQ == 0,
            Q_comps = Q_comps+1;
            Q_on_rel(Q_comps) = Q_on_temp + correctionR + 1;
            Q_on_abs(i) = posR(selected_beat) - Q_on_rel(Q_comps);
        else
            mistakeQ = 0;
        end
    end

    % All possible Qons are sorted and the smallest are deleted
    if min(Q_on_rel)<10,
        Q_on_rel_tmp = sort(Q_on_rel,'ascend');
        clear('Q_on_rel');
        Q_on_rel = Q_on_rel_tmp(max(find(Q_on_rel_tmp<10))+1:end);
    else
        Q_on_rel = sort(Q_on_rel,'ascend');
    end

    % 2nd step of Q-on computations - see function PRESCISE_Q
    for i = 2:2
        Q_on_final_temp(i,:) = precise_Q_on(ECGf(i,posR(selected_beat) - 150 : posR(selected_beat) + Q_on_rel(end)+25 -150),Q_on_rel);
    end

    % Maximal score is computed to select the best fitting Q-on
    maxScoreQ = max(sum(Q_on_final_temp(:,:)));
    if maxScoreQ == 0,
        Q_on_rel_final = median(Q_on_rel);
    else
        maxScoreSelected = find(sum(Q_on_final_temp(:,:))>=maxScoreQ);
        Q_on_rel_final = Q_on_rel(maxScoreSelected(1));
    end
    if (median(Q_on_rel) - Q_on_rel_final) < -25,
        Q_on_rel_final = round(median(Q_on_rel));
    end

    Q_on_rel_final = Q_on_rel_final - 150;
    Q_on_abs_final = Q_on_rel_final + posR(selected_beat);

    % 3rd step of Q-on computations - see function: CORRECT_Q
    correctionQ = [];
    leads = [1 2 8 9];
    for i = 1:length(leads);
        correctionQ =  [correctionQ correct_Q_on(ECGf(leads(i),:),Q_on_abs_final,Q_on_abs_final-20,Q_on_abs_final+30)];
    end
    correctionQ = median(correctionQ);

    Q_on_rel_final = Q_on_rel_final + correctionQ;
    Q_on_abs_final = Q_on_abs_final + correctionQ;
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%% Q-on computation END %%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    R_pos_in_summedECG = 75; nextR_pos_summedECG_distance = 200;

    summedECG = zeros(1,751);
     for i = 1:12,
        % Computation of summed and filtered ECG
        if (i == 8)||(i == 9),
            if(i == 8),
                summedECG = slidingavg(slidingavg(slidingavg(ECGf(12,(posR(selected_beat)-R_pos_in_summedECG):(posR(next_beat)-nextR_pos_summedECG_distance)),50),10),3);
            else
                summedECG = summedECG + slidingavg(slidingavg(slidingavg(ECGf(12,(posR(selected_beat)-R_pos_in_summedECG):(posR(next_beat)-nextR_pos_summedECG_distance)),50),10),3);
            end
        end
    end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %Inverting signal when negative
    if abs(min(summedECG))>max(summedECG),
        summedECG = -1*summedECG;
    end
    %Correction of R-peak on summedECG in +-20 sample radius
    [unused correctionR] = max(summedECG(R_pos_in_summedECG-20:R_pos_in_summedECG+20));
    R_pos_in_summedECG = R_pos_in_summedECG - (20-correctionR+1);
    Q_on_for_summedECG = R_pos_in_summedECG - Q_on_rel_final;

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%% T-off computation START %%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    T_error = 0;
    %Searching for first and second maxima/minima of the T-wave
    [unused maxT] = max(abs(summedECG(R_pos_in_summedECG+min(150,round(RR_dist(selected_beat)/2)):(min(length(summedECG(R_pos_in_summedECG:end)),400+R_pos_in_summedECG))))-summedECG(Q_on_for_summedECG));
    if ~exist('maxT','var'),
        maxT = 0;
    end
    if summedECG(R_pos_in_summedECG+200+maxT)<0,
        zaporne_T = 1;
        maxT = maxT + R_pos_in_summedECG + 200;
        [velikostT2 maxT2] = max(summedECG(maxT:end - min(length(summedECG)-maxT,100))-summedECG(maxT));
        maxT2 = maxT2 -1;
        if ((velikostT2<(summedECG(end - min(length(summedECG)-maxT,100))-summedECG(maxT))+100)&&(velikostT2>(summedECG(end - min(length(summedECG)-maxT,100))-summedECG(maxT))-100)),
            maxT2=0;
        end
    else
        maxT2 = 0;
        maxT = maxT + R_pos_in_summedECG + 200;
        zaporne_T =0;
    end
    if maxT>length(summedECG)-50,
        maxT = length(summedECG)-200;
    end
    maxT2_rel = maxT2 - maxT;

    %Signal used for T-off detection is shifted by value of Q-on 
    Tecg = summedECG(maxT:end-50)-summedECG(Q_on_for_summedECG);

    %%% 1st step of T-off computation - see function: FIND_T_OFF
    [Toff_temp, T_error] = find_T_off(Tecg,maxT, maxT2, maxT2_rel,zaporne_T);
    
    %   posTmax_out = maxT - R_pos_in_summedECG + posR(selected_beat);
    T_off_rel_final = Toff_temp + maxT;
    T_off_abs_final = Toff_temp + maxT - R_pos_in_summedECG + posR(selected_beat);

    % 2nd step of T-off computations - see function: CORRECT_T_OFF
    correctionT = [];
    leads = [1 2 8 9 10];
    for i = 1:length(leads);
        correctionT(i) =  correct_T_off(ECGf(leads(i),T_off_abs_final-200:T_off_abs_final));
    end

    correctionTfinal = min(correctionT);
    T_off_abs_final = T_off_abs_final - correctionTfinal;
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%% T-off computation START %%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    % Plotting of the results START 
    if plot_enabled,
        %%% Draws detected R(selected_beat):R(next_beat)
        % figure(counter*100);plot(ECGf(1,posR(selected_beat):posR(next_beat)));
        % Draws preliminary and corrected results
        figure(counter);
        plot(ECGf(2,Q_on_abs_final -100:T_off_abs_final+100),'r');
        hold on;
        plot(ECGf(1,Q_on_abs_final -100:T_off_abs_final+100));
        plot(ECGf(8,Q_on_abs_final -100:T_off_abs_final+100));
        plot(ECGf(9,Q_on_abs_final -100:T_off_abs_final+100));
        plot(100,-500:500,'k');
        plot(100 + correctionQ, -500:500,'g');
        %%%%%%%%%%%
        plot(length(ECGf(i,Q_on_abs_final -100:T_off_abs_final+100))-100+correctionTfinal,-500:500,'k');
        plot(length(ECGf(i,Q_on_abs_final -100:T_off_abs_final+100))-100 - correctionTfinal,-500:500,'g');
    end
    
    % Saving final results
    CINC_2006_results(counter,:) = [Q_on_abs_final,T_off_abs_final];
    CINC_2006_QT_lengths(counter) = (T_off_abs_final-Q_on_abs_final);
    CINC_2006_differences(counter) = abs(CINC_2006_Reference_results(counter)-CINC_2006_QT_lengths(counter));

    clear ECG*; clear Q*; clear R*; clear T*; clear cor*; clear m*; clear n*; clear pos*; clear r*; clear z*; clear s*; clear t*; clear u*;
end
fclose(fid_record);
% Final score computation based on final reference QT measurements (not included) 
omitted_results = find((CINC_2006_QT_lengths > 500) | (CINC_2006_QT_lengths < 250));
if length(omitted_results)>27
    disp('Sorry you are omitting to many results. More than 27.')
end
square = (CINC_2006_differences.^2);
square(omitted_results) = -1;

RESULT = sqrt(mean(square(find(square ~= -1))));
RESULT = RESULT*((549-length(omitted_results))/549)
save('QTresults','CINC_2006_results')