基于Python的心电图上位机诊断软件

前言

本软件设计基于Python编程语言,实现从心电图数据的读取、心电图的绘制、对心电图的操作、相关心电数据的显示以及病症结论的收录及显示。相关算法部分调用自Python专门处理心电数据的第三方库Heartpy。

心电数据的读取

XML解析

由于从老师处得到的心电数据是以XML格式存储的,本文就只介绍如何用Python来解析XML格式的心电数据,至于什么是XML,请自行bd~

可以看到这边收录了几个XML格式的心电数据文件,但是根据文件特性我们不能像打开TXT那样直接使用它。

 用浏览器打开是这个样子。

 我们调用Python中的LXML库来解析,文件操作用OS和GLOB库。

from lxml import etree#导入lxml库
import os
import glob
path = 'D:\ECG' #设置路径
path_list = os.listdir(path) #获取目录中的内容
path_list.sort(key=lambda x:int(x.split('.')[0])) #整理文件数据及类型
path_file_number = glob.glob('D:\ECG\*.xml') #获取此路径下的所有XML文件并返回一个List
local = 0
sj = open(os.path.join(path,path_list[local]),'rb') #从第一个开始打开文件
tree = etree.parse(sj) #将xml解析为树结构
root = tree.getroot() #获得该树的树根

 由于存储的心电数据是基于临床十二导联记录下来的,故XML文件里会有12个类似于<I>,<II>......,<avf>这样的12个节点,我们仅需对这12个节点做操作即可,开头的节点删去。

for child in root[1:2]: #从第二个节点开始操作
    lst1 = []
    lst1 = child.text
    lst1 = lst1.split(" ")
    lst1 = lst1[:-1:] #由于发现心电数据中最后一位总是有空格存在,故删掉最后一位。
    I = [ int(float(i)) for i in lst1 ] #将心电数据先转化为浮点型再转化为整型,用于绘制心电图
    #print(I)
for child in root[2:3]:
    lst2 = []
    lst2 = child.text
    lst2 = lst2.split(" ")
    lst2 = lst2[:-1:]
    II = [ int(float(i)) for i in lst2 ]
    #print(II)
for child in root[3:4]:
    lst3 = []
    lst3 = child.text
    lst3 = lst3.split(" ")
    lst3 = lst3[:-1:]
    III = [ int(float(i)) for i in lst3 ]
    #print(III)
for child in root[4:5]:
    lst4 = []
    lst4 = child.text
    lst4 = lst4.split(" ")
    lst4 = lst4[:-1:]
    AVF = [ int(float(i)) for i in lst4 ]
    #print(AVF)  
for child in root[5:6]:
    lst5 = []
    lst5 = child.text
    lst5 = lst5.split(" ")
    lst5 = lst5[:-1:]
    AVR = [ int(float(i)) for i in lst5 ]
    #print(AVR)
for child in root[6:7]:
    lst6 = []
    lst6 = child.text
    lst6 = lst6.split(" ")
    lst6 = lst6[:-1:]
    AVL = [ int(float(i)) for i in lst6 ]
    #print(AVL)
for child in root[7:8]:
    lst7 = []
    lst7 = child.text
    lst7 = lst7.split(" ")
    lst7 = lst7[:-1:]
    V1 = [ int(float(i)) for i in lst7 ]
    #print(V1)
for child in root[8:9]:
    lst8 = []
    lst8 = child.text
    lst8 = lst8.split(" ")
    lst8 = lst8[:-1:]
    V2 = [ int(float(i)) for i in lst8 ]
    #print(V2)
for child in root[9:10]:
    lst9 = []
    lst9 = child.text
    lst9 = lst9.split(" ")
    lst9 = lst9[:-1:]
    V3 = [ int(float(i)) for i in lst9 ]
    #print(V3)
for child in root[10:11]:
    lst10 = []
    lst10 = child.text
    lst10 = lst10.split(" ")
    lst10 = lst10[:-1:]
    V4 = [ int(float(i)) for i in lst10 ]
    #print(V4)
for child in root[11:12]:
    lst11 = []
    lst11 = child.text
    lst11 = lst11.split(" ")
    lst11 = lst11[:-1:]
    V5 = [ int(float(i)) for i in lst11 ]
    #print(V5)
for child in root[12:13]:
    lst12 = []
    lst12 = child.text
    lst12 = lst12.split(" ") 
    lst12 = lst12[:-1:]
    V6 = [ int(float(i)) for i in lst12 ]
    #print(V6)

我们截取其中一个节点的信息,可以看到已经成功解析并读取上来。

心电图的绘制

 成功得到心电数据之后,我们调用Python中的2D绘图库Matplotlib来进行心电图的绘制。

导入相关模块

import matplotlib.pyplot as  plt
import matplotlib
import numpy as  np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2Tk
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure

我们将心电数据中的12个节点依次绘制出心电信号曲线,组成一张完整的十二导联心电图。

a = plt.subplot(12,1,1); #12张中的第一张
plt.plot(np.linspace(0, 100 * np.pi, 5000),I) #5000个数据就给5000个点
plt.ylabel('I')
plt.plot()
plt.grid()

a = plt.subplot(12,1,2);
plt.plot(np.linspace(0, 100 * np.pi, 5000),II)
plt.ylabel('II')
plt.plot()
plt.grid()

a = plt.subplot(12,1,3);
plt.plot(np.linspace(0, 100 * np.pi, 5000),III)
plt.ylabel('III')
plt.plot()
plt.grid()

a = plt.subplot(12,1,4);
plt.plot(np.linspace(0, 100 * np.pi, 5000),AVF)
plt.ylabel('AVF')
plt.plot()
plt.grid()

a = plt.subplot(12,1,5);
plt.plot(np.linspace(0, 100 * np.pi, 5000),AVR)
plt.ylabel('AVR')
plt.plot()
plt.grid()

a = plt.subplot(12,1,6);
plt.plot(np.linspace(0, 100 * np.pi, 5000),AVL)
plt.ylabel('AVL')
plt.plot()
plt.grid()

a = plt.subplot(12,1,7);
plt.plot(np.linspace(0, 100 * np.pi, 5000),V1)
plt.ylabel('V1')
plt.plot()
plt.grid()

a = plt.subplot(12,1,8);
plt.plot(np.linspace(0, 100 * np.pi, 5000),V2)
plt.ylabel('V2')
plt.plot()
plt.grid()

a = plt.subplot(12,1,9);
plt.plot(np.linspace(0, 100 * np.pi, 5000),V3)
plt.ylabel('V3')
plt.plot()
plt.grid()

a = plt.subplot(12,1,10);
plt.plot(np.linspace(0, 100 * np.pi, 5000),V4) 
plt.ylabel('V4')
plt.plot()
plt.grid()

a = plt.subplot(12,1,11);
plt.plot(np.linspace(0, 100 * np.pi, 5000),V5)
plt.ylabel('V5')
plt.plot()
plt.grid()

a = plt.subplot(12,1,12);
plt.plot(np.linspace(0, 100 * np.pi, 5000),V6)
plt.ylabel('V6')
plt.plot()
plt.grid()
plt.show()

成功绘制心电图~

Tkinter设计上位机界面

Matplotlib与Tkinter的集成

Python有许多关于上位机界面设计的库,我们采用纯代码设计的Tkinter库,方便又快捷。

导入TK库

import tkinter as tk

利用TK自带的画布及绘图器作画,将心电图摆上去。

Matplotlib中的这个FigureCanvasXAgg渲染器是个很奇怪的东西~至于是什么自行BD

至于组件的摆放有很多种方式,PACK啊什么的,我这里采用Place绝对坐标的方式摆放,就是爱折腾,缺点就是得最大化才能看得清软件全貌。。

window = tk.Tk()  #创建窗口
window.title("心电数据")

# set a figure
f = Figure(figsize=(11, 7), dpi=100) #创建一个画布
a = f.add_subplot(12,1,1) 
a.plot(np.linspace(0, 100 * np.pi, 5000),I)
a.set_ylabel('I') 
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,2);
a.plot(np.linspace(0, 100 * np.pi, 5000),II)
a.set_ylabel('II')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,3);
a.plot(np.linspace(0, 100 * np.pi, 5000),III)
a.set_ylabel('III')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,4);
a.plot(np.linspace(0, 100 * np.pi, 5000),AVF)
a.set_ylabel('AVF')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,5);
a.plot(np.linspace(0, 100 * np.pi, 5000),AVR)
a.set_ylabel('AVR')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,6);
a.plot(np.linspace(0, 100 * np.pi, 5000),AVL)
a.set_ylabel('AVL')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,7);
a.plot(np.linspace(0, 100 * np.pi, 5000),V1)
a.set_ylabel('V1')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,8);
a.plot(np.linspace(0, 100 * np.pi, 5000),V2)
a.set_ylabel('V2')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,9);
a.plot(np.linspace(0, 100 * np.pi, 5000),V3)
a.set_ylabel('V3')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,10);
a.plot(np.linspace(0, 100 * np.pi, 5000),V4)
a.set_ylabel('V4')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,11);
a.plot(np.linspace(0, 100 * np.pi, 5000),V5)
a.set_ylabel('V5')
plt.ylabel('I')
a.grid()
a = f.add_subplot(12,1,12);
a.plot(np.linspace(0, 100 * np.pi, 5000),V6)
a.set_ylabel('V6')
plt.ylabel('I')
a.plot()
a.grid()

canvas = FigureCanvasTkAgg(f, master=window)
canvas.draw()
canvas.get_tk_widget().place(x=10,y=200) #采用绝对坐标摆放
toolbar = NavigationToolbar2Tk(canvas, window) #画布底部的操作栏,不要可以删去
toolbar.update()

window.mainloop() #循环显示窗口

 至此成功把心电图摆上去啦

心电图操作 

切换心电图

def nextstep():
    path = 'D:\ECG'
    path_list = os.listdir(path)
    path_list.sort(key=lambda x:int(x.split('.')[0]))
    global local
    global path_file_number
    path_file_number=glob.glob('D:\ECG\*.xml')
    local = local + 1
    if local == (len(path_file_number)): #由最后一张心电图切换至第一张
        local = 0
    fw = open(os.path.join(path,path_list[local]),'rb')
    tree = etree.parse(fw)
    root = tree.getroot()
    for child in root[1:2]:
        lst1 = []
        lst1 = child.text
        lst1 = lst1.split(" ")
        lst1 = lst1[:-1:]
        I = [ int(float(i)) for i in lst1 ]
        #print(I)

#创建按钮绑定函数事件
button = tk.Button(window, text='下一个',bg='sky blue',width=12, height=2,command=nextstep)
button.place(x=980,y=30)

心电数据滤波

心电数据处理部分都调用第三方库Heartpy来处理。

导入心电数据处理模块

import heartpy as hp

三种滤波方式(高通、低通、带通) 

#高通滤波
def high_filter():
    path = 'D:\ECG'
    path_list = os.listdir(path)
    path_list.sort(key=lambda x:int(x.split('.')[0]))
    #print(path_list)
    global local
    local = local
    fw = open(os.path.join(path,path_list[local]),'rb')
    tree = etree.parse(fw)
    root = tree.getroot()
    for child in root[1:2]:
        lst1 = []
        lst1 = child.text
        lst1 = lst1.split(" ")
        lst1 = lst1[:-1:]
        I = [ int(float(i)) for i in lst1 ]
        I = hp.filter_signal(I, cutoff=0.75, sample_rate=500.0, order=3, filtertype='highpass')
        #print(I)

#低通滤波
        I = hp.filter_signal(I, cutoff=15, sample_rate=500.0, order=3, filtertype='lowpass')

#带通滤波
        I = hp.filter_signal(I, cutoff=[0.75, 15], sample_rate=500.0, order=3, filtertype='bandpass')

正常情况心电图 

带通滤波后 

心电特性数据读取处理 

还是一样利用Heartpy这个专门处理心电数据的第三方库来帮我们处理

    I = hp.scale_data(I)
    working_data, measures = hp.process(I, 500.0)

    print(working_data)
    print(measures)

 通过输出结果我们可以很轻易地从里面提取到有用的信息,例如R峰的定位、各类波的间期持续时间、以及得到HRV分析的一些常用数据指标。

以输出“心率”这个数值为例

def heart_rate():
    path = 'D:\ECG'
    global path_list
    path_list = os.listdir(path)
    path_list.sort(key=lambda x:int(x.split('.')[0]))
    global local
    global path_file_number
    local = local
    path_file_number=glob.glob('D:\ECG\*.xml')
    fw = open(os.path.join(path,path_list[local]),'rb')
    tree = etree.parse(fw)
    root = tree.getroot()
    for child in root[11:12]:
        lst1 = []
        lst1 = child.text
        lst1 = lst1.split(" ")
        lst1 = lst1[:-1:]
        I = [ int(float(i)) for i in lst1 ]
    I = hp.scale_data(I) #心电数据处理
    working_data, measures = hp.process(I, 500.0)

    t1.delete("0.0","15.0") #清空text框内的数据
    t1.insert("end",measures['bpm']) #提取BPM数值并输入进text框

t1 = tk.Text(window, width=12, height=2) #创建一个text文本框用于显示BPM数值
t1.place(x=1300,y=40)

#创建一个label组件用于单位显示
l2 =tk.Label(window,bg='white',width=10,height=2,text='bpm') 
l2.place(x=1400,y=35)

 显示结果

  • 4
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w9241212

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值