00001 /*!@file AppPsycho/psycho-search.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: Laurent Itti <itti@usc.edu> 00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-searchGabor_elno.C $ 00036 // $Id: psycho-searchGabor_elno.C 12962 2010-03-06 02:13:53Z irock $ 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/Types.H" 00054 #include "Video/VideoFrame.H" 00055 #include "rutz/time.h" 00056 #include "Raster/Raster.H" 00057 #include <deque> 00058 #include "Media/MPEGStream.H" 00059 #include "Media/MediaOpts.H" 00060 00061 #define CACHELEN 150 00062 00063 #include <ctype.h> 00064 #include <vector> 00065 #include <string> 00066 #include <fstream> 00067 00068 using namespace std; 00069 00070 //! number of frames in the mask 00071 #define NMASK 10 00072 00073 // ###################################################################### 00074 static bool cacheFrame(nub::soft_ref<InputMPEGStream>& mp, 00075 std::deque<VideoFrame>& cache) 00076 { 00077 const VideoFrame frame = mp->readVideoFrame(); 00078 if (!frame.initialized()) return false; // end of stream 00079 00080 cache.push_front(frame); 00081 return true; 00082 } 00083 00084 static int submain(const int argc, char** argv) 00085 { 00086 MYLOGVERB = LOG_INFO; // suppress debug messages 00087 00088 // Instantiate a ModelManager: 00089 ModelManager manager("Psycho Movie"); 00090 00091 // Instantiate our various ModelComponents: 00092 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00093 manager.addSubComponent(d); 00094 00095 nub::soft_ref<EyeTrackerConfigurator> 00096 etc(new EyeTrackerConfigurator(manager)); 00097 manager.addSubComponent(etc); 00098 00099 nub::soft_ref<EventLog> el(new EventLog(manager)); 00100 manager.addSubComponent(el); 00101 00102 nub::soft_ref<InputMPEGStream> mp (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream")); 00103 manager.addSubComponent(mp); 00104 manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true"); 00105 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00106 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00107 00108 00109 00110 // Parse command-line: 00111 if (manager.parseCommandLine(argc, argv, "<imagelist.txt>", 1, 1) == false) 00112 return(1); 00113 00114 // hook our various babies up and do post-command-line configs: 00115 nub::soft_ref<EyeTracker> et = etc->getET(); 00116 d->setEyeTracker(et); 00117 d->setEventLog(el); 00118 et->setEventLog(el); 00119 00120 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0) 00121 d->setModelParamVal("SDLslaveMode", true); 00122 00123 // let's get all our ModelComponent instances started: 00124 manager.start(); 00125 00126 00127 // let's pre-load all the image names so that we can randomize them later: 00128 FILE *f = fopen(manager.getExtraArg(0).c_str(), "r"); 00129 if (f == NULL) LFATAL("Cannot read stimulus file"); 00130 char line[1024]; 00131 std::vector<std::string> ilist, tlist, rlist, slist, vlist; 00132 00133 int correct = 0, accuracy = 100, total = 0; 00134 00135 while(fgets(line, 1024, f)) 00136 { 00137 std::vector<std::string> tokens; 00138 // each line has four filenames: first the imagelet that contains 00139 // only the target, second the image that contains the target in 00140 // its environment, third the image to report position of target 00141 //fourth the name of the spec file for the search array 00142 LINFO("line reads %s",line); 00143 split(line," ", std::back_inserter(tokens)); 00144 00145 // now line is at the imagelet, line2 at the image 00146 tlist.push_back(std::string(tokens[0])); 00147 ilist.push_back(std::string(tokens[1])); 00148 rlist.push_back(std::string(tokens[2])); 00149 slist.push_back(std::string(tokens[3])); 00150 vlist.push_back(std::string(tokens[4])); 00151 00152 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()); 00153 00154 } 00155 fclose(f); 00156 00157 // randomize stimulus presentation order: 00158 int nimg = ilist.size(); int imindex[nimg]; 00159 for (int i = 0; i < nimg; i ++) imindex[i] = i; 00160 randShuffle(imindex, nimg); 00161 00162 // let's display an ISCAN calibration grid: 00163 d->clearScreen(); 00164 d->displayISCANcalib(); 00165 d->waitForKey(); 00166 00167 // let's do an eye tracker calibration: 00168 d->displayText("<SPACE> to calibrate; other key to skip"); 00169 int c = d->waitForKey(); 00170 if (c == ' ') d->displayEyeTrackerCalibration(3, 3); 00171 00172 // we are ready to start: 00173 d->clearScreen(); 00174 00175 d->displayText("<SPACE> to start experiment"); 00176 d->waitForKey(); 00177 00178 //******************* main loop:***********************// 00179 for (int im = 0; im < nimg; im++) { 00180 int imnum = imindex[im]; 00181 00182 // load up the images and show a fixation cross on a blank screen: 00183 d->clearScreen(); 00184 LINFO("Loading '%s' / '%s'...", ilist[imnum].c_str(),tlist[imnum].c_str()); 00185 00186 // get the imagelet and place it at a random position: 00187 00188 if(!Raster::fileExists(tlist[imnum])) 00189 { 00190 // stop all our ModelComponents 00191 manager.stop(); 00192 LFATAL("i couldnt find image file %s", tlist[imnum].c_str()); 00193 } 00194 00195 Image< PixRGB<byte> > img = Raster::ReadRGB(tlist[imnum]); 00196 Image< PixRGB<byte> > rndimg(d->getDims(), NO_INIT); 00197 rndimg.clear(d->getGrey()); 00198 int rndx = 0,rndy = 0; 00199 00200 SDL_Surface *surf1 = d->makeBlittableSurface(img, true); 00201 char buf[256]; 00202 00203 if(!Raster::fileExists(ilist[imnum])) 00204 { 00205 manager.stop(); 00206 LFATAL("i couldnt find image file %s", ilist[imnum].c_str()); 00207 } 00208 img = Raster::ReadRGB(ilist[imnum]); 00209 SDL_Surface *surf2 = d->makeBlittableSurface(img, true); 00210 00211 //randShuffle(mindex, NMASK); 00212 00213 // load up the reporting number image: 00214 if(!Raster::fileExists(rlist[imnum])) 00215 { 00216 // stop all our ModelComponents 00217 manager.stop(); 00218 LFATAL(" i couldnt find image file %s", tlist[imnum].c_str()); 00219 } 00220 00221 img = Raster::ReadRGB(rlist[imnum]); 00222 SDL_Surface *surf3 = d->makeBlittableSurface(img, true); 00223 00224 // give a chance to other processes if single-CPU: 00225 usleep(200000); 00226 00227 // ready to go whenever the user is ready: 00228 d->displayFixationBlink(); 00229 //d->waitForKey(); 00230 d->waitNextRequestedVsync(false, true); 00231 00232 //************************ Display target************************// 00233 sprintf(buf, "===== Showing imagelet: %s at (%d, %d) =====", 00234 tlist[imnum].c_str(), rndx, rndy); 00235 00236 d->pushEvent(buf); 00237 d->displaySurface(surf1, 0, true); 00238 usleep(2000000); 00239 00240 d->clearScreen(); 00241 00242 //************************ Display array************************// 00243 00244 00245 00246 00247 // LINFO("Loading %s", manager.getExtraArg(ilist[imnum]).c_str()); 00248 Image<PixRGB<byte> > cimage = 00249 Raster::ReadRGB(ilist[imnum]); 00250 //transparent pixel should be in upper left hand corner 00251 PixRGB<byte> currTransPixel = cimage[0]; 00252 00253 // cache initial movie frames: 00254 bool streaming = true; 00255 //LINFO("Buffering '%s'...", manager.getExtraArg(vlist[imnum]).c_str()); 00256 mp->setFileName(vlist[imnum].c_str()); 00257 00258 std::deque<VideoFrame> cache; 00259 for (uint j = 0; j < CACHELEN; j ++) 00260 { 00261 streaming = cacheFrame(mp, cache); 00262 if (streaming == false) break; // all movie frames got cached! 00263 } 00264 // LINFO("'%s' ready.", manager.getExtraArg(index[i]*2).c_str()); 00265 00266 // give a chance to other processes (useful on single-CPU machines): 00267 sleep(1); system("/bin/sync"); 00268 00269 // display fixation to indicate that we are ready: 00270 d->displayFixation(); 00271 00272 // ready to go whenever the user is ready: 00273 d->waitForKey(true); int frame = 0; 00274 d->waitNextRequestedVsync(false, true); 00275 d->pushEvent(std::string("===== Playing movie: ") + 00276 vlist[imnum].c_str() + " ====="); 00277 00278 // start the eye tracker: 00279 et->track(true); 00280 00281 // blink the fixation: 00282 d->displayFixationBlink(); 00283 00284 // create an overlay: 00285 d->createVideoOverlay(VIDFMT_YUV420P,mp->getWidth(),mp->getHeight()); // mpeg stream returns YUV420P 00286 00287 // play the movie: 00288 rutz::time start = rutz::time::wall_clock_now(); 00289 while(cache.size()) 00290 { 00291 // let's first cache one more frame: 00292 if (streaming) streaming = cacheFrame(mp, cache); 00293 00294 // get next frame to display and put it into our overlay: 00295 VideoFrame vidframe = cache.back(); 00296 d->displayVideoOverlay_image(vidframe, frame, 00297 SDLdisplay::NEXT_VSYNC, 00298 cimage, currTransPixel,4); 00299 cache.pop_back(); 00300 00301 ++frame; 00302 } 00303 rutz::time stop = rutz::time::wall_clock_now(); 00304 const double secs = (stop-start).sec(); 00305 LINFO("%d frames in %.02f sec (~%.02f fps)", frame, secs, frame/secs); 00306 00307 // destroy the overlay. Somehow, mixing overlay displays and 00308 // normal displays does not work. With a single overlay created 00309 // before this loop and never destroyed, the first movie plays 00310 // ok but the other ones don't show up: 00311 d->destroyYUVoverlay(); 00312 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00313 00314 00315 00316 00317 00318 00319 // start the eye tracker: 00320 et->track(true); 00321 00322 // show the image: 00323 d->pushEvent(std::string("===== Showing search image: ") + ilist[imnum] + 00324 std::string(" =====")); 00325 00326 d->displaySurface(surf2, 0, true); 00327 00328 // wait for key; it will record reaction time in the logs: 00329 d->waitForKey(); 00330 // stop the eye tracker: 00331 et->track(false); //we just want to record eye movements while subjects view the array 00332 00333 00334 //*********************** Display reporting image**************// 00335 // show the reporting image: 00336 d->pushEvent(std::string("===== Showing reporting image: ") + rlist[imnum] + 00337 std::string(" =====")); 00338 d->displaySurface(surf3, 0, true); 00339 00340 usleep(200000); 00341 d->displayText("Input the target number:"); 00342 00343 string inputString = d->getString('\n'); 00344 00345 //check user response 00346 char tmp[40]; 00347 00348 //lets open the spec file and extract the target number 00349 ifstream specFile(slist[imnum].c_str(), ifstream::in); 00350 00351 bool found =false; 00352 string testLine; 00353 std::vector<std::string> specTokens; 00354 00355 if(specFile.is_open()) 00356 { 00357 while(!specFile.eof() && !found) 00358 { 00359 getline(specFile, testLine); 00360 string::size_type loc = testLine.find("target", 0); 00361 00362 if(loc != string::npos) 00363 found = true; 00364 } 00365 if(!found) 00366 { 00367 manager.stop(); 00368 LFATAL("couldnt find the target number from spec file"); 00369 } 00370 00371 split(testLine," ", std::back_inserter(specTokens)); 00372 00373 std::string responseString; 00374 responseString = inputString; 00375 int intResponse = atoi(responseString.c_str()), intActual = atoi(specTokens[1].c_str()); 00376 00377 total++; 00378 00379 if (intResponse == intActual) 00380 { 00381 correct ++; 00382 accuracy = correct * 100 / total; 00383 sprintf(tmp, "Correct! Accuracy is %d%%", accuracy); 00384 d->displayText(tmp); 00385 d->pushEvent(std::string("===== Correct =====")); 00386 usleep(500000); 00387 } 00388 else 00389 { 00390 accuracy = correct * 100 / total; 00391 sprintf(tmp, "Wrong! Accuracy is %d%%", accuracy); 00392 d->displayText(tmp); 00393 d->pushEvent(std::string("===== Wrong =====")); 00394 usleep(500000); 00395 } 00396 00397 specFile.close(); 00398 } 00399 else 00400 { 00401 d->displayText("no target file found!"); 00402 LFATAL("couldnt open the file -%s-",slist[imnum].c_str()); 00403 } 00404 00405 00406 // free the imagelet and image: 00407 SDL_FreeSurface(surf1); SDL_FreeSurface(surf2); SDL_FreeSurface(surf3); 00408 00409 00410 // let's do a quiinckie eye tracker calibration once in a while: 00411 /*if (im > 0 && im % 20 == 0) { 00412 d->displayText("Ready for quick recalibration"); 00413 d->waitForKey(); 00414 d->displayEyeTrackerCalibration(3, 3); 00415 d->clearScreen(); 00416 d->displayText("Ready to continue with the images"); 00417 d->waitForKey(); 00418 }*/ 00419 00420 //allow for a break after 50 trials then recalibrate 00421 00422 if (im==50) 00423 { 00424 d->displayText("You may take a break press space to continue when ready"); 00425 d->waitForKey(); 00426 // let's display an ISCAN calibration grid: 00427 d->clearScreen(); 00428 d->displayISCANcalib(); 00429 d->waitForKey(); 00430 // let's do an eye tracker calibration: 00431 d ->displayText("<SPACE> to calibrate; other key to skip"); 00432 int c = d->waitForKey(); 00433 if (c == ' ') d->displayEyeTrackerCalibration(3, 3); 00434 } 00435 00436 00437 00438 et->recalibrate(d,15); 00439 00440 } 00441 00442 d->clearScreen(); 00443 d->displayText("Experiment complete. Thank you!"); 00444 d->waitForKey(); 00445 00446 // stop all our ModelComponents 00447 manager.stop(); 00448 00449 // all done! 00450 return 0; 00451 } 00452 00453 // ###################################################################### 00454 00455 extern "C" int main(const int argc, char** argv) 00456 { 00457 // simple wrapper around submain() to catch exceptions (because we 00458 // want to allow PsychoDisplay to shut down cleanly; otherwise if we 00459 // abort while SDL is in fullscreen mode, the X server won't return 00460 // to its original resolution) 00461 try 00462 { 00463 return submain(argc, argv); 00464 } 00465 catch (...) 00466 { 00467 REPORT_CURRENT_EXCEPTION; 00468 } 00469 00470 return 1; 00471 } 00472 // ###################################################################### 00473 /* So things look consistent in everyone's emacs... */ 00474 /* Local Variables: */ 00475 /* indent-tabs-mode: nil */ 00476 /* End: */