psycho-searchGaborOnline.C

Go to the documentation of this file.
00001 /*!@file AppPsycho/psycho-searchGaborOnline.C Psychophysics display for a search for a
00002   target that is presented to the observer prior to the search */
00003 
00004 // //////////////////////////////////////////////////////////////////// //
00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00006 // University of Southern California (USC) and the iLab at USC.         //
00007 // See http://iLab.usc.edu for information about this project.          //
00008 // //////////////////////////////////////////////////////////////////// //
00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00011 // in Visual Environments, and Applications'' by Christof Koch and      //
00012 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00013 // pending; application number 09/912,225 filed July 23, 2001; see      //
00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00015 // //////////////////////////////////////////////////////////////////// //
00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00017 //                                                                      //
00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00019 // redistribute it and/or modify it under the terms of the GNU General  //
00020 // Public License as published by the Free Software Foundation; either  //
00021 // version 2 of the License, or (at your option) any later version.     //
00022 //                                                                      //
00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00026 // PURPOSE.  See the GNU General Public License for more details.       //
00027 //                                                                      //
00028 // You should have received a copy of the GNU General Public License    //
00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00031 // Boston, MA 02111-1307 USA.                                           //
00032 // //////////////////////////////////////////////////////////////////// //
00033 //
00034 // Primary maintainer for this file: Farhan Baluch <fbaluch@usc.edu>
00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-searchGaborOnline.C $
00036 // $Id: psycho-searchGaborOnline.C 10794 2009-02-08 06:21:09Z itti $
00037 //
00038 
00039 #include "Component/ModelManager.H"
00040 #include "Image/ColorOps.H" // for makeRGB()
00041 #include "Image/CutPaste.H" // for inplacePaste()
00042 #include "Image/Image.H"
00043 #include "Image/MathOps.H"  // for inplaceSpeckleNoise()
00044 #include "Psycho/PsychoDisplay.H"
00045 #include "Psycho/EyeTrackerConfigurator.H"
00046 #include "Psycho/EyeTracker.H"
00047 #include "Psycho/PsychoOpts.H"
00048 #include "Component/EventLog.H"
00049 #include "Component/ComponentOpts.H"
00050 #include "Raster/Raster.H"
00051 #include "Util/MathFunctions.H"
00052 #include "Util/StringUtil.H"
00053 #include "Util/stats.H"
00054 #include "Util/Timer.H"
00055 #include "Image/ShapeOps.H"
00056 #include "Image/DrawOps.H"
00057 #include <ctype.h>
00058 #include <vector>
00059 #include <string>
00060 #include <fstream>
00061 #include <deque>
00062 
00063 using namespace std;
00064 
00065 //! number of frames in the mask
00066 #define NMASK 10
00067 
00068 struct gPatch
00069 {
00070     int patchNo;
00071     Point2D<int> pos;
00072 };
00073 
00074 
00075 // ######################################################################
00076 static int submain(const int argc, char** argv)
00077 {
00078   MYLOGVERB = LOG_INFO;  // suppress debug messages
00079 
00080   // Instantiate a ModelManager:
00081   ModelManager manager("Psycho Search");
00082 
00083   // Instantiate our various ModelComponents:
00084   nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager));
00085   manager.addSubComponent(d);
00086 
00087   nub::soft_ref<EyeTrackerConfigurator>
00088     etc(new EyeTrackerConfigurator(manager));
00089   manager.addSubComponent(etc);
00090 
00091   nub::soft_ref<EventLog> el(new EventLog(manager));
00092   manager.addSubComponent(el);
00093 
00094   manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy");
00095   manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN");
00096 
00097   // Parse command-line:
00098   if (manager.parseCommandLine(argc, argv, "<imagelist.txt> <subjectname>", 2, 2) == false)
00099     return(1);
00100 
00101   // hook our various babies up and do post-command-line configs:
00102   nub::soft_ref<EyeTracker> et = etc->getET();
00103   d->setEyeTracker(et);
00104   d->setEventLog(el);
00105   et->setEventLog(el);
00106   //ONLINE
00107   et->requestQuickEyeS();
00108 
00109   // let's get all our ModelComponent instances started:
00110   manager.start();
00111   string subjName =std::string(manager.getExtraArg(1).c_str());
00112 
00113   // let's pre-load all the image names so that we can randomize them later:
00114   FILE *f = fopen(manager.getExtraArg(0).c_str(), "r");
00115   if (f == NULL) LFATAL("Cannot read stimulus file");
00116   char line[1024];
00117   std::vector<std::string> ilist, tlist, rlist, slist;
00118 int correct = 0, accuracy = 100, total = 0;
00119 
00120   while(fgets(line, 1024, f))
00121     {
00122        std::vector<std::string> tokens;
00123      // each line has four filenames: first the imagelet that contains
00124       // only the target, second the image that contains the target in
00125       // its environment, third the image to report position of target
00126       //fourth the name of the spec file for the search array
00127       LINFO("line reads %s",line);
00128       split(line," ", std::back_inserter(tokens));
00129 
00130       // now line is at the imagelet, line2 at the image
00131       tlist.push_back(std::string(tokens[0]));
00132       ilist.push_back(std::string(tokens[1]));
00133       rlist.push_back(std::string(tokens[2]));
00134       slist.push_back(std::string(tokens[3]));
00135       LINFO("\nNew pair \nline1 reads: %s, \nline2 reads:%s, \nline 3 reads %s,\nline 4 reads %s,",tokens[0].c_str(), tokens[1].c_str(),tokens[2].c_str(), tokens[3].c_str());
00136 
00137     }
00138   fclose(f);
00139 
00140   // randomize stimulus presentation order:
00141   int nimg = ilist.size(); int imindex[nimg];
00142   for (int i = 0; i < nimg; i ++) imindex[i] = i;
00143   randShuffle(imindex, nimg);
00144 
00145   // let's display an ISCAN calibration grid:
00146   d->clearScreen();
00147   d->displayISCANcalib();
00148   d->waitForKey();
00149 
00150   // let's do an eye tracker calibration:
00151   d->displayText("<SPACE> to calibrate; other key to skip");
00152   int c = d->waitForKey();
00153   if (c == ' ') et->calibrateOnline(d);
00154 
00155   // we are ready to start:
00156   d->clearScreen();
00157 
00158   d->displayText("<SPACE> to start experiment");
00159   d->waitForKey();
00160 
00161 //store the latest 120 samples~500ms worth of data
00162   int sampleCnt=0,numSamples=50;
00163   std::deque<Point2D<int> > currentEyeSamples(numSamples);
00164   Point2D<int>meanFixation;
00165   stats<float> Stats;
00166 
00167 
00168   //******************* main loop:***********************//
00169   for (int im = 0; im < nimg; im++) {
00170     int imnum = imindex[im];
00171 
00172     // load up the images and show a fixation cross on a blank screen:
00173     d->clearScreen();
00174     LINFO("Loading '%s' / '%s'...", ilist[imnum].c_str(),tlist[imnum].c_str());
00175 
00176     // get the imagelet and place it at a random position:
00177 
00178     if(!Raster::fileExists(tlist[imnum]))
00179     {
00180       // stop all our ModelComponents
00181       manager.stop();
00182       LFATAL("i couldnt find image file %s", tlist[imnum].c_str());
00183     }
00184 
00185     Image< PixRGB<byte> > targetImg = Raster::ReadRGB(tlist[imnum]);
00186     Image< PixRGB<byte> > rndimg(d->getDims(), NO_INIT);
00187     rndimg.clear(d->getGrey());
00188     int rndx = 0,rndy = 0;
00189 
00190     SDL_Surface *surf1 = d->makeBlittableSurface(targetImg, true);
00191     char buf[256];
00192 
00193     if(!Raster::fileExists(ilist[imnum]))
00194     {
00195       manager.stop();
00196       LFATAL("i couldnt find image file %s", ilist[imnum].c_str());
00197     }
00198     Image<PixRGB<byte> > ArrayImg = Raster::ReadRGB(ilist[imnum]);
00199     SDL_Surface *surf2 = d->makeBlittableSurface(ArrayImg, true);
00200 
00201     //randShuffle(mindex, NMASK);
00202 
00203    // load up the reporting number image:
00204        if(!Raster::fileExists(rlist[imnum]))
00205     {
00206       // stop all our ModelComponents
00207       manager.stop();
00208       LFATAL(" i couldnt find image file %s", tlist[imnum].c_str());
00209     }
00210 
00211     Image< PixRGB<byte> > reportImg = Raster::ReadRGB(rlist[imnum]);
00212     //SDL_Surface *surf3 = d->makeBlittableSurface(reportImg, true);
00213 
00214     // give a chance to other processes if single-CPU:
00215     usleep(200000);
00216 
00217     // ready to go whenever the user is ready:
00218     d->displayFixationBlink();
00219     //d->waitForKey();
00220     d->waitNextRequestedVsync(false, true);
00221 
00222     //************************ Display target************************//
00223     sprintf(buf, "===== Showing imagelet: %s at (%d, %d) =====",
00224             tlist[imnum].c_str(), rndx, rndy);
00225 
00226     d->pushEvent(buf);
00227     d->displaySurface(surf1, 0, true);
00228     usleep(2000000);
00229 
00230     d->clearScreen();
00231     SDL_FreeSurface(surf1);
00232     //************************ Display array************************//
00233 
00234      // start the eye tracker:
00235     et->track(true);
00236 
00237     std::vector<std::string> stimNameTokens;
00238     split(ilist[imnum],"/",std::back_inserter(stimNameTokens));
00239 
00240     et->setCurrentStimFile(std::string(subjName + "_" + stimNameTokens[stimNameTokens.size()-1]));
00241     currentEyeSamples.resize(0);
00242     sampleCnt =0;
00243 
00244     // show the image:
00245       d->pushEvent(std::string("===== Showing search image: ") + ilist[imnum] +
00246                  std::string(" ====="));
00247 
00248 
00249     d->displaySurface(surf2, 0, true);
00250 
00251     Image<PixRGB<byte> > dispImage(1920,1080,NO_INIT);
00252     // wait for key; it will record reaction time in the logs:
00253     d->checkForKey();
00254     Timer timer(100);
00255     timer.reset();
00256     double startTime=timer.getSecs();
00257     bool timeOut=false;
00258     while(d->checkForKey() < 0 && !timeOut)
00259     {
00260       dispImage = ArrayImg;
00261       //ONLINE
00262       Point2D<int> tempEye = et->getCalibEyePos();
00263       if(tempEye.i >0 && tempEye.i <1960 && tempEye.j>0 &&tempEye.j<1080)
00264         currentEyeSamples.push_back(tempEye);
00265       // Point2D<int> tempEye(960,540);
00266       // currentEyeSamples.push_back(tempEye);
00267       //drawCircle(dispImage, Point2D<int> (currentEyeSamples.back()),5,PixRGB<byte>(255,255,255));
00268       drawCircle(dispImage, tempEye,5,PixRGB<byte>(255,255,255));
00269       surf2 = d->makeBlittableSurface(dispImage,true);
00270       d->displaySurface(surf2,-2);
00271       LINFO("getting live eyepos %d,%d",currentEyeSamples.back().i,
00272               currentEyeSamples.back().j);
00273 
00274       if(currentEyeSamples.size() ==(size_t)numSamples)
00275         {
00276           currentEyeSamples.pop_front();
00277           sampleCnt=numSamples;
00278         }
00279       if(sampleCnt < numSamples);
00280        sampleCnt++;
00281 
00282       SDL_FreeSurface(surf2);
00283       if(timer.getSecs() >= startTime + 10)
00284         timeOut=true;
00285     }
00286 
00287 
00288     // stop the eye tracker:
00289     et->track(false); //we just want to record eye movements while subjects view the array
00290     LINFO("getting out of track mode");
00291     LINFO("currenteye samples available %d",(int)currentEyeSamples.size());
00292     //lets take the mean of the last 120 samples and assume that is the target
00293     //d::deque<Point2D<int> >::const_iterator itr = currentEyeSamples.begin();
00294     std::vector<float> xVals(sampleCnt),yVals(sampleCnt);
00295     float xMean,yMean;
00296 
00297     Point2D<int>temp;
00298     for(int i =0;i <(int)currentEyeSamples.size();i++) //sampleCnt);
00299     {
00300       LINFO("accessing front cnt=%d %d",i,currentEyeSamples[i].i);
00301       temp = currentEyeSamples[i];
00302       LINFO("\n counting %d,%d",temp.i,temp.j);
00303         xVals[i]=temp.i;
00304         yVals[i]=temp.j;
00305     }
00306 
00307     if(sampleCnt > 0)
00308       {
00309         xMean = Stats.mean(xVals);
00310         yMean = Stats.mean(yVals);
00311       }
00312     else
00313       {
00314         xMean = 0;
00315         yMean=0;
00316       }
00317 
00318     LINFO("mean target %f,%f",xMean,yMean);
00319     //check user response
00320     char tmp[40];
00321 
00322     //lets open the spec file and extract the target number
00323     ifstream specFile(slist[imnum].c_str(), ifstream::in);
00324 
00325     bool found =false;
00326     string testLine;
00327     std::vector<std::string> specTokens,xTokens,yTokens,gTokens;
00328     std::vector<gPatch> gPatches(32);
00329     int targetNum=0,patchCnt=0;
00330 
00331     LINFO("opening specfile");
00332    if(specFile.is_open())
00333       {
00334         while(!specFile.eof() && !found)
00335           {
00336             getline(specFile, testLine);
00337             LINFO("got line %s",testLine.c_str());
00338             string::size_type loc = testLine.find("target", 0);
00339             string::size_type loc2 = testLine.find("Gabor",0);
00340             string::size_type loc3 = testLine.find("Xpos",0);
00341             string::size_type loc4 = testLine.find("Ypos",0);
00342 
00343             if(loc != string::npos)
00344             {
00345                 split(testLine," ", std::back_inserter(specTokens));
00346                 targetNum = atoi(specTokens[1].c_str());
00347                 specTokens.resize(0);
00348                 found = true;
00349             }
00350 
00351             if(loc2 != string::npos)
00352             {
00353 
00354                     split(testLine," ", std::back_inserter(gTokens));
00355                     gPatches[patchCnt].patchNo = atoi(gTokens[1].c_str());
00356                     LINFO("gPatches[%d].patchNo = %d",patchCnt,gPatches[patchCnt].patchNo);
00357                     gTokens.resize(0);
00358 
00359             }
00360 
00361             if(loc3 != string::npos)
00362             {
00363                 split(testLine,"\t", std::back_inserter(xTokens));
00364                 gPatches[patchCnt].pos.i = atoi(xTokens[1].c_str())+120;
00365                 LINFO("gPatches[%d].pos.i = %d",patchCnt,gPatches[patchCnt].pos.i);
00366                 xTokens.resize(0);
00367             }
00368             if(loc4 != string::npos)
00369             {
00370                 split(testLine,"\t", std::back_inserter(yTokens));
00371                 gPatches[patchCnt].pos.j = atoi(yTokens[1].c_str())+120;
00372                 LINFO("gPatches[%d].pos.j = %d",patchCnt,gPatches[patchCnt].pos.j);
00373                 yTokens.resize(0);
00374                 if(patchCnt < 31)
00375                       patchCnt++;
00376              }
00377 
00378            }
00379 
00380         LINFO("specfile parsed finding target x y");
00381 
00382          if(!found)
00383           {
00384             manager.stop();
00385             LFATAL("couldnt find the target number from spec file");
00386           }
00387 
00388 
00389        //now lets find which patch they were looking at and check if
00390        //that was a target
00391         std::vector<float> euclidDists(32);
00392         float minDist=500000;
00393         int minIdx=-1;
00394 
00395 
00396         for(int i=0;i<32;i++)
00397         {
00398           LINFO("finding distance between fix %f,%f and gabor%d,%d",xMean,yMean,gPatches[i].pos.i,gPatches[i].pos.j);
00399             euclidDists[i] = gPatches[i].pos.distance(Point2D<int>((int)xMean,(int)yMean));
00400             if(euclidDists[i] < minDist)
00401             {
00402                 minDist = euclidDists[i];
00403                 minIdx = i;
00404             }
00405             LINFO("euclidDist %d %f",i,euclidDists[i]);
00406 
00407         }
00408         if(minIdx == -1)
00409           {  minIdx =0;
00410             LINFO("cannot find a match in euclid distance");
00411           }
00412         LINFO("found min IDx to be %d", minIdx);
00413 
00414         int txPos = gPatches[minIdx].pos.i, tyPos = gPatches[minIdx].pos.j;
00415         SDL_Surface *surf4;
00416         //while(d->checkForKey() < 0)
00417         // {
00418         for(int i=0;i<20;i++)
00419           {
00420 
00421             // int txPos = gPatches[i].pos.i, tyPos = gPatches[i].pos.j;
00422             //    LINFO("txpos typos (%d,%d)",txPos,tyPos);
00423             drawCircle(ArrayImg, Point2D<int> (txPos,tyPos),5,PixRGB<byte>(255,255,255));
00424             surf4 = d->makeBlittableSurface(ArrayImg, true);
00425             d->displaySurface(surf4,-2);
00426             drawCircle(ArrayImg, Point2D<int> (txPos,tyPos),5,PixRGB<byte>(255,0,0));
00427             //drawRect(ArrayImg,Rectangle::tlbrI(tyPos-(240/2),txPos - (240/2),tyPos+(240/2), txPos+(240/2)),PixRGB<byte>(0,0,0),5);
00428             surf4 = d->makeBlittableSurface(ArrayImg, true);
00429             d->displaySurface(surf4,-2);
00430           }
00431 
00432 
00433              SDL_FreeSurface(surf4);
00434             // }
00435 
00436        LINFO("after showing selection");
00437 
00438         total++;
00439         if (minIdx == targetNum)
00440             {
00441               correct ++;
00442               accuracy = correct * 100 / total;
00443               sprintf(tmp, "Correct! Accuracy is %d%%", accuracy);
00444               d->displayText(tmp);
00445               d->pushEvent(std::string("===== Correct ====="));
00446               usleep(500000);
00447             }
00448         else
00449             {
00450               accuracy = correct * 100 / total;
00451               sprintf(tmp, "Wrong! Accuracy is %d%%", accuracy);
00452               d->displayText(tmp);
00453               d->pushEvent(std::string("===== Wrong ====="));
00454               usleep(500000);
00455             }
00456         specFile.close();
00457       }
00458 
00459   else
00460       {
00461         d->displayText("no target file found!");
00462         LFATAL("couldnt open the file -%s-",slist[imnum].c_str());
00463       }
00464 
00465    LINFO("out of spec file zone");
00466    // free the imagelet and image:
00467    //SDL_FreeSurface(surf1); SDL_FreeSurface(surf2); //SDL_FreeSurface(surf3);
00468    //SDL_FreeSurface(surf4);
00469 
00470 
00471     // let's do a quiinckie eye tracker calibration once in a while:
00472     /*if (im > 0 && im % 20 == 0) {
00473       d->displayText("Ready for quick recalibration");
00474       d->waitForKey();
00475       d->displayEyeTrackerCalibration(3, 3);
00476       d->clearScreen();
00477       d->displayText("Ready to continue with the images");
00478       d->waitForKey();
00479       }*/
00480 
00481     //allow for a break after 50 trials then recalibrate
00482 
00483        if (im==50)
00484       {
00485         d->displayText("You may take a break press space to continue when ready");
00486         d->waitForKey();
00487         // let's display an ISCAN calibration grid:
00488         d->clearScreen();
00489         d->displayISCANcalib();
00490         d->waitForKey();
00491         // let's do an eye tracker calibration:
00492         d ->displayText("<SPACE> to calibrate; other key to skip");
00493         int c = d->waitForKey();
00494         if (c == ' ') et->calibrateOnline(d);
00495         }
00496   }
00497 
00498 
00499   //et->recalibrate(d,20);
00500   d->clearScreen();
00501   d->displayText("Experiment complete. Thank you!");
00502   d->waitForKey();
00503 
00504   // stop all our ModelComponents
00505   manager.stop();
00506 
00507   // all done!
00508   return 0;
00509 }
00510 
00511 
00512 
00513 // ######################################################################
00514 
00515 extern "C" int main(const int argc, char** argv)
00516 {
00517   // simple wrapper around submain() to catch exceptions (because we
00518   // want to allow PsychoDisplay to shut down cleanly; otherwise if we
00519   // abort while SDL is in fullscreen mode, the X server won't return
00520   // to its original resolution)
00521   try
00522     {
00523       return submain(argc, argv);
00524     }
00525   catch (...)
00526     {
00527       REPORT_CURRENT_EXCEPTION;
00528     }
00529 
00530   return 1;
00531 }
00532 // ######################################################################
00533 /* So things look consistent in everyone's emacs... */
00534 /* Local Variables: */
00535 /* indent-tabs-mode: nil */
00536 /* End: */
Generated on Sun May 8 08:40:09 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3