diff --git a/debug_script/Replace_missing.py b/debug_script/Replace_missing.py new file mode 100644 index 0000000..40d564e --- /dev/null +++ b/debug_script/Replace_missing.py @@ -0,0 +1,18 @@ +from pathlib import Path +import pandas as pd +import numpy as np + +samp_np = 10395 +missing_second = 11495+11463-6300 +missing_length = 17.29-2.81 +fs = 500 +missging_bcg = Path(rf"E:\code\DataCombine2023\ZD5Y2\OrgBCG_Text\{samp_np}\OrgBCG_Raw_500.txt") + +bcg_data = pd.read_csv(missging_bcg, header=None).to_numpy().reshape(-1) +miss_start = int(missing_second * fs) +bcg_data_filled = np.concatenate([bcg_data[:miss_start], + np.full(int(missing_length * fs), np.mean(bcg_data)), + bcg_data[miss_start:]]) + +output_path = Path(rf"E:\code\DataCombine2023\ZD5Y2\OrgBCG_Text\{samp_np}\OrgBCG_Raw_500_filled.txt") +np.savetxt(output_path, bcg_data_filled, fmt="%d") diff --git a/debug_script/show_orgBcg_psg_resp.py b/debug_script/show_orgBcg_psg_resp.py new file mode 100644 index 0000000..f6456a5 --- /dev/null +++ b/debug_script/show_orgBcg_psg_resp.py @@ -0,0 +1,71 @@ +from pathlib import Path +from matplotlib import pyplot as plt +import pandas as pd +import numpy as np +from func.Filters.Preprocessing import Butterworth_for_ECG_PreProcess, Butterworth_for_BCG_PreProcess + +# # 原始 +# psg_dir = Path(r"E:\code\DataCombine2023\ZD5Y2\PSG_Text\10787") +# org_bcg_dir = Path(r"E:\code\DataCombine2023\ZD5Y2\OrgBCG_Text\10787") +# tho_file_path = psg_dir / "Effort Tho_Raw_100.txt" +# abd_file_path = psg_dir / "Effort Abd_Raw_100.txt" +# tho_fs = 100 +# abd_fs = 100 +# orgbcg_file_path = org_bcg_dir / "OrgBCG_Raw_500.txt" +# orgbcg_fs = 500 + +# 对齐后 +psg_dir = Path(r"E:\code\DataCombine2023\ZD5Y2\PSG_Aligned\10787") +org_bcg_dir = Path(r"E:\code\DataCombine2023\ZD5Y2\OrgBCG_Aligned\10787") +tho_file_path = psg_dir / "Effort Tho_Sync_100.txt" +abd_file_path = psg_dir / "Effort Abd_Sync_100.txt" +tho_fs = 100 +abd_fs = 100 +orgbcg_file_path = org_bcg_dir / "OrgBCG_Sync_1000.txt" +orgbcg_fs = 1000 + + +tho_raw = pd.read_csv(tho_file_path, header=None).to_numpy().reshape(-1) +abd_raw = pd.read_csv(abd_file_path, header=None).to_numpy().reshape(-1) +orgbcg_raw = pd.read_csv(orgbcg_file_path, header=None).to_numpy().reshape(-1) + +tho_filtered = Butterworth_for_ECG_PreProcess(tho_raw, tho_fs, type="bandpass", low_cut=0.01, high_cut=15.0, order=4) +abd_filtered = Butterworth_for_ECG_PreProcess(abd_raw, abd_fs, type="bandpass", low_cut=0.01, high_cut=15.0, order=4) +orgbcg_filtered = Butterworth_for_BCG_PreProcess(orgbcg_raw, orgbcg_fs, type="bandpass", low_cut=0.01, high_cut=0.7, order=4) + + +# 降采样 +orgbcg_filtered = orgbcg_filtered[::orgbcg_fs//10] # 从500Hz降采样到10Hz +tho_filtered = tho_filtered[::tho_fs//10] # 从100Hz降采样到10Hz +abd_filtered = abd_filtered[::abd_fs//10] # 从100Hz降采样到10Hz + +plt.figure(figsize=(12, 8)) +psg_x = np.linspace(0, len(tho_filtered) / 10, len(tho_filtered)) +orgbcg_x = np.linspace(0, len(orgbcg_filtered) / 10, len(orgbcg_filtered)) + +plt.subplot(3, 1, 1) +plt.plot(psg_x, tho_filtered, label='Thoracic Effort', color='blue') +plt.title('Thoracic Effort Signal (Filtered)') +plt.xlabel('Time (s)') +plt.ylabel('Amplitude') +plt.grid() +plt.legend() + +plt.subplot(3, 1, 2) +plt.plot(psg_x, abd_filtered, label='Abdominal Effort', color='green') +plt.title('Abdominal Effort Signal (Filtered)') +plt.xlabel('Time (s)') +plt.ylabel('Amplitude') +plt.grid() +plt.legend() + +plt.subplot(3, 1, 3) +plt.plot(orgbcg_x, orgbcg_filtered, label='Original BCG', color='red') +plt.title('Original BCG Signal (Filtered)') +plt.xlabel('Time (s)') +plt.ylabel('Amplitude') +plt.grid() +plt.legend() +plt.tight_layout() +plt.show() + diff --git a/func/Module_approximately_align.py b/func/Module_approximately_align.py index a094a2d..8328c1c 100644 --- a/func/Module_approximately_align.py +++ b/func/Module_approximately_align.py @@ -48,6 +48,7 @@ ButtonState = { "radioButton_NTHO": False, "radioButton_NABD": False, "radioButton_custom": False, + "radioButton_customFreq": False, "radioButton_freqTHO": False, "radioButton_freqABD": False, }, @@ -71,6 +72,7 @@ ButtonState = { "radioButton_NTHO": False, "radioButton_NABD": False, "radioButton_custom": False, + "radioButton_customFreq": False, "radioButton_freqTHO": False, "radioButton_freqABD": False, } @@ -303,6 +305,7 @@ class MainWindow_approximately_align(QMainWindow): self.ui.radioButton_custom.clicked.connect(self.__enableAlign__) self.ui.radioButton_freqTHO.clicked.connect(self.__EstimateFrequencySelect__) self.ui.radioButton_freqABD.clicked.connect(self.__EstimateFrequencySelect__) + self.ui.radioButton_customFreq.clicked.connect(self.__EstimateFrequencySelect__) @overrides def closeEvent(self, event): @@ -353,6 +356,7 @@ class MainWindow_approximately_align(QMainWindow): self.ui.spinBox_SelectEpoch.setMinimum(0) self.ui.radioButton_freqABD.setChecked(False) self.ui.radioButton_freqTHO.setChecked(False) + self.ui.radioButton_customFreq.setChecked(False) self.ui.radioButton_freqTHO.setText("备选1") self.ui.radioButton_freqABD.setText("备选2") @@ -664,22 +668,22 @@ class MainWindow_approximately_align(QMainWindow): response = self.data.estimate_frequency(tho_bias_list) tho_y = response.data["estimate_y"] - tho_frequency = response.data["frequency"] + tho_frequency_ratio = response.data["frequency"] tho_slope = response.data["slope"] tho_intercept = response.data["intercept"] response = self.data.estimate_frequency(abd_bias_list) abd_y = response.data["estimate_y"] - abd_frequency = response.data["frequency"] + abd_frequency_ratio = response.data["frequency"] abd_slope = response.data["slope"] abd_intercept = response.data["intercept"] result = self.__plot__(epoch_min, epoch_max, tho_bias_list, abd_bias_list, tho_y, abd_y, - tho_frequency, abd_frequency) + tho_frequency_ratio, abd_frequency_ratio) Config["estimate"] = {} - Config["estimate"]["tho_frequency"] = tho_frequency + Config["estimate"]["tho_frequency"] = tho_frequency_ratio Config["estimate"]["tho_slope"] = tho_slope Config["estimate"]["tho_intercept"] = tho_intercept - Config["estimate"]["abd_frequency"] = abd_frequency + Config["estimate"]["abd_frequency"] = abd_frequency_ratio Config["estimate"]["abd_slope"] = abd_slope Config["estimate"]["abd_intercept"] = abd_intercept @@ -687,6 +691,7 @@ class MainWindow_approximately_align(QMainWindow): self.ui.radioButton_freqABD.setText(str(Config["estimate"]["abd_frequency"] * Config["InputConfig"]["orgBcgFreq"])) ButtonState["Current"]["radioButton_freqTHO"] = True ButtonState["Current"]["radioButton_freqABD"] = True + ButtonState["Current"]["radioButton_customFreq"] = True if not result.status: PublicFunc.text_output(self.ui, "(1/1)" + result.info, Constants.TIPS_TYPE_ERROR) @@ -719,15 +724,30 @@ class MainWindow_approximately_align(QMainWindow): def __EstimateFrequencySelect__(self): PublicFunc.__disableAllButton__(self, ButtonState) if self.ui.radioButton_freqTHO.isChecked(): - frequency = Config["estimate"]["tho_frequency"] - Config["estimate_freq"] = frequency + frequency_ratio = Config["estimate"]["tho_frequency"] + Config["estimate_freq"] = frequency_ratio Config["estimate_slope"] = Config["estimate"]["tho_slope"] Config["estimate_intercept"] = Config["estimate"]["tho_intercept"] elif self.ui.radioButton_freqABD.isChecked(): - frequency = Config["estimate"]["abd_frequency"] - Config["estimate_freq"] = frequency + frequency_ratio = Config["estimate"]["abd_frequency"] + Config["estimate_freq"] = frequency_ratio Config["estimate_slope"] = Config["estimate"]["abd_slope"] Config["estimate_intercept"] = Config["estimate"]["abd_intercept"] + elif self.ui.radioButton_customFreq.isChecked(): + # self.ui.lineEdit_customFreq.text() + try: + float(self.ui.lineEdit_customFreq.text()) + except ValueError: + PublicFunc.msgbox_output(self, "自定义频率输入不合法,请输入数字类型数据!", Constants.MSGBOX_TYPE_ERROR) + PublicFunc.finish_operation(self, ButtonState) + return + + frequency_ratio = float(self.ui.lineEdit_customFreq.text()) / Config["InputConfig"]["orgBcgFreq"] + Config["estimate_freq"] = frequency_ratio + Config["estimate_slope"] = None + Config["estimate_intercept"] = None + + else: return @@ -1046,12 +1066,12 @@ class MainWindow_approximately_align(QMainWindow): return Result().success(info=Constants.DRAW_FINISHED) def DrawAlignScatter(self, epoch_min, epoch_max, tho_bias_list, abd_bias_list, tho_y, abd_y, - tho_frequency, abd_frequency): + tho_frequency_ratio, abd_frequency_ratio): try: ax1 = self.fig.add_subplot(211) ax1.scatter(linspace(epoch_min, epoch_max, len(tho_bias_list)), tho_bias_list, alpha=0.2) ax1.plot(linspace(epoch_min, epoch_max, len(tho_bias_list)), tho_y, color='orange', - label=f"THO Frequency: {tho_frequency} Hz") + label=f"THO Frequency_ratio: {tho_frequency_ratio}") ax1.axhline(y=0, color='red', linestyle='--', alpha=0.3) ax1.set_xlabel("Epoch") ax1.set_ylabel("Tho Bias / s") @@ -1061,7 +1081,7 @@ class MainWindow_approximately_align(QMainWindow): ax2 = self.fig.add_subplot(212) ax2.scatter(linspace(epoch_min, epoch_max, len(abd_bias_list)), abd_bias_list, alpha=0.2) ax2.plot(linspace(epoch_min, epoch_max, len(abd_bias_list)), abd_y, color='orange', - label=f"ABD Frequency: {abd_frequency} Hz") + label=f"ABD Frequency_ratio: {abd_frequency_ratio}") ax2.axhline(y=0, color='red', linestyle='--', alpha=0.3) ax2.set_xlabel("Epoch") ax2.set_ylabel("Abd Bias / s") @@ -1260,12 +1280,12 @@ class Data: if Config["PSGConfig"]["PSGDelBase"]: # 减去四秒钟平均滤波 self.processed_Tho = self.processed_Tho - convolve( - self.processed_Tho, ones(int(4 * temp_frequency)) / int(4 * temp_frequency), mode='same') + self.processed_Tho, ones(int(10 * temp_frequency)) / int(10 * temp_frequency), mode='same') self.processed_Abd = self.processed_Abd - convolve( - self.processed_Abd, ones(int(4 * temp_frequency)) / int(4 * temp_frequency), mode='same') + self.processed_Abd, ones(int(10 * temp_frequency)) / int(10 * temp_frequency), mode='same') if Config["orgBcgConfig"]["orgBcgDelBase"]: self.processed_orgBcg = self.processed_orgBcg - convolve( - self.processed_orgBcg, ones(int(4 * temp_frequency)) / int(4 * temp_frequency), mode='same') + self.processed_orgBcg, ones(int(10 * temp_frequency)) / int(10 * temp_frequency), mode='same') except Exception as e: return Result().failure( info=Constants.APPROXIMATELY_DELETE_BASE_FAILURE + Constants.FAILURE_REASON[ @@ -1306,9 +1326,16 @@ class Data: try: # 用[::]完成 temp_frequency = Config["TempFrequency"] - self.processed_downsample_Tho = self.processed_Tho[::int(temp_frequency / Config["ApplyFrequency"])] - self.processed_downsample_Abd = self.processed_Abd[::int(temp_frequency / Config["ApplyFrequency"])] - self.processed_downsample_orgBcg = self.processed_orgBcg[::int(temp_frequency / Config["ApplyFrequency"])] + # self.processed_downsample_Tho = self.processed_Tho[::int(temp_frequency / Config["ApplyFrequency"])] + # self.processed_downsample_Abd = self.processed_Abd[::int(temp_frequency / Config["ApplyFrequency"])] + # self.processed_downsample_orgBcg = self.processed_orgBcg[::int(temp_frequency / Config["ApplyFrequency"])] + # 用resample完成 + self.processed_downsample_Tho = resample( + self.processed_Tho, int(Config["PSG_seconds"] * Config["ApplyFrequency"])) + self.processed_downsample_Abd = resample( + self.processed_Abd, int(Config["PSG_seconds"] * Config["ApplyFrequency"])) + self.processed_downsample_orgBcg = resample( + self.processed_orgBcg, int(Config["orgBcg_seconds"] * Config["ApplyFrequency"])) except Exception as e: @@ -1333,10 +1360,10 @@ class Data: v = self.processed_downsample_orgBcg[ Config["orgBcgConfig"]["PreCut"]:len(self.processed_downsample_orgBcg) - Config["orgBcgConfig"][ "PostCut"]].copy() - a *= MULTIPLE_FACTOER - v *= MULTIPLE_FACTOER - a = a.astype(int64) - v = v.astype(int64) + # a *= MULTIPLE_FACTOER + # v *= MULTIPLE_FACTOER + # a = a.astype(int64) + # v = v.astype(int64) tho_relate = correlate(a, v, mode='full') tho_relate = tho_relate / (MULTIPLE_FACTOER ** 2) tho_relate2 = - tho_relate @@ -1358,12 +1385,12 @@ class Data: v = self.processed_downsample_orgBcg[ Config["orgBcgConfig"]["PreCut"]:len(self.processed_downsample_orgBcg) - Config["orgBcgConfig"][ "PostCut"]].copy() - a *= 100 - v *= 100 - a = a.astype(int64) - v = v.astype(int64) + # a *= 100 + # v *= 100 + # a = a.astype(int64) + # v = v.astype(int64) abd_relate = correlate(a, v, mode='full') - abd_relate = abd_relate / 10000 + # abd_relate = abd_relate / 10000 abd_relate2 = - abd_relate result = {"abd_relate": abd_relate, "abd_relate2": abd_relate2} @@ -1397,7 +1424,7 @@ class Data: # 获取epoch try: epoch_second = Params.APPROXIMATELY_ALIGN_CONFIG_NEW_CONTENT["Second_PerEpoch"] - epoch_min = max(0, Config["pos"] // epoch_second // Config["ApplyFrequency"] + 1) + epoch_min = max(0, Config["pos"] // (epoch_second * Config["ApplyFrequency"])) + 1 epoch_max = min(len(self.processed_downsample_Tho) // epoch_second // Config["ApplyFrequency"] - 1, (len(self.processed_downsample_orgBcg) + Config["pos"]) // epoch_second // Config[ "ApplyFrequency"] - 1) @@ -1476,16 +1503,17 @@ class Data: if slope != 0: drift_rate = slope / epoch_second # frequency = temp_freq * (1 - drift_rate) - frequency = 1 - drift_rate + # frequency_ratio = 1 - drift_rate + frequency_ratio = 1 / (1 + drift_rate) else: # frequency = float(temp_freq) - frequency = 1 + frequency_ratio = 1 theilsen_y = theilsen.predict(X) return Result().success(info=Constants.APPROXIMATELY_ESTIMATE_FREQUENCY_FINISHED, data={"estimate_y": theilsen_y, - "frequency": frequency, + "frequency": frequency_ratio, "slope": slope, "intercept": theilsen.intercept_}, ) diff --git a/func/Module_cut_pair_file.py b/func/Module_cut_pair_file.py index caf50a3..adfb2b4 100644 --- a/func/Module_cut_pair_file.py +++ b/func/Module_cut_pair_file.py @@ -420,7 +420,7 @@ class Data: self.alignInfo = read_csv(Path(Config["Path"]["InputAlignInfo"]), encoding=Params.UTF8_ENCODING, header=None).to_numpy().reshape(-1) - cleaned_str = re.sub(r'np\.(int64|float64|float32|int32)\((.*?)\)', r'\2', self.alignInfo[0]) + cleaned_str = re.sub(r'np\.\w+\(\s*([^)]+?)\s*\)', r'\1', self.alignInfo[0]) self.alignInfo = literal_eval(cleaned_str) except Exception as e: diff --git a/ui/MainWindow/MainWindow_approximately_align.py b/ui/MainWindow/MainWindow_approximately_align.py index 551ad05..0761d39 100644 --- a/ui/MainWindow/MainWindow_approximately_align.py +++ b/ui/MainWindow/MainWindow_approximately_align.py @@ -16,10 +16,10 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) from PySide6.QtWidgets import (QAbstractSpinBox, QApplication, QCheckBox, QGridLayout, - QGroupBox, QHBoxLayout, QLabel, QMainWindow, - QPushButton, QRadioButton, QSizePolicy, QSpacerItem, - QSpinBox, QStatusBar, QTextBrowser, QVBoxLayout, - QWidget) + QGroupBox, QHBoxLayout, QLabel, QLineEdit, + QMainWindow, QPushButton, QRadioButton, QSizePolicy, + QSpacerItem, QSpinBox, QStatusBar, QTextBrowser, + QVBoxLayout, QWidget) class Ui_MainWindow_approximately_align(object): def setupUi(self, MainWindow_approximately_align): @@ -400,26 +400,43 @@ class Ui_MainWindow_approximately_align(object): self.groupBox_2.setMinimumSize(QSize(0, 0)) self.layoutWidget = QWidget(self.groupBox_2) self.layoutWidget.setObjectName(u"layoutWidget") - self.layoutWidget.setGeometry(QRect(11, 18, 401, 41)) + self.layoutWidget.setGeometry(QRect(11, 18, 411, 60)) self.verticalLayout_3 = QVBoxLayout(self.layoutWidget) self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_3 = QHBoxLayout() - self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") - self.radioButton_freqTHO = QRadioButton(self.layoutWidget) - self.radioButton_freqTHO.setObjectName(u"radioButton_freqTHO") - self.radioButton_freqTHO.setFont(font1) + self.gridLayout_5 = QGridLayout() + self.gridLayout_5.setObjectName(u"gridLayout_5") + self.radioButton_customFreq = QRadioButton(self.layoutWidget) + self.radioButton_customFreq.setObjectName(u"radioButton_customFreq") + self.radioButton_customFreq.setFont(font1) - self.horizontalLayout_3.addWidget(self.radioButton_freqTHO) + self.gridLayout_5.addWidget(self.radioButton_customFreq, 1, 0, 1, 1) self.radioButton_freqABD = QRadioButton(self.layoutWidget) self.radioButton_freqABD.setObjectName(u"radioButton_freqABD") self.radioButton_freqABD.setFont(font1) - self.horizontalLayout_3.addWidget(self.radioButton_freqABD) + self.gridLayout_5.addWidget(self.radioButton_freqABD, 0, 1, 1, 1) + + self.radioButton_freqTHO = QRadioButton(self.layoutWidget) + self.radioButton_freqTHO.setObjectName(u"radioButton_freqTHO") + self.radioButton_freqTHO.setFont(font1) + + self.gridLayout_5.addWidget(self.radioButton_freqTHO, 0, 0, 1, 1) + + self.lineEdit_customFreq = QLineEdit(self.layoutWidget) + self.lineEdit_customFreq.setObjectName(u"lineEdit_customFreq") + sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed) + sizePolicy1.setHorizontalStretch(0) + sizePolicy1.setVerticalStretch(0) + sizePolicy1.setHeightForWidth(self.lineEdit_customFreq.sizePolicy().hasHeightForWidth()) + self.lineEdit_customFreq.setSizePolicy(sizePolicy1) + self.lineEdit_customFreq.setFont(font1) + + self.gridLayout_5.addWidget(self.lineEdit_customFreq, 1, 1, 1, 1) - self.verticalLayout_3.addLayout(self.horizontalLayout_3) + self.verticalLayout_3.addLayout(self.gridLayout_5) self.verticalLayout.addWidget(self.groupBox_2) @@ -530,7 +547,7 @@ class Ui_MainWindow_approximately_align(object): self.verticalLayout.setStretch(2, 3) self.verticalLayout.setStretch(3, 4) self.verticalLayout.setStretch(4, 3) - self.verticalLayout.setStretch(5, 2) + self.verticalLayout.setStretch(5, 3) self.verticalLayout.setStretch(6, 4) self.verticalLayout.setStretch(7, 1) self.verticalLayout.setStretch(8, 6) @@ -607,8 +624,9 @@ class Ui_MainWindow_approximately_align(object): self.pushButton_GetPos.setText(QCoreApplication.translate("MainWindow_approximately_align", u"\u8ba1\u7b97\u5bf9\u9f50", None)) self.radioButton_NABD.setText(QCoreApplication.translate("MainWindow_approximately_align", u"\u5907\u90094", None)) self.groupBox_2.setTitle(QCoreApplication.translate("MainWindow_approximately_align", u"\u91c7\u6837\u7387\u4f30\u8ba1", None)) - self.radioButton_freqTHO.setText(QCoreApplication.translate("MainWindow_approximately_align", u"\u5907\u90091", None)) + self.radioButton_customFreq.setText(QCoreApplication.translate("MainWindow_approximately_align", u"\u81ea\u5b9a\u4e49", None)) self.radioButton_freqABD.setText(QCoreApplication.translate("MainWindow_approximately_align", u"\u5907\u90092", None)) + self.radioButton_freqTHO.setText(QCoreApplication.translate("MainWindow_approximately_align", u"\u5907\u90091", None)) self.groupBox_view_partially.setTitle(QCoreApplication.translate("MainWindow_approximately_align", u"\u5c40\u90e8\u89c2\u6d4b", None)) self.label_9.setText(QCoreApplication.translate("MainWindow_approximately_align", u"Epoch\uff1a", None)) self.pushButton_JUMP.setText(QCoreApplication.translate("MainWindow_approximately_align", u"\u8df3\u8f6c", None)) diff --git a/ui/MainWindow/MainWindow_approximately_align.ui b/ui/MainWindow/MainWindow_approximately_align.ui index ade58d4..8637518 100644 --- a/ui/MainWindow/MainWindow_approximately_align.ui +++ b/ui/MainWindow/MainWindow_approximately_align.ui @@ -25,7 +25,7 @@ 数据粗同步 - + @@ -728,14 +728,38 @@ 11 18 - 401 - 41 + 411 + 60 - - + + + + + + 12 + + + + 自定义 + + + + + + + + 12 + + + + 备选2 + + + + @@ -747,16 +771,19 @@ - - + + + + + 0 + 0 + + 12 - - 备选2 -