Konstantin Sering, Nora Umbach, and Dominic

Achrolab – Using non-Python supported
devices for a vision lab
Konstantin Sering, Nora Umbach, and Dominic Wabersich
¨ bingen, Tu
¨ bingen, Germany
Department of Psychology, University of Tu
[email protected]
Introduction
The eyeone-Wrapper
We are psychologists running a vision laboratory focussing on achromatic
(grayscale) human perception. In our psychology education we get very
limited insight in mathematics and computer science. Therefore we want
to run our laboratory with one easy programming language, which has
modern features and is free and open source software.
Setup and Goals
Our goal was to run our whole laboratory setup (Figure 1) with Python.
This means we wanted an easy to use interface to all hardware we use in
this laboratory. We chose Python because it is an easy to learn and very
flexible and powerful programming language.
Figure 1: The setup: A booth with white walls and a small rectangular cut-out in the front. Behind the cut-out is a small black box
with a grayscale monitor. Red, green, and blue fluorescent tubes to
illuminate the booth.
The technical Goal
We want to make color perception experiments in which the color of the
walls matches the background color of the monitor.
Hardware
The photometer we used is designed to measure colors of print products
and match these to the colors on the monitor. But we want to measure
illuminated surfaces. We wrote a wrapper for the photometer in order to
access all functions of the photometer directly out of Python. The following
listing shows exemplary parts of the wrapper’s class definition.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Photometer
cd )
EyeOne (i1Basic) Pro spectro-photometer (max. luminance 300 m
2
73
74
75
76
from c t y p e s import c d l l , c i n t , c l o n g , c f l o a t , c c h a r p
from e x c e p t i o n s import I O E r r o r , T y p e E r r o r # i f i t f a i l s t o l o a d d l l
import E y e O n e C o n s t a n t s
c l a s s EyeOne ( o b j e c t ) :
”””
e n c a p s u l a t e s t h e f u n c t i o n s o f t h e EyeOne . d l l .
EyeOne g i v e s you an o b j e c t
a v a i l a b l e . For t h e methods
− I 1 I s C o n n e c t e d −− c h e c k s
− I 1 G e t T r i S t i m u l u s −− g e t
”””
def
EyeOne w i t h t h e f o l l o w i n g Methods
there are ctypes prototypes defined .
i f t h e EyeOne i s c o n n e c t e d
the c o l o r vector
grayscale-Monitor
Monochrome 21.3” Monitor EIZO RadiForce GS320 (high performance
cd )
monitor for x-ray diagnostics, max. luminance 1000 m
2
History of our programs
Most of our programs started as a simple imperative Python script that
executed a bunch of commands. Then we capsulated these commands
in several functions, e. g. to measure a color with our photometer. In the
end, most of our functions were capsulated into classes because most of
them could be intuitively grouped and needed all access to the tubes,
the monitor, and/or the photometer.
i n i t ( s e l f , dummy=F a l s e ) :
s e l f . dummy = dummy
i f not s e l f . dummy :
try :
s e l f . e y e o n e = c d l l . EyeOne
except :
r a i s e ( I O E r r o r , ’ ’ ’ Cannot l o a d EyeOne . d l l . P l e a s e i n s t a l l
EyeOne . d l l b e f o r e r u n n i n g t h i s a g a i n o r u s e
EyeOne (dummy=True ) t o c r e a t e EyeOne o b j e c t . ’ ’ ’ )
## i n i t i a l i z e c f u n c t i o n s and s e t p r o t o t y p e s
# I1 IsConnected
s e l f . e y e o n e . I 1 I s C o n n e c t e d . r e s t y p e = c i n t #enum I 1 E r r o r T y p e
s e l f . eye one . I1 IsConnected . argtypes = [ ]
s e l f . eye one . I1 IsConnected . doc = s e l f . I1 IsConnected . doc
s e l f . I1 IsConnected = s e l f . eye one . I1 IsConnected
# I1 GetTriStimulus
s e l f . e y e o n e . I 1 G e t T r i S t i m u l u s . r e s t y p e = c i n t #enum I 1 E r r o r T y p e
s e l f . eye one . I 1 G e t T r i S t i m u l u s . a r g t y p e s = [ c f l o a t ∗ EyeOneConstants .
TRISTIMULUS SIZE , c l o n g ]
s e l f . eye one . I1 GetTriStimulus . doc = s e l f . I1 GetTriStimulus . doc
s e l f . I1 GetTriStimulus = s e l f . eye one . I1 GetTriStimulus
else :
# s e t s t a n d a r d v a l u e s f o r dummy
s e l f . calibrated = False
s e l f . measurement triggered = False
C-Headers and Python
######################################################################
#
f u n c t i o n d e f i n i t i o n s below are o n l y c a l l e d i f o b j e c t i s
#
#
i n i t i a l i z e d w i t h dummy=True
#
#
doc−s t r i n g s a r e a l s o u s e d f o r t h e d l l −f u n c t i o n s
#
######################################################################
def I 1 I s C o n n e c t e d ( s e l f ) :
”””
t e s t s i f t h e EyeOne i s c o n n e c t e d .
R e t u r n s enum I 1 E r r o r T y p e ( c i n t ) . e N o E r r o r ( 0 ) , i f c o n n e c t e d .
e D e v i c e N o t C o n n e c t e d ( 2 ) , i f no EyeOne i s p r e s e n t .
For d e t a i l s s e e E y e O n e C o n s t a n t s . py
”””
#o n l y c a l l e d i f s e l f . dummy==True
return EyeOneConstants . eNoError
def I 1 G e t T r i S t i m u l u s ( s e l f , t r i s t i m u l u s , i n d e x ) :
”””
g e t s t h e c o l o r v e c t o r o f a p r e v i o u s t r i g g e r e d measurement .
I n p u t : t r i s t i m u l u s i s a c f l o a t a r r a y w i t h TRISTIMULUS SIZE e l e m e n t s .
i n d e x i s a c i n t . The c o l o r v a l u e s a r e s t o r e d i n t h e a r r a y .
R e t u r n s enum I 1 E r r o r T y p e ( c i n t ) . e N o E r r o r ( 0 ) , i f no e r r o r
o c c u r s ; e N o D a t a A v a i l a b l e ( 8 ) , i f no measurement h a s been
t r i g g e r e d or i f the s p e c i f i e d i n d e x i s out of range .
For d e t a i l s s e e E y e O n e C o n s t a n t s . py
”””
#o n l y c a l l e d i f s e l f . dummy==True
i f not i s i n s t a n c e ( t r i s t i m u l u s , c f l o a t ∗ E y e O n e C o n s t a n t s . TRISTIMULUS SIZE )
:
r a i s e ( T y p e E r r o r , ” t r i s t i m u l u s h a s t o be i n s t a n c e o f c f l o a t ∗
TRISTIMULUS SIZE” )
i f s e l f . measurement triggered :
return EyeOneConstants . eNoError
else :
return EyeOneConstants . eNoDataAvailable
Fluorescent Tubes
Four sets each with three Osram T8 fluorescent tubes Red (58W / Color
60), Green (58W / Color 66), Blue (58W / Color 67), dimmable, driven
by IODA-PCI12K4EXTENDED PCI multifunction card, 12 bit
When we run an experiment we simply load a colortable object and
select the wanted colors. Then we set the colors of the tubes with a tubes
object. Alternatively, you can also copy and paste the correct values directly
in your experiment file.
Figure 3: Description of the I1 GetTriStimulus function in the
EyeOne C SDK manual.
There were two c-header files shipped with the SDK. These header files
define the prototypes of the functions and some global variables.
1 I1 ErrorType I1 GetTriStimulus ( float t r i s t i m u l u s [
TRISTIMULUS SIZE ] , long i n d e x ) ;
2 typedef I 1 E r r o r T y p e (∗ F P t r I 1 G e t T r i S t i m u l u s ) ( f l o a t [
TRISTIMULUS SIZE ] , long ) ;
1 #define TRISTIMULUS SIZE 3
We translated this into Python with ctypes. See the long listing for more
details.
1 # I1 GetTriStimulus
2 I 1 G e t T r i S t i m u l u s . r e s t y p e = c i n t #enum I 1 E r r o r T y p e
3 I 1 G e t T r i S t i m u l u s . a r g t y p e s = [ c f l o a t ∗ EyeOneConstants .
TRISTIMULUS SIZE , c l o n g ]
4 I1 GetTriStimulus . doc = s e l f . I1 GetTriStimulus . doc
1 TRISTIMULUS SIZE = 3
Dummy Mode
You can create an EyeOne or a wasco class object in dummy mode.
This object does not need access to the real hardware, but behaves as
if it had access to the hardware.
In order to implement this dummy mode, all functions of the dll file
are defined in the class. When you load a real EyeOne Pro photometer all these member functions are overwritten with the dll prototype
definitions while the object gets initialized.
cdll vs. windll
Under MS Windows there are two different kinds of dll files. A dll file
can export its functions in cdll format or in windll format. If you load
the dll file in the wrong format Python gives you, for a beginner, strange
exceptions. It took us two weeks to figure out what was wrong.
The achrolab-Module
We organized our code in three Python modules (Figure 2). The access
to our hardware is capsulated in two stand alone modules, eyeone for
the photometer, and wasco for the multifunctioning card controlling the
tubes. In addition, we wrote some easy to use classes which give us readable code and which hide all the technical details not relevant for running
the experiments.
The achrolab module gives the functionality to automatically measure
the colors of the monitor and matches the luminance of the walls to these
colors.
iterativeColorTubes.py
colorentry.py
colortable.py
monitor.py
devtubes.py
tubes.py
achrolab
eyeone
EyeOne.py
EyeOneConstants.py
Color Matching
Before we could start to do automatic color matching we needed to control the fluorescent tubes and the photometer out of Python. After we
achieved both, we wrote a small program, that does the color matching
automatically in five steps:
1. We measure all colors of our achromatic monitor and store them in an
object of our class ColorTable.
2. We change the setup to measure the color of the wall and calibrate the
fluorescent tubes. In this step we try to find inverse functions for all
three color channels.
3. We set the color of the fluorescent tubes to that point in the color space
which is given by the inverse functions.
4. We adjust the color with the inverse functions in an iterative manner.
5. We fine-tune the color with a gradient like method.
WascoConstants.py
Figure 2: Most important files and folder structure of the achrolab
module with the two submodules eyeone and wasco.
Next Steps:
• Make the matching procedure faster
• Write test cases and build up a unit testing framework for the achrolab
module and the two wrappers
• Separate measurement and matching code from the ColorTable class,
in order to get a small ColorTable class which exclusively stores all
information of the colors
• Achieve control over 10 bit of our grayscale monitor out of psychopy
Nice to have:
• Emulate the full functionality of the photometer and the wasco card in
dummy mode
References
wasco
wasco.py
Work in Progress
Color Management
In our psychophysical experiments, we want to use our measured colors.
In order to do this, we have written the class ColorTable. ColorTable
stores all configurations of each single color; i. e. the values of the fluorescent tubes and the value of the monitor.
http://github.com/derNarr/achrolab/
http://github.com/derNarr/eyeone/
http://github.com/derNarr/wasco/
http://github.com/derNarr/achrolabutils/
http://www.ctypes.org/
http://www.psychopy.org/