Package mvpa :: Package misc :: Package plot :: Module topo
[hide private]
[frames] | no frames]

Source Code for Module mvpa.misc.plot.topo

  1  #emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil; encoding: utf-8 -*- 
  2  #ex: set sts=4 ts=4 sw=4 et: 
  3  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## 
  4  # 
  5  #   See COPYING file distributed along with the PyMVPA package for the 
  6  #   copyright and license terms. 
  7  # 
  8  #   The initial version of the code was contributed by Ingo Fründ and is 
  9  #   Coypright (c) 2008 by Ingo Fründ ingo.fruend@googlemail.com 
 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  # TODO : add optional plotting labels for the sensors 
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 # give sane defaults 76 if plothead_kwargs is None: 77 plothead_kwargs = {} 78 79 # error function to fit the sensor locations to a sphere 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 # initial guess of sphere parameters (radius and center) 88 params = (1, 0, 0, 0) 89 90 # do fit 91 (r, cx, cy, cz), stuff = leastsq(err, params) 92 93 # Generate a grid and interpolate using the griddata module 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 # project the sensor locations onto the sphere 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 # fit topology onto xy projection of sphere 105 topo = griddata(sproj[:, 0], sproj[:, 1],\ 106 N.ravel(N.array(topography)), x, y) 107 108 # mask values outside the head 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 # show surface 115 map = P.imshow(topo, origin="lower", extent=(-r, r, -r, r), **kwargs) 116 P.axis('off') 117 118 if plothead: 119 # plot scaled head outline 120 head = plotHeadOutline(scale=r, shift=(cx, cy), **plothead_kwargs) 121 else: 122 head = None 123 124 if plotsensors: 125 # plot projected sensor locations 126 127 # reorder sensors so the ones below plotted first 128 # TODO: please fix with more elegant solution 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 # factor used all the time 165 fac = 2 * N.pi * 0.01 166 167 # Koordinates for the ears 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 # Coordinates for the Head 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 # Coordinates for the Nose 200 NoseX = N.array([.18 * rmax, 0, -.18 * rmax]) 201 NoseY = N.array([rmax - 0.004, rmax * 1.15, rmax - 0.004]) 202 203 # Combine to one 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