• Tidak ada hasil yang ditemukan

A Simple OpenGL/GLUT GUI Example

Dalam dokumen Eye Tracking Methodology : Theory and Practice (Halaman 140-144)

Head-Mounted System Calibration

10.2 A Simple OpenGL/GLUT GUI Example

10.1.7

Tet_CallbackFunction

t y p e d e f v o i d (*T e t _ C a l l b a c k F u n c t i o n)(

E T e t _ C a l l b a c k R e a s o n reason ,

v o i d* pData ,

v o i d* p A p p D a t a );

Listing 10.8 Tet_CallbackFunctiondeclaration

The*Tet_CallbackFunction’sETet_CallbackReasonargument will be set toTET_CALLBACK_GAZE_DATAorTET_CALLBACK_TIMER. The optional argu- ment pAppDatacan point to any user-defined structure, and the argumentpData should be cast to(STet_GazeData *)if the reason for the callback was set to TET_CALLBACK_GAZE_DATA(see Listing 10.9 for specification of theSTet_Gaze- Datadata structure).

10.2 A SimpleOpenGL/GLUTGUI Example 113

i n t main (i n t argc , c h a r ** argv ) {

p t h r e a d _ t t h r e a d ;

// object that holds global state ( like s i n g l e t o n ) s t a t e = n e w S t a t e ();

// i n i t i a l i z e GLUT , c r e a t e w i n d o w g l u t I n i t (& argc , argv );

g l u t I n i t D i s p l a y M o d e ( G L U T _ D O U B L E | G L U T _ R G B A | G L U T _ D E P T H );

g l u t C r e a t e W i n d o w ( " Demo " );

// setup GUI c a l l b a c k s and m a i n _ l o o p g l u t D i s p l a y F u n c ( o n _ e x p o s e );

g l u t R e s h a p e F u n c ( o n _ r e s i z e );

g l u t K e y b o a r d F u n c ( o n _ k e y _ p r e s s );

g l u t M o u s e F u n c ( o n _ b u t t o n _ p r e s s );

g l u t M o t i o n F u n c ( o n _ m o t i o n _ n o t i f y );

g l u t I d l e F u n c ( m a i n _ l o o p );

// i n i t i a l i z e mutex for access to global state info p t h r e a d _ m u t e x _ i n i t (& s t a t emutex , NULL );

// create tobii thread ( this sets tobii thread r u n n i n g ) p t h r e a d _ c r e a t e (& thread , NULL ,& t o b i i _ t h r e a d , NULL );

// glut main loop g l u t M a i n L o o p ();

// t h r e a d and m u t e x c l e a n u p p t h r e a d _ c a n c e l ( t h r e a d );

p t h r e a d _ m u t e x _ d e s t r o y (& s t a t em u t e x );

r e t u r n(0);

}

Listing 10.10 GUI initialization and Tobii thread creation

The basic steps of a simple Linux eye tracking application involve:

1. Initializing the GUI, in this caseGLUT; setting up the GUI and eye tracking (ET) threads

2. Specifying the main ET thread and GUI drawing loop

In this example, the application simply draws the real-time gaze point on the blank screen as the user looks at the screen. Three gaze points are rendered: the left and right gaze points (represented by a green and red dot, respectively, although this is not detailed in the given listings), and the average gaze point represented by a blue dot. Also omitted from the listings are two additional feedback disks that are drawn to indicate the user’s head position relative to the display. For brevity,OpenGLcalls are

v o i d m a i n _ l o o p (v o i d) {

// clear screen . . .

// view t r a n s f o r m a t i o n . . .

// o b j e c t t r a n s f o r m a t i o n . . .

if( s t a t es t a t u s . c o n n e c t e d ) { if( s t a t es t a t u s . c a l i b r a t i n g &&

s t a t es t a t u s . c a l i b s t a r t e d ) {

// block until Tobii thread a d v a n c e s to new p o i n t // draw c a l i b r a t i o n dot at (x,y) c o o r d i n a t e s

// signal Tobii thread to advance to new point }

e l s e if( s t a t es t a t u s . r u n n i n g ) { // draw left gaze point

// ( n o r m a l i z e d c o o r d i n a t e s s c a l e d to w i n d o w size ) d r a w _ p o i n t ( s t a t exlw, hs t a t eylh);

// draw right gaze point

d r a w _ p o i n t ( s t a t exrw, hs t a t eyrh);

// draw a v e r a g e gaze point x=(s t a t exl+s t a t exr)/2w;

y=(s t a t eyl+s t a t eyr)/2h;

d r a w _ p o i n t (x,hy);

// draw c u r r e n t c a m e r a eye p o i n t

// ( left eye , right eye , a v e r a g e of both ) . . .

} }

g l u t S w a p B u f f e r s ();

}

Listing 10.11 GUI main loop

omitted and simply replaced bydraw_point (x,y). In the actual example program, simple points are drawn.

Listing 10.10 shows themainroutine where theGLUTGUI toolkit is initialized, the Tobii thread is created, and flow of control is given up to the GUI after specifying an idle loopmain_loopthat executes whenever there is no pending GUI event. The

10.2 A SimpleOpenGL/GLUTGUI Example 115 objectstateserves as a repository of global variables, similar to a C++ singleton pattern.

Listing 10.11 sketches the main features of the GUI’s idle loop. There are two main drawing states for when the eye tracker is calibrating or running. When calibrating, calibration dots are drawn in a synchronized fashion with the ET thread (see Chap.11 for details). When running, simple OpenGLpoints are drawn at the coordinates of the left, right, and average eye gaze location. Not shown in Listing 10.11 are two translucent disks (gluDisks) that are drawn to indicate the (mirrored) position of the eyes with respect to the camera, although this type of visual feedback is very useful for proper self-positioning by the user.

Listing 10.12 is the top-level sketch of the ET thread. It is a POSIX thread, and is initialized to allow asynchronous cancellation (by the parent thread). The first task is to initialize internal Tobii state variables viaTet_Init. The remainder of the thread is a loop that contains the primary eye tracking modes of calibrating and running.

Note that in thisGLUTimplementation, the user controls transitions into these modes by selecting menu items that change thestate→statusbitfield and thereby mode transitions.

v o i d* t o b i i _ t h r e a d (v o i d* ap ) {

// allow thread to be c a n c e l e d

// ( a s y n c h r o n o u s l y , e . g . , i m m e d i a t e l y )

p t h r e a d _ s e t c a n c e l s t a t e ( P T H R E A D _ C A N C E L _ E N A B L E , NULL );

p t h r e a d _ s e t c a n c e l t y p e ( P T H R E A D _ C A N C E L _ A S Y N C H R O N O U S , NULL );

// i n i t i a l i z e Tobii

// ( this is c r i t i c a l - - - sets up thread - s p e c i f i c data ) T e t _ I n i t ();

w h i l e(! states t a t u s . f i n i s h e d ) { if(! states t a t u s . c o n n e c t e d )

if( states t a t u s . c o n n e c t ) t o b i i _ c o n n e c t ();

e l s e {

if( states t a t u s . c a l i b r a t i n g ) t o b i i _ c a l i b r a t e ();

e l s e if( states t a t u s . r u n n i n g ) t o b i i _ r u n ();

e l s e if( states t a t u s . d i s c o n n e c t ) t o b i i _ d i s c o n n e c t ();

} }

// clean exit p t h r e a d _ e x i t ( NULL );

}

Listing 10.12 Tobii thread

Listing 10.13 shows a typical connection to the eye tracking host (server). Note that because thestateobject is shared between the GUI and ET threads, any alteration (writing) of its internal variables must be treated as a critical section. Thus, changing

itsstatusbitfield is performed only after locking the POSIXmutex(that is itself stored in thestateobject).

v o i d t o b i i _ c o n n e c t () {

T e t _ C o n n e c t ( statetobiiHost , statetobiiPort , " l o g f i l e " );

p t h r e a d _ m u t e x _ l o c k (& statemutex );

states t a t u s . c o n n e c t e d = 1;

states t a t u s . c o n n e c t = 0;

p t h r e a d _ m u t e x _ u n l o c k (& statemutex );

}

Listing 10.13 Tobii thread: connect to eye tracker

Listing 10.14 shows the calibration pseudocode.Tet_CalibClearis called once per calibration to clear out all calibration information on the eye tracking server.

Once this is accomplished, successive calibration point coordinates are sent to the eye tracking server one after another. Synchronization with the GUI thread is discussed in detail in Chap. 11. Once all point coordinates have been sent, Tet_CalibCalculateAndSetis called to complete the calibration. At this point, Tet_CalibGetResultcould be called to examine internal calibration error.

Listings 10.15 and 10.16 show the use of theTet_StartandTet_Disconnect calls, respectively. Note thatTet_Startis a blocking function and transfers control to the eye tracking client callback, in this case the gazeDataReceiverfunction, shown in Listing 10.17.

Listing 10.17 consumes gaze data as they become available and copies the data to the sharedstateobject. If it is detected that the operating mode has transitioned to stop (idle) mode, a call toTet_Stopis made, returning control to the point where Tet_Startwas called.

Dalam dokumen Eye Tracking Methodology : Theory and Practice (Halaman 140-144)