[Matlab] Pan Tompkins Algoritması Kullanılarak EKG’de QRS Complex Tespiti ve Ritm Analizi
16 12 2007Biomedical Signal Analysis dersinde Timur Düzenli ve Salih Aslan ile beraber yaptığımız bir proje bu. EKG verileri üzerinden QRS Complex kısımlarının tespitinden bahsedeceğim. Öncelikle nedir ve neden önemlidir buna değinelim.
Çoğumuzun bildiği üzere EKG (ECG - Electrocardiogram) kalp aktivitelerinin elektriksel olarak kaydıdır. Electrocardiograph ile ölçülerek kaydedilir.
EKG analiz edilerek kalp hakkında oldukça detaylı bilgiler elde edilebilir. Tıp okumadığım için çok detaylı bilemem tabii ki. Fakat EKG sinyalinin birazdan bahsedeceğim kısımlarının zamanlaması hastalıklar hakkında oldukça yararlı bilgiler sağlamaktadır.
Tipik bir EKG sinyali aşağıdaki şekilde gösterilebilir.

Burada görüleceği gibi EKG sinyali belirli tipik kısımlar içerir. Bunlar P,Q,R,S ve T kısımlarıdır. Hepsi kalp hakkında önemli bilgiler taşır. Bu yazıda, sinyalde QRS Comlex’in yerinin bulunmasından bahsedeceğim. Bu bilgi önemlidir çünkü QRS Complex’in kaç milisaniye sürdüğü çıplak gözle anlaşılamaz.
Konuya girmeden önce şundan da bahsetmek isterim. Doktorların, ellerine gelen EKG kaydına bakarak karar vermesinde bahsedeceğim konu oldukça önem taşır. EKG sinyalleri elektriksel olarak zayıf sinyaller olduğu için kaydedilmesi ve çevre gürültülerden arındırılması işi oldukça zahmetlidir. Demek istediğim, ham bir EKG kaydından hiç bir doktor hiç bir teşhis koyamaz. O şeritteki net ve anlaşılır EKG’nin nasıl o hale geldiğini ele alacağız. Fakat tüm aşamaları koca bir kitap yapan bu analizin sadece küçük bir kısmı bu yazıca mevcut.
Analiz için kullanacağım önceden kaydedilmiş EKG verileri elimde mevcut. 4000 örnekten oluşan veriyi şuradan alabilirsiniz.
Analize başlayalım,
close all
clear all
clc
%% Degisken ve Sabitlerin Tanimlanmasi
fs=200; % sample rate
%% Sinyalin Matlab Ortamina Alinmasi
hamsinyal=load('ECG3');
plot(hamsinyal);
title('Ham Sinyal');
figure

Görüldüğü gibi sinyal oldukça fazla gürültü içeriyor. Hem yüksek frekanslı hem de şebekeden kaynaklanan ek bileşenler sinyale karışmış durumda. Öncelikle sinyali DC sıfır seviyesine çekelim
%% DC Bilesenlerin Atilmasi
dcsizsinyal=(hamsinyal-mean(hamsinyal));
plot(dcsizsinyal);
title('DC Bilesenleri Atilan Sinyal');
figure

Şimdi de sinyalden yüksek frekanslı gürültüleri temizleyelim. Bunun için bir alçak geçiren (low pass) filtre tasarlayıp sinyali bu filtreden geçirmemiz gerekli. Filtre tipi olarak 10 point avarage filtre kullanalım.
%% Filtre
% 10 point avarage filter
B=(1/10)*ones(1,10);
A=1;
freqz(B,A);
title('10 Point Moving Avarage Filtre');
figure
avaragefiltrelisinyal=filter(B,A,dcsizsinyal);
plot(avaragefiltrelisinyal)
title('Moving Avarage (Low Pass) Filtreden Gecmis Sinyal');
figure


Yüksek frekanslı gürültülerin azaldığını görebiliriz. Fakat hala kusursuz değil. Şimdi de düşük frekanslı gürültüleri yok etmek için bir yüksek geçiren (high pass) filtre tasarlayıp sinyali bu filtreden geçirelim. Bunun için de Derivative Based filtre oluşturacağız.
%% Derivative Based Filter B=(1/1.0025)*[1 -1];% 1.0025 normalizasyon degeri A=[1 -0.995]; freqz(B,A); title(’Derivative Based Filter’); figure derivativefiltrelisinyal=filter(B,A,avaragefiltrelisinyal); plot(derivativefiltrelisinyal) title(’Derivative Based (High Pass) Filtreden Gecmis Sinyal’); figure


Sinyalden atmamız gereken bir de 60Hz’lik şebeke gürültüsü var. Kayıt esnasında cihazın şehir şebekesinden kaptığı gütültü de sinyalle beraber kaydedilmiş durumda. Zaten bakıldığında çıplak gözle de görülebiliyor. Bu arada kullandığımız örnek yabancı bir kaynaktan olduğu için şebeke gürültüsü 60Hz. Bizim şebekemiz 50Hz. Onu da hatırlatayım.
60Hz’lik gürültüyü atmak için bir filtre tasarlanacak ve önceki filtreden çıkan sinyal bu filtreden geçirilecek. Önemli bir sorun da, şebekeden kapılan gürültünün yalnızca 60Hz değil aynı zamanda bunun harmonikleri olarak ortaya çıkması. Bu yüzden yalnızca 60Hz’i bastıran bir filtre işimiz görmeyecek. Bunun için 60Hz ve tüm harmoniklerini bastırmamızı sağlayacak bir Comb filtre tasarlamamız gerekir. Aşağıda yazdığım filtre katsayılarının nasıl bulunduğuna değinmeyeceğim, zira çok uzun mesele
Zaten ben de hazır kullandım. Bulmaya çalışmak apayrı bir mesele.
%% Comb Filter % 60Hz sebeke gurultusunu ve harmoniklerini bastiran filtre B=conv([1 1],[0.6310 -0.2149 0.1512 -0.1288 0.1227 -0.1288 0.1512 -0.2149 0.6310]); A=1; freqz(B,A); title(’Comb Filter’); figure comb=filter(B,A,derivativefiltrelisinyal); plot(comb) title(’Comb (60Hz ve Harmoniklerini Bastiran) Filtreden Gecmis Sinyal’);


Çok kusursuz olmasa da sinyal işlenebilecek hale geldi. Şimdi ise fark alarak sinyaldeki en keskin tepeleri bulalım. Bunlar bize R noktalarını verecek. Ardından da bu R noktasının sağında ve solunda en düşük seviyedeki noktaları bulalım. Bunlar da Q ve S noktalarını verecek.
%% Differentiator
differentiatorcikisi=diff(comb);
%% Squaring Operation
kare=differentiatorcikisi.*differentiatorcikisi;
%% Moving Window Integrator
window=ones(1,30); % N=30 moving window
integral= medfilt1(filter(window,1,kare),10);
delay = ceil(length(window)/2);
integral = integral(delay:length(integral));
%% QRS Arama
max_h = max(integral);
thresh = 0.2;
poss_reg = integral>(thresh*max_h);
sol = find(diff([0 poss_reg'])==1);
sag = find(diff([poss_reg' 0])==-1);
for i=1:length(sol)
[maxdeger(i) maxloc(i)] = max( comb(sol(i):sag(i)) );
maxloc(i) = maxloc(i)-1+sol(i); % offset ekle
[mindeger(i) minloc(i)] = min( comb(sol(i):sag(i)) );
%mindeger Q noktasini verir
minloc(i) = minloc(i)-1+sol(i); % offset ekle
% Q noktasi icin offset
end
% minpozisyon=ones(1,4000)*-max(comb);
% minpozisyon(1,minloc)=max(comb);
maxpozisyon=ones(1,4000)*(-max(comb));
maxpozisyon(1,maxloc)=max(comb);
% maxpozisyon(1,sol)=max(comb);
% maxpozisyon(1,sag)=max(comb);
% maxpozisyon(1,minloc)=max(comb);
figure
% plot(comb(1:(length(comb)/4)),’b-’)
plot(comb)
title(’R noktalari belirlenmis sinyal’);
hold on
% plot(minloc,mindeger,’r–’)
plot(maxloc,maxdeger,’g-’)
figure
plot(comb)
title(’R noktalari isaretlenmi� sinyal’);
hold on
%plot(minpozisyon,’r') % Q baslangici
plot(maxpozisyon,’r')
legend(’Comb Filtreden Gecen Sinyal’,'R’);


Görüldüğü gibi QRS Complex’in yeri artık tam olarak bilinebiliyor. İstenen ölçümler buradan çıkarılabilir.
Bu arada sondan üçüncü Complex’de bir hata var. Algoritma burada hatalı sonuç veriyor. Gürültülerin kusursuz bir şekilde atılamamasından kaynaklanan bir hata söz konusu. P noktası olması gerekenden daha büyük olduğu için R olarak algılanmış ve bir hataya sebebiyet vermiş. Fakat sinyalin geri kalanı istediğimiz veriyi elde etmemiz için yeterli veriyi sağlıyor.
Şimdi elde ettiğimiz verilerden bazı sonuçlara ulaşmaya çalışalım. Girilen veride kaç adet beat (vuruş) bulunduğunu tespit edelim. R noktalarını tespit ettiğimize göre ve R noktası kadar vuruş bulunduğunu bildiğime göre kolaylıkla sonuca ulaşabiliriz.
%% Beat Sayisi beat=length(maxloc) heartrate=beat/(length(hamsinyal)/fs)
Cevap olarak şu sonuçları elde ediyoruz.
beat =
25
heartrate =
1.2500
Analiz sırasında P noktalarından birisi de R olarak algılanmıştı. Aslında beat sayısı 24 olmalıydı.
Şimdi de RR aralığının ortalamasını bulalım.
%% RR Araligi Ortalamasi
ortalama=0;
for i=1:beat-1
ortalama=ortalama+(maxloc(i+1)-maxloc(i)-1)/fs;
u(i)=(maxloc(i+1)-maxloc(i)-1)/fs;
end
ortalama=1000*ortalama/beat
% ms cinsinden bulmak icin 1000 ile carpildi.
Sonuç olarak elde edilen çıktı,
ortalama =
745.8000
%% Standart Sapma variance=var(u)*1000 %ms cinsinden
variance = 22.4692
QRS genişliklerinin tespiti için ise zaten bulmuş olduğumuz noktaların farkları alınabilir.
% QRS Genisligi
for i=1:beat
qrs(i)=sag(i)-minloc(i)
end
Çıktısında da QRS genişliklerinin birbirine yakın değerde olduğu gözlenebilir.
Görüldüğü gibi hastalık teşhisi için canla başla çalışanlar yalnızca doktorlar değil
Bizleri de unutmayın.




yine oturmus temiz temiz yazmissin. takdir ettigim blogcu kardesim, buralara tekrar gelirsin umarim.
Kendim de tilt+ denilen bir tür kalp rahatsızlığına sahip olduğum için belki bu konu çok ilgimi çekti. Bu anladığım kadarıyla MatLab diliyle mi yazılmış. Bir de hepsi bu kadar mı kodun. Son halinde doktorların işine yarayabilecek hale gelmiş mi? Şu anda kullanılan sistem bunun aynısı mı? Demek istediğim tıp cihazlarında da bu tür programlar kullanılıyor mu ? Yoksa farklı bir sistem mi var? Peki bu sonuçta üretilen kodun sıfır hatalı olduğundan nasıl emin olabiliyorsunuz. Sağlık şakaya gelmez çünkü..
Buradaki kod yazının en başında da yazdığım gibi sadece Biomedical Signal Analysis dersinin bir projesi için yazdığımız kodlar. Herhangi bir profesyonel amaç gütmüyor. Sonucu hala kusursuz değil. Şu anda aletlerde kullanılan birçok değişik yöntem var. Pan Tompkins de bunlardan yalnızca biri. Cihazların içinde kullanılan kod direkt olarak matlab kodu olmayabilir. Bu durum kullanılan mikro kontrolcüye bağlı. Sinyal işleme açısından en etkili aracım Matlab olduğunu söyleyebilirim. Bir takım mikrokontrolcüler için matlab algorimasını doğrudan adapte edilebildiğini okumuştum ama açıkçası denemedim. Bu tarz bir donanımım da yok. Kodun sıfır hatalı olmadığını da yazıda belirtmiştim zaten. Önemli olan her bir belirleyici işaretin (p,q,r,s,t) sürelerini bilmek. Bunun için de tepe noktalarını kesin bilebilmek yeterli. Ana dediğim gibi gerçekten tertemiz bir sinyal elde etmek yukarıdaki ders projesini aşan bir konu. Vücuttan alınan sinyal genlik olarak çok küçük olduğu için ortam gürültülerinden çok etkileniyor. EEG gibi sinyaller daha da düşük genliğe sahip olduğu için onlarda durum daha da karmşıklaşıyor. Bize öğretilene göre en sağlıklı ölçümü EKG için kalbe en yakın, EEG için beyne en yakın yerden almak.Bu da cerrahi bir müdahale gerektirdiği için zor bir işlem. Dediğiniz gibi sağlık şakaya gelmez. Zaten kimse, bu kodu hastanemizde kendi EKG cihazımızı yapalım maksadıyla almaz. Alırsa da onun ciddiyetinden şüphe ederim. Bu sadece konuyu açıklamak ve genel anlamda bu işlemin nasıl yapıldığını göstermek üzere yazılmış bilgilendirici bir yazı. Sağlığın şakaya gelmeyeceğini doktoraların da öğrenmesi dileğiyle. Saygılar.
Pardon. Sağlık şakaya gelmez sözümü yanlış anlamamışsınızdır umarım. Ben onu zaten var olan bir doğruyu vurgulamak için söyledim. Sizin kodunuzda herhangi bir hata olduğunu da ima etmek istemedim. Üretilen kodun sıfır hatalı olduğundan emin olmak derken de yani örneğin bir yerlerden ilk hazır verileri aldığınızı söylemiştiniz. Daha sonra program bu verileri alıyor ve artık doktorlar tarafından kullanılabilecek yeni veriler oluşturuyor. Bu ilk aldığınız verileri bu profesyonel hastanelerde kullanılan sistemlerden tekrar geçirip sonuçları karşılaştırıyormusunuz demek istemiştim. Kodların bir ders projesi için hazırlanmış olduğunu belirttiğiniz için buna gerek kalmadığı anlaşılmış oldu. Gerçekten böyle ilginç bir konuda makale hazırladığınız için ve hastalık teşhisi için canla başla çalıştığınız için teşekkürler!
Hi Aydin,
I’m Savitha from India. I saw this blog of yours on Pan-Tompkins algorithm..would you mind sharing it with me in English!The whole page would be of great help to me..please!I’m not familiar with this language that you have written your blog in(I’m guessing its Turkish!!)..Please help me!
Awaiting your reply
Savitha
abi hocanın kabul etmeme sebebi pan tompkins algoritmasıymış. bu arada yazılarımı silmişsin, lavaştan vaz mı geçtin?
Gerçekten çok anlamlı ve başarılı bir çalışma. Böyle bir paylaşımdan dolayı teşekkür eder, ellerin dert görmesin Aydın Tarık Zengin derim
Ben de bu dönem Biomedikal alıyorum, ve bütün olarak yapmış olduğun çalışma bizim 2 x %10 projemiz. Eğer yardımcı olabilirsen bir sorum olacak. Moving window output un dan sonra elimdeki sinyalin tekrar ECG sinyaline dönüştürme işleminde bir sıkıntım var. Karesi alına ve negatif komponenti olmayan bir sinyali tekrar eski haline nasıl getiriyoruz..? Şimdiden teşekkür eder, iyi çalışmalar dilerim..
By the way, savitha, if you still need the English one of the project, I can translate this for you. You can write an e-mail address here for me to send it..
P.S : You have limited time to get..