eegprep 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eegprep/ICL_feature_extractor.py +90 -0
- eegprep/ICL_feature_extractor_compare_helper.py +19 -0
- eegprep/__init__.py +1 -0
- eegprep/eeg_autocorr.py +61 -0
- eegprep/eeg_autocorr_compare_helper.py +13 -0
- eegprep/eeg_autocorr_fftw.py +68 -0
- eegprep/eeg_autocorr_fftw_compare_helper.py +20 -0
- eegprep/eeg_autocorr_welch.py +61 -0
- eegprep/eeg_autocorr_welch_compare_helper.py +13 -0
- eegprep/eeg_checkset.py +110 -0
- eegprep/eeg_eeglab2mne.py +39 -0
- eegprep/eeg_mne2eeglab.py +46 -0
- eegprep/eeg_mne2eeglab_epochs.py +187 -0
- eegprep/eeg_rpsd.py +65 -0
- eegprep/eeg_rpsd_compare_helper.py +17 -0
- eegprep/eeglabcompat.py +117 -0
- eegprep/iclabel.py +51 -0
- eegprep/iclabel_helper.py +26 -0
- eegprep/iclabel_net.py +136 -0
- eegprep/iclabel_net_load_py_measures.py +137 -0
- eegprep/pop_loadset.py +97 -0
- eegprep/pop_reref.py +44 -0
- eegprep/pop_reref_compare_helper.py +31 -0
- eegprep/pop_reref_helper.py +22 -0
- eegprep/pop_resample.py +42 -0
- eegprep/pop_saveset.py +201 -0
- eegprep/save_struct_as_hdf5.py +50 -0
- eegprep/topoplot.py +194 -0
- eegprep/topoplot_compare_helper.py +29 -0
- eegprep-0.0.2.dist-info/LICENSE +20 -0
- eegprep-0.0.2.dist-info/METADATA +56 -0
- eegprep-0.0.2.dist-info/RECORD +34 -0
- eegprep-0.0.2.dist-info/WHEEL +5 -0
- eegprep-0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Example to export MNE epochs to EEGLAB dataset
|
|
2
|
+
# Events are not handled correctly in this example but it works
|
|
3
|
+
|
|
4
|
+
import mne
|
|
5
|
+
from mne.datasets import sample
|
|
6
|
+
from mne.preprocessing import ICA
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from scipy.io import savemat
|
|
11
|
+
|
|
12
|
+
# Load example data
|
|
13
|
+
def eeg_mne2eeglab_epochs(epochs, ica):
|
|
14
|
+
|
|
15
|
+
# export to EEGLAB dataset
|
|
16
|
+
data = epochs.get_data() # Get the data from the epochs
|
|
17
|
+
n_epochs, n_channels, n_times = data.shape
|
|
18
|
+
ica_weights = ica.get_components() # ICA weights (n_components x n_channels)
|
|
19
|
+
|
|
20
|
+
# create identity matrix of size n_channels x n_channels
|
|
21
|
+
ica_sphere = np.eye(n_channels) # ICA sphere (n_channels x n_channels)
|
|
22
|
+
|
|
23
|
+
# Compute the mixing matrix (inverse weights)
|
|
24
|
+
ica_inverse_weights = np.linalg.pinv(ica_weights) # Shape: (n_channels, n_components)
|
|
25
|
+
|
|
26
|
+
ica_channels = ica.info['ch_names']
|
|
27
|
+
raw_channels = epochs.info['ch_names'] # Assuming you have the raw object
|
|
28
|
+
ica_channel_indices = [raw_channels.index(ch) for ch in ica_channels]
|
|
29
|
+
ica_channel_indices = np.array(ica_channel_indices)
|
|
30
|
+
|
|
31
|
+
ica_act = ica.get_sources(epochs).get_data(copy=True).transpose(1, 2, 0) # Get the ICA activations
|
|
32
|
+
|
|
33
|
+
print('Reference conversion may not be accurate...')
|
|
34
|
+
if 'custom_ref_applied' in epochs.info and epochs.info['custom_ref_applied']:
|
|
35
|
+
ref = 'common' # Custom reference was applied
|
|
36
|
+
else:
|
|
37
|
+
ref = 'average' # Default to average reference
|
|
38
|
+
|
|
39
|
+
eeglab_dict = {
|
|
40
|
+
'setname' : '',
|
|
41
|
+
'filename' : '',
|
|
42
|
+
'filepath' : '',
|
|
43
|
+
'subject' : '',
|
|
44
|
+
'group' : '',
|
|
45
|
+
'condition' : '',
|
|
46
|
+
'session' : np.array([]),
|
|
47
|
+
'comments' : '',
|
|
48
|
+
'nbchan' : n_channels,
|
|
49
|
+
'trials' : n_epochs,
|
|
50
|
+
'pnts' : n_times,
|
|
51
|
+
'srate' : epochs.info['sfreq'],
|
|
52
|
+
'xmin' : epochs.times[0],
|
|
53
|
+
'xmax' : epochs.times[-1],
|
|
54
|
+
'times' : epochs.times,
|
|
55
|
+
'data' : data,
|
|
56
|
+
'icaact' : ica_act,
|
|
57
|
+
'icawinv' : ica_inverse_weights,
|
|
58
|
+
'icasphere' : ica_weights,
|
|
59
|
+
'icaweights' : ica_sphere,
|
|
60
|
+
'icachansind' : ica_channel_indices,
|
|
61
|
+
'chanlocs' : np.array([]),
|
|
62
|
+
'urchanlocs' : np.array([]),
|
|
63
|
+
'chaninfo' : np.array([]),
|
|
64
|
+
'ref' : ref,
|
|
65
|
+
'event' : np.array([]),
|
|
66
|
+
'urevent' : np.array([]),
|
|
67
|
+
'eventdescription': np.array([]),
|
|
68
|
+
'epoch' : np.array([]),
|
|
69
|
+
'epochdescription': np.array([]),
|
|
70
|
+
'reject' : np.array([]),
|
|
71
|
+
'stats' : np.array([]),
|
|
72
|
+
'specdata' : np.array([]),
|
|
73
|
+
'specicaact' : np.array([]),
|
|
74
|
+
'splinefile' : np.array([]),
|
|
75
|
+
'icasplinefile' : np.array([]),
|
|
76
|
+
'dipfit' : np.array([]),
|
|
77
|
+
'history' : np.array([]),
|
|
78
|
+
'saved' : np.array([]),
|
|
79
|
+
'etc' : np.array([]),
|
|
80
|
+
'datfile' : np.array([]),
|
|
81
|
+
'run' : np.array([]),
|
|
82
|
+
'roi' : np.array([]),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# create channel locations
|
|
86
|
+
ch_names = epochs.ch_names
|
|
87
|
+
|
|
88
|
+
ch_locs = epochs.info['chs']
|
|
89
|
+
|
|
90
|
+
theta_all = []
|
|
91
|
+
radius_all = []
|
|
92
|
+
sph_theta_all = []
|
|
93
|
+
sph_phi_all = []
|
|
94
|
+
sph_radius_all = []
|
|
95
|
+
X_all = []
|
|
96
|
+
Y_all = []
|
|
97
|
+
Z_all = []
|
|
98
|
+
for ch in ch_locs:
|
|
99
|
+
if 'loc' in ch and ch['loc'] is not None:
|
|
100
|
+
X_all.append(ch['loc'][1]*1000)
|
|
101
|
+
Y_all.append(-ch['loc'][0]*1000)
|
|
102
|
+
Z_all.append(ch['loc'][2]*1000)
|
|
103
|
+
hypotxy = math.hypot(X_all[-1],Y_all[-1])
|
|
104
|
+
sph_radius_all.append(math.hypot(hypotxy,Z_all[-1]))
|
|
105
|
+
|
|
106
|
+
az = math.atan2(Y_all[-1],X_all[-1])/math.pi*180
|
|
107
|
+
horiz = math.atan2(Z_all[-1],hypotxy)/math.pi*180
|
|
108
|
+
|
|
109
|
+
sph_theta_all.append(az)
|
|
110
|
+
sph_phi_all.append(horiz)
|
|
111
|
+
|
|
112
|
+
theta_all.append(-az) # warning inverse notation compared to MATLAB to match
|
|
113
|
+
radius_all.append(0.5 - horiz/180) # warning inverse notation compared to MATLAB to match
|
|
114
|
+
|
|
115
|
+
d_list = [{
|
|
116
|
+
'labels': ch_name,
|
|
117
|
+
'theta': theta,
|
|
118
|
+
'radius': radius,
|
|
119
|
+
'X': X,
|
|
120
|
+
'Y': Y,
|
|
121
|
+
'Z': Z,
|
|
122
|
+
'sph_theta': sph_theta,
|
|
123
|
+
'sph_phi': sph_phi,
|
|
124
|
+
'sph_radius': sph_radius,
|
|
125
|
+
'type': 'EEG',
|
|
126
|
+
'urchan': 0,
|
|
127
|
+
'ref': ''
|
|
128
|
+
} for ch_name, theta, radius, X, Y, Z, sph_theta, sph_phi, sph_radius in zip(ch_names, theta_all, radius_all, X_all, Y_all, Z_all, sph_theta_all, sph_phi_all, sph_radius_all)]
|
|
129
|
+
# Create the list of dictionaries with a string field
|
|
130
|
+
# d_list = [{
|
|
131
|
+
# 'labels': ch_name,
|
|
132
|
+
# 'theta': math.atan2(ch_loc[0], ch_loc[1]),
|
|
133
|
+
# 'radius': math.hypot(ch_loc[1], ch_loc[0]),
|
|
134
|
+
# 'X': ch_loc[1]*1000,
|
|
135
|
+
# 'Y': ch_loc[0]*1000,
|
|
136
|
+
# 'Z': ch_loc[2]*1000,
|
|
137
|
+
# 'sph_theta': 0,
|
|
138
|
+
# 'sph_phi': 0,
|
|
139
|
+
# 'sph_radius': 0,
|
|
140
|
+
# 'type': 'EEG',
|
|
141
|
+
# 'urchan': 0,
|
|
142
|
+
# 'ref': ''
|
|
143
|
+
# } for ch_name, ch_loc in zip(ch_names, ch_locs_xyz)]
|
|
144
|
+
|
|
145
|
+
# convert d_list to a numpy array
|
|
146
|
+
d_list = np.array(d_list)
|
|
147
|
+
eeglab_dict['chanlocs'] = d_list
|
|
148
|
+
|
|
149
|
+
# # Step 4: Save the EEGLAB dataset as a .mat file
|
|
150
|
+
return eeglab_dict
|
|
151
|
+
|
|
152
|
+
#print("EEGLAB dataset saved successfully!")
|
|
153
|
+
|
|
154
|
+
def test_eeg_mne2eeeglab_epochs():
|
|
155
|
+
sample_data_folder = mne.datasets.sample.data_path()
|
|
156
|
+
sample_data_raw_file = (
|
|
157
|
+
sample_data_folder / "MEG" / "sample" / "sample_audvis_filt-0-40_raw.fif"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
raw = mne.io.read_raw_fif(sample_data_raw_file)
|
|
161
|
+
|
|
162
|
+
# extract data epochs
|
|
163
|
+
events = mne.find_events(raw, stim_channel="STI 014")
|
|
164
|
+
event_dict = {
|
|
165
|
+
"auditory/left": 1,
|
|
166
|
+
"auditory/right": 2,
|
|
167
|
+
"visual/left": 3,
|
|
168
|
+
"visual/right": 4,
|
|
169
|
+
"smiley": 5,
|
|
170
|
+
"buttonpress": 32,
|
|
171
|
+
}
|
|
172
|
+
epochs = mne.Epochs(
|
|
173
|
+
raw,
|
|
174
|
+
events,
|
|
175
|
+
event_id=event_dict,
|
|
176
|
+
tmin=-0.2,
|
|
177
|
+
tmax=0.5,
|
|
178
|
+
preload=True,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
ica = ICA(n_components=15, random_state=97, max_iter=800)
|
|
182
|
+
ica.fit(raw)
|
|
183
|
+
|
|
184
|
+
EEG = eeg_mne2eeglab_epochs(epochs, ica)
|
|
185
|
+
savemat('output_file.mat', EEG) # use pop_saveset
|
|
186
|
+
|
|
187
|
+
# test_eeg_mne2eeeglab_epochs()
|
eegprep/eeg_rpsd.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numpy.fft import fft
|
|
3
|
+
from scipy.signal.windows import hamming
|
|
4
|
+
|
|
5
|
+
def eeg_rpsd(EEG, nfreqs=None, pct_data=100):
|
|
6
|
+
# clean input cutoff freq
|
|
7
|
+
nyquist = EEG['srate'] // 2
|
|
8
|
+
if nfreqs is None or nfreqs > nyquist:
|
|
9
|
+
nfreqs = nyquist
|
|
10
|
+
nfreqs = int(nfreqs)
|
|
11
|
+
|
|
12
|
+
# setup constants
|
|
13
|
+
ncomp = EEG['icaweights'].shape[0]
|
|
14
|
+
|
|
15
|
+
# Hamming window
|
|
16
|
+
n_points = min(EEG['pnts'], EEG['srate'])
|
|
17
|
+
m = n_points
|
|
18
|
+
isOddLength = m % 2
|
|
19
|
+
if isOddLength:
|
|
20
|
+
x = np.arange(0, (m - 1) / 2 + 1) / (m - 1)
|
|
21
|
+
else:
|
|
22
|
+
x = np.arange(0, m / 2) / (m - 1)
|
|
23
|
+
|
|
24
|
+
a = 0.54
|
|
25
|
+
window = a - (1 - a) * np.cos(2 * np.pi * x)
|
|
26
|
+
if isOddLength:
|
|
27
|
+
window = np.concatenate([window, window[-2::-1]])
|
|
28
|
+
else:
|
|
29
|
+
window = np.concatenate([window, window[::-1]])
|
|
30
|
+
|
|
31
|
+
cutoff = (EEG['pnts'] // n_points) * n_points
|
|
32
|
+
index = np.add.outer(np.ceil(np.arange(0, cutoff - n_points + 1, n_points/2)).astype(int), np.arange(0, n_points)).astype(int).transpose()
|
|
33
|
+
|
|
34
|
+
np.random.seed(0) # rng('default') in MATLAB
|
|
35
|
+
n_seg = index.shape[1] * EEG['trials']
|
|
36
|
+
subset = np.random.permutation(n_seg)[:int(n_seg * pct_data / 100)]
|
|
37
|
+
|
|
38
|
+
# calculate windowed spectrums
|
|
39
|
+
psdmed = np.zeros((ncomp, nfreqs))
|
|
40
|
+
for it in range(ncomp):
|
|
41
|
+
temp = np.reshape(EEG['icaact'][it, index, :], (1, index.shape[0], index.shape[1] * EEG['trials']))
|
|
42
|
+
temp = temp[:, :, subset] * window[:, np.newaxis]
|
|
43
|
+
temp = fft(temp, n_points, axis=1)
|
|
44
|
+
temp = np.abs(temp) ** 2
|
|
45
|
+
temp = temp[:, 1:nfreqs + 1, :] * 2 / (EEG['srate'] * np.sum(window ** 2))
|
|
46
|
+
if nfreqs == nyquist:
|
|
47
|
+
temp[:, -1, :] /= 2
|
|
48
|
+
psdmed[it, :] = 20 * np.log10(np.median(temp, axis=2))
|
|
49
|
+
|
|
50
|
+
return psdmed
|
|
51
|
+
|
|
52
|
+
def test_eeg_rpsd():
|
|
53
|
+
EEG = {
|
|
54
|
+
'srate': 256,
|
|
55
|
+
'icaweights': np.random.randn(10, 256),
|
|
56
|
+
'pnts': 1000,
|
|
57
|
+
'trials': 5,
|
|
58
|
+
'icaact': np.random.randn(10, 1000, 5)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
psdmed = eeg_rpsd(EEG, 100)
|
|
62
|
+
assert psdmed.shape == (10, 100)
|
|
63
|
+
assert np.all(np.isfinite(psdmed))
|
|
64
|
+
|
|
65
|
+
# test_eeg_rpsd()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from eeg_rpsd import eeg_rpsd
|
|
2
|
+
from pop_loadset import pop_loadset
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
if len(sys.argv) > 1:
|
|
6
|
+
eeglab_file_path = sys.argv[1]
|
|
7
|
+
else:
|
|
8
|
+
eeglab_file_path = '/System/Volumes/Data/data/matlab/eeglab/sample_data/eeglab_data_epochs_ica.set'
|
|
9
|
+
EEG = pop_loadset(eeglab_file_path)
|
|
10
|
+
|
|
11
|
+
# Print the loaded data
|
|
12
|
+
res = eeg_rpsd(EEG, 100)
|
|
13
|
+
|
|
14
|
+
# save in a matlab file
|
|
15
|
+
import scipy.io
|
|
16
|
+
scipy.io.savemat('eeg_rpsd_data.mat', {'grid': res})
|
|
17
|
+
print('Saved eeg_rpsd_data.mat')
|
eegprep/eeglabcompat.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from oct2py import Oct2Py, get_log
|
|
2
|
+
from pop_saveset import pop_saveset
|
|
3
|
+
from pop_loadset import pop_loadset
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
eeglab = Oct2Py(logger=get_log())
|
|
9
|
+
oc = Oct2Py(logger=get_log())
|
|
10
|
+
eeglab.logger = get_log("new_log")
|
|
11
|
+
|
|
12
|
+
def pop_resample( EEG, freq): # 2 additional parameters in MATLAB (never used)
|
|
13
|
+
|
|
14
|
+
pop_saveset(EEG, 'tmp.set') # 0.8 seconds
|
|
15
|
+
EEG2 = eeglab.pop_loadset('tmp.set') # 2 seconds
|
|
16
|
+
EEG2 = eeglab.pop_resample(EEG2, freq) # 2.4 seconds
|
|
17
|
+
eeglab.pop_saveset(EEG2, 'tmp2.set') # 2.4 seconds
|
|
18
|
+
EEG3 = pop_loadset('tmp2.set') # 0.2 seconds
|
|
19
|
+
|
|
20
|
+
# delete temporary files
|
|
21
|
+
os.remove('tmp.set')
|
|
22
|
+
os.remove('tmp2.set')
|
|
23
|
+
return EEG3
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def pop_eegfiltnew(locutoff=None,hicutoff=None,revfilt=False,plotfreqz=False):
|
|
27
|
+
# error if locutoff and hicutoff are none
|
|
28
|
+
if locutoff==None and hicutoff==None:
|
|
29
|
+
raise('Cannot have low cutoff and high cutoff not defined')
|
|
30
|
+
|
|
31
|
+
pop_saveset(EEG, 'tmp.set') # 0.8 seconds
|
|
32
|
+
EEG2 = eeglab.pop_loadset('tmp.set') # 2 seconds
|
|
33
|
+
EEG3 = pop_eegfiltnew(EEG2, 'locutoff',locutoff,'hicutoff',hicutoff,'revfilt',revfilt,'plotfreqz',plotfreqz)
|
|
34
|
+
eeglab.pop_saveset(EEG2, 'tmp2.set') # 2.4 seconds
|
|
35
|
+
EEG4 = pop_loadset('tmp2.set') # 0.2 seconds
|
|
36
|
+
|
|
37
|
+
# delete temporary files
|
|
38
|
+
os.remove('tmp.set')
|
|
39
|
+
os.remove('tmp2.set')
|
|
40
|
+
return EEG4
|
|
41
|
+
|
|
42
|
+
def clean_artifacts( EEG, ChannelCriterion=False, LineNoiseCriterion=False, FlatlineCriterion=False, BurstCriterion=False, WindowCriterion=0, Highpass=[0.25, 0.75], WindowCriterionTolerances=[float('-inf'), 8]):
|
|
43
|
+
|
|
44
|
+
if ChannelCriterion == False or ChannelCriterion == 'off':
|
|
45
|
+
ChannelCriterion='off'
|
|
46
|
+
else:
|
|
47
|
+
ChannelCriterion='on'
|
|
48
|
+
|
|
49
|
+
if LineNoiseCriterion == False or LineNoiseCriterion == 'off':
|
|
50
|
+
LineNoiseCriterion='off'
|
|
51
|
+
else:
|
|
52
|
+
LineNoiseCriterion='on'
|
|
53
|
+
|
|
54
|
+
if FlatlineCriterion == False or FlatlineCriterion == 'off':
|
|
55
|
+
FlatlineCriterion='off'
|
|
56
|
+
else:
|
|
57
|
+
FlatlineCriterion='on'
|
|
58
|
+
|
|
59
|
+
if BurstCriterion == False or BurstCriterion == 'off':
|
|
60
|
+
BurstCriterion='off'
|
|
61
|
+
else:
|
|
62
|
+
BurstCriterion='on'
|
|
63
|
+
|
|
64
|
+
pop_saveset(EEG, 'tmp.set') # 0.8 seconds
|
|
65
|
+
EEG2 = eeglab.pop_loadset('tmp.set') # 2 seconds
|
|
66
|
+
EEG3 = eeglab.clean_artifacts(EEG2, 'ChannelCriterion', ChannelCriterion, \
|
|
67
|
+
'LineNoiseCriterion', LineNoiseCriterion, \
|
|
68
|
+
'FlatlineCriterion', FlatlineCriterion, \
|
|
69
|
+
'BurstCriterion', BurstCriterion, \
|
|
70
|
+
'WindowCriterion', WindowCriterion, \
|
|
71
|
+
'Highpass',Highpass, \
|
|
72
|
+
'WindowCriterionTolerances', WindowCriterionTolerances)
|
|
73
|
+
eeglab.pop_saveset(EEG3, 'tmp2.set') # 2.4 seconds
|
|
74
|
+
EEG4 = pop_loadset('tmp2.set') # 0.2 seconds
|
|
75
|
+
|
|
76
|
+
# delete temporary files
|
|
77
|
+
os.remove('tmp.set')
|
|
78
|
+
os.remove('tmp2.set')
|
|
79
|
+
return EEG3
|
|
80
|
+
|
|
81
|
+
# sys.exit()
|
|
82
|
+
|
|
83
|
+
eeglab.logger.setLevel(logging.WARNING)
|
|
84
|
+
eeglab.warning('off', 'backtrace')
|
|
85
|
+
|
|
86
|
+
# On the command line, type "octave-8.4.0" OCTAVE_EXECUTABLE or OCTAVE var
|
|
87
|
+
path2eeglab = '/System/Volumes/Data/data/matlab/eeglab/' # init >10 seconds
|
|
88
|
+
eeglab.addpath(path2eeglab + '/functions/guifunc')
|
|
89
|
+
eeglab.addpath(path2eeglab + '/functions/popfunc')
|
|
90
|
+
eeglab.addpath(path2eeglab + '/functions/adminfunc')
|
|
91
|
+
eeglab.addpath(path2eeglab + '/plugins/firfilt')
|
|
92
|
+
eeglab.addpath(path2eeglab + '/functions/sigprocfunc')
|
|
93
|
+
eeglab.addpath(path2eeglab + '/functions/miscfunc')
|
|
94
|
+
eeglab.addpath(path2eeglab + '/plugins/dipfit')
|
|
95
|
+
eeglab.addpath(path2eeglab + '/plugins/clean_rawdata')
|
|
96
|
+
res = eeglab.version()
|
|
97
|
+
print('Running EEGLAB commands in compatibility mode with Octave ' + res)
|
|
98
|
+
eeglab.logger.setLevel(logging.INFO)
|
|
99
|
+
|
|
100
|
+
eeglab_file_path = '/System/Volumes/Data/data/matlab/eeglab/sample_data/eeglab_data_epochs_ica.set'
|
|
101
|
+
|
|
102
|
+
EEG = pop_loadset(eeglab_file_path)
|
|
103
|
+
EEG = pop_eegfiltnew(EEG, locutoff=5,hicutoff=25,revfilt=True,plotfreqz=False)
|
|
104
|
+
|
|
105
|
+
# EEG = eeglab.pop_loadset(eeglab_file_path)
|
|
106
|
+
# TMPEEG = eeglab.pop_eegfiltnew(EEG, 'locutoff',5,'hicutoff',25,'revfilt',1,'plotfreqz',0)
|
|
107
|
+
# CLEANEDEEG = eeglab.clean_artifacts(TMPEEG, 'ChannelCriterion', 'off',
|
|
108
|
+
# 'LineNoiseCriterion', 'off',
|
|
109
|
+
# 'FlatlineCriterion', 'off',
|
|
110
|
+
# 'BurstCriterion', 'off',
|
|
111
|
+
# 'WindowCriterion', 0,
|
|
112
|
+
# 'Highpass',[0.25, 0.75],
|
|
113
|
+
# 'WindowCriterionTolerances', [-10000000, 8])
|
|
114
|
+
|
|
115
|
+
# clean_artifacts( EEG, ChannelCriterion='on' )
|
|
116
|
+
|
|
117
|
+
|
eegprep/iclabel.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from .ICL_feature_extractor import ICL_feature_extractor
|
|
2
|
+
from .iclabel_net import ICLabelNet
|
|
3
|
+
import torch
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
def iclabel(EEG, algorithm='default'):
|
|
7
|
+
# issue error if algorithm is not 'default'
|
|
8
|
+
if algorithm != 'default':
|
|
9
|
+
raise ValueError('Only the default algorithm is supported')
|
|
10
|
+
|
|
11
|
+
#ICLABEL Extract ICLabel features from an EEG dataset.
|
|
12
|
+
features = ICL_feature_extractor(EEG, True)
|
|
13
|
+
|
|
14
|
+
# Equivalent of MATLAB code reshaping
|
|
15
|
+
features[0] = np.single(np.concatenate([features[0],-features[0],features[0][:, ::-1, :, :],-features[0][:, ::-1, :, :]], axis=3))
|
|
16
|
+
features[1] = np.single(np.tile(features[1], (1, 1, 1, 4)))
|
|
17
|
+
features[2] = np.single(np.tile(features[2], (1, 1, 1, 4)))
|
|
18
|
+
print('Feature 0 shape:', features[0].shape)
|
|
19
|
+
print('Feature 1 shape:', features[1].shape)
|
|
20
|
+
print('Feature 2 shape:', features[2].shape)
|
|
21
|
+
|
|
22
|
+
# Load the ICLabelNet model
|
|
23
|
+
model = ICLabelNet('netICL.mat')
|
|
24
|
+
|
|
25
|
+
# Convert the features to torch tensors
|
|
26
|
+
image = torch.tensor(features[0]).permute(-1, 2, 0, 1)
|
|
27
|
+
psdmed = torch.tensor(features[1]).permute(-1, 2, 0, 1)
|
|
28
|
+
autocorr = torch.tensor(features[2]).permute(-1, 2, 0, 1)
|
|
29
|
+
|
|
30
|
+
# Get the output from the model
|
|
31
|
+
output = model(image, psdmed, autocorr)
|
|
32
|
+
output_np = output.detach().numpy()
|
|
33
|
+
output_np = output_np.T # Transpose the array
|
|
34
|
+
output_np = np.reshape(output_np, (-1, 4), order='F') # Reshape to have 4 columns
|
|
35
|
+
output_np = np.mean(output_np, axis=1) # Compute the mean along the second axis (columns)
|
|
36
|
+
output_np = np.reshape(output_np, (7, -1), order='F') # Reshape to have 7 rows
|
|
37
|
+
output_np = output_np.T # Transpose back
|
|
38
|
+
|
|
39
|
+
if 'ic_classification' not in EEG['etc']:
|
|
40
|
+
EEG['etc']['ic_classification'] = {}
|
|
41
|
+
if 'ICLabel' not in EEG['etc']['ic_classification']:
|
|
42
|
+
EEG['etc']['ic_classification']['ICLabel'] = {}
|
|
43
|
+
|
|
44
|
+
EEG['etc']['ic_classification']['ICLabel']['classes'] = np.array(
|
|
45
|
+
['Brain', 'Muscle', 'Eye', 'Heart', 'Line Noise', 'Channel Noise', 'Other'],
|
|
46
|
+
dtype=np.object
|
|
47
|
+
)
|
|
48
|
+
EEG['etc']['ic_classification']['ICLabel']['classifications'] = output_np
|
|
49
|
+
EEG['etc']['ic_classification']['ICLabel']['version'] = 'default'
|
|
50
|
+
|
|
51
|
+
return EEG
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from iclabel import iclabel
|
|
2
|
+
from pop_loadset import pop_loadset
|
|
3
|
+
from pop_saveset import pop_saveset
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
# check if there are 2 arguments otherwise issue an error
|
|
7
|
+
if len(sys.argv) != 3:
|
|
8
|
+
raise ValueError('Please provide the input and output file paths as command line arguments')
|
|
9
|
+
|
|
10
|
+
# get first command line argument
|
|
11
|
+
eeglab_file_path_in = sys.argv[1]
|
|
12
|
+
eeglab_file_path_out = sys.argv[2]
|
|
13
|
+
|
|
14
|
+
EEG = pop_loadset(eeglab_file_path_in)
|
|
15
|
+
# EEG['data'] = EEG['data'].reshape(EEG['data'].shape[0], -1)
|
|
16
|
+
# EEG['icaact'] = EEG['icaact'].reshape(EEG['icaact'].shape[0], -1)
|
|
17
|
+
# EEG['nbchan'] = EEG['data'].shape[0]
|
|
18
|
+
# EEG['pnts'] = EEG['data'].shape[1]
|
|
19
|
+
# EEG['trials'] = 1
|
|
20
|
+
|
|
21
|
+
# Print the loaded data
|
|
22
|
+
print('Data shape:', EEG['data'].shape)
|
|
23
|
+
EEG = iclabel(EEG, 'default')
|
|
24
|
+
|
|
25
|
+
# save dataset
|
|
26
|
+
pop_saveset(EEG, eeglab_file_path_out)
|
eegprep/iclabel_net.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import scipy.io
|
|
2
|
+
import torch
|
|
3
|
+
import scipy
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
class Reshape(torch.nn.Module):
|
|
7
|
+
def __init__(self, shape):
|
|
8
|
+
super().__init__()
|
|
9
|
+
self.shape = shape
|
|
10
|
+
|
|
11
|
+
def forward(self, x):
|
|
12
|
+
return x.view(x.shape[0], *self.shape)
|
|
13
|
+
|
|
14
|
+
class Concatenate(torch.nn.Module):
|
|
15
|
+
def __init__(self, dim):
|
|
16
|
+
super().__init__()
|
|
17
|
+
self.dim = dim
|
|
18
|
+
|
|
19
|
+
def forward(self, x: list):
|
|
20
|
+
return torch.cat(x, dim=self.dim)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ICLabelNet(torch.nn.Module):
|
|
24
|
+
def __init__(self, mat_path):
|
|
25
|
+
super().__init__()
|
|
26
|
+
iclabel_matlab = scipy.io.loadmat(mat_path)
|
|
27
|
+
params = iclabel_matlab['params'][0]
|
|
28
|
+
i = 11
|
|
29
|
+
print('shape of param', i, torch.tensor(params[i][1]).shape)
|
|
30
|
+
self.discriminator_image_layer1_conv = torch.nn.Conv2d(in_channels=1, out_channels=128, kernel_size=4, stride=2, padding=1, dilation=1)
|
|
31
|
+
print(self.discriminator_image_layer1_conv.weight.shape)
|
|
32
|
+
self.discriminator_image_layer1_conv.weight = torch.nn.Parameter(torch.tensor(params[0][1]).permute(3, 2, 0, 1))
|
|
33
|
+
self.discriminator_image_layer1_conv.bias = torch.nn.Parameter(torch.tensor(params[1][1]).squeeze())
|
|
34
|
+
self.discriminator_image_layer1_relu = torch.nn.LeakyReLU(0.2)
|
|
35
|
+
self.discriminator_image_layer2_conv = torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=4, stride=2, padding=1, dilation=1)
|
|
36
|
+
self.discriminator_image_layer2_conv.weight = torch.nn.Parameter(torch.tensor(params[2][1]).permute(3, 2, 0, 1))
|
|
37
|
+
self.discriminator_image_layer2_conv.bias = torch.nn.Parameter(torch.tensor(params[3][1]).squeeze())
|
|
38
|
+
self.discriminator_image_layer2_relu = torch.nn.LeakyReLU(0.2)
|
|
39
|
+
self.discriminator_image_layer3_conv = torch.nn.Conv2d(in_channels=256, out_channels=512, kernel_size=4, stride=2, padding=1, dilation=1)
|
|
40
|
+
self.discriminator_image_layer3_conv.weight = torch.nn.Parameter(torch.tensor(params[4][1]).permute(3, 2, 0, 1))
|
|
41
|
+
self.discriminator_image_layer3_conv.bias = torch.nn.Parameter(torch.tensor(params[5][1]).squeeze())
|
|
42
|
+
self.discriminator_image_layer3_relu = torch.nn.LeakyReLU(0.2)
|
|
43
|
+
self.discriminator_psdmed_layer1_conv_conv = torch.nn.Conv2d(in_channels=1, out_channels=128, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
|
|
44
|
+
self.discriminator_psdmed_layer1_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[6][1]).permute(3, 2, 0, 1))
|
|
45
|
+
self.discriminator_psdmed_layer1_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[7][1]).squeeze())
|
|
46
|
+
self.discriminator_psdmed_layer1_conv_relu = torch.nn.LeakyReLU(0.2)
|
|
47
|
+
self.discriminator_psdmed_layer2_conv_conv = torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
|
|
48
|
+
self.discriminator_psdmed_layer2_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[8][1]).permute(3, 2, 0, 1))
|
|
49
|
+
self.discriminator_psdmed_layer2_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[9][1]).squeeze())
|
|
50
|
+
self.discriminator_psdmed_layer2_conv_relu = torch.nn.LeakyReLU(0.2)
|
|
51
|
+
self.discriminator_psdmed_layer3_conv_conv = torch.nn.Conv2d(in_channels=256, out_channels=1, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
|
|
52
|
+
self.discriminator_psdmed_layer3_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[10][1]).unsqueeze(3).permute(3, 2, 0, 1))
|
|
53
|
+
self.discriminator_psdmed_layer3_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[11][1]).squeeze(1))
|
|
54
|
+
self.discriminator_psdmed_layer3_conv_relu = torch.nn.LeakyReLU(0.2)
|
|
55
|
+
self.discriminator_autocorr_layer1_conv_conv = torch.nn.Conv2d(in_channels=1, out_channels=128, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
|
|
56
|
+
self.discriminator_autocorr_layer1_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[12][1]).permute(3, 2, 0, 1))
|
|
57
|
+
self.discriminator_autocorr_layer1_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[13][1]).squeeze())
|
|
58
|
+
self.discriminator_autocorr_layer1_conv_relu = torch.nn.LeakyReLU(0.2)
|
|
59
|
+
self.discriminator_autocorr_layer2_conv_conv = torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
|
|
60
|
+
self.discriminator_autocorr_layer2_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[14][1]).permute(3, 2, 0, 1))
|
|
61
|
+
self.discriminator_autocorr_layer2_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[15][1]).squeeze())
|
|
62
|
+
self.discriminator_autocorr_layer2_conv_relu = torch.nn.LeakyReLU(0.2)
|
|
63
|
+
self.discriminator_autocorr_layer3_conv_conv = torch.nn.Conv2d(in_channels=256, out_channels=1, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
|
|
64
|
+
self.discriminator_autocorr_layer3_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[16][1]).unsqueeze(3).permute(3, 2, 0, 1))
|
|
65
|
+
self.discriminator_autocorr_layer3_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[17][1]).squeeze(1))
|
|
66
|
+
self.discriminator_autocorr_layer3_conv_relu = torch.nn.LeakyReLU(0.2)
|
|
67
|
+
self.discriminator_psdmed_reshape = Reshape((100, 1, 1))
|
|
68
|
+
self.discriminator_psdmed_concat1 = Concatenate(dim=2)
|
|
69
|
+
self.discriminator_psdmed_concat2 = Concatenate(dim=3)
|
|
70
|
+
self.discriminator_autocorr_reshape = Reshape((100, 1, 1))
|
|
71
|
+
self.discriminator_autocorr_concat1 = Concatenate(dim=2)
|
|
72
|
+
self.discriminator_autocorr_concat2 = Concatenate(dim=3)
|
|
73
|
+
self.discriminator_concat = Concatenate(dim=1)
|
|
74
|
+
self.discriminator_conv = torch.nn.Conv2d(in_channels=712, out_channels=7, kernel_size=4, stride=1, padding=0, dilation=1)
|
|
75
|
+
self.discriminator_conv.weight = torch.nn.Parameter(torch.tensor(params[18][1]).permute(3, 2, 0, 1))
|
|
76
|
+
self.discriminator_conv.bias = torch.nn.Parameter(torch.tensor(params[19][1]).squeeze())
|
|
77
|
+
self.discriminator_softmax = torch.nn.Softmax(dim=1)
|
|
78
|
+
|
|
79
|
+
def forward(self, image, psdmed, autocorr):
|
|
80
|
+
x_image = self.discriminator_image_layer1_conv(image)
|
|
81
|
+
x_image = self.discriminator_image_layer1_relu(x_image)
|
|
82
|
+
x_image = self.discriminator_image_layer2_conv(x_image)
|
|
83
|
+
x_image = self.discriminator_image_layer2_relu(x_image)
|
|
84
|
+
x_image = self.discriminator_image_layer3_conv(x_image)
|
|
85
|
+
x_image = self.discriminator_image_layer3_relu(x_image)
|
|
86
|
+
print('x_image', x_image.shape)
|
|
87
|
+
|
|
88
|
+
x_psdmed = self.discriminator_psdmed_layer1_conv_conv(psdmed)
|
|
89
|
+
x_psdmed = self.discriminator_psdmed_layer1_conv_relu(x_psdmed)
|
|
90
|
+
x_psdmed = self.discriminator_psdmed_layer2_conv_conv(x_psdmed)
|
|
91
|
+
x_psdmed = self.discriminator_psdmed_layer2_conv_relu(x_psdmed)
|
|
92
|
+
x_psdmed = self.discriminator_psdmed_layer3_conv_conv(x_psdmed)
|
|
93
|
+
x_psdmed = self.discriminator_psdmed_layer3_conv_relu(x_psdmed)
|
|
94
|
+
x_psdmed = self.discriminator_psdmed_reshape(x_psdmed)
|
|
95
|
+
x_psdmed = self.discriminator_psdmed_concat1([x_psdmed]*4)
|
|
96
|
+
x_psdmed = self.discriminator_psdmed_concat2([x_psdmed]*4)
|
|
97
|
+
print('x_psdmed', x_psdmed.shape)
|
|
98
|
+
|
|
99
|
+
x_autocorr = self.discriminator_autocorr_layer1_conv_conv(autocorr)
|
|
100
|
+
x_autocorr = self.discriminator_autocorr_layer1_conv_relu(x_autocorr)
|
|
101
|
+
x_autocorr = self.discriminator_autocorr_layer2_conv_conv(x_autocorr)
|
|
102
|
+
x_autocorr = self.discriminator_autocorr_layer2_conv_relu(x_autocorr)
|
|
103
|
+
x_autocorr = self.discriminator_autocorr_layer3_conv_conv(x_autocorr)
|
|
104
|
+
x_autocorr = self.discriminator_autocorr_layer3_conv_relu(x_autocorr)
|
|
105
|
+
x_autocorr = self.discriminator_autocorr_reshape(x_autocorr)
|
|
106
|
+
x_autocorr = self.discriminator_autocorr_concat1([x_autocorr]*4)
|
|
107
|
+
x_autocorr = self.discriminator_autocorr_concat2([x_autocorr]*4)
|
|
108
|
+
print('x_autocorr', x_autocorr.shape)
|
|
109
|
+
|
|
110
|
+
x = self.discriminator_concat([x_image, x_psdmed, x_autocorr])
|
|
111
|
+
x = self.discriminator_conv(x)
|
|
112
|
+
print('x', x.shape)
|
|
113
|
+
# subtract max value to avoid overflow
|
|
114
|
+
x = x - torch.max(x, dim=1, keepdim=True).values
|
|
115
|
+
x = self.discriminator_softmax(x)
|
|
116
|
+
|
|
117
|
+
return x
|
|
118
|
+
|
|
119
|
+
# if __name__ == "__main__":
|
|
120
|
+
# model = ICLabelNet('netICL.mat')
|
|
121
|
+
# image_mat = scipy.io.loadmat('net_vars.mat')['in_image']
|
|
122
|
+
# psdmed_mat = scipy.io.loadmat('net_vars.mat')['in_psdmed']
|
|
123
|
+
# autocorr_mat = scipy.io.loadmat('net_vars.mat')['in_autocorr']
|
|
124
|
+
# # assuming third dimension is trivial and last dimension is channel. First two dimensions (32 x 32) are size of topoplot
|
|
125
|
+
# image = torch.tensor(image_mat).permute(-1, 2, 0, 1)
|
|
126
|
+
# print('image shape', image.shape)
|
|
127
|
+
# psdmed = torch.tensor(psdmed_mat).permute(-1, 2, 0, 1)
|
|
128
|
+
# print('psd shape', psdmed.shape)
|
|
129
|
+
# autocorr = torch.tensor(autocorr_mat).permute(-1, 2, 0, 1)
|
|
130
|
+
# print('autocorr shape', autocorr.shape)
|
|
131
|
+
# output = model(image, psdmed, autocorr)
|
|
132
|
+
# print(output.shape)
|
|
133
|
+
|
|
134
|
+
# # save the output to a mat file
|
|
135
|
+
# scipy.io.savemat('output4.mat', {'output': output.detach().numpy()})
|
|
136
|
+
|