From d7195feff5add0ff2abdd9dfeff73dd98e5810b5 Mon Sep 17 00:00:00 2001 From: Yorusora <2944763079@qq.com> Date: Fri, 21 Feb 2025 16:00:31 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=B0=86=E5=8E=9F=E6=9C=AC=E7=9A=84=E6=AD=A5=E9=AA=A4=E4=B8=AD?= =?UTF-8?q?=E7=9A=84append.py=E7=9A=84=E5=8A=9F=E8=83=BD=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=85=A5=E6=AD=A4=E7=95=8C=E9=9D=A2=E4=B8=AD=E7=9A=84=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E4=B8=AD=202=E3=80=81=E4=BC=98=E5=8C=96=E4=BA=86=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- append.py | 52 +++++++++++++++++++++++++++++ ecg_label_check.py | 82 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 append.py diff --git a/append.py b/append.py new file mode 100644 index 0000000..1281cf0 --- /dev/null +++ b/append.py @@ -0,0 +1,52 @@ +import pandas as pd +import numpy as np +from pathlib import Path + +def find_TPeak(data,peaks,th=50): + """ + 找出真实的J峰或R峰 + :param data: BCG或ECG数据 + :param peaks: 初步峰值(从label中导出的location_R) + :param th: 范围阈值 + :return: 真实峰值 + """ + return_peak = [] + for peak in peaks: + if peak>len(data):continue + min_win,max_win = max(0,int(peak-th)),min(len(data),int(peak+th)) + return_peak.append(np.argmax(data[min_win:max_win])+min_win) + return np.array(return_peak) + +def Rpeak_Detection(input_dir, fs, th1): + raw_ecg = pd.read_csv(Path(input_dir) / "filter_ecg.txt").to_numpy().reshape(-1) + # raw_ecg = raw_ecg[200*sample_rate:] + #######################限制幅值处理############################################ + # for i in range(len(raw_ecg)): + # if raw_ecg[i] > 300 or raw_ecg[i] < -300: + # raw_ecg[i] = 0 + ############################################################################## + all_R_peak = np.array([]) + + ##############################切割处理########################################## + for file_num in range(1, len(list((Path(input_dir) / "label").rglob("*location_J.txt"))) + 1): + R_peak_dir = Path(input_dir) / "label" / (str(file_num) + 'location_J.txt') + R_peak = np.array(pd.read_csv(R_peak_dir, header=None)).reshape(-1) + R_peak = R_peak + fs * 3600 * (file_num - 1) + all_R_peak = np.append(all_R_peak, R_peak) + ############################################################################## + all_R_peak = find_TPeak(raw_ecg, all_R_peak, th=int(th1 * fs / 1000)) + + RR_Interval = np.full(len(all_R_peak) - 1, np.nan) + + for i in range(len(all_R_peak) - 1): + RR_Interval[i] = all_R_peak[i + 1] - all_R_peak[i] + + RRIV = np.full(len(RR_Interval) - 1, np.nan) + for i in range(len(RR_Interval) - 1): + RRIV[i] = RR_Interval[i + 1] - RR_Interval[i] + + Interval = np.full(len(raw_ecg), np.nan) + for i in range(len(all_R_peak) - 1): + Interval[all_R_peak[i]: all_R_peak[i + 1]] = all_R_peak[i + 1] - all_R_peak[i] + + return all_R_peak.reshape(-1) \ No newline at end of file diff --git a/ecg_label_check.py b/ecg_label_check.py index 8d0927d..3628a2f 100644 --- a/ecg_label_check.py +++ b/ecg_label_check.py @@ -9,7 +9,9 @@ import sys from logging import NOTSET, getLogger, FileHandler, Formatter, StreamHandler, info, error, debug from time import time, strftime, localtime +import pandas as pd import numpy as np +from PyQt5 import QtGui from PyQt5.QtGui import QFont, QDoubleValidator, QIntValidator from matplotlib.ticker import FuncFormatter from numpy import load, nan, zeros, append, linspace, place @@ -20,9 +22,10 @@ from datetime import datetime from pathlib import Path from PyQt5.QtCore import QCoreApplication, QTimer from PyQt5.QtWidgets import QFileDialog, QMainWindow, QMessageBox, QButtonGroup, QApplication, QTableWidgetItem, \ - QLineEdit, QAction, QTableWidget + QLineEdit, QAction, QTableWidget, QDialog, QVBoxLayout, QLabel, QPushButton, QDialogButtonBox from scipy.signal import butter, filtfilt, find_peaks +import append from MainWindow import Ui_MainWindow use("Qt5Agg") # 声明使用QT5 @@ -153,7 +156,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.radioButton_data1_fillterMode.setChecked(True) self.radioButton_data2_fillterMode.setChecked(True) self.radioButton_move_preset_1.setChecked(True) - self.pushButton_append.setEnabled(False) self.pushButton_outputLabel.setEnabled(False) self.figToolbar.action_Label_Single.setEnabled(False) self.figToolbar.action_Label_Multiple.setEnabled(False) @@ -362,7 +364,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): # 更新界面 self.groupBox_inputSetting.setEnabled(False) self.groupBox_autoplay.setEnabled(True) - self.pushButton_append.setEnabled(True) self.pushButton_outputLabel.setEnabled(True) self.figToolbar.action_Label_Single.setEnabled(True) self.figToolbar.action_Label_Multiple.setEnabled(True) @@ -388,6 +389,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): # 保存路径文件是否存在的检查 if not Path(self.lineEdit_savepath.text()).exists(): Path(self.lineEdit_savepath.text()).touch() + np.savetxt(Path(self.lineEdit_savepath.text()), self.label2, fmt='%d', newline='\n') info("Finished Input Data.") self.textBrowser_update("提示:导入数据完成") @@ -574,22 +576,25 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.textBrowser_update(f"操作:跳转到x坐标: {str(int(x))}") def slot_btn_append(self): - # TODO - reply = QMessageBox.question(self, "警告:确认操作", f"你正在执行<片段合并>,请确保需要被合并的片段已完成标注工作。你确定要将片段合并,并将结果保存到{self.lineEdit_resample1000Hz_save_path.text()}?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) - if reply == QMessageBox.Yes: - for idx in range(len(self.ecg_seq)): - DataFrame(self.ecg_seq[idx].reshape(-1)).to_csv( - str(Path(self.lineEdit_detect_Rpeaks_save_path.text()) / f"{idx + 1}ecg.txt"), index=False, - header=None) - DataFrame(self.R_peak_seq[idx].reshape(-1)).to_csv( - str(Path(self.lineEdit_detect_Rpeaks_save_path.text()) / f"{idx + 1}Rpeak.txt"), index=False, - header=None) - info(f"Saved Data {idx + 1} to Directory {self.lineEdit_detect_Rpeaks_save_path.text()}.") - self.textBrowser_update( - f"提示:保存片段{idx + 1}成功至文件夹{self.lineEdit_detect_Rpeaks_save_path.text()}") - self.msgBox.setText(f"保存成功至{self.lineEdit_detect_Rpeaks_save_path.text()}") + dialog = CustomMessageBox(self) + reply = dialog.exec_() + if reply == QDialog.Accepted: + if self.lineEdit_rootpath.text() == "" or len(list((Path(self.lineEdit_rootpath.text()) / "label").rglob("*location_J.txt"))) == 0: + info(f"*location_J.txt Files not Exist in the Directory.") + self.textBrowser_update(f"错误:无法进行<片段合并>") + self.msgBox.setText(f"目录下不存在*location_J.txt文件") + self.msgBox.setIcon(QMessageBox.Warning) + self.msgBox.exec() + return + final_Rpeak = append.Rpeak_Detection(self.lineEdit_rootpath.text(), int(dialog.lineEdit_fs.text()), int(dialog.lineEdit_th1.text())) + pd.DataFrame(final_Rpeak).to_csv(Path(self.lineEdit_rootpath.text()) / "final_Rpeak.txt", index=False, header=None) + info(f"Saved final_Rpeak to Directory {self.lineEdit_rootpath.text()}.") + self.textBrowser_update(f"提示:保存final_Rpeak.txt成功至文件夹{self.lineEdit_rootpath.text()}") + self.msgBox.setText(f"保存成功至{self.lineEdit_rootpath.text()}") self.msgBox.setIcon(QMessageBox.Information) self.msgBox.exec() + else: + self.textBrowser_update("提示:操作取消") def slot_btn_outputLabel(self): np.savetxt(Path(self.lineEdit_savepath.text()), self.label2, fmt='%d', newline='\n') @@ -973,6 +978,49 @@ class CustomNavigationToolbar(NavigationToolbar2QT): self.canvas.mpl_disconnect(self.cid_mouse_hold) self.cid_mouse_hold = None +class CustomMessageBox(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + + self.resize(300, 300) + self.setWindowTitle("警告:确认操作") + layout = QVBoxLayout() + self.label = QLabel("是否执行<片段合并>?请在执行前确保此份数据的所有片段都已被打标!请输入相应参数后执行任务。") + self.label1 = QLabel(" ") + self.label_fs = QLabel("信号采样率(Hz):") + self.label_th1 = QLabel("寻峰阈值(个):") + self.lineEdit_fs = QLineEdit() + self.lineEdit_th1 = QLineEdit() + + font = QtGui.QFont() + font.setFamily("黑体") + font.setPointSize(14) + self.label.setFont(font) + self.label1.setFont(font) + self.label_fs.setFont(font) + self.label_th1.setFont(font) + self.lineEdit_fs.setFont(font) + self.lineEdit_th1.setFont(font) + + self.label.setWordWrap(True) + self.label.setFixedWidth(300) + self.lineEdit_fs.setText("1000") + self.lineEdit_th1.setText("130") + + layout.addWidget(self.label) + layout.addWidget(self.label1) + layout.addWidget(self.label_fs) + layout.addWidget(self.lineEdit_fs) + layout.addWidget(self.label_th1) + layout.addWidget(self.lineEdit_th1) + + self.button_box = QDialogButtonBox(QDialogButtonBox.Yes | QDialogButtonBox.No) + self.button_box.setFont(font) + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) + layout.addWidget(self.button_box) + self.setLayout(layout) + # 主函数 if __name__ == '__main__': app = QApplication(sys.argv)