Files
Depression_TMS/algorithm_V1/bdf_to_mat.py

204 lines
7.4 KiB
Python
Raw Normal View History

2026-06-01 13:18:36 +08:00
# -*- coding: utf-8 -*-
"""
Convert BDF file to MAT format.
This script converts a BDF (Biosemi Data Format) EEG file to .mat format,
matching the structure of eeg_data.mat.
Structure of eeg_data.mat:
- data: (n_samples, n_channels) float64
- chn: (1, n_channels) object - channel names
- sample_rate: (1, 1) int64
- node_number: (1, 1) int64
- t: (n_samples, 1) float64 - time vector in seconds
- electrode_name: (1, n_channels) object - electrode names (10-20 system)
- electrode_xyz: (n_channels, 3) float64 - electrode 3D coordinates
- electrode_coord_system: (1,) <U21
- meta: (1, 1) structured - metadata
"""
import numpy as np
import scipy.io
import mne
from datetime import datetime
def get_standard_electrode_coords():
"""
Standard 10-20 system electrode coordinates.
Returns a dictionary mapping electrode names to x, y, z coordinates.
Coordinates are approximate spherical projections.
"""
coords = {
'FP1': (-0.0293, 0.0903, -0.0033),
'FP2': (0.0293, 0.0903, -0.0033),
'FPZ': (0.0, 0.0903, -0.0033),
'AF7': (-0.0658, 0.0734, -0.0224),
'AF3': (-0.0350, 0.0812, -0.0183),
'AF4': (0.0350, 0.0812, -0.0183),
'AF8': (0.0658, 0.0734, -0.0224),
'F7': (-0.0815, 0.0467, -0.0336),
'F5': (-0.0667, 0.0503, -0.0351),
'F3': (-0.0489, 0.0560, -0.0370),
'F1': (-0.0254, 0.0584, -0.0384),
'FZ': (0.0, 0.0584, -0.0384),
'F2': (0.0254, 0.0584, -0.0384),
'F4': (0.0489, 0.0560, -0.0370),
'F6': (0.0667, 0.0503, -0.0351),
'F8': (0.0815, 0.0467, -0.0336),
'FT7': (-0.0880, 0.0229, -0.0397),
'FC5': (-0.0699, 0.0317, -0.0402),
'FC3': (-0.0514, 0.0362, -0.0411),
'FC1': (-0.0268, 0.0383, -0.0419),
'FCZ': (0.0, 0.0383, -0.0419),
'FC2': (0.0268, 0.0383, -0.0419),
'FC4': (0.0514, 0.0362, -0.0411),
'FC6': (0.0699, 0.0317, -0.0402),
'FT8': (0.0880, 0.0229, -0.0397),
'T7': (-0.0958, 0.0, -0.0411),
'T8': (0.0958, 0.0, -0.0411),
'C5': (-0.0739, 0.0, -0.0425),
'C3': (-0.0544, 0.0, -0.0436),
'C1': (-0.0283, 0.0, -0.0444),
'CZ': (0.0, 0.0, -0.0444),
'C2': (0.0283, 0.0, -0.0444),
'C4': (0.0544, 0.0, -0.0436),
'C6': (0.0739, 0.0, -0.0425),
'TP7': (-0.0880, -0.0229, -0.0397),
'CP5': (-0.0699, -0.0317, -0.0402),
'CP3': (-0.0514, -0.0362, -0.0411),
'CP1': (-0.0268, -0.0383, -0.0419),
'CPZ': (0.0, -0.0383, -0.0419),
'CP2': (0.0268, -0.0383, -0.0419),
'CP4': (0.0514, -0.0362, -0.0411),
'CP6': (0.0699, -0.0317, -0.0402),
'TP8': (0.0880, -0.0229, -0.0397),
'P7': (-0.0815, -0.0467, -0.0336),
'P5': (-0.0667, -0.0503, -0.0351),
'P3': (-0.0489, -0.0560, -0.0370),
'P1': (-0.0254, -0.0584, -0.0384),
'PZ': (0.0, -0.0584, -0.0384),
'P2': (0.0254, -0.0584, -0.0384),
'P4': (0.0489, -0.0560, -0.0370),
'P6': (0.0667, -0.0503, -0.0351),
'P8': (0.0815, -0.0467, -0.0336),
'PO7': (-0.0658, -0.0734, -0.0224),
'PO5': (-0.0503, -0.0744, -0.0258),
'PO3': (-0.0350, -0.0812, -0.0183),
'POZ': (0.0, -0.0829, -0.0172),
'PO4': (0.0350, -0.0812, -0.0183),
'PO6': (0.0503, -0.0744, -0.0258),
'PO8': (0.0658, -0.0734, -0.0224),
'O1': (-0.0293, -0.0903, -0.0033),
'OZ': (0.0, -0.0903, -0.0033),
'O2': (0.0293, -0.0903, -0.0033),
'CB1': (-0.0618, -0.0380, -0.0387),
'CB2': (0.0618, -0.0380, -0.0387),
'A1': (-0.0958, 0.0, 0.0),
'A2': (0.0958, 0.0, 0.0),
}
return coords
def bdf_to_mat(bdf_path, output_path, subject_id='unknown', session_id='unknown'):
"""
Convert BDF file to MAT format matching eeg_data.mat structure.
Parameters
----------
bdf_path : str
Path to the input BDF file.
output_path : str
Path to the output MAT file.
subject_id : str, optional
Subject identifier. Default is 'unknown'.
session_id : str, optional
Session identifier. Default is 'unknown'.
"""
print(f'Loading BDF file: {bdf_path}')
raw = mne.io.read_raw_bdf(bdf_path, preload=True, verbose=False)
# Get basic info
ch_names = raw.ch_names
n_channels = len(ch_names)
sfreq = int(raw.info['sfreq'])
data = raw.get_data()
# BDF data shape: (n_channels, n_samples)
# Convert to eeg_data.mat format: (n_samples, n_channels)
data = data.T
n_samples = data.shape[0]
# Create time vector (in seconds)
t = np.arange(n_samples) / sfreq
t = t.reshape(-1, 1)
# Create channel names array (matching eeg_data.mat structure)
chn = np.array([[name] for name in ch_names], dtype=object)
# Create electrode names (same as channel names for BDF)
electrode_name = np.array([[name] for name in ch_names], dtype=object)
# Get electrode coordinates
standard_coords = get_standard_electrode_coords()
electrode_xyz = np.zeros((n_channels, 3))
for i, name in enumerate(ch_names):
if name in standard_coords:
electrode_xyz[i] = standard_coords[name]
else:
print(f'Warning: No standard coordinate for electrode {name}')
# Create metadata structure
start_time_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
meta = np.array([(subject_id, session_id, 'CMS/DRL', start_time_str)],
dtype=[('subject_id', 'O'), ('session_id', 'O'),
('ref', 'O'), ('start_time', 'O')])
# Create the EEG structure (matching eeg_data.mat format)
eeg_struct = np.array([(data, chn, [[sfreq]], [[n_channels]],
t, electrode_name, electrode_xyz,
'buzsaki', meta)],
dtype=[('data', 'O'), ('chn', 'O'),
('sample_rate', 'O'), ('node_number', 'O'),
('t', 'O'), ('electrode_name', 'O'),
('electrode_xyz', 'O'), ('electrode_coord_system', 'O'),
('meta', 'O')])
# Save to MAT file
print(f'Saving to: {output_path}')
scipy.io.savemat(output_path, {'eeg': eeg_struct}, do_compression=True)
print(f'\nConversion complete!')
print(f' Channels: {n_channels}')
print(f' Samples: {n_samples}')
print(f' Duration: {n_samples / sfreq:.2f} seconds')
print(f' Sample rate: {sfreq} Hz')
print(f' Data shape: {data.shape}')
def main():
# File paths
bdf_path = r'D:\Ivey\Code_New_Proj\Debug_Depression\algorithm_version_0521_v0\0515-18.bdf'
output_path = r'D:\Ivey\Code_New_Proj\Debug_Depression\algorithm_version_0521_v0\0515-18.mat'
# Convert
bdf_to_mat(bdf_path, output_path, subject_id='lvpeng', session_id='01')
# Verify the output
print('\n=== Verification ===')
mat_data = scipy.io.loadmat(output_path)
eeg = mat_data['eeg'][0, 0]
print(f'Output file keys: {list(mat_data.keys())}')
print(f'eeg.data shape: {eeg["data"].shape}')
print(f'eeg.chn shape: {eeg["chn"].shape}')
print(f'eeg.sample_rate: {eeg["sample_rate"][0, 0]}')
print(f'eeg.t shape: {eeg["t"].shape}')
print(f'eeg.electrode_name: {eeg["electrode_name"].shape}')
print(f'eeg.electrode_xyz shape: {eeg["electrode_xyz"].shape}')
print(f'eeg.electrode_coord_system: {eeg["electrode_coord_system"][0]}')
if __name__ == '__main__':
main()