EyeTrackerISCAN.C

Go to the documentation of this file.
00001 /*!@file Psycho/EyeTrackerISCAN.C Abstraction for an ISCAN eye-tracker */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the 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/Psycho/EyeTrackerISCAN.C $
00035 // $Id: EyeTrackerISCAN.C 14159 2010-10-22 04:04:17Z ilink $
00036 //
00037 
00038 #ifndef PSYCHO_EYETRACKERISCAN_C_DEFINED
00039 #define PSYCHO_EYETRACKERISCAN_C_DEFINED
00040 
00041 #include "Psycho/EyeTrackerISCAN.H"
00042 
00043 #include "Component/OptionManager.H"
00044 #include "Devices/ParPort.H"
00045 #include "Devices/Serial.H"
00046 #include "Psycho/PsychoOpts.H"
00047 #include "Psycho/PsychoDisplay.H"
00048 #include "Util/sformat.H"
00049 #include "Util/stats.H"
00050 #include <cmath>
00051 #include "Image/Point2D.H"
00052 #include "Image/DrawOps.H"
00053 #include "Image/AffineTransform.H"
00054 #include <fstream>
00055 
00056 // ######################################################################
00057 EyeTrackerISCAN::EyeTrackerISCAN(OptionManager& mgr,
00058                                  const std::string& descrName,
00059                                  const std::string& tagName) :
00060   EyeTracker(mgr, descrName, tagName),
00061   itsParTrig(&OPT_EyeTrackerParTrig, this),
00062   itsSerDev(&OPT_EyeTrackerSerDev, this),
00063   itsParDev(&OPT_EyeTrackerParDev, this),
00064   itsSerial(new Serial(mgr, "ISCAN Serial Port", "ISCANSerial")),
00065   itsParPort(new ParPort(mgr, "ISCAN Parallel Port", "ISCANParPort")),
00066   itsRecalibCount(0)
00067 {
00068   addSubComponent(itsSerial);
00069   addSubComponent(itsParPort);
00070   itsRequestQuickEyeS = false;
00071 
00072 }
00073 
00074 // ######################################################################
00075 EyeTrackerISCAN::~EyeTrackerISCAN()
00076 {  }
00077 
00078 // ######################################################################
00079 void EyeTrackerISCAN::start1()
00080 {
00081   // configure our serial and parallel ports:
00082   itsSerial->setModelParamVal("DevName", itsSerDev.getVal());
00083   itsParPort->setModelParamVal("ISCANParPortDevName", itsParDev.getVal());
00084   itsSerial->configure(itsSerDev.getVal().c_str(),
00085                        115200, "8N1", false, false, 0);
00086 
00087   EyeTracker::start1();
00088 }
00089 
00090 // ######################################################################
00091 void EyeTrackerISCAN::start2()
00092 {
00093   // if using parallel trigger, be sure to initialize in non-tracking mode:
00094   if (itsParTrig.getVal())
00095     itsParPort->WriteData(255, 255); // turn all data bits to on
00096 
00097   EyeTracker::start2();
00098 }
00099 
00100 // ######################################################################
00101 void EyeTrackerISCAN::calibrate(nub::soft_ref<PsychoDisplay> d)
00102 {
00103   // let's display an ISCAN calibration grid:
00104   d->clearScreen();
00105   d->displayISCANcalib();
00106   d->waitForKey(true);
00107 
00108   // now run a 9-point calibration:
00109   d->clearScreen();
00110   d->displayText("<SPACE> to calibrate; other key to skip");
00111   int c = d->waitForKey(true);
00112   if (c == ' ') d->displayEyeTrackerCalibration(3, 3);
00113 }
00114 
00115 // ######################################################################
00116 void EyeTrackerISCAN::recalibrate(nub::soft_ref<PsychoDisplay> d, int repeats)
00117 {
00118   ++itsRecalibCount;
00119   if (itsRecalibCount == repeats)
00120     {
00121       itsRecalibCount = 0;
00122       d->clearScreen();
00123       d->displayText("Ready for quick recalibration");
00124       d->waitForKey(true);
00125       d->clearScreen();
00126       d->displayISCANcalib();
00127       d->waitForKey(true);
00128       d->displayEyeTrackerCalibration(3, 3);
00129       d->clearScreen();
00130       d->displayText("Ready to continue");
00131       d->waitForKey(true);
00132     }
00133 }
00134 
00135 // ######################################################################
00136 void EyeTrackerISCAN::calibrateOnline(nub::soft_ref<PsychoDisplay> d)
00137 {
00138 
00139 
00140         CalibrationTransform::Data pts;
00141         pts = getCalibrationSet(d);
00142         LINFO("got calibration set computing transform");
00143         itsAffine.computeTransform(pts);
00144 }
00145 
00146 
00147 // ######################################################################
00148 void EyeTrackerISCAN::startTracking()
00149 {
00150   if (itsParTrig.getVal())
00151     {
00152         if(itsRequestQuickEyeS)
00153         {
00154           LINFO("\n creating new Thread now");
00155             //clear the eyePosEvents buffer and create the thread
00156             itsEyePosEvents.resize(0);
00157             itsEyePosEvents.reserve(240 * 60 * 60);
00158 
00159             if (0 != pthread_create(&itsEyePosPollThread, NULL,
00160                                     &EyeTrackerISCAN::eyePosPollThread,
00161                                     (void*)(this)))
00162                 LFATAL("Cannot create thread");
00163 
00164             // start tracker using parallel port:
00165             itsParPort->WriteData(255, 0); // turn all data bits to off
00166         }
00167         else
00168         {         // start tracker using parallel port:
00169           LINFO("requestEyeS was false");
00170             itsParPort->WriteData(255, 0); // turn all data bits to off
00171         }
00172 
00173     }
00174   else
00175     {
00176       // start tracker using serial port:
00177       LINFO("using serial port so screwed");
00178       char cmd[1]; cmd[0] = 132; // ISCAN start command
00179       itsSerial->write(cmd, 1);
00180     }
00181 }
00182 
00183 // ######################################################################
00184 void EyeTrackerISCAN::stopTracking()
00185 {
00186   if (itsParTrig.getVal())
00187     {
00188          if(itsRequestQuickEyeS)
00189         {
00190 
00191                 //stop thread recording live eyePos
00192             if (0 != pthread_cancel(itsEyePosPollThread))
00193                 LFATAL("pthread_cancel failed");
00194 
00195             if (0 != pthread_join(itsEyePosPollThread, NULL))
00196                 LFATAL("pthread_join failed");
00197 
00198                 // stop tracker using parallel port:
00199             itsParPort->WriteData(255, 255); // turn all data bits to on
00200 
00201              //#####################theEyeSFiledump##################/
00202 
00203                 //TODO:dumpdata to eyeS file
00204                 //iterate through eyePosevents
00205                 //format time write to eyeData class use IO for eyeS
00206 
00207 
00208             if (itsEyePosEvents.empty() == false)
00209             {
00210                 const char *fname = getCurrentStimFile().c_str();
00211                 std::ofstream ofs(fname);
00212                 if (!ofs.is_open())
00213                     LERROR("Couldn't open file '%s' for writing.", fname);
00214                 else
00215                 {
00216                     std::vector<EyePosEvent>::const_iterator itr = itsEyePosEvents.begin();
00217 
00218                     while (itr != itsEyePosEvents.end())
00219                     {
00220                         const uint64 t = itr->tim;
00221                         const int usec = int(t % 1000ULL);
00222                         const int msec = int((t / 1000ULL) % 1000ULL);
00223                         const int sec  = int((t / 1000000ULL) % 60ULL);
00224                         const int minu = int((t / 60000000ULL) % 60ULL);
00225                         const int hour = int(t / 3600000000ULL);
00226                         ofs << sformat("%03d:%02d:%02d.%03d.%03d",
00227                                        hour, minu, sec, msec, usec)
00228                             << " " << itr->pt.i << " "  << itr->pt.j << std::endl;
00229                         ++ itr;
00230                     }
00231                     ofs.close();
00232                     LINFO("Saved log to '%s'", fname);
00233                 }
00234                                                                                                          }
00235 
00236 
00237 
00238             //################endfiledump######################
00239 
00240 
00241         }
00242          else
00243          {
00244             // stop tracker using parallel port:
00245             itsParPort->WriteData(255, 255); // turn all data bits to on
00246          }
00247 
00248 
00249 
00250     }
00251   else
00252     {
00253       // stop tracker using serial port:
00254       char cmd[1]; cmd[0] = 136; // ISCAN stop command
00255       itsSerial->write(cmd, 1);
00256     }
00257 }
00258 
00259 // ######################################################################
00260 bool EyeTrackerISCAN::isFixating()
00261 {
00262   LFATAL("Unimplemented for now");
00263   return false;
00264 }
00265 
00266 // ######################################################################
00267 bool EyeTrackerISCAN::isSaccade()
00268 {
00269   LFATAL("Unimplemented for now");
00270   return false;
00271 }
00272 
00273 // ######################################################################
00274 Point2D<int> EyeTrackerISCAN::getEyePos() const
00275 {
00276   /*if (!itsParTrig.getVal())
00277         LFATAL("must use parallel-port triggering");
00278 
00279     int val = itsCurrentRawEyePos.atomic_get();
00280         //   return itsCurrentRawEyePos.set(val & 0xfff,
00281         //                       (val & 0xfff000) >> 12);
00282 
00283     return Point2D<int>(val & 0xfff,(val & 0xfff000 >> 12));
00284   */
00285 
00286   unsigned char buf[256];
00287   int n;
00288   Point2D<int> eyePos(-1,-1);
00289   //prefix byte 0 = prefix byte 1 =  hex value = 0x44
00290 
00291   n =   itsSerial->read(buf,256);
00292   bool gotHeader =false;
00293 
00294   for(int i=0;i<n-1;i++)
00295     {
00296 
00297       if(gotHeader && n-i>5) //ensure enough data to read
00298         {
00299           eyePos.i = buf[i+2] <<8;
00300           eyePos.i += buf[i+1];
00301           eyePos.j = buf[i+4] <<8;
00302           eyePos.j += buf[i+3];
00303           break;
00304 
00305         }
00306 
00307       if(buf[i] == 0x44 && buf[i+1] == 0x44)
00308         gotHeader = true;
00309     }
00310 
00311       return eyePos;
00312 }
00313 
00314 // ######################################################################
00315 Point2D<int> EyeTrackerISCAN::getFixationPos() const
00316 {
00317   LFATAL("Unavailable on DML tracker, sorry.");
00318   return Point2D<int>(0, 0);
00319 }
00320 
00321 // ######################################################################
00322 Point2D<int> EyeTrackerISCAN::getCalibEyePos()
00323 {
00324          if (!itsParTrig.getVal())
00325              LFATAL("must use parallel-port triggering");
00326 
00327          int val = itsCurrentCalibEyePos.atomic_get();
00328              //return itsCurrentCalibEyePos.set(val & 0xfff,
00329              //                    (val & 0xfff000) >> 12);
00330          int val2 = itsCurrentRawEyePos.atomic_get();
00331          LINFO("eye pos at getRawEyePos %d,%d",val2 & 0xfff,(val2 & 0xfff000 >> 12));
00332          LINFO("Eye Pos at thread calib = (%d,%d)",val & 0xfff,(val & 0xfff000 >> 12));
00333          //return Point2D<int>(val & 0xfff,(val & 0xfff000 >> 12));
00334          return Point2D<int>(itsCurrentCalibEyePosX.atomic_get(),itsCurrentCalibEyePosY.atomic_get());
00335 
00336         //Point2D<double> rawPos = Point2D<double>(getEyePos());
00337         //return Point2D<int>(itsAffine.getCalibrated(rawPos));
00338 }
00339 
00340 // ######################################################################
00341 CalibrationTransform::Data EyeTrackerISCAN::getCalibrationSet(nub::soft_ref<PsychoDisplay> d) const
00342 {
00343     /*get the calibration data for online calibration*/
00344 
00345     LINFO("\n getting calibration set...");
00346 
00347 
00348      //start with simple test to see if someone is fixating then
00349     //say fixating with location
00350 
00351     int fixWindow = 75;
00352     std::vector<Point2D<int> > temp(fixWindow);
00353     std::vector<float> tempX(fixWindow);
00354     std::vector<float> tempY(fixWindow);
00355     float xMean,yMean,xStd,yStd,xVar,yVar;
00356 
00357     //you need a minimum of 4 calibration points to ensure inverse matrix
00358     //computations are legal
00359 
00360     const int nptshoriz=3, nptsvertic=3;
00361     const int npts = nptshoriz*nptsvertic;
00362     Image<double> displayImage(1920,1080,ZEROS);
00363     Dims dispDims = displayImage.getDims();
00364     int w=dispDims.w(), h = dispDims.h();
00365 
00366 
00367     int deltax = w / (nptshoriz + 1), deltay = h / (nptsvertic + 1);
00368 
00369     // list all the points we want:
00370     std::vector<Point2D<int> > pts;
00371     for (int j = deltay-1; j < h - deltay; j += deltay)
00372       for (int i = deltax-1; i < w - deltax; i += deltax)
00373         pts.push_back(Point2D<int>(i, j));
00374 
00375     Point2D<int> tempPt,tempCalibPt;
00376     std::vector<Point2D<int> > eyeMeans(npts);
00377     stats<float> Stats;
00378     bool fixated;
00379     d->clearScreen();
00380     char tmp[50];
00381     bool happy=false; //happy with calib or not
00382     double diffX=0.0F,diffY=0.0F;
00383     AffineTransform tempAffine;
00384     CalibrationTransform::Data finalPts;
00385 
00386 
00387     while(!happy)
00388     {
00389       //startTracking();
00390       //event log code marked as EL-code eventually should be removed
00391       //EL-code
00392       d->pushEventBegin("EyeTrackerCalibration");
00393        //~EL-code
00394       CalibrationTransform::Data firstFix;
00395 
00396         for (int j=0;j<npts;j++)
00397         {
00398                 d->clearScreen();
00399                 tempPt = pts[j];
00400                 //d->drawCalibPoint(tempPt);
00401                 //rintf(tmp,"press key when ready");
00402                 d->displayFixation();
00403                 d->waitForKey();
00404                 d->displayFixationBlink();
00405                 d->clearScreen();
00406                 fixated = false;
00407                 d->drawCalibPoint(tempPt);
00408                 //EL-code
00409                 d->pushEventBegin(sformat("eyeTrackerCalibration at (%d, %d)", tempPt.i, tempPt.j));
00410                        while(fixated==false)
00411                 {
00412 
00413 
00414                            for(int i=0; i < fixWindow; i++)
00415                             {
00416                                 temp[i] = getEyePos();
00417                                 if((temp[i].i < 0) || (temp[i].j<0))
00418                                   i--;
00419                                 else
00420                                 {
00421                                   LINFO("I got eyePos %d,%d",temp[i].i, temp[i].j);
00422                                   tempX[i] = temp[i].i;
00423                                   tempY[i] = temp[i].j;
00424                                 }
00425                             }
00426 
00427                                xMean = Stats.mean(tempX);
00428                             yMean = Stats.mean(tempY);
00429                                xStd = Stats.findS(tempX,xMean);
00430                             xVar = Stats.S2;
00431                             yStd = Stats.findS(tempY,yMean);
00432                               yVar = Stats.S2;
00433 
00434 
00435                         if((xVar < 5.0) && (yVar < 5.0) && (xMean > 0) && (yMean >0))
00436                         {
00437                                 //EL-code
00438                                 d->pushEventEnd(sformat("eyeTrackerCalibration at (%d, %d)", tempPt.i, tempPt.j));
00439                                 fixated = true;
00440                                  d->displayText("Found stable fixation");
00441                                 LINFO("####Calibration Point %d #######",j+1);
00442                                 LINFO("found fixation varX = %f, var Y =%f....meanX = %f meanY = %f ",xStd,yStd,xMean,yMean);
00443                                 LINFO("raw (%f,%f) scr(%d,%d)",xMean,yMean,pts[j].i,pts[j].j);
00444                                 eyeMeans[j] = Point2D<int>((int)xMean,(int)yMean);
00445                                 firstFix.addData(Point2D<double>(xMean,yMean),Point2D<double>(tempPt));
00446                         }
00447 
00448                     }
00449                     }
00450         //lets display a grid show original screen points in red squares and calib pts
00451         //in blue
00452         //EL-Code
00453         d->pushEventEnd("EyeTrackerCalibration");
00454         //    stopTracking();
00455         tempAffine.computeTransform(firstFix);
00456         for (int j=0;j<npts;j++)
00457         {
00458                 tempPt = pts[j];
00459                 d->drawCalibPoint(tempPt);
00460                 tempCalibPt = Point2D<int>(tempAffine.getCalibrated(Point2D<double>(eyeMeans[j])));
00461                 d->drawPointColor(tempCalibPt,PixRGB<byte>(0,0,255));
00462                 diffX += abs(tempPt.i - tempCalibPt.i);
00463                 diffY += abs(tempPt.j - tempCalibPt.j);
00464                 LINFO("raw(%d,%d),calib(%d,%d)",tempPt.i,tempPt.j,tempCalibPt.i,tempCalibPt.j);
00465 
00466         }
00467         d->waitForKey();
00468         sprintf(tmp,"avg X difference = %f, avg Y difference = %f \n--press 5  to repeat Calibration",diffX/npts,diffY/npts);
00469         d->displayText(tmp);
00470         int c=d->waitForKey(true);
00471         if(c != '5')
00472         {
00473                 happy = true;
00474                 finalPts= firstFix;
00475                 LINFO("setfirstfix");
00476 
00477         }
00478      }
00479 
00480     d->clearScreen();
00481     d->displayText("Calibrated!");
00482     LINFO("calibration ended");
00483     return finalPts;
00484 }
00485 
00486 
00487 // ####################################################################
00488 void EyeTrackerISCAN::requestQuickEyeS()
00489 {
00490     itsRequestQuickEyeS=true;
00491 }
00492 
00493 //#####################################################################
00494 
00495 
00496 void* EyeTrackerISCAN::eyePosPollThread(void* p)
00497 {
00498   EyeTrackerISCAN* et = static_cast<EyeTrackerISCAN*>(p);
00499 
00500   Timer timer(1000000);
00501 
00502   timer.reset();
00503 
00504   while (true)
00505     {
00506       // read one sample from the serial port
00507       Point2D<int> raw;
00508 
00509           //###############serial reading code###################/
00510       unsigned char buf[256];
00511       int n;
00512       //prefix byte 0 = prefix byte 1 =  hex value = 0x44
00513 
00514       n =   et->itsSerial->read(buf,256);
00515       bool gotHeader =false;
00516 
00517       for(int i=0;i<n-1;i++)
00518       {
00519 
00520           if(gotHeader && n-i>5) //ensure enough data to read
00521           {
00522               raw.i = buf[i+2] <<8;
00523               raw.i += buf[i+1];
00524               raw.j = buf[i+4] <<8;
00525               raw.j += buf[i+3];
00526               break;
00527           }
00528 
00529           if(buf[i] == 0x44 && buf[i+1] == 0x44)
00530               gotHeader = true;
00531       }
00532 
00533 
00534       EyePosEvent ev;
00535       ev.tim = timer.get();
00536       ev.pt = Point2D<int16>(et->itsAffine.getCalibrated(Point2D<double>(raw)));
00537       //LINFO("Eye Pos at thread raw = (%d,%d) calib=(%d,%d)",raw.i,raw.j, ev.pt.i,ev.pt.j);
00538       et->itsEyePosEvents.push_back(ev);
00539       et->itsCurrentRawEyePos.atomic_set(raw.i + (raw.j << 12));
00540       et->itsCurrentCalibEyePos.atomic_set(ev.pt.i + (ev.pt.j << 12));
00541       et->itsCurrentCalibEyePosX.atomic_set(ev.pt.i);
00542       et->itsCurrentCalibEyePosY.atomic_set(ev.pt.j);
00543     }
00544 
00545   return NULL;
00546 }
00547 
00548 // #####################################################################
00549 
00550 
00551 /* So things look consistent in everyone's emacs... */
00552 /* Local Variables: */
00553 /* mode: c++ */
00554 /* indent-tabs-mode: nil */
00555 /* End: */
00556 
00557 #endif // PSYCHO_EYETRACKERISCAN_C_DEFINED
Generated on Sun May 8 08:41:13 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3