Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3c0540e

Browse files
committed
adding interpolation
1 parent f6cd5a9 commit 3c0540e

File tree

3 files changed

+320
-0
lines changed

3 files changed

+320
-0
lines changed

scripts/computeg.m

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
% load('-mat', 'test_computeg.mat')
2+
% g = computeg(x,y,z,xelec,yelec,zelec, params);
3+
% save('-mat', 'test_computeg_results.mat', 'g');
4+
5+
% compute G function
6+
% ------------------
7+
function g = computeg(x,y,z,xelec,yelec,zelec, params)
8+
9+
unitmat = ones(length(x(:)),length(xelec));
10+
EI = unitmat - sqrt((repmat(x(:),1,length(xelec)) - repmat(xelec,length(x(:)),1)).^2 +...
11+
(repmat(y(:),1,length(xelec)) - repmat(yelec,length(x(:)),1)).^2 +...
12+
(repmat(z(:),1,length(xelec)) - repmat(zelec,length(x(:)),1)).^2);
13+
14+
g = zeros(length(x(:)),length(xelec));
15+
m = params(2);
16+
maxn = params(3);
17+
18+
for n = 1:maxn % 200
19+
if ismatlab
20+
L = legendre(n,EI);
21+
else % Octave legendre function cannot process 2-D matrices
22+
for icol = 1:size(EI,2)
23+
tmpL = legendre(n,EI(:,icol));
24+
if icol == 1, L = zeros([ size(tmpL) size(EI,2)]); end
25+
L(:,:,icol) = tmpL;
26+
end
27+
end
28+
g = g + ((2*n+1)/(n^m*(n+1)^m))*squeeze(L(1,:,:));
29+
end
30+
g = g/(4*pi);

scripts/eeg_interp.py

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import numpy as np
2+
from scipy.linalg import pinv
3+
from scipy.special import lpmv
4+
5+
def eeg_interp(EEG, bad_chans, method='spherical', t_range=None, params=None):
6+
# set defaults
7+
if method not in ('spherical','sphericalKang','sphericalCRD','sphericalfast'):
8+
raise ValueError(f"Unknown method {method}")
9+
if t_range is None:
10+
t_range = (EEG.xmin, EEG.xmax)
11+
if params is None:
12+
if method=='spherical':
13+
params = (0,4,7)
14+
elif method=='sphericalKang':
15+
params = (1e-8,3,50)
16+
elif method=='sphericalCRD':
17+
params = (1e-5,4,500)
18+
else:
19+
if len(params)!=3:
20+
raise ValueError("params must be length-3 tuple")
21+
method = 'spherical'
22+
23+
# ensure channel locations present
24+
locs = EEG.chanlocs
25+
if not locs or any(('X' not in ch or 'Y' not in ch or 'Z' not in ch) for ch in locs):
26+
raise RuntimeError("Channel locations required for interpolation")
27+
28+
# convert bad_chans from labels to indices if needed
29+
if isinstance(bad_chans, list) and isinstance(bad_chans[0], str):
30+
labels = [ch['labels'] for ch in locs]
31+
bad_idx = [labels.index(lbl) for lbl in bad_chans]
32+
else:
33+
bad_idx = sorted(bad_chans)
34+
35+
good_idx = [i for i in range(EEG.nbchan) if i not in bad_idx]
36+
if method=='sphericalfast':
37+
# drop bad, later reshuffle if desired
38+
data = EEG.data.copy()
39+
data = np.delete(data, bad_idx, axis=0)
40+
EEG.data = data
41+
EEG.nbchan = data.shape[0]
42+
return EEG
43+
44+
# extract Cartesian positions and normalize to unit sphere
45+
def _norm(ch_ids):
46+
xyz = np.vstack([ [locs[i][c] for i in ch_ids] for c in ('X','Y','Z') ])
47+
rad = np.linalg.norm(xyz, axis=0)
48+
return xyz / rad
49+
50+
xyz_good = _norm(good_idx)
51+
xyz_bad = _norm(bad_idx)
52+
53+
# reshape data to (n_chan, n_timepoints)
54+
d = EEG.data.reshape(EEG.nbchan, -1)
55+
56+
# compute interpolated signals for bad channels
57+
bad_data = spheric_spline(
58+
xelec=xyz_good[0], yelec=xyz_good[1], zelec=xyz_good[2],
59+
xbad =xyz_bad[0], ybad =xyz_bad[1], zbad =xyz_bad[2],
60+
values=d[good_idx,:],
61+
params=params
62+
)
63+
64+
# restore original time range if needed
65+
if t_range != (EEG.xmin, EEG.xmax):
66+
start, end = t_range
67+
ts = np.arange(EEG.nbchan) # dummy
68+
# here you would mask out-of-range portions as in MATLAB
69+
70+
# assemble full data array
71+
full = np.zeros_like(d)
72+
full[good_idx,:] = d[good_idx,:]
73+
full[bad_idx,:] = bad_data
74+
75+
EEG.data = full.reshape(EEG.nbchan, EEG.pnts, EEG.trials)
76+
return EEG
77+
78+
def spheric_spline(xelec, yelec, zelec, xbad, ybad, zbad, values, params):
79+
# values: (n_good, n_points)
80+
Gelec = computeg(xelec, yelec, zelec, xelec, yelec, zelec, params)
81+
Gsph = computeg(xbad, ybad, zbad, xelec, yelec, zelec, params)
82+
83+
meanvals = values.mean(axis=1, keepdims=True)
84+
V = values - meanvals
85+
V = np.vstack([V, np.zeros((1, V.shape[1]))])
86+
87+
lam = params[0]
88+
A = np.vstack([Gelec + np.eye(Gelec.shape[0])*lam,
89+
np.ones((1, Gelec.shape[0]))])
90+
C = pinv(A) @ V
91+
92+
allres = Gsph @ C
93+
meanval_broadcast = values.mean() # scalar mean across all good channels and time points
94+
allres += meanval_broadcast
95+
return allres
96+
97+
def computeg(x, y, z, xelec, yelec, zelec, params):
98+
# x,y,z are points to interpolate; xelec,... electrode locations
99+
X = x.ravel()[:,None]; Y = y.ravel()[:,None]; Z = z.ravel()[:,None]
100+
E = 1 - np.sqrt((X - xelec[None,:])**2 + (Y - yelec[None,:])**2 + (Z - zelec[None,:])**2)
101+
102+
m, maxn = params[1], int(params[2])
103+
g = np.zeros((E.shape[0], E.shape[1]))
104+
for n in range(1, maxn+1):
105+
Pn = lpmv(0, n, E) # shape (E.shape)
106+
g += ((2*n+1)/(n**m*(n+1)**m)) * Pn
107+
108+
return g/(4*np.pi)
109+
110+
def test_spheric_spline():
111+
import numpy as np
112+
from scipy.io import loadmat, savemat
113+
114+
# generate random electrode positions on the unit sphere
115+
rng = np.random.default_rng(0)
116+
n_good, n_bad, n_pts = 10, 2, 100
117+
xyz = rng.normal(size=(3, n_good))
118+
xyz /= np.linalg.norm(xyz, axis=0)
119+
xbad = rng.normal(size=(3, n_bad))
120+
xbad /= np.linalg.norm(xbad, axis=0)
121+
122+
# random “good” channel data
123+
values = rng.standard_normal((n_good, n_pts))
124+
125+
# write to MATLAB file
126+
mat = {
127+
'xelec': xyz[0],
128+
'yelec': xyz[1],
129+
'zelec': xyz[2],
130+
'xbad': xbad[0],
131+
'ybad': xbad[1],
132+
'zbad': xbad[2],
133+
'values': values,
134+
'params': (0.0, 4.0, 7.0)
135+
}
136+
savemat('test_spheric_spline.mat', mat)
137+
138+
# compute in Python
139+
py_res = spheric_spline(
140+
xelec=xyz[0], yelec=xyz[1], zelec=xyz[2],
141+
xbad=xbad[0], ybad=xbad[1], zbad=xbad[2],
142+
values=values, params=(0, 4, 7)
143+
)
144+
145+
# # load MATLAB result (assumed saved as `mat_res` in test.mat)
146+
mat_data = loadmat('test_spheric_spline_results.mat')
147+
mat_res = mat_data['allres'] # Assuming the MATLAB result is saved as 'mat_res'
148+
149+
# # compare
150+
diff = np.abs(py_res - mat_res)
151+
152+
# do a proper max abs and rel difference
153+
max_abs_diff = np.max(np.abs(py_res - mat_res))
154+
max_rel_diff = np.max(np.abs(py_res - mat_res) / np.abs(mat_res))
155+
print(f"Max absolute difference: {max_abs_diff}")
156+
print(f"Max relative difference: {max_rel_diff}")
157+
158+
def test_computeg():
159+
import numpy as np
160+
from scipy.io import loadmat, savemat
161+
# test computeg
162+
x = np.linspace(0, 1, 100)
163+
y = np.linspace(0, 1, 100)
164+
z = np.linspace(0, 1, 100)
165+
xelec = np.linspace(0, 1, 10)
166+
yelec = np.linspace(0, 1, 10)
167+
zelec = np.linspace(0, 1, 10)
168+
params = (0.0, 4.0, 7.0)
169+
170+
# save to mat file
171+
mat = {
172+
'x': x,
173+
'y': y,
174+
'z': z,
175+
'xelec': xelec,
176+
'yelec': yelec,
177+
'zelec': zelec,
178+
'params': params
179+
}
180+
savemat('test_computeg.mat', mat)
181+
182+
# compute in Python
183+
g = computeg(x, y, z, xelec, yelec, zelec, params)
184+
print("g.shape python:", g.shape)
185+
186+
# load MATLAB result
187+
mat_data = loadmat('test_computeg_results.mat')
188+
mat_res = mat_data['g']
189+
print("g.shape matlab:", mat_res.shape)
190+
191+
# compare
192+
diff = np.abs(g - mat_res)
193+
194+
# do a proper max abs and rel difference
195+
max_abs_diff = np.max(np.abs(g - mat_res))
196+
max_rel_diff = np.max(np.abs(g - mat_res) / np.abs(mat_res))
197+
print(f"Max absolute difference: {max_abs_diff}")
198+
print(f"Max relative difference: {max_rel_diff}")
199+
200+
if __name__ == '__main__':
201+
print("Running test_computeg")
202+
test_computeg()
203+
print("\nRunning test_spheric_spline")
204+
test_spheric_spline()
205+
206+

scripts/spheric_spline.m

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
% load('-mat', 'test_spheric_spline.mat')
3+
% [xbad, ybad, zbad, allres] = spheric_spline( xelec, yelec, zelec, xbad, ybad, zbad, values, params)
4+
% save('-mat', 'test_spheric_spline_results.mat', 'allres');
5+
6+
% -----------------
7+
% spherical splines
8+
% -----------------
9+
% function [x, y, z, Res] = spheric_spline_old( xelec, yelec, zelec, values)
10+
%
11+
% SPHERERES = 20;
12+
% [x,y,z] = sphere(SPHERERES);
13+
% x(1:(length(x)-1)/2,:) = []; x = [ x(:)' ];
14+
% y(1:(length(y)-1)/2,:) = []; y = [ y(:)' ];
15+
% z(1:(length(z)-1)/2,:) = []; z = [ z(:)' ];
16+
%
17+
% Gelec = computeg(xelec,yelec,zelec,xelec,yelec,zelec);
18+
% Gsph = computeg(x,y,z,xelec,yelec,zelec);
19+
%
20+
% % equations are
21+
% % Gelec*C + C0 = Potential (C unknown)
22+
% % Sum(c_i) = 0
23+
% % so
24+
% % [c_1]
25+
% % * [c_2]
26+
% % [c_ ]
27+
% % xelec [c_n]
28+
% % [x x x x x] [potential_1]
29+
% % [x x x x x] [potential_ ]
30+
% % [x x x x x] = [potential_ ]
31+
% % [x x x x x] [potential_4]
32+
% % [1 1 1 1 1] [0]
33+
%
34+
% % compute solution for parameters C
35+
% % ---------------------------------
36+
% meanvalues = mean(values);
37+
% values = values - meanvalues; % make mean zero
38+
% C = pinv([Gelec;ones(1,length(Gelec))]) * [values(:);0];
39+
%
40+
% % apply results
41+
% % -------------
42+
% Res = zeros(1,size(Gsph,1));
43+
% for j = 1:size(Gsph,1)
44+
% Res(j) = sum(C .* Gsph(j,:)');
45+
% end
46+
% Res = Res + meanvalues;
47+
% Res = reshape(Res, length(x(:)),1);
48+
49+
function [xbad, ybad, zbad, allres] = spheric_spline( xelec, yelec, zelec, xbad, ybad, zbad, values, params)
50+
51+
newchans = length(xbad);
52+
numpoints = size(values,2);
53+
params = double(params);
54+
55+
%SPHERERES = 20;
56+
%[x,y,z] = sphere(SPHERERES);
57+
%x(1:(length(x)-1)/2,:) = []; xbad = [ x(:)'];
58+
%y(1:(length(x)-1)/2,:) = []; ybad = [ y(:)'];
59+
%z(1:(length(x)-1)/2,:) = []; zbad = [ z(:)'];
60+
61+
Gelec = computeg(xelec,yelec,zelec,xelec,yelec,zelec,params);
62+
Gsph = computeg(xbad,ybad,zbad,xelec,yelec,zelec,params);
63+
64+
% compute solution for parameters C
65+
% ---------------------------------
66+
meanvalues = mean(values);
67+
values = values - repmat(meanvalues, [size(values,1) 1]); % make mean zero
68+
69+
values = [values;zeros(1,numpoints)];
70+
lambda = params(1);
71+
C = pinv([Gelec+eye(size(Gelec))*lambda;ones(1,length(Gelec))]) * values;
72+
%C = pinv([Gelec;ones(1,length(Gelec))]) * values;
73+
74+
clear values;
75+
allres = zeros(newchans, numpoints);
76+
77+
% apply results
78+
% -------------
79+
for j = 1:size(Gsph,1)
80+
allres(j,:) = sum(C .* repmat(Gsph(j,:)', [1 size(C,2)]));
81+
end
82+
allres = allres + repmat(meanvalues, [size(allres,1) 1]);
83+
84+

0 commit comments

Comments
 (0)