# BDF 预处理脚本改进对比说明

本文对比原始预处理脚本 `Step1_0Preprocess.m` 和改进版 `Step1_0Preprocess_improved.m`,说明具体做了哪些改动,以及这些改动对后续分类模型的意义。

---
## 1. 总体思路变化

- **原始脚本 `Step1_0Preprocess.m`**
  - 目标:从原始 BDF 中快速提取每段 2 s 的 PSD 特征并打上 4 类情绪标签。
  - 预处理流程相对简单:一次滤波、一次全局基线校正 + 平均参考、非重叠分段、绝对功率特征。

- **改进版 `Step1_0Preprocess_improved.m`**
  - 目标:提高预处理质量,增强特征稳定性,统一输出结构,方便后续 SVM/LDA/CNN/CapsNet 直接使用。
  - 核心改动:
    - 更合理的带通 + 工频陷波;
    - 按 trial 的局部基线校正与平均参考;
    - 带重叠的分段;
    - 绝对功率改为相对功率;
    - 输出中显式保存频带信息。

---
## 2. 滤波与陷波

### 原始脚本

```matlab
EEG = pop_eegfiltnew(EEG,3,50);
```

- 只做了 3–50 Hz 带通,未显式对 50 Hz 工频做陷波处理。

### 改进版

```matlab
EEG = pop_eegfiltnew(EEG,1,50);      % 1–50 Hz 带通
EEG = pop_eegfiltnew(EEG,49,51,[],1);% 49–51 Hz 工频陷波
```

**改进点:**
- 下限从 3 Hz 放宽到 1 Hz,保留更多情绪相关的慢波信息;
- 显式对 50 Hz 工频加入陷波,降低电源噪声对 PSD 的影响。

---
## 3. 基线和平均参考策略

### 原始脚本(简化描述)

```matlab
baseline = data(:, EEG.event(typeorder-1).latency : EEG.event(typeorder).latency);
m_baseline = mean(baseline,2);
for i = 1:length(data)
    data(:,i) = data(:,i) - m_baseline;   % 基线一次性作用于全程
end

m_data = mean(data,1);                    % 全程平均参考
for i = 1:size(data,1)
    data(i,:) = data(i,:) - m_data;
end
```

- 基线段来自事件 3→4,但对 **整个记录 `data`** 一次性做校正;
- 平均参考也是对整个时间段做一次;
- 不同 trial 之间会互相影响。

### 改进版

```matlab
baseIdx = round(EEG.event(typeorder-1).latency) : round(EEG.event(typeorder).latency);
taskIdx = round(EEG.event(typeorder).latency)   : round(EEG.event(typeorder+1).latency);

% 边界保护
baseIdx(baseIdx<1) = 1;  baseIdx(baseIdx>size(data,2)) = size(data,2);
taskIdx(taskIdx<1) = 1;  taskIdx(taskIdx>size(data,2)) = size(data,2);

trialData = data(:, taskIdx);    % 只取当前 trial 的任务段
baseData  = data(:, baseIdx);    % 当前 trial 的基线段

m_base    = mean(baseData,2);
trialData = trialData - m_base;  % 基线只作用于本 trial

m_ref     = mean(trialData,1);
trialData = trialData - m_ref;   % 平均参考也只在本 trial 内做
```

**改进点:**
- 基线校正与平均参考都 **局限在当前 trial 内**,不同 trial 不会互相“牵连”;
- 对 `baseIdx` / `taskIdx` 做边界保护,避免索引越界;
- 与 3‑4‑5 事件段的真实实验结构更加一致。

---
## 4. 3‑4‑5 事件结构与任务段

两版脚本都通过事件 type = 3, 4, 5 来确定一个 trial:

```matlab
for typeorder = 2:length(EEG.event)-1
    if EEG.event(1,typeorder).type == 4 && ...
       EEG.event(1,typeorder-1).type == 3 && ...
       EEG.event(1,typeorder+1).type == 5
        % 认为这里是一个完整的 trial
    end
end
```

- **type=3**:基线段开始(刺激前安静期);
- **type=4**:任务段开始(视频/刺激开始);
- **type=5**:任务段结束。

**改进版的变化:**
- 明确使用 3→4 作为基线区间,4→5 作为任务区间;
- 基线和参考都只应用在这个任务段上,而不是全程。

---
## 5. 分段方式:非重叠 vs 重叠

### 原始脚本

- 采样率 512 Hz,`trialLength = 1024`,相当于每段 2 s;
- 任务段 `TaskData` 被整齐切成若干 **不重叠** 的 2 s 段:

```matlab
pointNumber = fix(segTotalLength / trialLength); % 不重叠
sampleInput = TaskData(:, 1+(pointOrder-1)*trialLength : pointOrder*trialLength);
```

### 改进版

- 保持 2 s 作为窗口长度,但允许 **1 s 步长**,即 50% 重叠:

```matlab
winLength_s = 2;   step_s = 1;
trialLength = winLength_s * fs;
stepLength  = step_s * fs;

pointNumber = floor((segTotalLength - trialLength)/stepLength) + 1;
idxStart    = 1 + (pointOrder-1)*stepLength;
idxEnd      = idxStart + trialLength - 1;
segData     = trialData(:, idxStart:idxEnd);
```

**改进点:**
- 使用滑动窗口 + 重叠,能从同一 trial 中提取更多段落样本;
- 时间覆盖更连续,有利于后续模型在时间维度上“看到”更多信息;
- 对小 trial(时长接近 2 s)仍有保护:`segTotalLength < trialLength` 直接跳过。

---
## 6. PSD 计算与特征归一化

### 原始脚本

```matlab
[Pxx,f] = pwelch(sampleInput(elecOrder,:),1024,[],1024,512,'linear');
ind1 = find(f>=bandLimit{bandOrder}(1),1,'first');
ind2 = find(f>=bandLimit{bandOrder}(2),1,'first');
Power(elecOrder,pointOrder) = mean(Pxx(ind1:ind2));  % 绝对功率
```

- 使用固定参数的 `pwelch`,输出每个频带的 **绝对功率**;
- 不考虑该段在 4–49 Hz 整体能量的变化。

### 改进版

```matlab
[Pxx,f] = pwelch(x,[],0.5,[],fs);   % 更通用的调用

% 总功率(4–49 Hz),用于归一化
indAll1 = find(f>=4,1,'first');
indAll2 = find(f>=49,1,'first');
totalPower = sum(Pxx(indAll1:indAll2));
if totalPower == 0, totalPower = eps; end

for b = 1:nBand
    ind1 = find(f>=bandLimit{b}(1),1,'first');
    ind2 = find(f>=bandLimit{b}(2),1,'first');
    bandP = mean(Pxx(ind1:ind2));
    PowerBand{b}(elecOrder,pointOrder) = bandP / totalPower; % 相对功率
end
```

**改进点:**
- `pwelch` 调用更稳健,减少版本兼容问题;
- 引入 4–49 Hz 总功率归一化,将每个频带功率转成 **相对功率**;
- 减少被试间/试次数间整体能量差异对分类的干扰,特征更可比。

---
## 7. 标签划分与情绪象限

两版脚本在标签划分上保持一致:

```matlab
Valunce = SubVA(Trialnum,3);
Arousal = SubVA(Trialnum,4);

if Valunce < 5 && Arousal < 5
    flag = 1; % LVLA
elseif Valunce > 5 && Arousal < 5
    flag = 2; % HVLA
elseif Valunce < 5 && Arousal > 5
    flag = 3; % LVHA
else
    flag = 4; % HVHA
end

Flag{Trialnum,1} = repmat(flag, pointNumber, 1);
```

- 使用 V/A 评分 5 作为分界线,将情绪分为 4 个象限:
  - 1:低效价 + 低唤醒 (LVLA)
  - 2:高效价 + 低唤醒 (HVLA)
  - 3:低效价 + 高唤醒 (LVHA)
  - 4:高效价 + 高唤醒 (HVHA)
- `Flag{Trialnum,1}` 中为该 trial 所有段重复相同标签,方便后续段级分类。

改进版只是保持了同样的标签规则,未改变情绪定义。

---
## 8. 输出结构的统一

### 原始脚本

```matlab
Taskdata{Trialnum,bandOrder} = Power;    % [通道 × 段数]
Flag{Trialnum,1} = repmat(flag,pointNumber,1);
save(...,'Taskdata','Flag');
```

- 只保存了 `Taskdata` 和 `Flag`,频带信息通过脚本中的 `bandLimit`、`bandName` 隐式给出。

### 改进版

```matlab
Taskdata{Trialnum,b} = PowerBand{b};
Flag{Trialnum,1}     = repmat(flag,pointNumber,1);
...
save(...,'Taskdata','Flag','bandLimit','bandName');
```

**改进点:**
- 显式保存 `bandLimit` 和 `bandName`,下游脚本可直接读取,不再依赖“约定俗成”的频带顺序;
- BDF 和 CNT 预处理脚本都对齐成相同结构,方便 `Step1_1SVM_KFold_SubBand_improved.m`、`Step1_1LDA_KFold_SubBand.m`、`Step1_1CNN_TrialLevel.m` 等脚本统一处理。

---
## 9. 改进总结

相对于原始 `Step1_0Preprocess.m`,改进版 `Step1_0Preprocess_improved.m` 的主要收益可以概括为:

1. **频带处理更合理**:1–50 Hz + 50 Hz 陷波,抑制工频噪声,保留更多低频信息;
2. **trial 内局部基线与平均参考**:每个 trial 单独去基线、做平均参考,避免不同 trial 互相影响;
3. **重叠分段**:2 s 窗 + 1 s 步长,提高样本量与时间覆盖度;
4. **相对功率特征**:对 4–49 Hz 总功率归一化,特征更稳健、更可比;
5. **索引安全与版本兼容**:增加边界保护、调整 `pwelch` 调用方式,减少潜在报错;
6. **输出结构更完整统一**:统一输出 `Taskdata/Flag/bandLimit/bandName`,为 SVM/LDA/CNN/CapsNet 等模型提供统一输入接口。

整体来说,改进版在不改变实验标签和 3‑4‑5 事件结构的前提下,大幅提升了预处理质量和后续建模的便利性。