1 """
2 User-level analysis commands, typically for measuring or generating SheetViews.
3
4 Most of this file consists of commands for creating SheetViews, paired
5 with a template for how to construct plots from these SheetViews.
6
7 For instance, the implementation of Activity plots consists of the
8 update_activity() command plus the Activity PlotGroupTemplate. The
9 update_activity() command reads the activity array of each Sheet and
10 makes a corresponding SheetView to put in the Sheet's sheet_views
11 dictionary, while the Activity PlotGroupTemplate specifies which
12 SheetViews should be plotted in which combination. See the help for
13 PlotGroupTemplate for more information.
14
15 Some of the commands are ordinary Python functions, but the rest are
16 ParameterizedFunctions, which act like Python functions but support
17 Parameters with defaults, bounds, inheritance, etc. These commands
18 are usually grouped together using inheritance so that they share a
19 set of parameters and some code, and only the bits that are specific
20 to that particular plot or analysis appear below. See the
21 superclasses for the rest of the parameters and code.
22
23 $Id: analysis.py 11316 2010-07-27 17:52:53Z ceball $
24 """
25 __version__='$Revision: 11316 $'
26
27
28 from math import pi, sin, cos
29 from PIL import Image, ImageDraw
30 import copy
31
32 from numpy.oldnumeric import array, maximum
33
34 import param
35 from param.parameterized import ParameterizedFunction
36 from param.parameterized import ParamOverrides
37
38 import topo
39 from topo.base.cf import Projection
40 from topo.base.sheet import Sheet
41 from topo.base.sheetview import SheetView
42 from topo.misc.distribution import Distribution
43 from topo.pattern.basic import GaussiansCorner, RawRectangle, Line
44 from topo.analysis.featureresponses import ReverseCorrelation
45 from topo.plotting.plotgroup import create_plotgroup, plotgroups
46
47 from topo.plotting.plotgroup import UnitMeasurementCommand,ProjectionSheetMeasurementCommand
48 from topo.analysis.featureresponses import Feature, PatternPresenter
49 from topo.analysis.featureresponses import SinusoidalMeasureResponseCommand, PositionMeasurementCommand, SingleInputResponseCommand
50
51
52
54 """
55 Helper function for save_plotgroup.
56
57 Comparison operator for deciding whether make_plots(update==False) is
58 safe for one plotgroup if the other has already been updated.
59
60 Treats plotgroups as the same if the specified list of attributes
61 (if present) match in both plotgroups.
62 """
63
64 attrs_to_check = ['pre_plot_hooks','keyname','sheet','x','y','projection','input_sheet','density','coords']
65
66 for a in attrs_to_check:
67 if hasattr(p1,a) or hasattr(p2,a):
68 if not (hasattr(p1,a) and hasattr(p2,a) and getattr(p1,a)== getattr(p2,a)):
69 return False
70
71 return True
72
73
74
76 """
77 Convenience command for saving a set of plots to disk. Examples:
78
79 save_plotgroup("Activity")
80 save_plotgroup("Orientation Preference")
81 save_plotgroup("Projection",projection=topo.sim['V1'].projections('Afferent'))
82
83 Some plotgroups accept optional parameters, which can be passed
84 like projection above.
85 """
86
87 equivalence_fn = param.Callable(default=_equivalent_for_plotgroup_update,doc="""
88 Function to call on plotgroups p1,p2 to determine if calling pre_plot_hooks
89 on one of them is sufficient to update both plots. Should return False
90 unless the commands are exact equivalents, including all relevant parameters.""")
91
92 use_cached_results = param.Boolean(default=False,doc="""
93 If True, will use the equivalence_fn to determine cases where
94 the pre_plot_hooks for a plotgroup can safely be skipped, to
95 avoid lengthy redundant computation. Should usually be
96 False for safety, but can be enabled for e.g. batch mode
97 runs using a related batch of plots.""")
98
99 saver_params = param.Dict(default={},doc="""
100 Optional parameters to pass to the underlying PlotFileSaver object.""")
101
102
103
104 previous_time=[-1]
105 previous_plotgroups=[]
106
108 p=ParamOverrides(self,params,allow_extra_keywords=True)
109
110 plotgroup = copy.deepcopy(plotgroups[name])
111
112
113
114
115
116
117 if 'projection' in params:
118 setattr(plotgroup,'sheet',params['projection'].dest)
119
120 plotgroup._set_name(name)
121
122
123
124 for n,v in p.extra_keywords().items():
125 plotgroup.set_param(n,v)
126
127
128 if (topo.sim.time() != self.previous_time[0]):
129 del self.previous_time[:]
130 del self.previous_plotgroups[:]
131 self.previous_time.append(topo.sim.time())
132
133
134 update=True
135 if p.use_cached_results:
136 for g in self.previous_plotgroups:
137 if p.equivalence_fn(g,plotgroup):
138 update=False
139 break
140
141 keywords=" ".join(["%s" % (v.name if isinstance(v,param.Parameterized) else str(v)) for n,v in p.extra_keywords().items()])
142 plot_description="%s%s%s" % (plotgroup.name," " if keywords else "",keywords)
143 if update:
144 self.previous_plotgroups.append(plotgroup)
145 self.debug("%s: Running pre_plot_hooks" % plot_description)
146 else:
147 self.message("%s: Using cached results from pre_plot_hooks" % plot_description)
148
149 plotgroup.make_plots(update=update)
150 plotgroup.filesaver.save_to_disk(**(p.saver_params))
151
152
153
154 -def decode_feature(sheet, preference_map = "OrientationPreference", axis_bounds=(0.0,1.0), cyclic=True, weighted_average=True):
155 """
156 Estimate the value of a feature from the current activity pattern on a sheet.
157
158 The specified preference_map should be measured before this
159 function is called.
160
161 If weighted_average is False, the feature value returned is the
162 value of the preference_map at the maximally active location.
163
164 If weighted_average is True, the feature value is estimated by
165 weighting the preference_map by the current activity level, and
166 averaging the result across all units in the sheet. The
167 axis_bounds specify the allowable range of the feature values in
168 the preference_map. If cyclic is true, a vector average is used;
169 otherwise an arithmetic weighted average is used.
170
171 For instance, if preference_map is OrientationPreference (a cyclic
172 quantity), then the result will be the vector average of the
173 activated orientations. For an orientation map this value should
174 be an estimate of the orientation present on the input.
175 """
176 d = Distribution(axis_bounds, cyclic)
177
178 if not (preference_map in sheet.sheet_views):
179 topo.sim.warning(preference_map + " should be measured before calling decode_orientations.")
180 else:
181 map = sheet.sheet_views[preference_map]
182 d.add(dict(zip(map.view()[0].ravel(), sheet.activity.ravel())))
183
184 if weighted_average:
185 return d.weighted_average()
186 else:
187 return d.max_value_bin()
188
189
190
192 """
193 Make a map of neural activity available for each sheet, for use in template-based plots.
194
195 This command simply asks each sheet for a copy of its activity
196 matrix, and then makes it available for plotting. Of course, for
197 some sheets providing this information may be non-trivial, e.g. if
198 they need to average over recent spiking activity.
199 """
200 for sheet in topo.sim.objects(Sheet).values():
201 activity_copy = array(sheet.activity)
202 new_view = SheetView((activity_copy,sheet.bounds),
203 sheet.name,sheet.precedence,topo.sim.time(),sheet.row_precedence)
204 sheet.sheet_views['Activity']=new_view
205
206
207 pg = create_plotgroup(name='Activity',category='Basic',
208 doc='Plot the activity for all Sheets.', auto_refresh=True,
209 pre_plot_hooks=[update_activity], plot_immediately=True)
210 pg.add_plot('Activity',[('Strength','Activity')])
211
212
214 """
215 Make available Red, Green, and Blue activity matrices for all appropriate sheets.
216 """
217 for sheet in topo.sim.objects(Sheet).values():
218 for c in ['Red','Green','Blue']:
219
220 if hasattr(sheet,'activity_%s'%c.lower()):
221 activity_copy = getattr(sheet,'activity_%s'%c.lower()).copy()
222 new_view = SheetView((activity_copy,sheet.bounds),
223 sheet.name,sheet.precedence,topo.sim.time(),sheet.row_precedence)
224 sheet.sheet_views['%sActivity'%c]=new_view
225
226
227 pg = create_plotgroup(name='RGB',category='Other',
228 doc='Combine and plot the red, green, and blue activity for all appropriate Sheets.', auto_refresh=True,
229 pre_plot_hooks=[update_rgb_activities], plot_immediately=True)
230 pg.add_plot('RGB',[('Red','RedActivity'),('Green','GreenActivity'),('Blue','BlueActivity')])
231
232
233
239
240
241 pg= create_plotgroup(name='Connection Fields',category="Basic",
242 doc='Plot the weight strength in each ConnectionField of a specific unit of a Sheet.',
243 pre_plot_hooks=[update_connectionfields],
244 plot_immediately=True, normalize='Individually', situate=True)
245 pg.add_plot('Connection Fields',[('Strength','Weights')])
246
247
248
250 """A callable Parameterized command for measuring or plotting units from a Projection."""
251
252
253 pg= create_plotgroup(name='Projection',category="Basic",
254 doc='Plot the weights of an array of ConnectionFields in a Projection.',
255 pre_plot_hooks=[update_projection],
256 plot_immediately=False, normalize='Individually',sheet_coords=True)
257 pg.add_plot('Projection',[('Strength','Weights')])
258
259
260
262 """
263 Add SheetViews for all of the Projections of the ProjectionSheet
264 specified by the sheet parameter, for use in template-based plots.
265 """
266
291
292
293 pg = create_plotgroup(name='Projection Activity',category="Basic",
294 doc='Plot the activity in each Projection that connects to a Sheet.',
295 pre_plot_hooks=[update_projectionactivity.instance()],
296 plot_immediately=True, normalize='Individually',auto_refresh=True)
297 pg.add_plot('Projection Activity',[('Strength','ProjectionActivity')])
298
299
301 """
302 Map receptive fields by reverse correlation.
303
304 Presents a large collection of input patterns, typically pixel
305 by pixel on and off, keeping track of which units in the specified
306 input_sheet were active when each unit in other Sheets in the
307 simulation was active. This data can then be used to plot
308 receptive fields for each unit. Note that the results are true
309 receptive fields, not the connection fields usually presented in
310 lieu of receptive fields, because they take all circuitry in
311 between the input and the target unit into account.
312
313 Note also that it is crucial to set the scale parameter properly when
314 using units with a hard activation threshold (as opposed to a
315 smooth sigmoid), because the input pattern used here may not be a
316 very effective way to drive the unit to activate. The value
317 should be set high enough that the target units activate at least
318 some of the time there is a pattern on the input.
319 """
320 static_parameters = param.List(default=["offset","size"])
321 __abstract = True
322
334
336
337
338 left, bottom, right, top = p.input_sheet.nominal_bounds.lbrt()
339 sheet_density = float(p.input_sheet.nominal_density)
340
341
342 vertical_divisions = (sheet_density * (top - bottom)) - 1
343 horizontal_divisions = (sheet_density * (right - left)) - 1
344
345
346 unit_size = 1.0 / sheet_density
347 half_unit_size = unit_size / 2.0
348 p['size'] = unit_size
349
350
351 y_range = (top - half_unit_size, bottom)
352 x_range = (right - half_unit_size, left)
353
354 return [Feature(name="x", range=x_range, step=float(x_range[1]-x_range[0])/horizontal_divisions),
355 Feature(name="y", range=y_range, step=float(y_range[1]-y_range[0])/vertical_divisions),
356 Feature(name="scale", range=(-p.scale, p.scale), step=p.scale*2)]
357
358 pg = create_plotgroup(name='RF Projection',category='Other',
359 doc='Measure receptive fields.',
360 pre_plot_hooks=[measure_rfs.instance(display=True,
361 pattern_presenter=PatternPresenter(RawRectangle(size=0.01,aspect_ratio=1.0)))],
362 normalize='Individually')
363
364 pg.add_plot('RFs',[('Strength','RFs')])
365
366
367
369 """
370 Return the orientation corresponding to the given direction.
371
372 Wraps the value to be in the range [0,pi), and rounds it slightly
373 so that wrapped values are precisely the same (to avoid biases
374 caused by vector averaging with keep_peak=True).
375
376 Note that in very rare cases (1 in 10^-13?), rounding could lead
377 to different values for a wrapped quantity, and thus give a
378 heavily biased orientation map. In that case, just choose a
379 different number of directions to test, to avoid that floating
380 point boundary.
381 """
382 return round(((dict(current_values)['direction'])+(pi/2)) % pi,13)
383
384
385
387 """
388 Measure preferences for sine gratings in various combinations.
389 Can measure orientation, spatial frequency, spatial phase,
390 ocular dominance, horizontal phase disparity, color hue, motion
391 direction, and speed of motion.
392
393 In practice, this command is useful for any subset of the possible
394 combinations, but if all combinations are included, the number of
395 input patterns quickly grows quite large, much larger than the
396 typical number of patterns required for an entire simulation.
397 Thus typically this command will be used for the subset of
398 dimensions that need to be evaluated together, while simpler
399 special-purpose routines are provided below for other dimensions
400 (such as hue and disparity).
401 """
402
403 num_ocularity = param.Integer(default=1,bounds=(1,None),softbounds=(1,3),doc="""
404 Number of ocularity values to test; set to 1 to disable or 2 to enable.""")
405
406 num_disparity = param.Integer(default=1,bounds=(1,None),softbounds=(1,48),doc="""
407 Number of disparity values to test; set to 1 to disable or e.g. 12 to enable.""")
408
409 num_hue = param.Integer(default=1,bounds=(1,None),softbounds=(1,48),doc="""
410 Number of hues to test; set to 1 to disable or e.g. 8 to enable.""")
411
412 num_direction = param.Integer(default=0,bounds=(0,None),softbounds=(0,48),doc="""
413 Number of directions to test. If nonzero, overrides num_orientation,
414 because the orientation is calculated to be perpendicular to the direction.""")
415
416 num_speeds = param.Integer(default=4,bounds=(0,None),softbounds=(0,10),doc="""
417 Number of speeds to test (where zero means only static patterns).
418 Ignored when num_direction=0.""")
419
420 max_speed = param.Number(default=2.0/24.0,bounds=(0,None),doc="""
421 The maximum speed to measure (with zero always the minimum).""")
422
423 subplot = param.String("Orientation")
424
425
427
428
429 features = \
430 [Feature(name="frequency",values=p.frequencies)]
431
432 if p.num_direction==0: features += \
433 [Feature(name="orientation",range=(0.0,pi),step=pi/p.num_orientation,cyclic=True)]
434
435 features += \
436 [Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True)]
437
438 if p.num_ocularity>1: features += \
439 [Feature(name="ocular",range=(0.0,1.0),step=1.0/p.num_ocularity)]
440
441 if p.num_disparity>1: features += \
442 [Feature(name="phasedisparity",range=(0.0,2*pi),step=2*pi/p.num_disparity,cyclic=True)]
443
444 if p.num_hue>1: features += \
445 [Feature(name="hue",range=(0.0,1.0),step=1.0/p.num_hue,cyclic=True)]
446
447 if p.num_direction>0 and p.num_speeds==0: features += \
448 [Feature(name="speed",values=[0],cyclic=False)]
449
450 if p.num_direction>0 and p.num_speeds>0: features += \
451 [Feature(name="speed",range=(0.0,p.max_speed),step=float(p.max_speed)/p.num_speeds,cyclic=False)]
452
453 if p.num_direction>0:
454
455 dr = Feature(name="direction",range=(0.0,2*pi),step=2*pi/p.num_direction,cyclic=True)
456 or_values = list(set([compute_orientation_from_direction([("direction",v)]) for v in dr.values]))
457 features += [dr, \
458 Feature(name="orientation",range=(0.0,pi),values=or_values,cyclic=True,
459 compute_fn=compute_orientation_from_direction)]
460
461 return features
462
463
464
466 """Measure an orientation preference map by collating the response to patterns."""
467
468 subplot = param.String("Orientation")
469
471 return [Feature(name="frequency",values=p.frequencies),
472 Feature(name="orientation",range=(0.0,pi),step=pi/p.num_orientation,cyclic=True),
473 Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True)]
474
475
476 pg= create_plotgroup(name='Orientation Preference',category="Preference Maps",
477 doc='Measure preference for sine grating orientation.',
478 pre_plot_hooks=[measure_sine_pref.instance()])
479 pg.add_plot('Orientation Preference',[('Hue','OrientationPreference')])
480 pg.add_plot('Orientation Preference&Selectivity',
481 [('Hue','OrientationPreference'), ('Confidence','OrientationSelectivity')])
482 pg.add_plot('Orientation Selectivity',[('Strength','OrientationSelectivity')])
483 pg.add_plot('Phase Preference',[('Hue','PhasePreference')])
484 pg.add_plot('Phase Selectivity',[('Strength','PhaseSelectivity')])
485 pg.add_static_image('Color Key','command/or_key_white_vert_small.png')
486
487
488 pg= create_plotgroup(name='Spatial Frequency Preference',category="Preference Maps",
489 doc='Measure preference for sine grating orientation and frequency.',
490 pre_plot_hooks=[measure_sine_pref.instance()])
491 pg.add_plot('Spatial Frequency Preference',[('Strength','FrequencyPreference')])
492 pg.add_plot('Spatial Frequency Selectivity',[('Strength','FrequencySelectivity')])
493
494
495
497 """Measure an ocular dominance preference map by collating the response to patterns."""
498
500 return [Feature(name="frequency",values=p.frequencies),
501 Feature(name="orientation",range=(0.0,pi),step=pi/p.num_orientation,cyclic=True),
502 Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True),
503 Feature(name="ocular",range=(0.0,1.0),values=[0.0,1.0])]
504
505 pg= create_plotgroup(name='Ocular Preference',category="Preference Maps",
506 doc='Measure preference for sine gratings between two eyes.',
507 pre_plot_hooks=[measure_sine_pref.instance()])
508 pg.add_plot('Ocular Preference',[('Strength','OcularPreference')])
509 pg.add_plot('Ocular Selectivity',[('Strength','OcularSelectivity')])
510
511
512
513
515 """Measure a phase disparity preference map by collating the response to patterns."""
516
517 num_disparity = param.Integer(default=12,bounds=(1,None),softbounds=(1,48),
518 doc="Number of disparity values to test.")
519
520 orientation = param.Number(default=pi/2,softbounds=(0.0,2*pi),doc="""
521 Orientation of the test pattern; typically vertical to measure
522 horizontal disparity.""")
523
524 static_parameters = param.List(default=["orientation","scale","offset"])
525
527 return [Feature(name="frequency",values=p.frequencies),
528 Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True),
529 Feature(name="phasedisparity",range=(0.0,2*pi),step=2*pi/p.num_disparity,cyclic=True)]
530
531
532 pg= create_plotgroup(name='PhaseDisparity Preference',category="Preference Maps",doc="""
533 Measure preference for sine gratings at a specific orentation differing in phase
534 between two input sheets.""",
535 pre_plot_hooks=[measure_phasedisparity.instance()],normalize='Individually')
536 pg.add_plot('PhaseDisparity Preference',[('Hue','PhasedisparityPreference')])
537 pg.add_plot('PhaseDisparity Preference&Selectivity',
538 [('Hue','PhasedisparityPreference'), ('Confidence','PhasedisparitySelectivity')])
539 pg.add_plot('PhaseDisparity Selectivity',[('Strength','PhasedisparitySelectivity')])
540 pg.add_static_image('Color Key','command/disp_key_white_vert_small.png')
541
542
543
545 """Measure a direction preference map by collating the response to patterns."""
546
547 num_phase = param.Integer(default=12)
548
549 num_direction = param.Integer(default=6,bounds=(1,None),softbounds=(1,48),
550 doc="Number of directions to test.")
551
552 num_speeds = param.Integer(default=4,bounds=(0,None),softbounds=(0,10),doc="""
553 Number of speeds to test (where zero means only static patterns).""")
554
555 max_speed = param.Number(default=2.0/24.0,bounds=(0,None),doc="""
556 The maximum speed to measure (with zero always the minimum).""")
557
558 subplot = param.String("Direction")
559
561
562 dr = Feature(name="direction",range=(0.0,2*pi),step=2*pi/p.num_direction,cyclic=True)
563 or_values = list(set([compute_orientation_from_direction([("direction",v)]) for v in dr.values]))
564
565 return [Feature(name="speed",values=[0],cyclic=False) if p.num_speeds is 0 else
566 Feature(name="speed",range=(0.0,p.max_speed),step=float(p.max_speed)/p.num_speeds,cyclic=False),
567 Feature(name="frequency",values=p.frequencies),
568 Feature(name="direction",range=(0.0,2*pi),step=2*pi/p.num_direction,cyclic=True),
569 Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True),
570 Feature(name="orientation",range=(0.0,pi),values=or_values,cyclic=True,
571 compute_fn=compute_orientation_from_direction)]
572
573
574 pg= create_plotgroup(name='Direction Preference',category="Preference Maps",
575 doc='Measure preference for sine grating movement direction.',
576 pre_plot_hooks=[measure_dr_pref.instance()])
577 pg.add_plot('Direction Preference',[('Hue','DirectionPreference')])
578 pg.add_plot('Direction Preference&Selectivity',[('Hue','DirectionPreference'),
579 ('Confidence','DirectionSelectivity')])
580 pg.add_plot('Direction Selectivity',[('Strength','DirectionSelectivity')])
581 pg.add_plot('Speed Preference',[('Strength','SpeedPreference')])
582 pg.add_plot('Speed Selectivity',[('Strength','SpeedSelectivity')])
583 pg.add_static_image('Color Key','command/dr_key_white_vert_small.png')
584
585
586
588 """Measure a hue preference map by collating the response to patterns."""
589
590 num_phase = param.Integer(default=12)
591
592 num_hue = param.Integer(default=8,bounds=(1,None),softbounds=(1,48),
593 doc="Number of hues to test.")
594
595 subplot = param.String("Hue")
596
597
598 static_parameters = param.List(default=[])
599
601 return [Feature(name="frequency",values=p.frequencies),
602 Feature(name="orientation",range=(0,pi),step=pi/p.num_orientation,cyclic=True),
603 Feature(name="hue",range=(0.0,1.0),step=1.0/p.num_hue,cyclic=True),
604 Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True)]
605
606
607 pg= create_plotgroup(name='Hue Preference',category="Preference Maps",
608 doc='Measure preference for colors.',
609 pre_plot_hooks=[measure_hue_pref.instance()],normalize='Individually')
610 pg.add_plot('Hue Preference',[('Hue','HuePreference')])
611 pg.add_plot('Hue Preference&Selectivity',[('Hue','HuePreference'), ('Confidence','HueSelectivity')])
612 pg.add_plot('Hue Selectivity',[('Strength','HueSelectivity')])
613
614
615
616
617 gaussian_corner = topo.pattern.basic.Composite(
618 operator = maximum, generators = [
619 topo.pattern.basic.Gaussian(size = 0.06,orientation=0,aspect_ratio=7,x=0.3),
620 topo.pattern.basic.Gaussian(size = 0.06,orientation=pi/2,aspect_ratio=7,y=0.3)])
621
622
624 """Measure a corner preference map by collating the response to patterns."""
625
626 scale = param.Number(default=1.0)
627
628 divisions=param.Integer(default=10)
629
630 pattern_presenter = param.Callable(PatternPresenter(gaussian_corner,apply_output_fns=False,duration=1.0))
631
632 x_range=param.NumericTuple((-1.2,1.2))
633
634 y_range=param.NumericTuple((-1.2,1.2))
635
636 num_orientation = param.Integer(default=4,bounds=(1,None),softbounds=(1,24),
637 doc="Number of orientations to test.")
638
639
640 static_parameters = param.List(default=["scale","offset"])
641
643 width =1.0*p.x_range[1]-p.x_range[0]
644 height=1.0*p.y_range[1]-p.y_range[0]
645 return [Feature(name="x",range=p.x_range,step=width/p.divisions),
646 Feature(name="y",range=p.y_range,step=height/p.divisions),
647 Feature(name="orientation",range=(0,2*pi),step=2*pi/p.num_orientation,cyclic=True)]
648
649
650 pg= create_plotgroup(name='Corner OR Preference',category="Preference Maps",
651 doc='Measure orientation preference for corner shape (or other complex stimuli that cannot be represented as fullfield patterns).',
652 pre_plot_hooks=[measure_corner_or_pref.instance()],
653 normalize='Individually')
654 pg.add_plot('Corner Orientation Preference',[('Hue','OrientationPreference')])
655 pg.add_plot('Corner Orientation Preference&Selectivity',[('Hue','OrientationPreference'),
656 ('Confidence','OrientationSelectivity')])
657 pg.add_plot('Corner Orientation Selectivity',[('Strength','OrientationSelectivity')])
658
659
661 """Generate the preference map for angle shapes, by collating the response to patterns."""
662
663 scale = param.Number(default=1.0)
664
665 size = param.Number(default=0.2)
666
667 positions = param.Integer(default=6)
668
669 x_range = param.NumericTuple((-1.0, 1.0))
670
671 y_range = param.NumericTuple((-1.0, 1.0))
672
673 num_or = param.Integer(default=4,bounds=(1,None),softbounds=(1,24),doc=
674 "Number of orientations to test.")
675
676 angle_0 = param.Number(default=0.25*pi,bounds=(0.0,pi),softbounds=(0.0,0.5*pi),doc=
677 "First angle to test.")
678
679 angle_1 = param.Number(default=0.75*pi,bounds=(0.0,pi),softbounds=(0.5*pi,pi),doc=
680 "Last angle to test.")
681
682 num_angle=param.Integer(default=4,bounds=(1,None),softbounds=(1,12),doc=
683 "Number of angles to test.")
684
685 key_img_fname=param.Filename(default='command/key_angles.png',doc=
686 "Name of the file with the image used to code angles with hues.")
687
688 pattern_presenter=PatternPresenter(GaussiansCorner(aspect_ratio=4.0,cross=0.85),apply_output_fns=False,duration=1.0)
689
690 static_parameters = param.List( default=[ "size", "scale", "offset" ] )
691
692
693
695 """Return the list of features to vary, generate hue code static image"""
696 x_step = ( p.x_range[1]-p.x_range[0] ) / float( p.positions - 1 )
697 y_step = ( p.y_range[1]-p.y_range[0] ) / float( p.positions - 1 )
698 o_step = 2.0*pi / p.num_or
699 if p.angle_0 < p.angle_1:
700 angle_0 = p.angle_0
701 angle_1 = p.angle_1
702 else:
703 angle_0 = p.angle_1
704 angle_1 = p.angle_0
705 a_range = ( angle_0, angle_1 )
706 a_step = ( angle_1 - angle_0 ) / float( p.num_angle - 1 )
707 self._make_key_image( p )
708 return [
709 Feature( name = "x", range = p.x_range, step = x_step ),
710 Feature( name = "y", range = p.y_range, step = y_step ),
711 Feature( name = "orientation", range = (0, 2*pi), step = o_step, cyclic = True ),
712 Feature( name = "angle", range = a_range, step = a_step,
713 value_offset = - angle_0,
714 value_multiplier = 1. / ( angle_1 - angle_0 ) )
715 ]
716
717
719 """Generate the image with keys to hues used to code angles
720 the image is saved on-the-fly, in order to fit the current
721 choice of angle range
722 """
723 width = 60
724 height = 300
725 border = 6
726 n_a = 7
727 angle_0 = p.angle_0
728 angle_1 = p.angle_1
729 a_step = 0.5 * ( angle_1 - angle_0 ) / float( n_a )
730 x_0 = border
731 x_1 = ( width - border ) / 2
732 x_a = x_1 + 2 * border
733 y_use = height - 2 * border
734 y_step = y_use / float( n_a )
735 y_d = int( float( 0.5 * y_step ) )
736 y_0 = border + y_d
737 l = 15
738
739 hues = [ "hsl(%2d,100%%,50%%)" % h for h in range( 0, 255, 255 / n_a) ]
740 angles = [ 0.5*angle_0 + a_step * a for a in range( n_a ) ]
741 y_pos = [ int( round( y_0 + y * y_step ) ) for y in range( n_a ) ]
742 deltas = [ ( int( round( l * cos( a ) ) ), int( round( l * sin( a ) ) ) )
743 for a in angles ]
744 lb_img = Image.new( "RGB", ( width, height ), "white" )
745 dr_img = ImageDraw.Draw( lb_img )
746
747 for h, y, d in zip( hues, y_pos, deltas ):
748 dr_img.rectangle( [ ( x_0, y - y_d ), ( x_1, y + y_d ) ], fill = h )
749 dr_img.line( [ ( x_a, y ), ( x_a + d[ 0 ], y + d[ 1 ] ) ], fill = "black" )
750 dr_img.line( [ ( x_a, y ), ( x_a + d[ 0 ], y - d[ 1 ] ) ], fill = "black" )
751
752 lb_img.save( p.key_img_fname )
753
754
755
756
757 pg= create_plotgroup(name='Corner Angle Preference',category="Preference Maps",
758 doc='Measure preference for angles in corner shapes',
759 normalize='Individually')
760 pg.pre_plot_hooks=[ measure_corner_angle_pref.instance() ]
761 pg.add_plot('Corner Angle Preference',[('Hue','AnglePreference')])
762 pg.add_plot('Corner Angle Preference&Selectivity',[('Hue','AnglePreference'),
763 ('Confidence','AngleSelectivity')])
764 pg.add_plot('Corner Angle Selectivity',[('Strength','AngleSelectivity')])
765 pg.add_plot('Corner Orientation Preference',[('Hue','OrientationPreference')])
766 pg.add_plot('Corner Orientation Preference&Selectivity',[('Hue','OrientationPreference'),
767 ('Confidence','OrientationSelectivity')])
768 pg.add_plot('Corner Orientation Selectivity',[('Strength','OrientationSelectivity')])
769 pg.add_static_image( 'Hue Code', measure_corner_angle_pref.instance().key_img_fname )
770
771
772
788
789
790 pg= create_plotgroup(name='Frequency Preference and Selectivity',category="Preference Maps",
791 pre_plot_hooks=[measure_frequency_pref.instance()], normalize='Individually',
792 doc='Measure best frequency preference and selectivity for auditory neurons.')
793
794 pg.add_plot('[Frequency Preference]', [('Strength','YPreference')])
795 pg.add_plot('[Frequency Selectivity]', [('Strength','YSelectivity')])
796
797
798 import types
799 __all__ = list(set([k for k,v in locals().items()
800 if isinstance(v,types.FunctionType) or
801 (isinstance(v,type) and issubclass(v,ParameterizedFunction))
802 and not v.__name__.startswith('_')]))
803