psycho-prosaccade.C

00001 /*!@file AppPsycho/psycho-movie.C Psychophysics display of movies */
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: Po-He Tseng <ptseng@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-movie-fixicon.C $
00035 // $Id: psycho-movie-fixicon.C 13712 2010-07-28 21:00:40Z itti $
00036 
00037 #include "Component/ModelManager.H"
00038 #include "Image/Image.H"
00039 #include "Raster/Raster.H"
00040 #include "Media/MPEGStream.H"
00041 #include "Media/MediaOpts.H"
00042 #include "Psycho/PsychoDisplay.H"
00043 #include "Psycho/EyeTrackerConfigurator.H"
00044 #include "Psycho/EyeTracker.H"
00045 #include "Psycho/PsychoOpts.H"
00046 #include "Component/EventLog.H"
00047 #include "Component/ComponentOpts.H"
00048 #include "Util/MathFunctions.H"
00049 #include "Util/Types.H"
00050 #include "Video/VideoFrame.H"
00051 #include "rutz/time.h"
00052 
00053 #include <deque>
00054 #include <sstream>
00055 
00056 // ######################################################################
00057 static int waitForFixation(const Point2D<int> fparr [], 
00058                                                                            const int arraylen,
00059                                                                            const int radius,         //in pixel
00060                                                                  double fixlen,            // in msec
00061                                                                                                          nub::soft_ref<EyeTracker> et, 
00062                                                                                                          nub::soft_ref<PsychoDisplay> d,
00063                                                                                                          const bool do_drift_correction = false)
00064 {
00065         Point2D<int> ip;                                
00066         double dist [arraylen], sdist;
00067         double tt=0, adur=0, ax=0, ay=0; 
00068         Timer timer, timer2;
00069         fixlen = fixlen/1000;
00070         int status = 1;
00071 
00072         // clean up queue for key response
00073         while(d->checkForKey() != -1);
00074 
00075         // wait for fixation
00076         timer2.reset();
00077         while(timer.getSecs() < fixlen) {
00078                 ip = et->getFixationPos();
00079 
00080                 // check distance between fixation point to all possible targets
00081                 for (int i=0; i<arraylen; i++)
00082                         dist[i] = sqrt(pow(ip.i-fparr[i].i, 2) + pow(ip.j-fparr[i].j, 2));
00083 
00084                 // get the closest target
00085                 if (arraylen == 1)
00086                         sdist = dist[0];
00087                 else
00088                         sdist = std::min(dist[0], dist[1]);
00089 
00090                 // inside of tolerance?
00091                 if (sdist > radius && (ip.i!=-1 && ip.j!=-1)){
00092                         timer.reset();
00093                 }else{
00094                         tt = timer.getSecs()-tt;                        
00095                         adur += tt;
00096                         ax += tt * ip.i;
00097                         ay += tt * ip.j;        
00098                 }
00099 
00100                 //LINFO("(%i, %i) - (%i, %i) dist: %f, %f", fparr[0].i, fparr[0].j, ip.i, ip.j, sdist, timer.getSecs());
00101 
00102                 // time out
00103                 if (timer2.getSecs() > 5 || d->checkForKey() > 0){
00104                         d->pushEvent("bad trial - time out / no response.");
00105                         status = -1;
00106                         break;
00107                 }
00108         }                               
00109         
00110         // do drift correction
00111         if (status == 1 && do_drift_correction == true) {
00112                 // when there's only 1 target on screen                 
00113                 et->manualDriftCorrection(Point2D<double>(ax/adur, ay/adur), 
00114                                                                                           Point2D<double>(fparr[0].i, fparr[0].j));             
00115                 LINFO("drift correction: (%i, %i) (%i, %i)", (int)(ax/adur), (int)(ay/adur), fparr[0].i, fparr[0].j);
00116         }
00117 
00118         return status;
00119 }
00120 
00121 // ######################################################################
00122 static int submain(const int argc, char** argv)
00123 {
00124   MYLOGVERB = LOG_INFO;  // suppress debug messages
00125 
00126   // Instantiate a ModelManager:
00127   ModelManager manager("Psycho Pro-Saccade");
00128 
00129   // Instantiate our various ModelComponents:
00130   nub::soft_ref<InputMPEGStream> mp
00131     (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream"));
00132   manager.addSubComponent(mp);
00133 
00134   nub::soft_ref<EventLog> el(new EventLog(manager));
00135   manager.addSubComponent(el);
00136 
00137   nub::soft_ref<EyeTrackerConfigurator>
00138     etc(new EyeTrackerConfigurator(manager));
00139   manager.addSubComponent(etc);
00140 
00141   nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager));
00142   manager.addSubComponent(d);
00143 
00144   manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true");
00145   manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy");
00146   manager.setOptionValString(&OPT_EyeTrackerType, "EL");
00147 
00148   // Parse command-line:
00149   if (manager.parseCommandLine(argc, argv, "<# of trials>", 1, 1) == false)
00150     return(1);
00151 
00152         // construct an array to indicate which trial is this:
00153         // 1: voluntary - leftward
00154         // 2: voluntary - rightward
00155   // 3: involuntary - (leftward, not necessarily)
00156         // 4: involuntary - (rightward, not necessarily)
00157         // 5: voluntary - leftward (anti-saccade)
00158         // 6: voluntary - rightward (anti-saccade)
00159   int ntrial = fromStr<int>(manager.getExtraArg(0).c_str());
00160         LINFO("Total Trials: %i", ntrial);
00161         int trials[ntrial];
00162         for (int i=0; i< ntrial; i++)
00163                 trials[i] = 1 + i % 6;
00164 
00165   // randomize stimulus presentation order:
00166         int trialindex[ntrial];
00167   for (int i = 0; i < ntrial; i ++) 
00168                 trialindex[i] = i;
00169   randShuffle(trialindex, ntrial);
00170 
00171   // hook our various babies up and do post-command-line configs:
00172   nub::soft_ref<EyeTracker> et = etc->getET();
00173   d->setEyeTracker(et);
00174   d->setEventLog(el);
00175   et->setEventLog(el);
00176 
00177   // EyeLink opens the screen for us, so make sure SDLdisplay is slave:
00178   if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0)
00179     d->setModelParamVal("SDLslaveMode", true);
00180 
00181   // let's get all our ModelComponent instances started:
00182   manager.start();
00183 
00184   // vision test (generate random letters again if pressing 'r')
00185         LINFO("*****************************************************************************");
00186         LINFO("visual acuity test: [r] to regenerate another string; other keys to continue.");
00187         int key;
00188         do {
00189                 d->displayRandomText(6, 8);
00190         key = d->waitForKey(true);
00191         } while(key == 114 || key == 101 || key == 116 || key == 102);
00192 
00193         // let's do an eye tracker calibration:
00194         d->pushEventBegin("Calibration");
00195         et->setBackgroundColor(d);
00196         et->calibrate(d);
00197         d->pushEventEnd("Calibration");
00198 
00199         LINFO("Press any key to start......");
00200   d->displayText("Press any key to start......", true, 0, 10);
00201   d->waitForKey(true);
00202 
00203         // main loop
00204         const int x = d->getWidth()/2;
00205         const int y = d->getHeight()/2;
00206         const int dist = x;
00207         int trialtype = 0;
00208         int tx = x, txt = x;
00209         const PixRGB<byte> green = PixRGB<byte>(0,255,0);
00210         const PixRGB<byte> yellow = PixRGB<byte>(255,255,0);
00211         Point2D<int> ip = Point2D<int>(0,y);;
00212         Point2D<int> targetpoint = Point2D<int>(0,y);
00213         int status;
00214         double countL = 0, countR = 0;
00215         Timer timer;
00216         const int fixation_tolerance = 135;
00217 
00218         for (int i=0; i < ntrial; i++) {
00219                 trialtype = trials[trialindex[i]];
00220 
00221                 // display task instruction at the center of the screen
00222                 d->clearScreen();
00223 
00224                 et->recalibrate(d,13);
00225 
00226                 // display ready signal, fixate on it for 1000ms before proceeding      
00227                 //d->displayText("X");
00228                 //waitForFixation(&fixpoint, 1, 60, 1000, et, d, true);
00229 
00230                 d->clearScreen();
00231 
00232                 //double delay = 1000000 + randomUpToIncluding(1000000);
00233                 double delay = 20000000 + randomUpToIncluding(10000000);
00234                 if (trialtype == 1 || trialtype == 2){
00235 
00236                         d->displayText("Same", true, 0, 10);
00237                         usleep(1000000);
00238 
00239                         // start eye tracker
00240                         et->track(true);
00241                         usleep(500000);
00242 
00243                         // involuntary, green fixation
00244                         d->clearScreen();
00245                         if (trialtype == 1)
00246                                 d->pushEvent("====== Trial Type: 1 (leftward, pro-saccade) ======");
00247                         else
00248                                 d->pushEvent("====== Trial Type: 2 (rightward, pro-saccade) ======");
00249 
00250                         // display cue, wait 0.5-1.5 seconds.....
00251                         d->displayFixation(x+dist/2, y, false);
00252                         d->displayFixation(x-dist/2, y, false);
00253                         d->displayFilledCircle(x, y, 5, green);
00254                         d->pushEvent("Display cue (green)");
00255                         usleep(delay);
00256 
00257                         // display target transiently
00258                         tx = x + dist * (trialtype-1.5);
00259                         timer.reset();
00260                         //d->displayFilledCircleBlink(tx, y, 3, green, 3, 1);
00261                         d->displayFilledCircle(x, y, 5, d->getGrey(), false);
00262                         d->displayFilledCircle(tx, y, 5, green);
00263                         d->pushEvent("Display target (green)");
00264 
00265                         // get response
00266                         targetpoint.i = tx;
00267                         status = waitForFixation(&targetpoint, 1, fixation_tolerance, 100, et, d);      
00268                         double tt = timer.getSecs();
00269 
00270                         LINFO("(pro-saccade) response time: %f", tt);
00271                         d->pushEvent(sformat("pro-saccade, response time: %f", tt));
00272 
00273                         d->displayCircle(targetpoint.i, targetpoint.j, 60, yellow);
00274                         usleep(1000000);
00275 
00276                         // display information
00277                         d->clearScreen();
00278                         if (status == -1) {
00279                                         d->displayText("Please be prepared", true, 0, 10);
00280                         } else {
00281                                 if (tt < 0.6)
00282                                         d->displayText("Good!", true, 0, 10);
00283                                 else
00284                                         d->displayText("You can do faster than that!", true, 0, 10);
00285                         }
00286 
00287                 }else if (trialtype == 3 || trialtype == 4){
00288 
00289                         d->displayText("Free", true, 0, 10);
00290                         usleep(500000);
00291 
00292                         // start eye tracker
00293                         et->track(true);
00294                         usleep(500000);
00295 
00296                         // voluntary, red fixation
00297                         d->clearScreen();
00298                         if (trialtype == 3)
00299                                 d->pushEvent("====== Trial Type: 3 (free) ======");
00300                         else
00301                                 d->pushEvent("====== Trial Type: 4 (free) ======");
00302 
00303                         Point2D<int> fparr [] = {Point2D<int>(x+dist/2,y), Point2D<int>(x-dist/2,y)};                   
00304 
00305                         // display cue, wait 0.5-1.5 seconds.....
00306                         d->displayFixation(x+dist/2, y, false);
00307                         d->displayFixation(x-dist/2, y, false);
00308                         d->displayFilledCircle(x, y, 5, green);
00309                         d->pushEvent("Display cue (green)");
00310                         usleep(delay);
00311 
00312                         // display go signal
00313                         timer.reset();
00314                         d->displayFilledCircle(x, y, 5, d->getGrey(), false);
00315                         //d->displayFilledCircle(x, y, 5, green);
00316                         d->pushEvent("GO signal");
00317 
00318                         //detect fixation on target and display response
00319                         status = waitForFixation(fparr, 2, fixation_tolerance, 100, et, d);
00320                         double tt = timer.getSecs();
00321 
00322                         ip = et->getEyePos();
00323                         int dir;
00324                         if (ip.i < x) {
00325                                 countL++;
00326                                 dir = -1;
00327                         } else {
00328                                 countR++;
00329                                 dir = 1;
00330                         }
00331 
00332                         LINFO("(free-saccade) response time: %f, [L: %i, R: %i]", tt, (int)countL, (int)countR);
00333                         d->pushEvent(sformat("free-saccade (%i), response time: %f, [L: %i, R: %i]", dir, tt, (int)countL, (int)countR));
00334 
00335                         // draw a big circle at target location for the prediction of computer (fake)
00336                         //if (randomDouble() > countL/(countL+countL)) {
00337                         if (ip.i < x) {
00338                                 d->displayCircle(x-dist/2, y , 60, yellow);
00339                         } else {
00340                                 d->displayCircle(x+dist/2, y , 60, yellow);
00341                         }
00342                         usleep(1000000);
00343 
00344                         // display information
00345                         d->clearScreen();
00346                         if (status == -1) {
00347                                         d->displayText("Please be prepared", true, 0, 10);
00348                         } else {
00349                                 if (tt < 0.2)
00350                                         d->displayText("Please hold your fixation until green circle appears", true, 0, 10);
00351                                 else if (tt < 0.6)
00352                                         d->displayText("Good!", true, 0, 10);
00353                                 else
00354                                         d->displayText("You can do faster than that!", true, 0, 10);
00355                         }
00356 
00357                 } else if (trialtype == 5 || trialtype == 6){
00358 
00359                         d->displayText("Opposite", true, 0, 10);
00360                         usleep(500000);
00361 
00362                         // start eye tracker
00363                         et->track(true);
00364                         usleep(500000);
00365 
00366                         // involuntary, green fixation
00367                         d->clearScreen();
00368                         if (trialtype == 5)
00369                                 d->pushEvent("====== Trial Type: 5 (leftward, anti-saccade) ======");
00370                         else
00371                                 d->pushEvent("====== Trial Type: 6 (rightward, anti-saccade) ======");
00372 
00373                         // display cue, wait 0.5-1.5 seconds.....
00374                         d->displayFixation(x+dist/2, y, false);
00375                         d->displayFixation(x-dist/2, y, false);
00376                         d->displayFilledCircle(x, y, 5, green);
00377                         d->pushEvent("Display cue (green)");
00378                         usleep(delay);
00379 
00380                         // display target transiently
00381                         tx = x + dist * (-1*(trialtype-5.5));
00382                         txt = x + dist * (trialtype-5.5);
00383                         timer.reset();
00384                         //d->displayFilledCircleBlink(tx, y, 3, green, 3, 1);
00385                         d->displayFilledCircle(x, y, 5, d->getGrey(), false);
00386                         d->displayFilledCircle(tx, y, 5, green);
00387                         d->pushEvent("Display target (green)");
00388 
00389                         // get response
00390                         targetpoint.i = txt;
00391                         status = waitForFixation(&targetpoint, 1, fixation_tolerance, 100, et, d);      
00392                         double tt = timer.getSecs();
00393 
00394                         LINFO("(anti-saccade) response time: %f", tt);
00395                         d->pushEvent(sformat("anti-saccade, response time: %f", tt));
00396 
00397                         d->displayCircle(targetpoint.i, targetpoint.j, 60, yellow);
00398                         usleep(1000000);
00399 
00400                         // display information
00401                         d->clearScreen();
00402                         if (status == -1) {
00403                                         d->displayText("Please be prepared", true, 0, 10);
00404                         } else {
00405                                 if (tt < 0.6)
00406                                         d->displayText("Good!", true, 0, 10);
00407                                 else
00408                                         d->displayText("You can do faster than that!", true, 0, 10);
00409                         }
00410 
00411                 }else{
00412                         //bug, die!!
00413                         d->displayColorDotFixation(x, y, PixRGB<byte>(0,0,255));
00414                         return(1);
00415                 } 
00416 
00417     // stop the eye tracker:
00418     usleep(50000);
00419     et->track(false);
00420 
00421                 // for displaying previous messages
00422                 usleep(600000);
00423 
00424                 /*
00425                 // used for auto fixation
00426                 int key = d->waitForKeyTimeout(2000);
00427                 if (key == 67 || key == 99 || key == 68 || key == 100){
00428                         d->pushEvent("Drift Correction");
00429                         LINFO("Drift Correction.  Press [Space] to continue, or [ESC] to calibrate.");
00430         et->recalibrate(d,13);                  
00431                 }*/
00432 
00433                 // display percentage completed
00434                 double completion = 100*((double)i+1)/double(ntrial);
00435                 //LINFO("completion %f, %f, %f", completion, (double)i+1, double(ntrial));
00436                 if ((int)completion % 5 == 0 && completion-(int)completion==0){
00437                         d->clearScreen();
00438                         d->displayText(sformat("%i %% completed", (int)completion), true, 0, 10);
00439                         usleep(1000000);
00440                 }
00441 
00442                 // take a break for a quarter of trials
00443                 if ((int)completion%25 == 0 && i<ntrial-1 && completion-(int)completion==0) {
00444                 d->clearScreen();
00445                 d->displayText("Please Take a Break", true, 0, 10);
00446                         LINFO("Break time.  Press [Space] to continue, or [ESC] to terminate the experiment.");
00447                 d->waitForKey(true);
00448                 d->displayText("Calibration", true, 0, 10);
00449       d->pushEventBegin("Calibration");
00450       et->calibrate(d);
00451       d->pushEventEnd("Calibration");
00452                 }
00453         }
00454 
00455   d->clearScreen();
00456   d->displayText("Experiment complete. Thank you!", true, 0, 10);
00457   d->waitForKey(true);
00458 
00459   // stop all our ModelComponents
00460   manager.stop();
00461 
00462   // all done!
00463   return 0;
00464 }
00465 
00466 // ######################################################################
00467 extern "C" int main(const int argc, char** argv)
00468 {
00469   // simple wrapper around submain() to catch exceptions (because we
00470   // want to allow PsychoDisplay to shut down cleanly; otherwise if we
00471   // abort while SDL is in fullscreen mode, the X server won't return
00472   // to its original resolution)
00473   try
00474     {
00475       return submain(argc, argv);
00476     }
00477   catch (...)
00478     {
00479       REPORT_CURRENT_EXCEPTION;
00480     }
00481 
00482   return 1;
00483 }
00484 
00485 // ######################################################################
00486 /* So things look consistent in everyone's emacs... */
00487 /* Local Variables: */
00488 /* indent-tabs-mode: nil */
00489 /* End: */
Generated on Sun May 8 08:04:12 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3