From 6b7ba17c6a1231147f0bd721d6bd3d65eeb62466 Mon Sep 17 00:00:00 2001 From: Yorusora Date: Thu, 29 May 2025 13:49:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86<=E4=BD=93=E5=8A=A8?= =?UTF-8?q?=E6=A0=87=E6=B3=A8>=E4=B8=AD=E4=BD=93=E5=8A=A8=E9=80=89?= =?UTF-8?q?=E5=8F=96=E5=8C=BA=E5=9F=9F=E7=9A=84=E5=88=A4=E5=88=AB=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- func/Module_approximately_align.py | 1 - func/Module_artifact_label.py | 181 ++++++++++++++++++++++------- func/utils/Constants.py | 10 +- 4 files changed, 151 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index ec99799..b38f597 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,17 @@ ## TODO LIST: 1、根据选定区域获取峰值时,将绘制的区域设置为有上限和下限的矩形,根据矩形获取到的横纵区域来计算峰值 -2、体动选取区域的判别尚未做的很完整,选中多个已有的体动的区域时可能会出现问题 +~~2、体动选取区域的判别尚未做的很完整,选中多个已有的体动的区域时可能会出现问题~~ 3、部分模块在导入失败后重新导入时会出现问题,已知模块有<人工纠正>、<体动标注>、<呼吸可用性及间期标注>、<睡眠呼吸暂停事件标注>,主要是涉及到按钮状态的设置,有待后续优化。当前将这些有涉及到的功能,禁止了导入数据后在不关闭界面的情况下直接重新导入 4、在J峰算法定位的时候滤波导致的信号偏移,导致之后的峰值坐标点与实际峰值出现偏移 -~~5、写一个脚本,用于直接从ConfigParams.py中读取文件命名规则,并直接自动化写入到一个markdown文件中。目的是方便文件命名的修改。~~ +~~5、写一个脚本,用于直接从ConfigParams.py中读取文件命名规则,并直接自动化写入到一个markdown文件中。目的是方便文件命名的修改~~ + +6、<呼吸可用性及间期标注>的体动显示做一个和一样的可以根据勾选来显示需要显示的体动 + +7、各个模块中的检测父级文件夹是否存在的功能仍存在问题,无法正确创建文件夹 ## 1、主菜单 diff --git a/func/Module_approximately_align.py b/func/Module_approximately_align.py index 3b64623..b0b8a8f 100644 --- a/func/Module_approximately_align.py +++ b/func/Module_approximately_align.py @@ -1041,7 +1041,6 @@ class Data: # 预重采样 try: if Config["InputConfig"]["ThoFreq"] != Config["TempFrequency"]: - print(int(Config["InputConfig"]["ThoFreq"]), int(Config["TempFrequency"])) self.processed_Tho = resample(self.processed_Tho, int(Config["PSG_seconds"] * Config["TempFrequency"])) diff --git a/func/Module_artifact_label.py b/func/Module_artifact_label.py index ccd4348..f092361 100644 --- a/func/Module_artifact_label.py +++ b/func/Module_artifact_label.py @@ -9,7 +9,7 @@ from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication, QTableWidg from matplotlib import gridspec, patches from matplotlib.backends.backend_qt import NavigationToolbar2QT from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg -from numpy import array +from numpy import array, append, sum as np_sum, nonzero from numpy.fft import fft, fftfreq from overrides import overrides from pandas import read_csv, DataFrame, concat @@ -655,7 +655,12 @@ class MainWindow_artifact_label(QMainWindow): def __slot_btn_label__(self): sender = self.sender() flag = False - select_row = array([]).astype(dict) + select_row = [] + select_type = array([0, 0, 0, 0, 0]) + flagf = False + flagb = False + start_time = 0 + end_time = 0 if sender == self.ui.pushButton_type_1: type = 1 @@ -678,61 +683,149 @@ class MainWindow_artifact_label(QMainWindow): for index, row in self.data.df_Artifact_a.iterrows(): value_startTime = row['startTime'] value_endTime = row['endTime'] + value_type = row['type'] - # if ((value_endTime >= int(self.ui.lineEdit_start_time.text()) and value_startTime <= int(self.ui.lineEdit_start_time.text())) or - # (value_endTime >= int(self.ui.lineEdit_end_time.text()) and value_startTime <= int(self.ui.lineEdit_end_time.text())) or - # (value_startTime >= int(self.ui.lineEdit_start_time.text()) and value_endTime <= int(self.ui.lineEdit_end_time.text())) or - # (value_startTime <= int(self.ui.lineEdit_start_time.text()) and value_endTime >= int(self.ui.lineEdit_end_time.text()))): - # select_row = append(select_row, row) + if ((value_startTime <= int(self.ui.lineEdit_end_time.text()) <= value_endTime) or + (value_startTime <= int(self.ui.lineEdit_start_time.text()) <= value_endTime) or + (value_startTime >= int(self.ui.lineEdit_start_time.text()) and value_endTime <= int(self.ui.lineEdit_end_time.text())) or + (0 < (int(self.ui.lineEdit_start_time.text()) - value_endTime) <= (2 * Config["InputConfig"]["UseFreq"])) or + (0 < (value_startTime - int(self.ui.lineEdit_end_time.text())) <= (2 * Config["InputConfig"]["UseFreq"]))): + select_row.append(row.to_dict()) + if row['type'] == 1: + select_type[0] = select_type[0] + 1 + elif row['type'] == 2: + select_type[1] = select_type[1] + 1 + elif row['type'] == 3: + select_type[2] = select_type[2] + 1 + elif row['type'] == 4: + select_type[3] = select_type[3] + 1 + elif row['type'] == 5: + select_type[4] = select_type[4] + 1 + if (0 < (int(self.ui.lineEdit_start_time.text()) - value_endTime) <= (2 * Config["InputConfig"]["UseFreq"])) and (value_type == type): + flagf = True + if (0 < (value_startTime - int(self.ui.lineEdit_end_time.text())) <= (2 * Config["InputConfig"]["UseFreq"])) and (value_type == type): + flagb = True - # TODO:体动选取区域的判别尚未做的很完整,选中多个已有的体动的区域时可能会出现问题 - if (type == 1 and row['type'] == 1) or (type == 2 and row['type'] == 2) or (type == 3 and row['type'] == 3): - if ((value_startTime > int(self.ui.lineEdit_start_time.text()) and (value_startTime - int(self.ui.lineEdit_end_time.text()) <= 2 * Config["InputConfig"]["UseFreq"])) or - (value_endTime < int(self.ui.lineEdit_end_time.text()) and (int(self.ui.lineEdit_end_time.text()) - value_endTime <= 2 * Config["InputConfig"]["UseFreq"]))): - reply = QMessageBox.question(self, '确认', '{}{},是否确认合并?'.format(Constants.ARTIFACT_LABEL_MERGE, row['number']), QMessageBox.Yes | QMessageBox.No, - QMessageBox.No) - if reply == QMessageBox.Yes: - self.data.df_Artifact_a.loc[self.data.df_Artifact_a['number'] == row['number'], - ['number', 'type', 'startTime', 'endTime']] = [ - int(row['number']), - int(type), - int(self.ui.lineEdit_start_time.text()) if int(self.ui.lineEdit_start_time.text()) < int(row['startTime']) else int(row['startTime']), - int(self.ui.lineEdit_end_time.text()) if int(self.ui.lineEdit_end_time.text()) > int(row['endTime']) else int(row['endTime']) - ] - new_row = {'number': int(row['number']), - 'type': int(type), - 'startTime': int(self.ui.lineEdit_start_time.text()), - 'endTime': int(self.ui.lineEdit_end_time.text())} - flag = True - break - else: - return - if value_startTime <= int(self.ui.lineEdit_end_time.text()) and int( - self.ui.lineEdit_start_time.text()) <= value_endTime: - PublicFunc.msgbox_output(self, f"{Constants.ARTIFACT_LABEL_OVERLAPPING}{row['number']}", Constants.MSGBOX_TYPE_ERROR) - return + count = np_sum(select_type >= 1) + if count >= 2 or (count == 1 and nonzero(select_type)[0][0] + 1 == type): + if count >= 2: + reply = QMessageBox.question(self, '确认', + '{}{}'.format(Constants.ARTIFACT_LABEL_MULTIPLE_ARTIFACT_COVER_OR_DELETE, [d["number"] for d in select_row]), + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No) + if reply == QMessageBox.No: + return - if not flag: - new_row = {'number': int(len(self.data.df_Artifact_a) + 1), - 'type': int(type), - 'startTime': int(self.ui.lineEdit_start_time.text()), - 'endTime': int(self.ui.lineEdit_end_time.text())} - self.data.df_Artifact_a = concat([self.data.df_Artifact_a, DataFrame([new_row])], ignore_index=True) - sorted_part = self.data.df_Artifact_a[['type', 'startTime', 'endTime']].sort_values(by='startTime').reset_index( + if flagf == True and flagb == False: + if type == 1 or type == 2 or type == 3: + PublicFunc.msgbox_output(self, Constants.ARTIFACT_LABEL_FRONT_TWO_SECONDS_MERGE, + Constants.MSGBOX_TYPE_INFO) + start_time = select_row[0]['startTime'] + end_time = int(self.ui.lineEdit_end_time.text()) + elif type == 4 or type == 5: + PublicFunc.msgbox_output(self, Constants.ARTIFACT_LABEL_FRONT_TWO_SECONDS_WARNING, + Constants.MSGBOX_TYPE_INFO) + select_row = select_row[1:] + start_time = int(self.ui.lineEdit_start_time.text()) + end_time = int(self.ui.lineEdit_end_time.text()) + if flagf == False and flagb == True: + if type == 1 or type == 2 or type == 3: + PublicFunc.msgbox_output(self, Constants.ARTIFACT_LABEL_BACK_TWO_SECONDS_MERGE, + Constants.MSGBOX_TYPE_INFO) + start_time = int(self.ui.lineEdit_start_time.text()) + end_time = select_row[-1]['endTime'] + elif type == 4 or type == 5: + PublicFunc.msgbox_output(self, Constants.ARTIFACT_LABEL_BACK_TWO_SECONDS_WARNING, + Constants.MSGBOX_TYPE_INFO) + select_row = select_row[:-1] + start_time = int(self.ui.lineEdit_start_time.text()) + end_time = int(self.ui.lineEdit_end_time.text()) + if flagf == True and flagb == True: + if type == 1 or type == 2 or type == 3: + PublicFunc.msgbox_output(self, Constants.ARTIFACT_LABEL_FRONT_AND_BACK_TWO_SECONDS_MERGE, + Constants.MSGBOX_TYPE_INFO) + start_time = select_row[0]['startTime'] + end_time = select_row[-1]['endTime'] + elif type == 4 or type == 5: + PublicFunc.msgbox_output(self, Constants.ARTIFACT_LABEL_FRONT_AND_BACK_TWO_SECONDS_WARNING, + Constants.MSGBOX_TYPE_INFO) + select_row = select_row[1:-1] + start_time = int(self.ui.lineEdit_start_time.text()) + end_time = int(self.ui.lineEdit_end_time.text()) + if flagf == False and flagb == False: + start_time = int(self.ui.lineEdit_start_time.text()) + end_time = int(self.ui.lineEdit_end_time.text()) + + for select_each_row in select_row: + self.data.df_Artifact_a = self.data.df_Artifact_a[self.data.df_Artifact_a['number'] != select_each_row["number"]] + else: + select_row = [] + select_type = array([0, 0, 0, 0, 0]) + for index, row in self.data.df_Artifact_a.iterrows(): + value_startTime = row['startTime'] + value_endTime = row['endTime'] + value_type = row['type'] + if ((value_startTime <= int(self.ui.lineEdit_end_time.text()) <= value_endTime) or + (value_startTime <= int(self.ui.lineEdit_start_time.text()) <= value_endTime) or + (value_startTime >= int(self.ui.lineEdit_start_time.text()) and value_endTime <= int( + self.ui.lineEdit_end_time.text()))): + select_row.append(row.to_dict()) + if row['type'] == 1: + select_type[0] = select_type[0] + 1 + elif row['type'] == 2: + select_type[1] = select_type[1] + 1 + elif row['type'] == 3: + select_type[2] = select_type[2] + 1 + elif row['type'] == 4: + select_type[3] = select_type[3] + 1 + elif row['type'] == 5: + select_type[4] = select_type[4] + 1 + + count = np_sum(select_type >= 1) + + if (count == 1 and nonzero(select_type)[0][0] + 1 != type): + reply = QMessageBox.question(self, '确认', + '{}{}'.format(Constants.ARTIFACT_LABEL_SINGLE_TYPE_NOT_EQUAL, + [d["number"] for d in select_row]), + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No) + if reply == QMessageBox.No: + return + + start_time = int(self.ui.lineEdit_start_time.text()) + end_time = int(self.ui.lineEdit_end_time.text()) + + for select_each_row in select_row: + self.data.df_Artifact_a = self.data.df_Artifact_a[self.data.df_Artifact_a['number'] != select_each_row["number"]] + elif count == 0: + start_time = int(self.ui.lineEdit_start_time.text()) + end_time = int(self.ui.lineEdit_end_time.text()) + else: + raise ValueError("count值不存在") + + new_row = { + 'number': 1, + 'type': int(type), + 'startTime': start_time, + 'endTime': end_time + } + + self.data.df_Artifact_a = concat([self.data.df_Artifact_a, DataFrame([new_row])], ignore_index=True) + self.data.df_Artifact_a[['type', 'startTime', 'endTime']] = self.data.df_Artifact_a[['type', 'startTime', 'endTime']].sort_values(by='startTime').reset_index( drop=True) - self.data.df_Artifact_a[['type', 'startTime', 'endTime']] = sorted_part + self.data.df_Artifact_a['number'] = range(1, len(self.data.df_Artifact_a) + 1) + self.update_tableWidget() self.update_Info() self.__plot_artifact__() - target_row = self.data.df_Artifact_a[ - self.data.df_Artifact_a.eq(int(self.ui.lineEdit_start_time.text())).any(axis=1)] + target_row = self.data.df_Artifact_a[self.data.df_Artifact_a.eq(start_time).any(axis=1)] if not target_row.empty: first_column_value = target_row.iloc[0, 0] # 获取第1列的值 else: raise AttributeError() - PublicFunc.text_output(self.ui, f"新增体动标签{first_column_value}, 类型{new_row['type']},从{int(self.ui.lineEdit_start_time.text())}ms到{int(self.ui.lineEdit_end_time.text())}ms", Constants.TIPS_TYPE_INFO) + PublicFunc.text_output(self.ui, f"新增体动标签{first_column_value}, 类型{new_row['type']},从{int(start_time)}ms到{int(end_time)}ms", Constants.TIPS_TYPE_INFO) self.save() diff --git a/func/utils/Constants.py b/func/utils/Constants.py index ab85a69..7cbdab4 100644 --- a/func/utils/Constants.py +++ b/func/utils/Constants.py @@ -324,8 +324,14 @@ class Constants: ARTIFACT_LABEL_JUMP_ARTIFACT: str = "跳转到体动" ARTIFACT_LABEL_RECOVER_SCALE: str = "尺度恢复" ARTIFACT_LABEL_MISS_ARGS: str = "打标参数未填写" - ARTIFACT_LABEL_OVERLAPPING: str = "当前所打标的片段存在重合,重合片段序号:" - ARTIFACT_LABEL_MERGE: str = "当前所打标的片段距离附近片段不到2秒,片段序号:" + ARTIFACT_LABEL_MULTIPLE_ARTIFACT_COVER_OR_DELETE: str = "所选区域包含多种类型的体动,是否需要删除它们并使用新标注进行覆盖?所选区域中含有的体动序号:" + ARTIFACT_LABEL_FRONT_TWO_SECONDS_MERGE: str = "所选区域前2秒内有与当前标注相同类型的体动,将执行合并" + ARTIFACT_LABEL_BACK_TWO_SECONDS_MERGE: str = "所选区域后2秒内有与当前标注相同类型的体动,将执行合并" + ARTIFACT_LABEL_FRONT_AND_BACK_TWO_SECONDS_MERGE: str = "所选区域前后2秒内都有与当前标注相同类型的体动,将执行合并" + ARTIFACT_LABEL_FRONT_TWO_SECONDS_WARNING: str = "所选区域前2秒内有与当前标注相同类型的体动,仅进行提示" + ARTIFACT_LABEL_BACK_TWO_SECONDS_WARNING: str = "所选区域后2秒内有与当前标注相同类型的体动,仅进行提示" + ARTIFACT_LABEL_FRONT_AND_BACK_TWO_SECONDS_WARNING: str = "所选区域前后2秒内都有与当前标注相同类型的体动,仅进行提示" + ARTIFACT_LABEL_SINGLE_TYPE_NOT_EQUAL: str = "所选区域仅包含一种类型的体动,但其类型与当前标注的类型不匹配,是否需要删除它们并使用新标注覆盖?所选区域中含有的体动序号:" ARTIFACT_LABEL_DELETE_ARTIFACT_SUCCESSFULLY: str = "体动被删除" ARTIFACT_LABEL_DELETE_ARTIFACT_FAILURE: str = "需要被删除的体动不存在" ARTIFACT_LABEL_ACTION_LABEL: str = f"标注体动({Params.ARTIFACT_LABEL_ACTION_LABEL_ARTIFACT_SHORTCUT_KEY})"