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: */