From 60e245b1e39e5f0146b62b681854e9c61f9ca1b4 Mon Sep 17 00:00:00 2001 From: marques Date: Tue, 11 Nov 2025 10:39:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=B9=85=E5=80=BC=E6=94=B9?= =?UTF-8?q?=E5=8F=98=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HYS_process.py | 93 ++++++++++++++++---------------- dataset_config/HYS_config.yaml | 2 +- signal_method/rule_base_event.py | 10 ++-- utils/operation_tools.py | 2 +- 4 files changed, 55 insertions(+), 52 deletions(-) diff --git a/HYS_process.py b/HYS_process.py index 50f3c6c..06156eb 100644 --- a/HYS_process.py +++ b/HYS_process.py @@ -12,7 +12,6 @@ 提供数据处理前后的可视化对比,帮助理解数据变化 绘制多条可用性趋势图,展示数据的可用区间、体动区间、低幅值区间等 -todo: 使用mask 屏蔽无用区间 # 低幅值区间规则标定与剔除 @@ -28,8 +27,10 @@ import numpy as np import signal_method import os from matplotlib import pyplot as plt + os.environ['DISPLAY'] = "localhost:10.0" + def process_one_signal(samp_id, show=False): signal_path = list((org_signal_root_path / f"{samp_id}").glob("OrgBCG_Sync_*.txt")) if not signal_path: @@ -69,12 +70,11 @@ def process_one_signal(samp_id, show=False): resp_data_1 = utils.downsample_signal_fast(original_signal=resp_data_0, original_fs=signal_fs, target_fs=resp_fs) resp_data_1 = utils.average_filter(raw_data=resp_data_1, sample_rate=resp_fs, window_size_sec=20) resp_data_2 = utils.butterworth(data=resp_data_1, _type=conf["resp_filter"]["filter_type"], - low_cut=conf["resp_filter"]["low_cut"], - high_cut=conf["resp_filter"]["high_cut"], order=conf["resp_filter"]["order"], - sample_rate=resp_fs) + low_cut=conf["resp_filter"]["low_cut"], + high_cut=conf["resp_filter"]["high_cut"], order=conf["resp_filter"]["order"], + sample_rate=resp_fs) print("Begin plotting signal data...") - # fig = plt.figure(figsize=(12, 8)) # # 绘制三个图raw_data、resp_data_1、resp_data_2 # ax0 = fig.add_subplot(3, 1, 1) @@ -116,7 +116,8 @@ def process_one_signal(samp_id, show=False): sampling_rate=resp_fs, **resp_low_amp_conf ) - print(f"resp_low_amp_mask_shape: {resp_low_amp_mask.shape}, num_low_amp: {np.sum(resp_low_amp_mask == 1)}, count_low_amp_positions: {len(resp_low_amp_position_list)}") + print( + f"resp_low_amp_mask_shape: {resp_low_amp_mask.shape}, num_low_amp: {np.sum(resp_low_amp_mask == 1)}, count_low_amp_positions: {len(resp_low_amp_position_list)}") else: resp_low_amp_mask, resp_low_amp_position_list = None, None print("resp_low_amp_mask is None") @@ -129,7 +130,8 @@ def process_one_signal(samp_id, show=False): sampling_rate=resp_fs, **resp_movement_conf ) - print(f"resp_movement_mask_shape: {resp_movement_mask.shape}, num_movement: {np.sum(resp_movement_mask == 1)}, count_movement_positions: {len(resp_movement_position_list)}") + print( + f"resp_movement_mask_shape: {resp_movement_mask.shape}, num_movement: {np.sum(resp_movement_mask == 1)}, count_movement_positions: {len(resp_movement_position_list)}") else: resp_movement_mask, resp_movement_position_list = None, None print("resp_movement_mask is None") @@ -144,11 +146,11 @@ def process_one_signal(samp_id, show=False): **resp_movement_revise_conf, verbose=False ) - print(f"After revise, resp_movement_mask_shape: {resp_movement_mask.shape}, num_movement: {np.sum(resp_movement_mask == 1)}, count_movement_positions: {len(resp_movement_position_list)}") + print( + f"After revise, resp_movement_mask_shape: {resp_movement_mask.shape}, num_movement: {np.sum(resp_movement_mask == 1)}, count_movement_positions: {len(resp_movement_position_list)}") else: print("resp_movement_mask revise is skipped") - # 分析Resp的幅值突变区间 resp_amp_change_conf = conf.get("resp_amp_change", None) if resp_amp_change_conf is not None and resp_movement_mask is not None: @@ -159,13 +161,12 @@ def process_one_signal(samp_id, show=False): sampling_rate=resp_fs, **resp_amp_change_conf, verbose=True) - print(f"amp_change_mask_shape: {resp_amp_change_mask.shape}, num_amp_change: {np.sum(resp_amp_change_mask == 1)}, count_amp_change_positions: {len(resp_amp_change_list)}") + print( + f"amp_change_mask_shape: {resp_amp_change_mask.shape}, num_amp_change: {np.sum(resp_amp_change_mask == 1)}, count_amp_change_positions: {len(resp_amp_change_list)}") else: resp_amp_change_mask = None print("amp_change_mask is None") - - # 分析Bcg的低幅值区间 bcg_low_amp_conf = conf.get("bcg_low_amp", None) if bcg_low_amp_conf is not None: @@ -174,10 +175,12 @@ def process_one_signal(samp_id, show=False): sampling_rate=bcg_fs, **bcg_low_amp_conf ) - print(f"bcg_low_amp_mask_shape: {bcg_low_amp_mask.shape}, num_low_amp: {np.sum(bcg_low_amp_mask == 1)}, count_low_amp_positions: {len(bcg_low_amp_position_list)}") + print( + f"bcg_low_amp_mask_shape: {bcg_low_amp_mask.shape}, num_low_amp: {np.sum(bcg_low_amp_mask == 1)}, count_low_amp_positions: {len(bcg_low_amp_position_list)}") else: bcg_low_amp_mask, bcg_low_amp_position_list = None, None print("bcg_low_amp_mask is None") + # 分析Bcg的高幅值伪迹区间 bcg_movement_conf = conf.get("bcg_movement", None) if bcg_movement_conf is not None: @@ -186,48 +189,48 @@ def process_one_signal(samp_id, show=False): sampling_rate=bcg_fs, **bcg_movement_conf ) - print(f"bcg_movement_mask_shape: {bcg_movement_mask.shape}, num_movement: {np.sum(bcg_movement_mask == 1)}, count_movement_positions: {len(bcg_movement_position_list)}") + print( + f"bcg_movement_mask_shape: {bcg_movement_mask.shape}, num_movement: {np.sum(bcg_movement_mask == 1)}, count_movement_positions: {len(bcg_movement_position_list)}") else: bcg_movement_mask = None print("bcg_movement_mask is None") + # 分析Bcg的幅值突变区间 if bcg_movement_mask is not None: bcg_amp_change_mask, bcg_amp_change_list = signal_method.position_based_sleep_recognition_v2( signal_data=bcg_data, movement_mask=bcg_movement_mask, sampling_rate=bcg_fs) - print(f"bcg_amp_change_mask_shape: {bcg_amp_change_mask.shape}, num_amp_change: {np.sum(bcg_amp_change_mask == 1)}, count_amp_change_positions: {len(bcg_amp_change_list)}") + print( + f"bcg_amp_change_mask_shape: {bcg_amp_change_mask.shape}, num_amp_change: {np.sum(bcg_amp_change_mask == 1)}, count_amp_change_positions: {len(bcg_amp_change_list)}") else: bcg_amp_change_mask = None print("bcg_amp_change_mask is None") - # 如果signal_data采样率过,进行降采样 if signal_fs == 1000: signal_data = utils.downsample_signal_fast(original_signal=signal_data, original_fs=signal_fs, target_fs=100) - signal_data_raw = utils.downsample_signal_fast(original_signal=signal_data_raw, original_fs=signal_fs, target_fs=100) + signal_data_raw = utils.downsample_signal_fast(original_signal=signal_data_raw, original_fs=signal_fs, + target_fs=100) signal_fs = 100 draw_tools.draw_signal_with_mask(samp_id=samp_id, - signal_data=signal_data, - signal_fs=signal_fs, - resp_data=resp_data, - resp_fs=resp_fs, - bcg_data=bcg_data, - bcg_fs=bcg_fs, - signal_disable_mask=manual_disable_mask, - resp_low_amp_mask=resp_low_amp_mask, - resp_movement_mask=resp_movement_mask, - resp_change_mask=resp_amp_change_mask, - resp_sa_mask=event_mask, - bcg_low_amp_mask=bcg_low_amp_mask, - bcg_movement_mask=bcg_movement_mask, - bcg_change_mask=bcg_amp_change_mask, - show=show, - save_path=save_samp_path / f"{samp_id}_Signal_Plots.png") - - - + signal_data=signal_data, + signal_fs=signal_fs, + resp_data=resp_data, + resp_fs=resp_fs, + bcg_data=bcg_data, + bcg_fs=bcg_fs, + signal_disable_mask=manual_disable_mask, + resp_low_amp_mask=resp_low_amp_mask, + resp_movement_mask=resp_movement_mask, + resp_change_mask=resp_amp_change_mask, + resp_sa_mask=event_mask, + bcg_low_amp_mask=bcg_low_amp_mask, + bcg_movement_mask=bcg_movement_mask, + bcg_change_mask=bcg_amp_change_mask, + show=show, + save_path=save_samp_path / f"{samp_id}_Signal_Plots.png") # 复制事件文件 到保存路径 sa_label_save_name = f"{samp_id}" + label_path.name @@ -240,22 +243,21 @@ def process_one_signal(samp_id, show=False): "SA_Score": score_mask, "Disable_Label": manual_disable_mask, "Resp_LowAmp_Label": resp_low_amp_mask if resp_low_amp_mask is not None else np.zeros(signal_second, dtype=int), - "Resp_Movement_Label": resp_movement_mask if resp_movement_mask is not None else np.zeros(signal_second, dtype=int), - "Resp_AmpChange_Label": resp_amp_change_mask if resp_amp_change_mask is not None else np.zeros(signal_second, dtype=int), + "Resp_Movement_Label": resp_movement_mask if resp_movement_mask is not None else np.zeros(signal_second, + dtype=int), + "Resp_AmpChange_Label": resp_amp_change_mask if resp_amp_change_mask is not None else np.zeros(signal_second, + dtype=int), "Bcg_LowAmp_Label": bcg_low_amp_mask if bcg_low_amp_mask is not None else np.zeros(signal_second, dtype=int), - "Bcg_Movement_Label": bcg_movement_mask if bcg_movement_mask is not None else np.zeros(signal_second, dtype=int), - "Bcg_AmpChange_Label": bcg_amp_change_mask if bcg_amp_change_mask is not None else np.zeros(signal_second, dtype=int) + "Bcg_Movement_Label": bcg_movement_mask if bcg_movement_mask is not None else np.zeros(signal_second, + dtype=int), + "Bcg_AmpChange_Label": bcg_amp_change_mask if bcg_amp_change_mask is not None else np.zeros(signal_second, + dtype=int) } mask_label_save_name = f"{samp_id}_Processed_Labels.csv" utils.save_process_label(save_path=save_samp_path / mask_label_save_name, save_dict=save_dict) - - - - - if __name__ == '__main__': yaml_path = Path("./dataset_config/HYS_config.yaml") disable_df_path = Path("./排除区间.xlsx") @@ -280,4 +282,3 @@ if __name__ == '__main__': # print(f"Processing sample ID: {samp_id}") # process_one_signal(samp_id, show=False) # print(f"Finished processing sample ID: {samp_id}\n\n") - diff --git a/dataset_config/HYS_config.yaml b/dataset_config/HYS_config.yaml index e6d02dc..0f5be51 100644 --- a/dataset_config/HYS_config.yaml +++ b/dataset_config/HYS_config.yaml @@ -50,7 +50,7 @@ resp_movement_revise: min_duration_sec: 1 resp_amp_change: - mav_calc_window_sec: 1 + mav_calc_window_sec: 4 threshold_amplitude: 0.25 threshold_energy: 0.4 diff --git a/signal_method/rule_base_event.py b/signal_method/rule_base_event.py index d13e6cb..ee6b6ba 100644 --- a/signal_method/rule_base_event.py +++ b/signal_method/rule_base_event.py @@ -602,8 +602,8 @@ def position_based_sleep_recognition_v3(signal_data, movement_mask, movement_lis if len(data_segment) % (mav_calc_window_sec * sampling_rate) != 0: data_segment = data_segment[:-(len(data_segment) % (mav_calc_window_sec * sampling_rate))] - mav_values = np.nanmax(data_segment.reshape(-1, mav_calc_window_sec * sampling_rate), axis=0) - np.nanmin( - data_segment.reshape(-1, mav_calc_window_sec * sampling_rate)) + mav_values = np.nanmax(data_segment.reshape(-1, mav_calc_window_sec * sampling_rate), axis=1) - np.nanmin( + data_segment.reshape(-1, mav_calc_window_sec * sampling_rate), axis=1) # 计算分位数 q20 = np.nanpercentile(mav_values, 20) q80 = np.nanpercentile(mav_values, 80) @@ -638,10 +638,12 @@ def position_based_sleep_recognition_v3(signal_data, movement_mask, movement_lis movement_end = movement_list[i][1] # 避免过短的片段 - if movement_end - movement_start <= sampling_rate: # 小于1秒的片段不考虑 + if movement_end - movement_start <= 1: # 小于1秒的片段不考虑 if verbose: print( f"Skipping movement segment {i + 1} due to insufficient length. movement start: {movement_start}, movement end: {movement_end}") + pre_valid_start = pre_valid_start + pre_valid_end = next_valid_end continue # 计算前后片段的幅值和能量 @@ -665,7 +667,7 @@ def position_based_sleep_recognition_v3(signal_data, movement_mask, movement_lis # print( # f"Significant position change detected between segments {movement_start} and {movement_end}: left:{pre_valid_start}to{pre_valid_end} left_mav={left_mav:.2f}, right_mav={right_mav:.2f}, amplitude_change={amplitude_change:.2f}, left_energy={left_energy:.2f}, right_energy={right_energy:.2f}, energy_change={energy_change:.2f}") if verbose: - print(f"Significant position change detected between segments {movement_start} and {movement_end}: left:{pre_valid_start}to{pre_valid_end} left_mav={left_mav:.2f}, right_mav={right_mav:.2f}, amplitude_change={amplitude_change:.2f}") + print(f"Significant position change detected between segments {movement_start}s and {movement_end}:s left:{pre_valid_start}to{pre_valid_end} left_mav={left_mav:.2f}, right:{next_valid_start}to{next_valid_end} right_mav={right_mav:.2f}, amplitude_change={amplitude_change:.2f}") # 记录姿势变化发生的时间点 用当前分割的体动的起始位置和结束位置表示 position_changes[movement_start:movement_end] = 1 position_change_list.append(movement_list[i]) diff --git a/utils/operation_tools.py b/utils/operation_tools.py index 23205be..095dd5c 100644 --- a/utils/operation_tools.py +++ b/utils/operation_tools.py @@ -222,7 +222,7 @@ def event_mask_2_list(mask, event_true=True): normal_2_event = -1 _append = 1 mask_start = np.where(np.diff(mask, prepend=_append, append=_append) == normal_2_event)[0] - mask_end = np.where(np.diff(mask, prepend=_append, append=_append) == event_2_normal)[0] + 1 + mask_end = np.where(np.diff(mask, prepend=_append, append=_append) == event_2_normal)[0] event_list =[[start, end] for start, end in zip(mask_start, mask_end)] return event_list