Konstantin Sering, Nora Umbach, and Dominic
Transcription
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 konstantin.sering@gmail.com 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/