14
14
15
15
MONTAGE_PATH = op .join (op .dirname (_CHANNELS_INIT_FILE ), 'data' , 'montages' )
16
16
17
- HEAD_SIZE_DEFAULT = 0.085 # in [m]
18
17
_str = 'U100'
19
18
20
19
21
- def _egi_256 ():
20
+ # In standard_1020, T9=LPA, T10=RPA, Nasion is the same as Iz with a
21
+ # sign-flipped Y value
22
+
23
+ def _egi_256 (head_size ):
22
24
fname = op .join (MONTAGE_PATH , 'EGI_256.csd' )
23
25
# Label, Theta, Phi, Radius, X, Y, Z, off sphere surface
24
26
options = dict (comments = '//' ,
@@ -27,45 +29,66 @@ def _egi_256():
27
29
pos = np .stack ([xs , ys , zs ], axis = - 1 )
28
30
29
31
# Fix pos to match Montage code
30
- # pos -= np.mean(pos, axis=0)
31
- pos = HEAD_SIZE_DEFAULT * (pos / np .linalg .norm (pos , axis = 1 ).mean ())
32
+ pos *= head_size / np .median (np .linalg .norm (pos , axis = 1 ))
33
+
34
+ # For this cap, the Nasion is the frontmost electrode,
35
+ # LPA/RPA we approximate by putting 75% of the way (toward the front)
36
+ # between the two electrodes that are halfway down the ear holes
37
+ nasion = pos [ch_names .index ('E31' )]
38
+ lpa = (0.75 * pos [ch_names .index ('E67' )] +
39
+ 0.25 * pos [ch_names .index ('E94' )])
40
+ rpa = (0.75 * pos [ch_names .index ('E219' )] +
41
+ 0.25 * pos [ch_names .index ('E190' )])
32
42
33
43
return make_dig_montage (
34
44
ch_pos = OrderedDict (zip (ch_names , pos )),
35
- coord_frame = 'head' ,
45
+ coord_frame = 'unknown' , nasion = nasion , lpa = lpa , rpa = rpa ,
36
46
)
37
47
38
48
39
- def _easycap (basename ):
49
+ def _easycap (basename , head_size ):
40
50
fname = op .join (MONTAGE_PATH , basename )
41
51
options = dict (skip_header = 1 , dtype = (_str , 'i4' , 'i4' ))
42
52
ch_names , theta , phi = _safe_np_loadtxt (fname , ** options )
43
53
44
- radii = np .full_like ( phi , 1 ) # XXX: HEAD_SIZE_DEFAULT should work
54
+ radii = np .full ( len ( phi ), head_size )
45
55
pos = _sph_to_cart (np .stack (
46
56
[radii , np .deg2rad (phi ), np .deg2rad (theta )],
47
57
axis = - 1 ,
48
58
))
49
-
50
- # scale up to realistic head radius (8.5cm == 85mm):
51
- pos *= HEAD_SIZE_DEFAULT # XXXX: this should be done through radii
59
+ nasion = np .concatenate ([[0 ], pos [ch_names .index ('Fpz' ), 1 :]])
60
+ nasion *= head_size / np .linalg .norm (nasion )
61
+ lpa = np .mean ([pos [ch_names .index ('FT9' )],
62
+ pos [ch_names .index ('TP9' )]], axis = 0 )
63
+ lpa *= head_size / np .linalg .norm (lpa ) # on sphere
64
+ rpa = np .mean ([pos [ch_names .index ('FT10' )],
65
+ pos [ch_names .index ('TP10' )]], axis = 0 )
66
+ rpa *= head_size / np .linalg .norm (rpa )
52
67
53
68
return make_dig_montage (
54
69
ch_pos = OrderedDict (zip (ch_names , pos )),
55
- coord_frame = 'head' ,
70
+ coord_frame = 'unknown' , nasion = nasion , lpa = lpa , rpa = rpa ,
56
71
)
57
72
58
73
59
- def _hydrocel (basename , fid_names = ('FidNz' , 'FidT9' , 'FidT10' )):
74
+ def _hydrocel (basename , head_size ):
75
+ fid_names = ('FidNz' , 'FidT9' , 'FidT10' )
60
76
fname = op .join (MONTAGE_PATH , basename )
61
77
options = dict (dtype = (_str , 'f4' , 'f4' , 'f4' ))
62
78
ch_names , xs , ys , zs = _safe_np_loadtxt (fname , ** options )
63
79
64
- pos = np .stack ([xs , ys , zs ], axis = - 1 ) * 0.01
80
+ pos = np .stack ([xs , ys , zs ], axis = - 1 )
65
81
ch_pos = OrderedDict (zip (ch_names , pos ))
66
- _ = [ch_pos .pop (n , None ) for n in fid_names ]
82
+ nasion , lpa , rpa = [ch_pos .pop (n ) for n in fid_names ]
83
+ scale = head_size / np .median (np .linalg .norm (pos , axis = - 1 ))
84
+ for value in ch_pos .values ():
85
+ value *= scale
86
+ nasion *= scale
87
+ lpa *= scale
88
+ rpa *= scale
67
89
68
- return make_dig_montage (ch_pos = ch_pos , coord_frame = 'head' )
90
+ return make_dig_montage (ch_pos = ch_pos , coord_frame = 'unknown' ,
91
+ nasion = nasion , rpa = rpa , lpa = lpa )
69
92
70
93
71
94
def _str_names (ch_names ):
@@ -79,39 +102,32 @@ def _safe_np_loadtxt(fname, **kwargs):
79
102
return (ch_names ,) + others
80
103
81
104
82
- def _biosemi (basename , fid_names = ('Nz' , 'LPA' , 'RPA' )):
105
+ def _biosemi (basename , head_size ):
106
+ fid_names = ('Nz' , 'LPA' , 'RPA' )
83
107
fname = op .join (MONTAGE_PATH , basename )
84
108
options = dict (skip_header = 1 , dtype = (_str , 'i4' , 'i4' ))
85
109
ch_names , theta , phi = _safe_np_loadtxt (fname , ** options )
86
110
87
- radii = np .full_like ( phi , 1 ) # XXX: HEAD_SIZE_DEFAULT should work
111
+ radii = np .full ( len ( phi ), head_size )
88
112
pos = _sph_to_cart (np .stack (
89
113
[radii , np .deg2rad (phi ), np .deg2rad (theta )],
90
114
axis = - 1 ,
91
115
))
92
116
93
- # scale up to realistic head radius (8.5cm == 85mm):
94
- pos *= HEAD_SIZE_DEFAULT # XXXX: this should be done through radii
95
-
96
117
ch_pos = OrderedDict (zip (ch_names , pos ))
97
- _ = [ch_pos .pop (n , None ) for n in fid_names ]
118
+ nasion , lpa , rpa = [ch_pos .pop (n ) for n in fid_names ]
98
119
99
- return make_dig_montage (ch_pos = ch_pos , coord_frame = 'head' )
120
+ return make_dig_montage (ch_pos = ch_pos , coord_frame = 'unknown' ,
121
+ nasion = nasion , lpa = lpa , rpa = rpa )
100
122
101
123
102
- def _mgh_or_standard (basename , fid_names = ('Nz' , 'LPA' , 'RPA' )):
124
+ def _mgh_or_standard (basename , head_size ):
125
+ fid_names = ('Nz' , 'LPA' , 'RPA' )
103
126
fname = op .join (MONTAGE_PATH , basename )
104
127
105
128
ch_names_ , pos = [], []
106
129
with open (fname ) as fid :
107
- # Default units are meters
108
- for line in fid :
109
- if 'UnitPosition' in line :
110
- units = line .split ()[1 ]
111
- scale_factor = dict (m = 1. , mm = 1e-3 )[units ]
112
- break
113
- else :
114
- raise RuntimeError ('Could not detect units in file %s' % fname )
130
+ # Ignore units as we will scale later using the norms anyway
115
131
for line in fid :
116
132
if 'Positions\n ' in line :
117
133
break
@@ -125,12 +141,18 @@ def _mgh_or_standard(basename, fid_names=('Nz', 'LPA', 'RPA')):
125
141
break
126
142
ch_names_ .append (line .strip (' ' ).strip ('\n ' ))
127
143
128
- pos = np .array (pos ) * scale_factor
129
-
144
+ pos = np .array (pos )
130
145
ch_pos = OrderedDict (zip (ch_names_ , pos ))
131
- _ = [ch_pos .pop (n , None ) for n in fid_names ]
132
-
133
- return make_dig_montage (ch_pos = ch_pos , coord_frame = 'head' )
146
+ nasion , lpa , rpa = [ch_pos .pop (n ) for n in fid_names ]
147
+ scale = head_size / np .median (np .linalg .norm (pos , axis = 1 ))
148
+ for value in ch_pos .values ():
149
+ value *= scale
150
+ nasion *= scale
151
+ lpa *= scale
152
+ rpa *= scale
153
+
154
+ return make_dig_montage (ch_pos = ch_pos , coord_frame = 'unknown' ,
155
+ nasion = nasion , lpa = lpa , rpa = rpa )
134
156
135
157
136
158
standard_montage_look_up_table = {
0 commit comments