original push
This commit is contained in:
203
algorithm_V1/bdf_to_mat.py
Normal file
203
algorithm_V1/bdf_to_mat.py
Normal file
@@ -0,0 +1,203 @@
|
||||
# -*- 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()
|
||||
Reference in New Issue
Block a user