如何在Python中定位一维信号中受刺激区域的索引
分离一维信号中的受刺激段并提取起止点
问题背景
现有一维信号,包含基线水平段和受刺激后的高振幅段,需要识别并分离出所有受刺激段,同时获取这些段的起始与终止时间点。
解决方案思路
核心是通过阈值检测区分基线与刺激段,再提取连续的高振幅区间:
- 利用基线信号的统计特征(均值+3倍标准差)设定阈值,过滤基线噪声的干扰
- 将信号转换为二值掩码,标记出超过阈值的区域
- 从掩码中提取连续的高振幅区间,转换为时间维度的起止点
完整实现代码
import numpy as np import matplotlib.pyplot as plt import random from scipy.ndimage import label, find_objects def generate_random_decimal_array(start, end, size): random_array = [random.uniform(start, end) for _ in range(size)] return random_array # ---------------------- # 1. 生成模拟信号 # ---------------------- fs = 200 # 采样频率(Hz) t_total = 5 # 总时长(秒) t = np.linspace(0, t_total, int(fs * t_total), endpoint=False) baseline_amplitude = 0.05 stimulus_duration = 1 # 刺激持续时长(秒) recovery_duration = 1 # 恢复时长(秒) num_stimuli = 3 # 刺激段数量 # 初始化基线噪声信号 signal = baseline_amplitude * np.random.randn(len(t)) # 随机生成刺激段的采样点起止索引 stimulus_periods = [] for _ in range(num_stimuli): start = random.randint(0, len(t) - int(fs * (stimulus_duration + recovery_duration))) end = start + int(fs * stimulus_duration) stimulus_periods.append((start, end)) # 叠加刺激信号和恢复段 for start, end in stimulus_periods: signal[start:end] = generate_random_decimal_array(0.3, 1.5, (end - start)) recovery_start = end recovery_end = recovery_start + int(fs * recovery_duration) signal[recovery_start:recovery_end] = baseline_amplitude * np.random.randn(recovery_end - recovery_start) # ---------------------- # 2. 检测受刺激段 # ---------------------- # 计算基线阈值:基线均值 + 3倍标准差(99.7%置信区间,过滤噪声) baseline_mean = np.mean(signal[signal < 0.2]) # 先粗略筛选基线点计算统计量 baseline_std = np.std(signal[signal < 0.2]) threshold = baseline_mean + 3 * baseline_std # 生成二值掩码:超过阈值的标记为1 mask = signal > threshold # 提取连续的高振幅区间(受刺激段) labeled_mask, num_features = label(mask) segments = find_objects(labeled_mask) # 转换为时间维度的起止点 stimulus_time_intervals = [] for seg in segments: start_idx = seg[0].start end_idx = seg[0].stop - 1 # 修正为区间最后一个采样点的索引 start_time = t[start_idx] end_time = t[end_idx] stimulus_time_intervals.append((round(start_time, 2), round(end_time, 2))) # ---------------------- # 3. 可视化验证 # ---------------------- plt.figure(figsize=(12, 6)) plt.plot(t, signal, label='原始信号', alpha=0.7) plt.axhline(y=threshold, color='r', linestyle='--', label=f'检测阈值: {round(threshold, 3)}') # 标记检测到的刺激段 for idx, (start_t, end_t) in enumerate(stimulus_time_intervals): plt.axvspan(start_t, end_t, color='g', alpha=0.2, label=f'检测到的刺激段 {idx+1}') plt.title('受刺激段检测结果') plt.xlabel('时间 (s)') plt.ylabel('振幅') plt.legend() plt.grid(True) plt.show() # 输出检测到的起止时间 print("检测到的受刺激段时间区间:") for i, (start, end) in enumerate(stimulus_time_intervals): print(f"段 {i+1}: 起始时间 {start}s,终止时间 {end}s")
关键说明
- 阈值设定:采用基线均值+3倍标准差,既能有效区分基线噪声和刺激信号,又能避免误判
- 区间提取:使用
scipy.ndimage的label和find_objects工具,快速定位连续的高振幅区域 - 结果验证:通过可视化对比原始信号和检测标记,确保结果准确性
内容的提问来源于stack exchange,提问作者Talha Anwar
相关产品推荐
相关产品推荐

