[visionegg] Re: Stimuli updates not rendering
- From: Thorsten Pfister <pfister@xxxxxxxxxxxxxxx>
- To: visionegg@xxxxxxxxxxxxx
- Date: Sat, 27 Oct 2007 12:23:17 +0200
Hi,
thanks for the input. Unfortunately I made no progress, so I'll post the
complete code.
First of all I'd like to explain the idea of the code a bit, so it won't
become too confusing.
This code is intended to be a support for scientists in our group who
have little to no
computer science background (i.e. are not able to use VisionEgg
directly). They will be
able to set up experiments by defining them in relatively simple and
sparse config files.
Consequently, ease of use will be a major factor, which also means that
some of the decisions
will be made for them.
The current status is, that this is very work in progress code - I'd
like to rough out
the general idea before making it robust (no warning/error conditions,
not every
kind of stimuli supported, maybe some bugs hidden in there - the
supplied config file
works though if you'd like to give it a test run; refactoring and
thorough testing will be
a major point later on). Below you'll find the code with some comments
stripped out as they
are mainly concerned with stuff that is on the todo-list - so if there
are any questions
please feel free to ask. Please disregard unused variables at this point
they are once
again serving as a reminder for me on the todo-list. =)
#------------------------------experiment.ini
(config)---------------------------------------
experiment_duration = 5
background_color = 0.0, 0.0, 0.0, 0.0
stimuli = stim1, stim2, stim3
stimuli_position_unit = percent
stimuli_horizontal_positions = 20, 50, 80
stimuli_vertical_positions = 20, 50, 80
stimuli_frequencies = 6, 12, 30
[ stim1 ]
class = Circle
radius = 25.0
color = 255, 0, 0
[ stim2 ]
class = Rectangle
size = 50, 50
color = 255, 0, 255, 1
orientation = 0
[ stim3 ]
class = Arrow
size = 64.0, 16.0
color = 1, 1, 1
orientation = 45
# --------------------------- code
----------------------------------------------
import VisionEgg
VisionEgg.start_default_logging()
VisionEgg.watch_exceptions()
from VisionEgg.Core import *
from VisionEgg.FlowControl import Presentation
from VisionEgg.MoreStimuli import *
from configobj import ConfigObj
import sys
import getopt
class SsvepStimulator:
def __init__(self):
self.verbosity = 1
# a list of stimuli as later defined by the user
# the added s is deliberate as to easier avoid potential
# namespace classes
self.stimulis = []
# direct access to the configuration
self.config = ConfigObj()
# initializing a screen for later use
self.screen = get_default_screen()
# initialize lists for vertical and horizontal stimuli
# positioning coordinates
self.stim_vert_pos = []
self.stim_hori_pos = []
# initialize a list which will later hold frequency values
# converted to relative intervals in seconds
self.stimulis_freq_sec = []
self.viewport = Viewport(screen=self.screen)
def configureStimuli(self, stimulus):
"""
The 'configureStimuli' function automagically configures
Stimuli objects according to user defined settings.
Some settings are not available to the user and are set by
DEFAULT. Defaults may overlap between this function and
VisionEgg. Nevertheless it is important to set them as to
avoid freak errors if at any time VisionEggs defaults change.
VisionEggs depreceated settings were disregarded.
User set options from the experiment configuratiion arrive in
string format. So necessary these values were converted to
their respective types.
Current supported stimuli:
Circle
Arrow
Rectangle
"""
if self.config[stimulus]['class'] == 'Circle':
stimulus = FilledCircle(
# DEFAULT: standard anchor of 'center' which is
# not redefinable by the user
anchor = 'center',
# DEFAULT: draw the stimulus
on = True,
# taking the radius from the config and
# converting it to float
radius =\
float(self.config[stimulus]['radius']),
# taking the color values from the config and
# converting it to float due to the mapping it
# works with RGB and RGBA
color = map(float,\
self.config[stimulus]['color']),
# just take the position as it has already
# been computed
# we are dealing with a tuple here for which
# we get the already computed positions by
# identifying them according to their index
# within the stimulis
position = (self.stim_hori_pos[self.config[\
'stimuli'].index(stimulus)], \
self.stim_vert_pos[self.config[\
'stimuli'].index(stimulus)]),
num_triangles = 500
)
return stimulus
elif self.config[stimulus]['class'] == 'Arrow':
stimulus = Arrow(
# DEFAULT: standard anchor of 'center' which is
# not redefinable by the user
anchor = 'center',
# DEFAULT: anti aliasing is on and not
# changeable by the user
anti_aliasing = True,
# DEFAULT: draw the stimulus
on = True,
# take the orientation as defined by the user
# converting it to float on the go
orientation = float(\
self.config[stimulus]['orientation']),
# taking the color values from the config and
# converting it to float due to the mapping it
# works with RGB and RGBA
color = map(float,\
self.config[stimulus]['color']),
# just take the position as it has already
# been computed
# we are dealing with a tuple here for which
# we get the already computed positions by
# identifying them according to their index
# within the stimulis
position = (self.stim_hori_pos[self.config[\
'stimuli'].index(stimulus)], \
self.stim_vert_pos[self.config[\
'stimuli'].index(stimulus)]),
# take the size as specified by the user
size = map(float, self.config[stimulus]['size'])
)
return stimulus
elif self.config[stimulus]['class'] == 'Rectangle':
stimulus = Target2D(
# DEFAULT: standard anchor of 'center' which is
# not redefinable by the user
anchor = 'center',
# DEFAULT: anti aliasing is on and not
# changeable by the user
anti_aliasing = True,
# DEFAULT: draw the stimulus
on = True,
# take the orientation as defined by the user
# converting it to float on the go
orientation = float(\
self.config[stimulus]['orientation']),
# taking the color values from the config and
# converting it to float due to the mapping it
# works with RGB and RGBA
color = map(float,\
self.config[stimulus]['color']),
# just take the position as it has already
# been computed
position = (self.stim_hori_pos[self.config[\
'stimuli'].index(stimulus)], \
self.stim_vert_pos[self.config[\
'stimuli'].index(stimulus)]),
# take the size as specified by the user
size = map(float, self.config[stimulus]['size'])
)
return stimulus
else:
pass # TODO error - unknown stimulus
def frequencies_to_sec(self, stimuli_frequencies):
monitor_refresh = VisionEgg.config.VISIONEGG_MONITOR_REFRESH_HZ
if filter((lambda x: x > monitor_refresh), stimuli_frequencies):
sys.exit()
else:
# check for every specified frequency
for frequency in stimuli_frequencies:
# if we are theoretically able to render it
if (monitor_refresh % frequency) == 0:
# we convert HZ to second intervals
self.stimulis_freq_sec.append(\
1 / frequency)
else:
pass
def frequency_logic(self, t):
# I had some logic in here before but stripped it out
# for now as it wasn't refined at all - for the problem
# it should just suffice to render nothing which it
# unfortunately doesn't do
self.stimulis[\
self.stimulis_freq_sec.index(\
interval)].__setattr__('on', False)
# print self.stimulis[\
# self.stimulis_freq_sec.index(\
# interval)].__getattribute__('on')
# I am returning the list of stimuli as I am of the impression
# that the controller needs this
return self.stimulis
def executeStimulator(self, config_file, log_file, verbosity_level):
"""
This function holds the core logic of the SSVEP stimulator.
Namely handling configuration options as specified by the user
in the experiment config. Creating the stimuli and rendering
them on screen.
"""
# connecting to the specified config_file
self.config = ConfigObj(config_file)
# compute positioning of stimuli according to a user specified
# list of either percentul or pixel values
# NOTE: percentual values are a service to the user, because
# of a pixels nature of non-divisionability, rounding errors
# may occur
if self.config['stimuli_position_unit'] == 'percent':
# compute vertical positions of stimuli according to
# their respective percentual value - the position
# will have to be in pixel values
self.stim_vert_pos = map(round, map(
(lambda x: float(self.screen.size[1]) / 100.0 \
* x), map(float, \
self.config['stimuli_vertical_positions'])))
# do the same for horizontal stimuli positions
self.stim_hori_pos = map(round, map(
(lambda x: float(self.screen.size[0]) / 100.0 \
* x), map(float, \
self.config['stimuli_horizontal_positions'])))
elif self.config['stimuli_position_unit'] == 'pixel':
# if the user decided to give us raw pixel values we
# are happy and simply copy them
self.stim_vert_pos = map(float, \
self.config['stimuli_vertical_positions'])
self.stim_hori_pos = map(float, \
self.config['stimuli_horizontal_positions'])
else:
pass
# use the list of stimuli definitions from the config file
self.stimulis = self.config['stimuli']
# and create actual Stimulus objects according their individual
# specification
self.stimulis = map(self.configureStimuli, self.stimulis)
# using the background color as defined in the config
self.screen.parameters.bgcolor = map(float,\
self.config['background_color'])
self.frequencies_to_sec(\
map(float, self.config['stimuli_frequencies']))
self.viewport = Viewport(screen=self.screen,\
stimuli=self.stimulis)
frequency_controller = FunctionController(\
during_go_func = self.frequency_logic,\
eval_frequency = Controller.EVERY_FRAME,\
temporal_variables = \
Controller.TIME_SEC_SINCE_GO)
presentation = Presentation( go_duration=(\
float(self.config['experiment_duration']),\
'seconds'),\
viewports=[self.viewport])
presentation.add_controller(self.viewport, 'stimuli',\
frequency_controller)
presentation.go()
def main(argv):
try:
# grab command line arguments
opts, args = getopt.getopt(argv, "hc:l:v:", \
["help", "config=", "log=", "verbosity="])
except getopt.GetoptError:
# we encountered a flag/options we could not handle, so we
# explain the proper usage of short/long flags for this program
usage() # TODO code the usage description
# and exit gracefully
sys.exit(2)
log_file = "testlog"
# DEFAULT: log both warning and error messages - more details can be
# found in the class init of SSVEP-Stimulator
verbosity_level = 1
# parse through given command line parameters and act accordingly
for opt, arg in opts:
if opt in ("-h", "--help"):
# if the user request help, show the proper usage
# of flags for this program
usage()
# and exit gracefully
sys.exit()
elif opt in ("-c", "--config"):
# if the users supplies us with a config, take it and
# use it for the experiment
config_file = arg
elif opt in ("-l", "--log"):
# name the log file as wished by the user
log_file = arg
elif opt in ("-v", "--verbosity"):
verbosity_level = arg
# pass the configuration along to the stimulator
# workhorse, after realizing an object of it
ssvepStim = SsvepStimulator()
# we need the name of the config file as string so
# we convert on the fly
ssvepStim.executeStimulator("".join(config_file), "".join(log_file), \
verbosity_level)
if __name__ == "__main__":
# go directly to the main function and let it handle the command line
# arguments - do not start at index zero as this carries the name of the
# script and the current working directory by default
main(sys.argv[1:])
#
--------------------------------------------------------------------------------------------------------------------
To see this code in action simply use: python code_file.py -c experiment.ini
The flow tries to be as straight forward as possible; grab config
options and
set them; grab stimuli definitions and initialize corresponding objects;
start
the stimulation with Hz rates (currently it should just set the
stimulis' 'on'
option to 'False' and not render them - albeit they do get rendered).
Thank you for having a look at the code, if there are any questions
please feel
free to ask. I hope it is not too confusing as there is a lot of code
which is just
concerned with getting values from the experiment config file.
Best regards,
Thorsten Pfister
P.S.: I hope this mail gets threaded right into the list, because I
neither get your
answers nor a daily digest via email and have to check them via the web
interface.
======================================
The Vision Egg mailing list
Archives: http://www.freelists.org/archives/visionegg
Website: http://www.visionegg.org/mailinglist.html
Other related posts: