00001 /*!@file AppPsycho/psychoWMS.C Psychophysics test to measure the influence of eyemovement on memory task performance */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psychoWMS.C $ 00035 // $Id: psychoWMS.C 12962 2010-03-06 02:13:53Z irock $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Image/Image.H" 00040 #include "Psycho/PsychoDisplay.H" 00041 #include "Psycho/EyeTrackerConfigurator.H" 00042 #include "Psycho/EyeTracker.H" 00043 #include "Psycho/PsychoOpts.H" 00044 #include "Component/EventLog.H" 00045 #include "Component/ComponentOpts.H" 00046 #include "Raster/Raster.H" 00047 #include "Util/MathFunctions.H" 00048 #include "Util/Types.H" 00049 #include "GameBoard/basic-graphics.H" 00050 #include <sys/types.h> 00051 #include <dirent.h> 00052 #include <errno.h> 00053 #include <vector> 00054 #include <string> 00055 #include <iostream> 00056 #include <SDL/SDL.h> 00057 #include <SDL/SDL_image.h> 00058 #include <stdio.h> 00059 #include <stdlib.h> 00060 #include <sstream> 00061 #include <time.h> 00062 #include "Image/DrawOps.H" 00063 #include "GameBoard/resize.h" 00064 #include <iostream> 00065 #include <fstream> 00066 #include <set> 00067 #include <algorithm> 00068 #include <ctime> 00069 00070 #ifndef INVT_HAVE_LIBSDL_IMAGE 00071 #include <cstdio> 00072 int main() 00073 { 00074 fprintf(stderr, "The SDL_image library must be installed to use this program\n"); 00075 return 1; 00076 } 00077 00078 #else 00079 00080 00081 00082 using namespace std; 00083 00084 // ###################################################################### 00085 00086 ModelManager manager("Psycho-WM"); 00087 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00088 map<uint,uint> testMap ; 00089 map<string,string> argMap ; 00090 map<string,vector<SDL_Rect*>*> clipsmap; 00091 00092 ////////////////////////////////////////////// 00093 // a functionf for stringigying things 00094 ////////////////////////////////////////////// 00095 template <class T> std::string stringify(T i) 00096 { 00097 ostringstream o ; 00098 o << i ; 00099 return o.str(); 00100 } 00101 00102 00103 00104 00105 00106 00107 00108 //////////////////////////////////////////////////////////////// 00109 ////This is our button factory 00110 //////////////////////////////////////////////////////////////// 00111 SDL_Surface* getButtonImage(string label , PixRGB<byte> txtcolor=PixRGB<byte>(0,0,0) , PixRGB<byte> bgcolor=PixRGB<byte>(255,255,255) ,Point2D<int> size = Point2D<int>(100,100) ,PixRGB<byte> bordercolor=PixRGB<byte>(0,0,0) , int border=3){ 00112 Image<PixRGB<byte> > textIm(d->getWidth(),d->getHeight(),ZEROS); 00113 textIm.clear(bgcolor); 00114 writeText(textIm, Point2D<int>((size.i - label.length()*10)/2,(size.j-20) /2),label.c_str(),txtcolor,bgcolor); 00115 SDL_Surface *surf = d->makeBlittableSurface(textIm , true); 00116 Uint32 bc = d->getUint32color(bordercolor); 00117 drawRectangle(surf,bc,0,0,size.i -1,size.j -1 ,border); 00118 SDL_Surface* blank =getABlankSurface(size.i , size.j); 00119 SDL_Rect clip; 00120 clip.x = 0 ; 00121 clip.y = 0 ; 00122 clip.w = size.i ; 00123 clip.h = size.j ; 00124 apply_surface(0,0,*surf,*blank,clip); 00125 dumpSurface(surf) ; 00126 return blank ; 00127 } 00128 00129 //////////////////////////////////////////////////////////////////////// 00130 ////This is the function for creating the keypad, in fact it generates 00131 ////12 buttons and associates the actions to the region for each button 00132 //////////////////////////////////////////////////////////////////////// 00133 00134 SDL_Surface* getKeyPad(string alphabet,map<string , SDL_Rect>& buttmap){ 00135 SDL_Surface* pad= getABlankSurface(d->getWidth()/4,d->getHeight()/3); 00136 SDL_Rect clip; 00137 clip.x=0; 00138 clip.y=0; 00139 int numofrows = alphabet.size()/3 +1; 00140 if(alphabet.size()%3 != 0 ) numofrows++ ; 00141 int numofcolumns = 3 ; 00142 clip.w= pad->w / numofcolumns ; 00143 clip.h = pad->h / numofrows ; 00144 00145 //keys for 1 to 9 00146 for( int i = 0 ; i < numofrows*3 ; i++){ 00147 SDL_Surface* but ; 00148 if((uint)i < alphabet.size()){ 00149 but = getButtonImage(alphabet.substr(i,1),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00150 }else{ 00151 but = getButtonImage(" ",PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00152 } 00153 00154 SDL_Rect cl ; 00155 cl.x = ((i)%numofcolumns)*(pad->w)/numofcolumns ; cl.y= ((i)/numofcolumns)*((pad->h)/numofrows) ; 00156 cl.w = clip.w ; 00157 cl.h = clip.h ; 00158 apply_surface( cl.x , cl.y ,*but,*pad,clip); 00159 if((uint)i < alphabet.size()) buttmap[alphabet.substr(i,1)] = cl ; 00160 dumpSurface(but); 00161 } 00162 SDL_Rect cl1 ; 00163 cl1.x = 0 ; cl1.y= (numofrows-1)*((pad->h)/numofrows) ; 00164 cl1.w = clip.w ; 00165 cl1.h = clip.h ; 00166 buttmap["!"] = cl1 ; 00167 SDL_Surface* but = getButtonImage(string("<-"),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00168 apply_surface(0, (numofrows-1)*((pad->h)/numofrows),*but,*pad,clip); 00169 dumpSurface(but); 00170 SDL_Rect cl2 ; 00171 cl2.x = (pad->w)/numofcolumns ; cl2.y= (numofrows-1)*((pad->h)/numofrows) ; 00172 cl2.w = clip.w ; 00173 cl2.h = clip.h ; 00174 buttmap[" "] = cl2 ; 00175 but = getButtonImage(string("spc"),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00176 apply_surface((pad->w)/numofcolumns, (numofrows-1)*((pad->h)/numofrows),*but,*pad,clip); 00177 dumpSurface(but); 00178 SDL_Rect cl3 ; 00179 cl3.x = 2*(pad->w)/numofcolumns ; cl3.y= (numofrows-1)*((pad->h)/numofrows) ; 00180 cl3.w = clip.w ; 00181 cl3.h = clip.h ; 00182 buttmap["*"] = cl3 ; 00183 but = getButtonImage(string("Ok"),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00184 apply_surface(2*(pad->w)/numofcolumns, (numofrows-1)*((pad->h)/numofrows),*but,*pad,clip); 00185 dumpSurface(but); 00186 return pad ; 00187 } 00188 00189 00190 00191 00192 /////////////////////////////////////////////////////////////////////////// 00193 /////this function listens to mouse clicks and then finds the region of the screen 00194 /////associated with the action, buttmap is the map of the region, offset is the offset of 00195 /////buttons 00196 /////////////////////////////////////////////////////////////////////////// 00197 string getPressedButtonCommand(map<string , SDL_Rect>& buttmap,Point2D<int> offset=Point2D<int>(0,0)){ 00198 int quit = 0 ; 00199 string s ; 00200 SDL_Event event ; 00201 while( quit!=2 ){ 00202 while( SDL_PollEvent( &event ) ) { 00203 if(event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT ){ 00204 for( map<string , SDL_Rect>::iterator it = buttmap.begin() ; it!=buttmap.end() ; ++it){ 00205 if(event.button.x >= (it->second).x + offset.i && event.button.x <= (it->second).x + (it->second).w + offset.i && event.button.y >= (it->second).y+ offset.j && event.button.y <= (it->second).y + (it->second).h + offset.j) { 00206 quit = 2 ; 00207 s = it->first ; 00208 break; 00209 } 00210 00211 } 00212 } 00213 00214 } 00215 } 00216 return s ; 00217 00218 } 00219 00220 00221 //////////////////////////////////////////////////// 00222 ////This function creates a virtual keypad, creates a map of buttons 00223 ////and their representation area and listens to the button press and at 00224 ////the end returns the keyed digits 00225 //////////////////////////////////////////////////// 00226 string getDigitSequenceFromSubject(string alphabet="0123456789" , uint maxl = 7 ){ 00227 d->showCursor(true) ; 00228 //let's creat a map to map actions to regions of the screen, each region is represented as an SDL_Rect 00229 map<string , SDL_Rect>* buttmap = new map<string , SDL_Rect>(); 00230 //now let's get the keypad surface while we get the actions map to regions 00231 SDL_Surface * keypad = getKeyPad(alphabet,*buttmap); 00232 //this will be the offset of displaying the keypad on the screen 00233 SDL_Rect offset ; 00234 offset.x = (d->getWidth() - keypad->w) /2; 00235 offset.y = (d-> getHeight() - keypad->h) /2; 00236 //now let's display the keypad 00237 d->displaySDLSurfacePatch(keypad , &offset,NULL , -2,false, true); 00238 //this will hold the final string keyed be the subject 00239 string p = string("") ; 00240 //this is a temporary string holding the last action related to the pressed key 00241 string tp = string(""); 00242 //now let's record subject's key press 00243 while( tp.compare("*")!=0 ){ 00244 //this button is actually the display for the current string 00245 SDL_Surface* dp = getButtonImage(p ,PixRGB<byte>(195,60,12) ,PixRGB<byte>(255,255,255) ,Point2D<int>(d->getWidth()/6,d->getHeight() /15) ,PixRGB<byte>(0,25,180) , 4) ; 00246 SDL_Rect offs ; offs.x = (d->getWidth() - dp->w) /2 ; offs.y = d->getHeight()/6 ; 00247 d->displaySDLSurfacePatch(dp , &offs , NULL , -2 , false ,true ) ; 00248 //now let's listen to button events 00249 tp = getPressedButtonCommand(*buttmap,Point2D<int>(offset.x,offset.y)) ; 00250 dumpSurface(dp) ; 00251 if(tp.compare("!")==0 && p.size()>=0 ) { 00252 if (p.size()>0) p = p.substr(0,p.size()-1) ; 00253 }else{ 00254 if(p.size() < maxl && tp.compare("*")!=0) { 00255 p +=tp ; 00256 } 00257 00258 } 00259 00260 } 00261 buttmap = 0 ; 00262 dumpSurface(keypad) ; 00263 d->clearScreen() ; 00264 return p ; 00265 00266 } 00267 00268 00269 00270 00271 int addArgument(const string st,const string delim="="){ 00272 int i = st.find(delim) ; 00273 argMap[st.substr(0,i)] = st.substr(i+1); 00274 00275 return 0 ; 00276 } 00277 00278 std::string getArgumentValue(string arg){ 00279 return argMap[arg] ; 00280 } 00281 00282 std::string getUsageComment(){ 00283 00284 string com = string("\nlist of arguments : \n"); 00285 00286 com += "\nlogfile=[logfilename.psy] {default = psycho-wm-subtraction.psy}\n" ; 00287 com += "\nmemo=[a_string_without_white_space]\n"; 00288 com += "\nrange=[x-y](the size of string){default=200-500} \n"; 00289 com += "\nsubject=[subject_name] \n" ; 00290 com += "\ndelay=[>0] (number of micro seconds){default=30000000}\n"; 00291 com += "\ntest-rounds=[>1] (number of tests ) {default=5}\n"; 00292 com += "\ndigit-onset=[>1] (number of frames that the string will remain onset ){default=10}\n"; 00293 com += "\nsubtraction-step=[>0] (the subtraction number){default=3} "; 00294 return com ; 00295 } 00296 00297 00298 extern "C" int main(const int argc, char** argv) 00299 { 00300 00301 MYLOGVERB = LOG_INFO; // suppress debug messages 00302 //let's push the initial value for the parameters 00303 argMap["experiment"]="subtraction-working-memory"; 00304 argMap["logfile"]="psycho-wm-subtraction.psy" ; 00305 argMap["string-size"]="5" ; 00306 argMap["test-rounds"]="5"; 00307 argMap["subject"]="" ; 00308 argMap["memo"]="" ; 00309 argMap["digit-onset"]="10" ; 00310 argMap["range"]="200-500"; 00311 argMap["delay"]="30000000" ; 00312 argMap["subtraction-step"]="3" ; 00313 manager.addSubComponent(d); 00314 nub::soft_ref<EventLog> el(new EventLog(manager)); 00315 manager.addSubComponent(el); 00316 d->setEventLog(el); 00317 nub::soft_ref<EyeTrackerConfigurator> 00318 etc(new EyeTrackerConfigurator(manager)); 00319 manager.addSubComponent(etc); 00320 00321 if (manager.parseCommandLine(argc, argv, 00322 "at least one argument needed", 1, -1)==false){ 00323 cout<<getUsageComment()<<endl; 00324 return(1); 00325 } 00326 00327 for(uint i = 0 ; i < manager.numExtraArgs() ; i++){ 00328 addArgument(manager.getExtraArg(i),std::string("=")) ; 00329 } 00330 00331 manager.setOptionValString(&OPT_EventLogFileName, argMap["logfile"]); 00332 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00333 nub::soft_ref<EyeTracker> eyet = etc->getET(); 00334 d->setEyeTracker(eyet); 00335 eyet->setEventLog(el); 00336 00337 00338 // let's get all our ModelComponent instances started: 00339 manager.start(); 00340 for(map<string,string>::iterator it= argMap.begin(); it!= argMap.end() ; ++it) d->pushEvent("arg:"+ it->first+" value:"+it->second ) ; 00341 // let's display an ISCAN calibration grid: 00342 d->clearScreen(); 00343 d->displayISCANcalib(); 00344 d->waitForMouseClick(); 00345 d->displayText("Here the experiment starts! click to start!"); 00346 d->waitForMouseClick(); 00347 d->clearScreen(); 00348 //let's see in what mode the user like to run the program 00349 00350 int numOfTests = atoi(argMap["test-rounds"].c_str()) ; 00351 int onsetDel = atoi(argMap["digit-onset"].c_str()) ; 00352 long opDelay = atol(argMap["delay"].c_str()); 00353 int in = argMap["range"].find("-") ; 00354 int rangeU = atoi( argMap["range"].substr(in+1).c_str()) ; 00355 int rangeL = atoi(argMap["range"].substr(0,in).c_str()) ; 00356 int step = atoi(argMap["subtraction-step"].c_str()); 00357 //let's count the rounds 00358 d->displayText("CLICK LEFT button to calibrate; RIGHT to skip"); 00359 int cl = d->waitForMouseClick(); 00360 if (cl == 1) d->displayEyeTrackerCalibration(3,5,1 , true); 00361 d->clearScreen(); 00362 int cr = 0 ; 00363 while( cr <numOfTests ){ 00364 00365 cr++; 00366 d->pushEvent("**************************************") ; 00367 d->showCursor(true); 00368 d->displayText("click one of the mouse buttons to start!"); 00369 d->waitForMouseClick() ; 00370 d->showCursor(false) ; 00371 int initialNum = rangeL+ random()%(rangeU-rangeL) ; 00372 d->clearScreen() ; 00373 d->pushEvent("the initial number is: "+stringify(initialNum)) ; 00374 d->displayFixationBlink(); 00375 d->displayText(stringify(initialNum),true,0) ; 00376 d->waitFrames(onsetDel) ; 00377 d->clearScreen() ; 00378 d->displayFixationBlink(d->getWidth()/2,d->getHeight()/2,2,3); 00379 d->pushEvent(std::string("===== Showing image: def.mpg =====")); 00380 eyet->track(true); 00381 d->pushEvent("manupulation starts") ; 00382 long st = d->getTimerValue(); 00383 long et = st ; 00384 while( et - st < opDelay ){ 00385 et = d->getTimerValue(); 00386 } 00387 usleep(50000); 00388 eyet->track(false); 00389 d->pushEvent("manipulation ends") ; 00390 string answer = getDigitSequenceFromSubject("0123456789" , 3); 00391 d->pushEvent("the reported number: "+answer); 00392 int finalNum = atoi(answer.c_str()) ; 00393 d->pushEvent("number of operations: "+stringify((initialNum-finalNum)/step)); 00394 } 00395 00396 d->clearScreen(); 00397 d->displayText("Experiment complete. Thank you!"); 00398 d->waitForMouseClick(); 00399 00400 // stop all our ModelComponents 00401 manager.stop(); 00402 00403 00404 // all done! 00405 return 0; 00406 } 00407 00408 #endif // INVT_HAVE_LIBSDL_IMAGE 00409