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 00055 #define CACHELEN 150 00056 00057 // ###################################################################### 00058 static bool cacheFrame(nub::soft_ref<InputMPEGStream>& mp, 00059 std::deque<VideoFrame>& cache) 00060 { 00061 const VideoFrame frame = mp->readVideoFrame(); 00062 if (!frame.initialized()) return false; // end of stream 00063 00064 cache.push_front(frame); 00065 return true; 00066 } 00067 00068 // ###################################################################### 00069 static int submain(const int argc, char** argv) 00070 { 00071 MYLOGVERB = LOG_INFO; // suppress debug messages 00072 00073 // Instantiate a ModelManager: 00074 ModelManager manager("Psycho Movie"); 00075 00076 // Instantiate our various ModelComponents: 00077 nub::soft_ref<InputMPEGStream> mp 00078 (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream")); 00079 manager.addSubComponent(mp); 00080 00081 nub::soft_ref<EventLog> el(new EventLog(manager)); 00082 manager.addSubComponent(el); 00083 00084 nub::soft_ref<EyeTrackerConfigurator> 00085 etc(new EyeTrackerConfigurator(manager)); 00086 manager.addSubComponent(etc); 00087 00088 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00089 manager.addSubComponent(d); 00090 00091 manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true"); 00092 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00093 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00094 00095 // Parse command-line: 00096 if (manager.parseCommandLine(argc, argv, 00097 "<movie1.mpg> ... <movieN.mpg>", 1, -1)==false) 00098 return(1); 00099 00100 // hook our various babies up and do post-command-line configs: 00101 nub::soft_ref<EyeTracker> et = etc->getET(); 00102 d->setEyeTracker(et); 00103 d->setEventLog(el); 00104 et->setEventLog(el); 00105 00106 // EyeLink opens the screen for us, so make sure SDLdisplay is slave: 00107 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0) 00108 d->setModelParamVal("SDLslaveMode", true); 00109 00110 // let's get all our ModelComponent instances started: 00111 manager.start(); 00112 00113 // vision test (generate random letters again if pressing 'r') 00114 LINFO("*****************************************************************************"); 00115 LINFO("visual acuity test: [r] to regenerate another string; other keys to continue."); 00116 int key; 00117 do { 00118 d->displayRandomText(7, 6); 00119 key = d->waitForKey(true); 00120 } while(key == 114 || key == 101 || key == 116 || key == 102); 00121 00122 // let's do an eye tracker calibration: 00123 d->pushEventBegin("Calibration"); 00124 et->calibrate(d); 00125 d->pushEventEnd("Calibration"); 00126 00127 // load fixation icon 00128 Image< PixRGB<byte> > fixicon = 00129 Raster::ReadRGB(d->getModelParamString("PsychoDisplayFixationIcon")); 00130 00131 // switching SDL from ilink to our SDL 00132 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0){ 00133 et->closeSDL(); 00134 d->openDisplay(); 00135 LINFO("Switching SDL: Eyelink-->iLab"); 00136 }else{ 00137 d->clearScreen(); 00138 } 00139 00140 LINFO("Press any key to start......"); 00141 d->displayText("Press any key to start......"); 00142 d->waitForKey(true); 00143 00144 // setup array of movie indices: 00145 uint nbmovies = manager.numExtraArgs(); 00146 uint nbmovses = 5; //number of movies per session 00147 int index[nbmovies], sindex[nbmovses]; 00148 00149 // only randomize order in its own session 00150 LINFO("Randomizing movies..."); 00151 int iidx = 0; 00152 for (uint i=0; i<nbmovies; i++) { 00153 sindex[i%nbmovses] = i; 00154 if (i%nbmovses == nbmovses-1){ 00155 randShuffle(sindex, nbmovses); 00156 for (uint j=0; j<nbmovses; j++) { 00157 index[iidx] = sindex[j]; 00158 iidx++; 00159 } 00160 } 00161 } 00162 00163 // main loop: 00164 int x, y, w, h; 00165 for (uint i = 0; i < nbmovies; i ++) 00166 { 00167 // cache initial movie frames: 00168 d->clearScreen(); 00169 bool streaming = true; 00170 LINFO("Buffering '%s'...", manager.getExtraArg(index[i]).c_str()); 00171 mp->setFileName(manager.getExtraArg(index[i])); 00172 00173 // get video display location 00174 w = mp->getWidth(); 00175 h = mp->getHeight(); 00176 x = (d->getWidth()-w)/2; 00177 y = (d->getHeight()-h)/2; 00178 00179 std::deque<VideoFrame> cache; 00180 for (uint j = 0; j < CACHELEN; j ++) 00181 { 00182 streaming = cacheFrame(mp, cache); 00183 if (streaming == false) break; // all movie frames got cached! 00184 } 00185 LINFO("'%s' ready.", manager.getExtraArg(index[i]).c_str()); 00186 LINFO("Video loaded (%i/%i). Press [Space] to continue, or [ESC] to quit the program.", i+1, nbmovies); 00187 00188 // give a chance to other processes (useful on single-CPU machines): 00189 sleep(1); if (system("/bin/sync")) LERROR("error in sync"); 00190 00191 // display fixation to indicate that we are ready: 00192 d->displayFixationIcon(fixicon); 00193 00194 // ready to go whenever the user is ready: 00195 d->waitForKey(true); int frame = 0; 00196 d->waitNextRequestedVsync(false, true); 00197 d->pushEvent(std::string("===== Playing movie: ") + 00198 manager.getExtraArg(index[i]) + " ====="); 00199 00200 // start the eye tracker: 00201 et->track(true); 00202 00203 // blink the fixation: 00204 d->displayFixationIconBlink(fixicon); 00205 00206 // create an overlay: 00207 d->createVideoOverlay(VIDFMT_YUV420P,mp->getWidth(),mp->getHeight()); // mpeg stream returns YUV420P 00208 00209 // play the movie: 00210 rutz::time start = rutz::time::wall_clock_now(); 00211 while(cache.size()) 00212 { 00213 // let's first cache one more frame: 00214 if (streaming) streaming = cacheFrame(mp, cache); 00215 00216 // get next frame to display and put it into our overlay: 00217 VideoFrame vidframe = cache.back(); 00218 d->displayVideoOverlay_pos(vidframe, frame, 00219 SDLdisplay::NEXT_VSYNC, 00220 x, y, w, h); 00221 cache.pop_back(); 00222 00223 ++frame; 00224 } 00225 rutz::time stop = rutz::time::wall_clock_now(); 00226 const double secs = (stop-start).sec(); 00227 LINFO("%d frames in %.04f sec (~%.04f fps)", frame, secs, frame/secs); 00228 00229 // destroy the overlay. Somehow, mixing overlay displays and 00230 // normal displays does not work. With a single overlay created 00231 // before this loop and never destroyed, the first movie plays 00232 // ok but the other ones don't show up: 00233 d->destroyYUVoverlay(); 00234 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00235 00236 // stop the eye tracker: 00237 usleep(50000); 00238 et->track(false); 00239 00240 // take a break for every 5 clips, or do a quick drift correction 00241 if (i<nbmovies-1){ 00242 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0){ 00243 d->closeDisplay(); 00244 et->openSDL(); 00245 if ((i+1)%5 == 0){ 00246 //5 clips, take a break 00247 d->clearScreen(); 00248 d->displayText("Please Take a Break"); 00249 LINFO("Break time. Press [Space] to continue, or [ESC] to terminate the experiment."); 00250 d->waitForKey(true); 00251 d->displayText("Calibration"); 00252 d->pushEventBegin("Calibration"); 00253 et->calibrate(d); 00254 d->pushEventEnd("Calibration"); 00255 }else{ 00256 LINFO("Drift Correction. Press [Space] to continue, or [ESC] to calibrate."); 00257 et->recalibrate(d,13); 00258 } 00259 LINFO("Switching SDL for Calibration "); 00260 }else{ 00261 // let's do a quickie eye tracker recalibration: 00262 et->recalibrate(d,13); 00263 } 00264 } 00265 } 00266 00267 d->clearScreen(); 00268 d->displayText("Experiment complete. Thank you!"); 00269 d->waitForKey(true); 00270 00271 // stop all our ModelComponents 00272 manager.stop(); 00273 00274 // all done! 00275 return 0; 00276 } 00277 00278 // ###################################################################### 00279 extern "C" int main(const int argc, char** argv) 00280 { 00281 // simple wrapper around submain() to catch exceptions (because we 00282 // want to allow PsychoDisplay to shut down cleanly; otherwise if we 00283 // abort while SDL is in fullscreen mode, the X server won't return 00284 // to its original resolution) 00285 try 00286 { 00287 return submain(argc, argv); 00288 } 00289 catch (...) 00290 { 00291 REPORT_CURRENT_EXCEPTION; 00292 } 00293 00294 return 1; 00295 } 00296 00297 // ###################################################################### 00298 /* So things look consistent in everyone's emacs... */ 00299 /* Local Variables: */ 00300 /* indent-tabs-mode: nil */ 00301 /* End: */