1
2
3
4
5
6
7
8
9
10
11
12 """Plot parameter distributions on a head surface (topography plots)."""
13
14 __docformat__ = 'restructuredtext'
15
16 import numpy as N
17
18 from mvpa.base import externals
19 externals.exists("pylab", raiseException=True)
20 import pylab as P
21 import matplotlib.numerix.ma as M
22
23 externals.exists("griddata", raiseException=True)
24 from griddata import griddata
25
26 externals.exists("scipy", raiseException=True)
27 from scipy.optimize import leastsq
28
29
30
31 -def plotHeadTopography(topography, sensorlocations, plotsensors=False,
32 resolution=51, masked=True, plothead=True,
33 plothead_kwargs=None, **kwargs):
34 """Plot distribution to a head surface, derived from some sensor locations.
35
36 The sensor locations are first projected onto the best fitting sphere and
37 finally projected onto a circle (by simply ignoring the z-axis).
38
39 :Parameters:
40 topography: array
41 A vector of some values corresponding to each sensor.
42 sensorlocations: (nsensors x 3) array
43 3D coordinates of each sensor. The order of the sensors has to match
44 with the `topography` vector.
45 plotsensors: bool
46 If True, sensor will be plotted on their projected coordinates.
47 No sensor are shown otherwise.
48 plothead: bool
49 If True, a head outline is plotted.
50 plothead_kwargs: dict
51 Additional keyword arguments passed to `plotHeadOutline()`.
52 resolution: int
53 Number of surface samples along both x and y-axis.
54 masked: bool
55 If True, all surface sample extending to head outline will be
56 masked.
57 **kwargs:
58 All additional arguments will be passed to `pylab.imshow()`.
59
60
61 :Returns:
62 (map, head, sensors)
63 The corresponding matplotlib objects are returned if plotted, ie.
64 if plothead is set to `False`, `head` will be `None`.
65
66 map
67 The colormap that makes the actual plot, a
68 matplotlib.image.AxesImage instance.
69 head
70 What is returned by `plotHeadOutline()`.
71 sensors
72 The dots marking the electrodes, a matplotlib.lines.Line2d
73 instance.
74 """
75
76 if plothead_kwargs is None:
77 plothead_kwargs = {}
78
79
80 def err(params):
81 r, cx, cy, cz = params
82 return (sensorlocations[:, 0] - cx) ** 2 \
83 + (sensorlocations[:, 1] - cy) ** 2 \
84 + (sensorlocations[:, 2] - cz) ** 2 \
85 - r ** 2
86
87
88 params = (1, 0, 0, 0)
89
90
91 (r, cx, cy, cz), stuff = leastsq(err, params)
92
93
94 x = N.arange(cx - r, cx + r, 2. * r / resolution)
95 y = N.arange(cy - r, cy + r, 2. * r / resolution)
96 x, y = P.meshgrid(x, y)
97
98
99 sphere_center = N.array((cx, cy, cz))
100 sproj = sensorlocations - sphere_center
101 sproj = r * sproj / N.c_[N.sqrt(N.sum(sproj ** 2, axis=1))]
102 sproj += sphere_center
103
104
105 topo = griddata(sproj[:, 0], sproj[:, 1],\
106 N.ravel(N.array(topography)), x, y)
107
108
109 if masked:
110 notinhead = N.greater_equal((x - cx) ** 2 + (y - cy) ** 2,
111 (1.0 * r) ** 2)
112 topo = M.masked_where(notinhead, topo)
113
114
115 map = P.imshow(topo, origin="lower", extent=(-r, r, -r, r), **kwargs)
116 P.axis('off')
117
118 if plothead:
119
120 head = plotHeadOutline(scale=r, shift=(cx, cy), **plothead_kwargs)
121 else:
122 head = None
123
124 if plotsensors:
125
126
127
128
129 zenum = [x[::-1] for x in enumerate(sproj[:, 2].tolist())]
130 zenum.sort()
131 indx = [ x[1] for x in zenum ]
132 sensors = P.plot(sproj[indx, 0], sproj[indx, 1], 'wo')
133 else:
134 sensors = None
135
136 return map, head, sensors
137
138
139 -def plotHeadOutline(scale=1, shift=(0, 0), color='k', linewidth='5', **kwargs):
140 """Plots a simple outline of a head viewed from the top.
141
142 The plot contains schematic representations of the nose and ears. The
143 size of the head is basically a unit circle for nose and ears attached
144 to it.
145
146 :Parameters:
147 scale: float
148 Factor to scale the size of the head.
149 shift: 2-tuple of floats
150 Shift the center of the head circle by these values.
151 color: matplotlib color spec
152 The color the outline should be plotted in.
153 linewidth: int
154 Linewidth of the head outline.
155 **kwargs:
156 All additional arguments are passed to `pylab.plot()`.
157
158 :Returns:
159 Matplotlib lines2D object
160 can be used to tweak the look of the head outline.
161 """
162
163 rmax = 0.5
164
165 fac = 2 * N.pi * 0.01
166
167
168 EarX1 = -1 * N.array(
169 [.497, .510, .518, .5299,
170 .5419, .54, .547, .532, .510,
171 rmax * N.cos(fac * (54 + 42))])
172 EarY1 = N.array(
173 [.0655, .0775, .0783, .0746, .0555,
174 -.0055, -.0932, -.1313, -.1384,
175 rmax * N.sin(fac * (54 + 42))])
176 EarX2 = N.array(
177 [rmax * N.cos(fac * (54 + 42)),
178 .510, .532, .547, .54, .5419,
179 .5299, .518, .510, .497] )
180 EarY2 = N.array(
181 [rmax * N.sin(fac * (54 + 42)),
182 -.1384, -.1313, -.0932, -.0055,
183 .0555, .0746, .0783, .0775, .0655] )
184
185
186 HeadX1 = N.fromfunction(
187 lambda x: rmax * N.cos(fac * (x + 2)), (21,))
188 HeadY1 = N.fromfunction(
189 lambda y: rmax * N.sin(fac * (y + 2)), (21,))
190 HeadX2 = N.fromfunction(
191 lambda x: rmax * N.cos(fac * (x + 28)), (21,))
192 HeadY2 = N.fromfunction(
193 lambda y: rmax * N.sin(fac * (y + 28)), (21,))
194 HeadX3 = N.fromfunction(
195 lambda x: rmax * N.cos(fac * (x + 54)), (43,))
196 HeadY3 = N.fromfunction(
197 lambda y: rmax * N.sin(fac * (y + 54)), (43,))
198
199
200 NoseX = N.array([.18 * rmax, 0, -.18 * rmax])
201 NoseY = N.array([rmax - 0.004, rmax * 1.15, rmax - 0.004])
202
203
204 X = N.concatenate((EarX2, HeadX1, NoseX, HeadX2, EarX1, HeadX3))
205 Y = N.concatenate((EarY2, HeadY1, NoseY, HeadY2, EarY1, HeadY3))
206
207 X *= 2 * scale
208 Y *= 2 * scale
209 X += shift[0]
210 Y += shift[1]
211
212 return P.plot(X, Y, color=color, linewidth=linewidth)
213