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,137 @@
|
|
|
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
|
+
data = scipy.io.loadmat('python_temp_reformated.mat')
|
|
122
|
+
image_mat = data['grid'][0][0]
|
|
123
|
+
psdmed_mat = data['grid'][0][1]
|
|
124
|
+
autocorr_mat = data['grid'][0][2]
|
|
125
|
+
# assuming third dimension is trivial and last dimension is channel. First two dimensions (32 x 32) are size of topoplot
|
|
126
|
+
image = torch.tensor(image_mat).permute(-1, 2, 0, 1)
|
|
127
|
+
print('image shape', image.shape)
|
|
128
|
+
psdmed = torch.tensor(psdmed_mat).permute(-1, 2, 0, 1)
|
|
129
|
+
print('psd shape', psdmed.shape)
|
|
130
|
+
autocorr = torch.tensor(autocorr_mat).permute(-1, 2, 0, 1)
|
|
131
|
+
print('autocorr shape', autocorr.shape)
|
|
132
|
+
output = model(image, psdmed, autocorr)
|
|
133
|
+
print(output.shape)
|
|
134
|
+
|
|
135
|
+
# save the output to a mat file
|
|
136
|
+
scipy.io.savemat('output4_py.mat', {'output': output.detach().numpy()})
|
|
137
|
+
|
eegprep/pop_loadset.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import scipy.io
|
|
2
|
+
import numpy as np
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
# Allows access using . notation
|
|
6
|
+
# class EEG:
|
|
7
|
+
# def __init__(self, **kwargs):
|
|
8
|
+
# self.__dict__.update(kwargs)
|
|
9
|
+
# def __getitem__(self, key):
|
|
10
|
+
# return self.__dict__[key]
|
|
11
|
+
# def __setitem__(self, key, value):
|
|
12
|
+
# self.__dict__[key] = value
|
|
13
|
+
|
|
14
|
+
default_empty = np.array([])
|
|
15
|
+
#default_empty = None
|
|
16
|
+
|
|
17
|
+
def loadset(file_path):
|
|
18
|
+
return pop_loadset(file_path)
|
|
19
|
+
|
|
20
|
+
def pop_loadset(file_path):
|
|
21
|
+
# Load MATLAB file
|
|
22
|
+
EEG = scipy.io.loadmat(file_path, struct_as_record=False, squeeze_me=True)
|
|
23
|
+
|
|
24
|
+
def new_check(obj):
|
|
25
|
+
# check if obj is a dictionary and apply recursively the function to each object not changing the struture of the dictionary
|
|
26
|
+
if isinstance(obj, dict):
|
|
27
|
+
return {key: new_check(obj[key]) for key in obj}
|
|
28
|
+
# check if obj is a numpy array and apply recursively the function to each object not changing the struture of the array
|
|
29
|
+
elif isinstance(obj, list):
|
|
30
|
+
if len(obj) == 0:
|
|
31
|
+
return default_empty
|
|
32
|
+
else:
|
|
33
|
+
return [new_check(item) for item in obj]
|
|
34
|
+
elif isinstance(obj, np.ndarray):
|
|
35
|
+
# check if empty and return none
|
|
36
|
+
if obj.size == 0:
|
|
37
|
+
return default_empty
|
|
38
|
+
# check if it is a numeric array
|
|
39
|
+
elif obj.dtype.kind in ['i', 'u', 'f', 'c']:
|
|
40
|
+
return obj
|
|
41
|
+
else:
|
|
42
|
+
return np.array([new_check(item) for item in obj])
|
|
43
|
+
# check if it is a scalar or a string and return it
|
|
44
|
+
elif np.isscalar(obj) or isinstance(obj, str):
|
|
45
|
+
return obj
|
|
46
|
+
# check if obj is a mat_struct object and convert it to a dictionary
|
|
47
|
+
elif isinstance(obj, scipy.io.matlab.mat_struct) or isinstance(obj, scipy.io.matlab.mio5_params.mat_struct):
|
|
48
|
+
dict_obj = {}
|
|
49
|
+
for field_name in obj._fieldnames:
|
|
50
|
+
field_value = getattr(obj, field_name)
|
|
51
|
+
dict_obj[field_name] = new_check(field_value)
|
|
52
|
+
return dict_obj
|
|
53
|
+
|
|
54
|
+
# check if EEG['data'] is a string, and if it the case, read the binary float32 file
|
|
55
|
+
#EEG = check_keys(EEG)
|
|
56
|
+
EEG = new_check(EEG)
|
|
57
|
+
if 'EEG' in EEG:
|
|
58
|
+
EEG = EEG['EEG']
|
|
59
|
+
|
|
60
|
+
if 'data' in EEG and isinstance(EEG['data'], str):
|
|
61
|
+
# get path from file_path
|
|
62
|
+
EEG['filepath'] = os.path.dirname(file_path)
|
|
63
|
+
file_name = EEG['filepath'] + os.sep + EEG['data']
|
|
64
|
+
EEG['data'] = np.fromfile(file_name, dtype='float32').reshape( EEG['pnts']*EEG['trials'], EEG['nbchan'])
|
|
65
|
+
EEG['data'] = EEG['data'].T.reshape(EEG['nbchan'], EEG['trials'], EEG['pnts']).transpose(0, 2, 1)
|
|
66
|
+
|
|
67
|
+
# compute ICA activations
|
|
68
|
+
if 'icaweights' in EEG and 'icasphere' in EEG and EEG['icaweights'].size > 0 and EEG['icasphere'].size > 0:
|
|
69
|
+
EEG['icaact'] = np.dot(np.dot(EEG['icaweights'], EEG['icasphere']), EEG['data'].reshape(int(EEG['nbchan']), -1))
|
|
70
|
+
EEG['icaact'] = EEG['icaact'].astype(np.float32)
|
|
71
|
+
EEG['icaact'] = EEG['icaact'].reshape(EEG['icaweights'].shape[0], -1, int(EEG['trials']))
|
|
72
|
+
|
|
73
|
+
# subtract 1 to EEG['icachansind'] to make it 0-based
|
|
74
|
+
if 'icachansind' in EEG and EEG['icachansind'].size > 0:
|
|
75
|
+
EEG['icachansind'] = EEG['icachansind'] - 1
|
|
76
|
+
|
|
77
|
+
# type conversion
|
|
78
|
+
EEG['xmin'] = float(EEG['xmin'])
|
|
79
|
+
EEG['xmax'] = float(EEG['xmax'])
|
|
80
|
+
EEG['srate'] = float(EEG['srate'])
|
|
81
|
+
|
|
82
|
+
return EEG
|
|
83
|
+
|
|
84
|
+
def test_pop_loadset():
|
|
85
|
+
file_path = './tmp2.set'
|
|
86
|
+
file_path = '/System/Volumes/Data/data/data/STUDIES/STERN/S04/Memorize.set' #'./eeglab_data_with_ica_tmp.set'
|
|
87
|
+
EEG = pop_loadset(file_path)
|
|
88
|
+
|
|
89
|
+
# print the keys of the EEG dictionary
|
|
90
|
+
print(EEG.keys())
|
|
91
|
+
|
|
92
|
+
# test_pop_loadset()
|
|
93
|
+
|
|
94
|
+
# STILL OPEN QUESTION: Better to have empty MATLAB arrays as None for empty numpy arrays (current default).
|
|
95
|
+
# The current default is to make it more MALTAB compatible. A lot of MATLAB function start indexing MATLAB
|
|
96
|
+
# empty arrays to add values to them. This is not possible with None and would create more conversion and
|
|
97
|
+
# bugs. However, None is more pythonic.
|
eegprep/pop_reref.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def pop_reref(EEG, ref):
|
|
4
|
+
|
|
5
|
+
# check if ref is not empty and not none
|
|
6
|
+
if ref is not None and ref != []:
|
|
7
|
+
raise ValueError('Feature not implemented: The ref parameter must be empty or None')
|
|
8
|
+
|
|
9
|
+
# Check if the number of channels in EEG['icachansind'] is the same as the number of channels in EEG['nbchan']
|
|
10
|
+
if len(EEG['icachansind']) == EEG['nbchan']:
|
|
11
|
+
# Subtract mean from EEG data using broadcasting in NumPy
|
|
12
|
+
EEG['data'] = EEG['data'] - np.mean(EEG['data'], axis=0)
|
|
13
|
+
|
|
14
|
+
#print(np.array2string(EEG['icaweights'][:10, :10], precision=3, suppress_small=True))
|
|
15
|
+
|
|
16
|
+
# Subtract mean from EEG icawinv using broadcasting in NumPy
|
|
17
|
+
EEG['icawinv'] = EEG['icawinv'] - np.mean(EEG['icawinv'], axis=0)
|
|
18
|
+
|
|
19
|
+
# show the first 10 rows and columns of EEG['icawinv']
|
|
20
|
+
# print it pretty line by line with 4 digit after the decimal point
|
|
21
|
+
|
|
22
|
+
# Compute the pseudoinverse of EEG['icawinv']
|
|
23
|
+
EEG['icaweights'] = np.linalg.pinv(EEG['icawinv'])
|
|
24
|
+
#print(' ')
|
|
25
|
+
#print(np.array2string(EEG['icaweights'][:10, :10], precision=3, suppress_small=True))
|
|
26
|
+
|
|
27
|
+
EEG['icasphere'] = np.eye(EEG['nbchan'])
|
|
28
|
+
|
|
29
|
+
# Compute the ICA activations
|
|
30
|
+
# data = EEG['data'].reshape(EEG['data'].shape[0], -1)
|
|
31
|
+
# EEG['icaact'] = np.dot(EEG['icaweights'], data)
|
|
32
|
+
# EEG['icaact'] = EEG['icaact'].reshape(EEG['icaweights'].shape[0], EEG['pnts'], EEG['trials'])
|
|
33
|
+
|
|
34
|
+
# Set EEG['ref'] to 'average'
|
|
35
|
+
EEG['ref'] = 'average'
|
|
36
|
+
|
|
37
|
+
# Update the reference for each channel in EEG['chanlocs']
|
|
38
|
+
for iChan in range(len(EEG['chanlocs'])):
|
|
39
|
+
EEG['chanlocs'][iChan]['ref'] = 'average'
|
|
40
|
+
|
|
41
|
+
return EEG
|
|
42
|
+
|
|
43
|
+
else:
|
|
44
|
+
raise ValueError('Feature not implemented: The number of channels in EEG[''icachansind''] must be the same as the number of channels in EEG[''nbchan'']')
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from pop_reref import pop_reref
|
|
2
|
+
from pop_loadset import pop_loadset
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import scipy.io
|
|
6
|
+
|
|
7
|
+
# Check if a parameter was provided
|
|
8
|
+
if len(sys.argv) > 1:
|
|
9
|
+
# The first element in sys.argv is the script name, so we access the second one for the argument
|
|
10
|
+
file_name = sys.argv[1]
|
|
11
|
+
print(f"The EEG file is: {file_name}")
|
|
12
|
+
print(f"Output will be in python_temp.mat (result field)")
|
|
13
|
+
else:
|
|
14
|
+
file_name = './eeglab_data_with_ica_tmp.set'
|
|
15
|
+
# raise("Provide the name of a file containing EEG (full path) as the first parameter.")
|
|
16
|
+
|
|
17
|
+
# check if file name exists on disk
|
|
18
|
+
if not os.path.isfile(file_name):
|
|
19
|
+
raise(f"File {file_name} does not exist.")
|
|
20
|
+
|
|
21
|
+
EEG = pop_loadset(file_name)
|
|
22
|
+
EEG = pop_reref(EEG, None)
|
|
23
|
+
EEG['chanlocs'] = []
|
|
24
|
+
EEG['urchanlocs'] = []
|
|
25
|
+
EEG['event'] = []
|
|
26
|
+
EEG['urevent'] = []
|
|
27
|
+
EEG['epoch'] = []
|
|
28
|
+
|
|
29
|
+
# save in a matlab file
|
|
30
|
+
scipy.io.savemat('python_temp.mat', EEG)
|
|
31
|
+
print('Saved python_temp.mat')
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from ICL_feature_extractor import ICL_feature_extractor
|
|
2
|
+
from pop_loadset import pop_loadset
|
|
3
|
+
from pop_saveset import pop_saveset
|
|
4
|
+
from pop_reref import pop_reref
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
# check if a parameter is present and if it is assign eeglab_file_path to it
|
|
8
|
+
if len(sys.argv) > 2:
|
|
9
|
+
eeglab_file_path_in = sys.argv[1]
|
|
10
|
+
eeglab_file_path_out = sys.argv[2]
|
|
11
|
+
else:
|
|
12
|
+
eeglab_file_path_in = './eeglab_data_with_ica_tmp.set'
|
|
13
|
+
eeglab_file_path_out = './eeglab_data_with_ica_tmp_averef.set'
|
|
14
|
+
|
|
15
|
+
EEG = pop_loadset(eeglab_file_path_in)
|
|
16
|
+
|
|
17
|
+
# Print the loaded data
|
|
18
|
+
EEG = pop_reref(EEG, [])
|
|
19
|
+
|
|
20
|
+
# save dataset
|
|
21
|
+
pop_saveset(EEG, eeglab_file_path_out)
|
|
22
|
+
|
eegprep/pop_resample.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
from scipy.io import savemat
|
|
4
|
+
import tempfile # need to do something with tempfile
|
|
5
|
+
from pop_loadset import pop_loadset
|
|
6
|
+
from pop_saveset import pop_saveset
|
|
7
|
+
from oct2py import octave as eeglab
|
|
8
|
+
|
|
9
|
+
def pop_resample( EEG, freq): # 2 additional parameters in MATLAB (never used)
|
|
10
|
+
|
|
11
|
+
pop_saveset(EEG, 'tmp.set') # 0.8 seconds
|
|
12
|
+
|
|
13
|
+
path2eeglab = '/System/Volumes/Data/data/matlab/eeglab/' # init >10 seconds
|
|
14
|
+
eeglab.addpath(path2eeglab + '/functions/guifunc')
|
|
15
|
+
eeglab.addpath(path2eeglab + '/functions/popfunc')
|
|
16
|
+
eeglab.addpath(path2eeglab + '/functions/adminfunc')
|
|
17
|
+
eeglab.addpath(path2eeglab + '/plugins/firfilt')
|
|
18
|
+
eeglab.addpath(path2eeglab + '/functions/sigprocfunc')
|
|
19
|
+
eeglab.addpath(path2eeglab + '/functions/miscfunc')
|
|
20
|
+
eeglab.addpath(path2eeglab + '/plugins/dipfit')
|
|
21
|
+
|
|
22
|
+
EEG2 = eeglab.pop_loadset('tmp.set') # 2 seconds
|
|
23
|
+
EEG2 = eeglab.pop_resample(EEG2, freq) # 2.4 seconds
|
|
24
|
+
eeglab.pop_saveset(EEG2, 'tmp2.set') # 2.4 seconds
|
|
25
|
+
|
|
26
|
+
EEG3 = pop_loadset('tmp2.set') # 0.2 seconds
|
|
27
|
+
|
|
28
|
+
# delete temporary files
|
|
29
|
+
os.remove('tmp.set')
|
|
30
|
+
os.remove('tmp2.set')
|
|
31
|
+
return EEG3
|
|
32
|
+
|
|
33
|
+
def test_pop_resample():
|
|
34
|
+
eeglab_file_path = './eeglab_data_with_ica_tmp.set'
|
|
35
|
+
EEG = pop_loadset(eeglab_file_path)
|
|
36
|
+
EEG2 = pop_resample(EEG, 100)
|
|
37
|
+
print(EEG2.keys())
|
|
38
|
+
print(EEG2['srate'])
|
|
39
|
+
|
|
40
|
+
# test_pop_resample()
|
|
41
|
+
|
|
42
|
+
|
eegprep/pop_saveset.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import scipy.io
|
|
2
|
+
import numpy as np
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
# Allows access using . notation
|
|
6
|
+
# class EEG:
|
|
7
|
+
# def __init__(self, **kwargs):
|
|
8
|
+
# self.__dict__.update(kwargs)
|
|
9
|
+
# def __getitem__(self, key):
|
|
10
|
+
# return self.__dict__[key]
|
|
11
|
+
# def __setitem__(self, key, value):
|
|
12
|
+
# self.__dict__[key] = value
|
|
13
|
+
|
|
14
|
+
default_empty = np.array([])
|
|
15
|
+
|
|
16
|
+
def flatten_dict_sub(d, parent_key='', sep='_'):
|
|
17
|
+
items = []
|
|
18
|
+
for k, v in d.items():
|
|
19
|
+
new_key = f"{parent_key}{sep}{k}" if parent_key else k
|
|
20
|
+
if isinstance(v, dict):
|
|
21
|
+
items.extend(flatten_dict_sub(v, new_key, sep=sep).items())
|
|
22
|
+
else:
|
|
23
|
+
items.append((new_key, v))
|
|
24
|
+
return dict(items)
|
|
25
|
+
|
|
26
|
+
def flatten_dict(data):
|
|
27
|
+
# Flatten each dictionary and collect the fields and types
|
|
28
|
+
flat_data = [flatten_dict_sub(item) for item in data]
|
|
29
|
+
fields = list(flat_data[0].keys())
|
|
30
|
+
dtypes = []
|
|
31
|
+
|
|
32
|
+
# Determine data types
|
|
33
|
+
for field in fields:
|
|
34
|
+
sample_value = flat_data[0][field]
|
|
35
|
+
if isinstance(sample_value, int):
|
|
36
|
+
dtypes.append((field, np.int32))
|
|
37
|
+
elif isinstance(sample_value, float):
|
|
38
|
+
dtypes.append((field, np.float64))
|
|
39
|
+
else:
|
|
40
|
+
dtypes.append((field, 'U{}'.format(max(len(str(item[field])) for item in flat_data))))
|
|
41
|
+
|
|
42
|
+
# Convert the flattened data to a list of tuples
|
|
43
|
+
data_tuples = [tuple(item[field] for field in fields) for item in flat_data]
|
|
44
|
+
|
|
45
|
+
# Create the rec.array
|
|
46
|
+
dtype = np.dtype([(key, 'O') for key in data[0].keys()])
|
|
47
|
+
rec_array = np.array(data_tuples, dtype=dtype).view(np.recarray)
|
|
48
|
+
return rec_array
|
|
49
|
+
|
|
50
|
+
def saveset(EEG, file_name):
|
|
51
|
+
return pop_saveset(EEG, file_name)
|
|
52
|
+
|
|
53
|
+
def pop_saveset(EEG, file_path):
|
|
54
|
+
# convert Events to structured array
|
|
55
|
+
# if 'event' in EEG:
|
|
56
|
+
# EEG['event'] = flatten_dict(EEG['event'])
|
|
57
|
+
|
|
58
|
+
# add 1 to EEG['icachansind'] to make it 1-based
|
|
59
|
+
if 'icachansind' in EEG and EEG['icachansind'].size > 0:
|
|
60
|
+
EEG['icachansind'] = EEG['icachansind'] + 1
|
|
61
|
+
|
|
62
|
+
# search for array of dictionaries and convert them to flatten_dicts
|
|
63
|
+
for key in EEG:
|
|
64
|
+
if isinstance(EEG[key], np.ndarray) and len(EEG[key]) > 0 and isinstance(EEG[key][0], dict):
|
|
65
|
+
EEG[key] = flatten_dict(EEG[key])
|
|
66
|
+
|
|
67
|
+
EEG['icaact'] = default_empty
|
|
68
|
+
scipy.io.savemat(file_path, EEG)
|
|
69
|
+
|
|
70
|
+
return EEG
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Example to export MNE epochs to EEGLAB dataset
|
|
74
|
+
# Events are not handled correctly in this example but it works
|
|
75
|
+
|
|
76
|
+
import mne
|
|
77
|
+
from mne.datasets import sample
|
|
78
|
+
import numpy as np
|
|
79
|
+
from scipy.io import savemat
|
|
80
|
+
|
|
81
|
+
def pop_saveset2(EEG, file_name):
|
|
82
|
+
|
|
83
|
+
# add 1 to EEG['icachansind'] to make it 1-based
|
|
84
|
+
if 'icachansind' in EEG and EEG['icachansind'].size > 0:
|
|
85
|
+
EEG['icachansind'] = EEG['icachansind'] + 1
|
|
86
|
+
|
|
87
|
+
eeglab_dict = {
|
|
88
|
+
'setname' : '',
|
|
89
|
+
'filename' : '',
|
|
90
|
+
'filepath' : '',
|
|
91
|
+
'subject' : '',
|
|
92
|
+
'group' : '',
|
|
93
|
+
'condition' : '',
|
|
94
|
+
'session' : np.array([]),
|
|
95
|
+
'comments' : '',
|
|
96
|
+
'nbchan' : float(EEG['nbchan']),
|
|
97
|
+
'trials' : float(EEG['trials']),
|
|
98
|
+
'pnts' : float(EEG['pnts']),
|
|
99
|
+
'srate' : float(EEG['srate']),
|
|
100
|
+
'xmin' : float(EEG['xmin']),
|
|
101
|
+
'xmax' : float(EEG['xmax']),
|
|
102
|
+
'times' : EEG['times'],
|
|
103
|
+
'data' : EEG['data'],
|
|
104
|
+
'icaact' : np.array([]),
|
|
105
|
+
'icawinv' : np.array([]),
|
|
106
|
+
'icasphere' : np.array([]),
|
|
107
|
+
'icaweights' : np.array([]),
|
|
108
|
+
'icachansind' : np.array([]),
|
|
109
|
+
'chanlocs' : np.array([]),
|
|
110
|
+
'urchanlocs' : np.array([]),
|
|
111
|
+
'chaninfo' : np.array([]),
|
|
112
|
+
'ref' : np.array([]),
|
|
113
|
+
'event' : np.array([]),
|
|
114
|
+
'urevent' : np.array([]),
|
|
115
|
+
'eventdescription': np.array([]),
|
|
116
|
+
'epoch' : np.array([]),
|
|
117
|
+
'epochdescription': np.array([]),
|
|
118
|
+
'reject' : np.array([]),
|
|
119
|
+
'stats' : np.array([]),
|
|
120
|
+
'specdata' : np.array([]),
|
|
121
|
+
'specicaact' : np.array([]),
|
|
122
|
+
'splinefile' : np.array([]),
|
|
123
|
+
'icasplinefile' : np.array([]),
|
|
124
|
+
'dipfit' : np.array([]),
|
|
125
|
+
'history' : np.array([]),
|
|
126
|
+
'saved' : np.array([]),
|
|
127
|
+
'etc' : np.array([]),
|
|
128
|
+
'datfile' : np.array([]),
|
|
129
|
+
'run' : np.array([]),
|
|
130
|
+
'roi' : np.array([]),
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Create the list of dictionaries with a string field
|
|
134
|
+
d_list = [{
|
|
135
|
+
'labels': c['labels'],
|
|
136
|
+
'theta': c['theta'] if not isinstance(c['theta'], np.ndarray) else None,
|
|
137
|
+
'radius': c['radius'] if not isinstance(c['radius'], np.ndarray) else None,
|
|
138
|
+
'X': c['X'] if not isinstance(c['X'], np.ndarray) else None,
|
|
139
|
+
'Y': c['Y'] if not isinstance(c['Y'], np.ndarray) else None,
|
|
140
|
+
'Z': c['Z'] if not isinstance(c['Z'], np.ndarray) else None,
|
|
141
|
+
'sph_theta': c['sph_theta'] if not isinstance(c['sph_theta'], np.ndarray) else None,
|
|
142
|
+
'sph_phi': c['sph_phi'] if not isinstance(c['sph_phi'], np.ndarray) else None,
|
|
143
|
+
'sph_radius': c['sph_radius'] if not isinstance(c['sph_radius'], np.ndarray) else None,
|
|
144
|
+
'type': c['type'] if not isinstance(c['type'], np.ndarray) else None,
|
|
145
|
+
'urchan': c['urchan'] if not isinstance(c['urchan'], np.ndarray) else None,
|
|
146
|
+
'ref': c['ref'] if not isinstance(c['ref'], np.ndarray) else None
|
|
147
|
+
} for c in EEG['chanlocs']]
|
|
148
|
+
|
|
149
|
+
dtype = np.dtype([
|
|
150
|
+
('labels', 'U100'), # String up to 100 characters
|
|
151
|
+
('theta', np.float64),
|
|
152
|
+
('radius', np.float64),
|
|
153
|
+
('X', np.float64),
|
|
154
|
+
('Y', np.float64),
|
|
155
|
+
('Z', np.float64),
|
|
156
|
+
('sph_theta', np.float64),
|
|
157
|
+
('sph_phi', np.float64),
|
|
158
|
+
('sph_radius', np.float64),
|
|
159
|
+
('type', 'U10'), # String up to 10 characters
|
|
160
|
+
('urchan', np.int32),
|
|
161
|
+
('ref', 'U100') # String up to 100 characters
|
|
162
|
+
])
|
|
163
|
+
|
|
164
|
+
# Convert the list of dictionaries to a structured NumPy array
|
|
165
|
+
eeglab_dict['chanlocs'] = np.array([
|
|
166
|
+
(
|
|
167
|
+
item['labels'],
|
|
168
|
+
item['theta'],
|
|
169
|
+
item['radius'],
|
|
170
|
+
item['X'],
|
|
171
|
+
item['Y'],
|
|
172
|
+
item['Z'],
|
|
173
|
+
item['sph_theta'],
|
|
174
|
+
item['sph_phi'],
|
|
175
|
+
item['sph_radius'],
|
|
176
|
+
item['type'],
|
|
177
|
+
item['urchan'],
|
|
178
|
+
item['ref']
|
|
179
|
+
)
|
|
180
|
+
for item in d_list
|
|
181
|
+
], dtype=dtype)
|
|
182
|
+
|
|
183
|
+
# # Step 4: Save the EEGLAB dataset as a .mat file
|
|
184
|
+
savemat(file_name, eeglab_dict)
|
|
185
|
+
|
|
186
|
+
from pop_loadset import pop_loadset
|
|
187
|
+
|
|
188
|
+
def test_pop_saveset():
|
|
189
|
+
file_path = './eeglab_data_with_ica_tmp.set'
|
|
190
|
+
EEG = pop_loadset(file_path)
|
|
191
|
+
pop_saveset( EEG, 'tmp.set')
|
|
192
|
+
pop_saveset2(EEG, 'tmp2.set') # does not do events and function above is better
|
|
193
|
+
# print the keys of the EEG dictionary
|
|
194
|
+
print(EEG.keys())
|
|
195
|
+
|
|
196
|
+
# test_pop_saveset()
|
|
197
|
+
|
|
198
|
+
# STILL OPEN QUESTION: Better to have empty MATLAB arrays as None for empty numpy arrays (current default).
|
|
199
|
+
# The current default is to make it more MALTAB compatible. A lot of MATLAB function start indexing MATLAB
|
|
200
|
+
# empty arrays to add values to them. This is not possible with None and would create more conversion and
|
|
201
|
+
# bugs. However, None is more pythonic.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import h5py
|
|
3
|
+
|
|
4
|
+
def save_dict_to_hdf5(data, filename, dataset_name):
|
|
5
|
+
# Create a structured dtype from the dictionary keys and their corresponding types
|
|
6
|
+
dtype = []
|
|
7
|
+
for key, value in data.items():
|
|
8
|
+
if isinstance(value, str):
|
|
9
|
+
dtype.append((key, 'S{}'.format(len(value))))
|
|
10
|
+
elif isinstance(value, int):
|
|
11
|
+
dtype.append((key, 'i4'))
|
|
12
|
+
elif isinstance(value, float):
|
|
13
|
+
dtype.append((key, 'f8'))
|
|
14
|
+
elif isinstance(value, np.ndarray):
|
|
15
|
+
dtype.append((key, 'f8', value.shape))
|
|
16
|
+
elif value is None:
|
|
17
|
+
dtype.append((key, 'S4'))
|
|
18
|
+
else:
|
|
19
|
+
raise TypeError(f"Unsupported data type for key {key}: {type(value)}")
|
|
20
|
+
|
|
21
|
+
structured_dtype = np.dtype(dtype)
|
|
22
|
+
|
|
23
|
+
# Convert dictionary values to a structured array
|
|
24
|
+
structured_data = np.array([tuple(
|
|
25
|
+
value.encode('utf-8') if isinstance(value, str) else
|
|
26
|
+
str(value).encode('utf-8') if value is None else
|
|
27
|
+
value for value in data.values()
|
|
28
|
+
)], dtype=structured_dtype)
|
|
29
|
+
|
|
30
|
+
# Save to HDF5
|
|
31
|
+
with h5py.File(filename, 'w') as hdf:
|
|
32
|
+
hdf.create_dataset(dataset_name, data=structured_data)
|
|
33
|
+
|
|
34
|
+
# Example usage
|
|
35
|
+
data = {
|
|
36
|
+
'labels': 'FPz',
|
|
37
|
+
'theta': np.array([0,1,2,3]),
|
|
38
|
+
'radius': 0.5066888888888889,
|
|
39
|
+
'X': 84.98123361344625,
|
|
40
|
+
'Y': 0,
|
|
41
|
+
'Z': -1.7860385037488253,
|
|
42
|
+
'sph_theta': 0,
|
|
43
|
+
'sph_phi': -1.203999999999994,
|
|
44
|
+
'sph_radius': 85,
|
|
45
|
+
'type': 'EEG',
|
|
46
|
+
'urchan': 1,
|
|
47
|
+
'ref': None
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
save_dict_to_hdf5(data, 'data.h5', 'dataset_name')
|