psycho-classic-antisaccade.C

Go to the documentation of this file.
00001 /*!@file AppPsycho/psycho-classic-antisaccade.C Psychophysics display of pro-/anti-saccade task.
00002    The paradigm is similar to Munoz et al., 1998 except that targets are 10 degree eccentric and 
00003    only overlap condition is used. 
00004    ./bin/psycho-classic-antisaccade <number of trials> <block(0) or interleave (1)>*/
00005 
00006 // //////////////////////////////////////////////////////////////////// //
00007 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00008 // University of Southern California (USC) and the iLab at USC.         //
00009 // See http://iLab.usc.edu for information about this project.          //
00010 // //////////////////////////////////////////////////////////////////// //
00011 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00012 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00013 // in Visual Environments, and Applications'' by Christof Koch and      //
00014 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00015 // pending; application number 09/912,225 filed July 23, 2001; see      //
00016 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00017 // //////////////////////////////////////////////////////////////////// //
00018 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00019 //                                                                      //
00020 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00021 // redistribute it and/or modify it under the terms of the GNU General  //
00022 // Public License as published by the Free Software Foundation; either  //
00023 // version 2 of the License, or (at your option) any later version.     //
00024 //                                                                      //
00025 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00026 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00027 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00028 // PURPOSE.  See the GNU General Public License for more details.       //
00029 //                                                                      //
00030 // You should have received a copy of the GNU General Public License    //
00031 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00032 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00033 // Boston, MA 02111-1307 USA.                                           //
00034 // //////////////////////////////////////////////////////////////////// //
00035 //
00036 // Primary maintainer for this file: Po-He Tseng <ptseng@usc.edu>
00037 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-movie-fixicon.C $
00038 // $Id: psycho-movie-fixicon.C 13712 2010-07-28 21:00:40Z itti $
00039 
00040 #include "Component/ModelManager.H"
00041 #include "Image/Image.H"
00042 #include "Raster/Raster.H"
00043 #include "Media/MPEGStream.H"
00044 #include "Media/MediaOpts.H"
00045 #include "Psycho/PsychoDisplay.H"
00046 #include "Psycho/EyeTrackerConfigurator.H"
00047 #include "Psycho/EyeTracker.H"
00048 #include "Psycho/PsychoOpts.H"
00049 #include "Component/EventLog.H"
00050 #include "Component/ComponentOpts.H"
00051 #include "Util/MathFunctions.H"
00052 #include "Util/Types.H"
00053 #include "Video/VideoFrame.H"
00054 #include "rutz/time.h"
00055 
00056 #include <deque>
00057 #include <sstream>
00058 
00059 // ######################################################################
00060 static int waitForFixation(const Point2D<int> fparr [], 
00061                                                                            const int arraylen,
00062                                                                            const int radius,         //in pixel
00063                                                                  double fixlen,            // in msec
00064                                                                                                          nub::soft_ref<EyeTracker> et, 
00065                                                                                                          nub::soft_ref<PsychoDisplay> d,
00066                                                                                                          const bool do_drift_correction = false)
00067 {
00068         Point2D<int> ip;                                
00069         double dist [arraylen], sdist;
00070         double tt=0, adur=0, ax=0, ay=0; 
00071         Timer timer, timer2;
00072         fixlen = fixlen/1000;
00073         int status = 1;
00074 
00075         // clean up queue for key response
00076         while(d->checkForKey() != -1);
00077 
00078         // wait for fixation
00079         timer2.reset();
00080         while(timer.getSecs() < fixlen) {
00081                 ip = et->getFixationPos();
00082 
00083                 // check distance between fixation point to all possible targets
00084                 for (int i=0; i<arraylen; i++)
00085                         dist[i] = sqrt(pow(ip.i-fparr[i].i, 2) + pow(ip.j-fparr[i].j, 2));
00086 
00087                 // get the closest target
00088                 if (arraylen == 1)
00089                         sdist = dist[0];
00090                 else
00091                         sdist = std::min(dist[0], dist[1]);
00092 
00093                 // inside of tolerance?
00094                 if (sdist > radius && (ip.i!=-1 && ip.j!=-1)){
00095                         timer.reset();
00096                 }else{
00097                         tt = timer.getSecs()-tt;                        
00098                         adur += tt;
00099                         ax += tt * ip.i;
00100                         ay += tt * ip.j;        
00101                 }
00102 
00103                 //LINFO("(%i, %i) - (%i, %i) dist: %f, %f", fparr[0].i, fparr[0].j, ip.i, ip.j, sdist, timer.getSecs());
00104 
00105                 // time out
00106                 if (timer2.getSecs() > 5 || d->checkForKey() > 0){
00107                         d->pushEvent("bad trial - time out / no response.");
00108                         status = -1;
00109                         break;
00110                 }
00111         }                               
00112         
00113         // do drift correction
00114         if (status == 1 && do_drift_correction == true) {
00115                 // when there's only 1 target on screen                 
00116                 et->manualDriftCorrection(Point2D<double>(ax/adur, ay/adur), 
00117                                                                                           Point2D<double>(fparr[0].i, fparr[0].j));             
00118                 LINFO("drift correction: (%i, %i) (%i, %i)", (int)(ax/adur), (int)(ay/adur), fparr[0].i, fparr[0].j);
00119         }
00120 
00121         return status;
00122 }
00123 
00124 // ######################################################################
00125 static int submain(const int argc, char** argv)
00126 {
00127   MYLOGVERB = LOG_INFO;  // suppress debug messages
00128 
00129   // Instantiate a ModelManager:
00130   ModelManager manager("Psycho Pro-Saccade");
00131 
00132   // Instantiate our various ModelComponents:
00133   nub::soft_ref<InputMPEGStream> mp
00134     (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream"));
00135   manager.addSubComponent(mp);
00136 
00137   nub::soft_ref<EventLog> el(new EventLog(manager));
00138   manager.addSubComponent(el);
00139 
00140   nub::soft_ref<EyeTrackerConfigurator>
00141     etc(new EyeTrackerConfigurator(manager));
00142   manager.addSubComponent(etc);
00143 
00144   nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager));
00145   manager.addSubComponent(d);
00146 
00147   manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true");
00148   manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy");
00149   manager.setOptionValString(&OPT_EyeTrackerType, "EL");
00150 
00151   // Parse command-line:
00152   if (manager.parseCommandLine(argc, argv, "<# of trials> <block(0) or interleave(1)>", 2, 2) == false)
00153     return(1);
00154 
00155         // construct an array to indicate which trial is this:
00156   // 1: prosaccade, left
00157         // 2: prosaccade, right
00158         // 3: antisaccade, left
00159         // 4: antisaccade, right
00160   int ntrial = fromStr<int>(manager.getExtraArg(0).c_str());
00161         int interleave = fromStr<int>(manager.getExtraArg(1).c_str());
00162         
00163         LINFO("Total Trials: %i", ntrial);
00164         int trials[ntrial];
00165         int trialindex[ntrial];
00166         if(interleave==1){
00167                 // interleave design
00168                 for (int i=0; i< ntrial; i++)
00169                         trials[i] = 1 + i % 4;
00170           // randomize stimulus presentation order:
00171           for (int i = 0; i < ntrial; i ++) 
00172                         trialindex[i] = i;
00173           randShuffle(trialindex, ntrial);
00174         }else{
00175                 // block design
00176                 int ntrial4 = ntrial/4;
00177                 int tmptrials[4][ntrial4];
00178                 int tmpidx[ntrial4];
00179                 int blockidx[4] = {0, 1, 0, 1};
00180                 for (int i=0; i<4; i++){
00181                         for (int j=0; j<ntrial4; j++){
00182                                 if (i%2==0)
00183                                         tmptrials[i][j] = 1 + j%2; //prosaccades
00184                                 else
00185                                         tmptrials[i][j] = 3 + j%2; //antisaccades                               
00186                         }
00187                 }
00188                 for (int i=0; i<ntrial4; i++)
00189                         tmpidx[i] = i;
00190 
00191                 // block order (as long as the first 2 blocks are not the same)
00192                 do {
00193                         randShuffle(blockidx, 4);
00194                 } while(blockidx[0] == blockidx[1]);
00195 
00196                 // assign trial
00197                 int c = 0;
00198                 for (int i=0; i<4; i++){
00199                         randShuffle(tmpidx, ntrial4);   
00200                         for (int j=0; j<ntrial4; j++){
00201                                 trials[c] = tmptrials[blockidx[i]][tmpidx[j]];
00202                                 trialindex[c] = c;
00203                                 c++;
00204                         }
00205                 }                       
00206         }
00207 
00208   // hook our various babies up and do post-command-line configs:
00209   nub::soft_ref<EyeTracker> et = etc->getET();
00210   d->setEyeTracker(et);
00211   d->setEventLog(el);
00212   et->setEventLog(el);
00213 
00214   // EyeLink opens the screen for us, so make sure SDLdisplay is slave:
00215   if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0)
00216     d->setModelParamVal("SDLslaveMode", true);
00217 
00218   // let's get all our ModelComponent instances started:
00219   manager.start();
00220 
00221   // vision test (generate random letters again if pressing 'r')
00222         LINFO("*****************************************************************************");
00223         LINFO("visual acuity test: [r] to regenerate another string; other keys to continue.");
00224         int key;
00225         do {
00226                 d->displayRandomText(6, 8);
00227         key = d->waitForKey(true);
00228         } while(key == 114 || key == 101 || key == 116 || key == 102);
00229 
00230         // let's do an eye tracker calibration:
00231         d->pushEventBegin("Calibration");
00232         et->setBackgroundColor(d);
00233         et->calibrate(d);
00234         d->pushEventEnd("Calibration");
00235 
00236         LINFO("Press any key to start......");
00237   d->displayText("Press any key to start......", true, 0, 10);
00238   d->waitForKey(true);
00239 
00240         // main loop
00241         const int x = d->getWidth()/2;
00242         const int y = d->getHeight()/2;
00243         const int dist = x;
00244         int trialtype = 0;
00245         int tx = x, txt = x;
00246         const PixRGB<byte> green = PixRGB<byte>(0,255,0);
00247         const PixRGB<byte> yellow = PixRGB<byte>(255,255,0);
00248         ///Point2D<int> ip = Point2D<int>(0,y);;
00249         Point2D<int> targetpoint = Point2D<int>(0,y);
00250         ///int status;
00251         Timer timer;
00252         const int fixation_tolerance = 135;
00253         int displayTask = 1;
00254 
00255         for (int i=0; i < ntrial; i++) {
00256                 trialtype = trials[trialindex[i]];
00257 
00258                 // display task instruction at the center of the screen
00259                 d->clearScreen();
00260 
00261                 if (i%10 == 0){
00262                         et->recalibrate(d,13);
00263                         d->clearScreen();
00264                 }
00265 
00266                 double delay = 1000000;
00267                 if (trialtype == 1 || trialtype == 2){
00268 
00269                         if (displayTask == 1){
00270                                 d->displayText("Same", true, 0, 10);
00271                                 if (interleave == 1)
00272                                         usleep(1000000);
00273                                 else
00274                                         d->waitForKey();
00275                         }
00276 
00277                         // start eye tracker
00278                         et->track(true);
00279                         usleep(500000);
00280 
00281                         // involuntary, green fixation
00282                         d->clearScreen();
00283                         if (trialtype == 1)
00284                                 d->pushEvent("====== Trial Type: 1 (leftward, pro-saccade) ======");
00285                         else
00286                                 d->pushEvent("====== Trial Type: 2 (rightward, pro-saccade) ======");
00287 
00288                         // display cue, wait 0.5-1.5 seconds.....
00289                         d->displayFixation(x+dist/2, y, false);
00290                         d->displayFixation(x-dist/2, y, false);
00291                         d->displayFilledCircle(x, y, 5, green);
00292                         d->pushEvent("Display cue (green)");
00293                         usleep(delay);
00294 
00295                         // display target 
00296                         tx = x + dist * (trialtype-1.5);
00297                         timer.reset();
00298                         //d->displayFilledCircleBlink(tx, y, 3, green, 3, 1);
00299                         //d->displayFilledCircle(x, y, 5, d->getGrey(), false);
00300                         d->displayFilledCircle(tx, y, 5, green);
00301                         d->pushEvent("Display target (green)");
00302 
00303                         // get response
00304                         targetpoint.i = tx;
00305                         /*status = */waitForFixation(&targetpoint, 1, fixation_tolerance, 100, et, d);  
00306                         double tt = timer.getSecs();
00307 
00308                         LINFO("(pro-saccade) response time: %f", tt);
00309                         d->pushEvent(sformat("pro-saccade, response time: %f", tt));
00310 
00311                         d->displayCircle(targetpoint.i, targetpoint.j, 60, yellow);
00312                         usleep(1000000);
00313 
00314                         // display information
00315                         d->clearScreen();
00316 
00317                 } else if (trialtype == 3 || trialtype == 4){
00318 
00319                         if (displayTask == 1){
00320                                 d->displayText("Opposite", true, 0, 10);
00321                                 if (interleave == 1)
00322                                         usleep(1000000);
00323                                 else
00324                                         d->waitForKey();
00325                         }
00326 
00327                         // start eye tracker
00328                         et->track(true);
00329                         usleep(500000);
00330 
00331                         // involuntary, green fixation
00332                         d->clearScreen();
00333                         if (trialtype == 3)
00334                                 d->pushEvent("====== Trial Type: 3 (leftward, anti-saccade) ======");
00335                         else
00336                                 d->pushEvent("====== Trial Type: 4 (rightward, anti-saccade) ======");
00337 
00338                         // display cue, wait 0.5-1.5 seconds.....
00339                         d->displayFixation(x+dist/2, y, false);
00340                         d->displayFixation(x-dist/2, y, false);
00341                         d->displayFilledCircle(x, y, 5, green);
00342                         d->pushEvent("Display cue (green)");
00343                         usleep(delay);
00344 
00345                         // display target
00346                         tx = x + dist * (-1*(trialtype-3.5));
00347                         txt = x + dist * (trialtype-3.5);
00348                         timer.reset();
00349                         //d->displayFilledCircleBlink(tx, y, 3, green, 3, 1);
00350                         //d->displayFilledCircle(x, y, 5, d->getGrey(), false);
00351                         d->displayFilledCircle(tx, y, 5, green);
00352                         d->pushEvent("Display target (green)");
00353 
00354                         // get response
00355                         targetpoint.i = txt;
00356                         /*status = */waitForFixation(&targetpoint, 1, fixation_tolerance, 100, et, d);  
00357                         double tt = timer.getSecs();
00358 
00359                         LINFO("(anti-saccade) response time: %f", tt);
00360                         d->pushEvent(sformat("anti-saccade, response time: %f", tt));
00361 
00362                         d->displayCircle(targetpoint.i, targetpoint.j, 60, yellow);
00363                         usleep(1000000);
00364 
00365                         // display information
00366                         d->clearScreen();
00367                 }else{
00368                         //bug, die!!
00369                         d->displayColorDotFixation(x, y, PixRGB<byte>(0,0,255));
00370                         return(1);
00371                 } 
00372 
00373     // stop the eye tracker:
00374     usleep(50000);
00375     et->track(false);
00376 
00377                 // display percentage completed
00378                 double completion = 100*((double)i+1)/double(ntrial);
00379                 //LINFO("completion %f, %f, %f", completion, (double)i+1, double(ntrial));
00380                 if ((int)completion % 5 == 0 && completion-(int)completion==0){
00381                         d->clearScreen();
00382                         d->displayText(sformat("%i %% completed", (int)completion), true, 0, 10);
00383                         usleep(1000000);
00384                 }
00385 
00386                 if (interleave == 0)
00387                         displayTask = 0;
00388 
00389                 // take a break for a quarter of trials
00390                 if ((int)completion%25 == 0 && i<ntrial-1 && completion-(int)completion==0) {
00391                 d->clearScreen();
00392                 d->displayText("Please Take a Break", true, 0, 10);
00393                         LINFO("Break time.  Press [Space] to continue, or [ESC] to terminate the experiment.");
00394                 d->waitForKey(true);
00395                 d->displayText("Calibration", true, 0, 10);
00396       d->pushEventBegin("Calibration");
00397       et->calibrate(d);
00398       d->pushEventEnd("Calibration");
00399                         displayTask = 1;
00400                 }
00401         }
00402 
00403   d->clearScreen();
00404   d->displayText("Experiment complete. Thank you!", true, 0, 10);
00405   d->waitForKey(true);
00406 
00407   // stop all our ModelComponents
00408   manager.stop();
00409 
00410   // all done!
00411   return 0;
00412 }
00413 
00414 // ######################################################################
00415 extern "C" int main(const int argc, char** argv)
00416 {
00417   // simple wrapper around submain() to catch exceptions (because we
00418   // want to allow PsychoDisplay to shut down cleanly; otherwise if we
00419   // abort while SDL is in fullscreen mode, the X server won't return
00420   // to its original resolution)
00421   try
00422     {
00423       return submain(argc, argv);
00424     }
00425   catch (...)
00426     {
00427       REPORT_CURRENT_EXCEPTION;
00428     }
00429 
00430   return 1;
00431 }
00432 
00433 // ######################################################################
00434 /* So things look consistent in everyone's emacs... */
00435 /* Local Variables: */
00436 /* indent-tabs-mode: nil */
00437 /* End: */
Generated on Sun May 8 08:04:11 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3