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: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-corner-movie.C $ 00035 // $Id: psycho-corner-movie.C 13712 2010-07-28 21:00:40Z itti $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Image/Image.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 <string> 00055 #include <fstream> 00056 #include <vector> 00057 #include <iostream> 00058 #include <algorithm> 00059 #include <assert.h> 00060 00061 #define CACHELEN 150 00062 00063 // ###################################################################### 00064 static bool cacheFrame(nub::soft_ref<InputMPEGStream>& mp, 00065 std::deque<VideoFrame>& cache) 00066 { 00067 const VideoFrame frame = mp->readVideoFrame(); 00068 if (!frame.initialized()) return false; // end of stream 00069 00070 cache.push_front(frame); 00071 return true; 00072 } 00073 00074 // ###################################################################### 00075 void Tokenize(const std::string& str, 00076 std::vector<std::string>& tokens, 00077 const std::string& delimiters = " ") 00078 { 00079 std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); 00080 std::string::size_type pos = str.find_first_of(delimiters, lastPos); 00081 while (std::string::npos != pos || std::string::npos != lastPos) 00082 { 00083 tokens.push_back(str.substr(lastPos, pos - lastPos)); 00084 lastPos = str.find_first_not_of(delimiters, pos); 00085 pos = str.find_first_of(delimiters, lastPos); 00086 } 00087 } 00088 00089 // ###################################################################### 00090 static int submain(const int argc, char** argv) 00091 { 00092 MYLOGVERB = LOG_INFO; // suppress debug messages 00093 00094 // Instantiate a ModelManager: 00095 ModelManager manager("Psycho Movie"); 00096 00097 // Instantiate our various ModelComponents: 00098 nub::soft_ref<InputMPEGStream> mp 00099 (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream")); 00100 manager.addSubComponent(mp); 00101 00102 nub::soft_ref<EventLog> el(new EventLog(manager)); 00103 manager.addSubComponent(el); 00104 00105 nub::soft_ref<EyeTrackerConfigurator> 00106 etc(new EyeTrackerConfigurator(manager)); 00107 manager.addSubComponent(etc); 00108 00109 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00110 manager.addSubComponent(d); 00111 00112 manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true"); 00113 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00114 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00115 00116 // Parse command-line: 00117 if (manager.parseCommandLine(argc, argv, 00118 "<display_loc.txt> <movie1.mpg> ... <movieN.mpg>", 2, -1)==false) 00119 return(1); 00120 00121 // hook our various babies up and do post-command-line configs: 00122 nub::soft_ref<EyeTracker> et = etc->getET(); 00123 d->setEyeTracker(et); 00124 d->setEventLog(el); 00125 et->setEventLog(el); 00126 00127 // EyeLink opens the screen for us, so make sure SDLdisplay is slave: 00128 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0) 00129 d->setModelParamVal("SDLslaveMode", true); 00130 00131 // let's get all our ModelComponent instances started: 00132 manager.start(); 00133 00134 // read the display_loc.txt 00135 const char * display_loc = manager.getExtraArg(0).c_str(); 00136 std::ifstream in( display_loc ); 00137 00138 if (! in) { 00139 LINFO("error: unable to open display_loc file: %s", display_loc); 00140 return -1; 00141 } 00142 00143 std::vector< std::vector<std::string> > cdl; //cdl = clip display location 00144 std::string line; 00145 while( !getline(in, line).eof()){ 00146 std::vector<std::string> tokens; 00147 Tokenize(line, tokens); 00148 cdl.push_back(tokens); 00149 } 00150 in.close(); 00151 00152 // setup array of movie indices: 00153 uint nbmovies = manager.numExtraArgs()-1; 00154 int index[nbmovies]; 00155 for (uint i = 0; i < nbmovies; i ++) index[i] = i; 00156 LINFO("Randomizing movies..."); randShuffle(index,nbmovies); 00157 00158 // calibration 00159 et->calibrate(d); 00160 00161 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0){ 00162 et->closeSDL(); 00163 d->openDisplay(); 00164 LINFO("Switching SDL: EyeLink-->iLab"); 00165 }else{ 00166 d->clearScreen(); 00167 } 00168 00169 d->displayText("<SPACE> to start watching movies"); 00170 d->waitForKey(); 00171 00172 // get background color 00173 PixRGB<byte> ori_bgcolor = d->getGrey(); 00174 PixRGB<byte> black_bgcolor = PixRGB<byte>(0,0,0); 00175 00176 // main loop: 00177 for (uint i = 0; i < nbmovies; i ++) 00178 { 00179 // obtain display location 00180 int x = 100, y = 100, w = 1280, h = 1024; //w = 1233, h = 856; 00181 int sw = d->getWidth(), sh = d->getHeight(); 00182 std::string playmovie = manager.getExtraArg(index[i]+1).c_str(); 00183 std::vector< std::vector<std::string> >::iterator iter_ii; 00184 std::vector<std::string>::iterator iter_jj; 00185 std::string loc = "default"; 00186 for(iter_ii=cdl.begin(); iter_ii!=cdl.end(); iter_ii++) 00187 { 00188 iter_jj = (*iter_ii).begin(); 00189 if(playmovie.find(*iter_jj, 0) != std::string::npos){ 00190 iter_jj++; 00191 loc = *iter_jj; 00192 if(loc.compare("0") == 0){ //center 00193 x = sw/2 - w/2; 00194 y = sh/2 - h/2; 00195 }else if(loc.compare("1") == 0){ //upper right 00196 x = sw - w; y = 0; 00197 }else if(loc.compare("2") == 0){ //upper left 00198 x = 0; y = 0; 00199 }else if(loc.compare("3") == 0){ //lower right 00200 x = sw - w; y = sh - h; 00201 }else if(loc.compare("4") == 0){ //lower left 00202 x = 0; y = sh - h; 00203 }else{ //default, display at the center 00204 x = sw/2 - w/2; 00205 y = sh/2 - h/2; 00206 } 00207 } 00208 } 00209 00210 d->changeBackgroundColor(black_bgcolor); 00211 // cache initial movie frames: 00212 d->clearScreen(); 00213 bool streaming = true; 00214 LINFO("Buffering '%s'...", manager.getExtraArg(index[i]+1).c_str()); 00215 mp->setFileName(manager.getExtraArg(index[i]+1)); 00216 00217 std::deque<VideoFrame> cache; 00218 for (uint j = 0; j < CACHELEN; j ++) 00219 { 00220 streaming = cacheFrame(mp, cache); 00221 if (streaming == false) break; // all movie frames got cached! 00222 } 00223 LINFO("'%s' ready.", manager.getExtraArg(index[i]).c_str()); 00224 00225 // give a chance to other processes (useful on single-CPU machines): 00226 sleep(1); if (system("/bin/sync")) LERROR("error in sync"); 00227 00228 // display fixation to indicate that we are ready: 00229 d->displayRedDotFixation(); 00230 00231 // ready to go whenever the user is ready: 00232 d->waitForKey(); int frame = 0; 00233 d->waitNextRequestedVsync(false, true); 00234 d->pushEvent(std::string("===== Playing movie: ") + 00235 manager.getExtraArg(index[i]+1) + " ====="); 00236 d->pushEvent(std::string("movie is displayed at location: ") + loc); 00237 00238 // start the eye tracker: 00239 et->track(true); 00240 00241 // blink the fixation: 00242 d->clearScreen(); 00243 d->displayRedDotFixationBlink(x+w/2, y+h/2); 00244 00245 // create an overlay: 00246 d->createVideoOverlay(VIDFMT_YUV420P, mp->getWidth(), mp->getHeight()); 00247 00248 // play the movie: 00249 d->changeBackgroundColor(PixRGB<byte>(0,0,0)); 00250 rutz::time start = rutz::time::wall_clock_now(); 00251 while(cache.size()) 00252 { 00253 // let's first cache one more frame: 00254 if (streaming) streaming = cacheFrame(mp, cache); 00255 00256 // get next frame to display and put it into our overlay: 00257 VideoFrame vidframe = cache.back(); 00258 d->displayVideoOverlay_pos(vidframe, frame, 00259 SDLdisplay::NEXT_VSYNC, 00260 x, y, w, h); 00261 cache.pop_back(); 00262 00263 ++frame; 00264 } 00265 rutz::time stop = rutz::time::wall_clock_now(); 00266 const double secs = (stop-start).sec(); 00267 LINFO("%d frames in %.02f sec (~%.02f fps)", frame, secs, frame/secs); 00268 00269 // destroy the overlay. Somehow, mixing overlay displays and 00270 // normal displays does not work. With a single overlay created 00271 // before this loop and never destroyed, the first movie plays 00272 // ok but the other ones don't show up: 00273 d->destroyYUVoverlay(); 00274 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00275 00276 // stop the eye tracker: 00277 usleep(50000); 00278 et->track(false); 00279 00280 if(i%10 == 0 && i> 0 && i<nbmovies-1) { 00281 // 5 minutes break, then do a full calibration 00282 d->changeBackgroundColor(ori_bgcolor); 00283 d->displayText("Please take a break, press <SPACE> to continue"); 00284 d->waitForKey(); 00285 00286 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0){ 00287 d->closeDisplay(); 00288 et->openSDL(); 00289 et->calibrate(d); 00290 et->closeSDL(); 00291 d->openDisplay(); 00292 LINFO("Switching SDL for quick calibration"); 00293 }else{ 00294 d->clearScreen(); 00295 d->displayISCANcalib(); 00296 d->waitForKey(); 00297 00298 d->displayText("<SPACE> for eye-tracker calibration"); 00299 d->waitForKey(); 00300 d->displayEyeTrackerCalibration(3, 3); 00301 d->clearScreen(); 00302 } 00303 00304 d->displayText("<SPACE> to start watching movies"); 00305 d->waitForKey(); 00306 d->changeBackgroundColor(black_bgcolor); 00307 } 00308 } 00309 00310 d->changeBackgroundColor(ori_bgcolor); 00311 d->clearScreen(); 00312 d->displayText("Experiment complete. Thank you!"); 00313 d->waitForKey(); 00314 00315 // stop all our ModelComponents 00316 manager.stop(); 00317 00318 // all done! 00319 return 0; 00320 } 00321 00322 // ###################################################################### 00323 extern "C" int main(const int argc, char** argv) 00324 { 00325 // simple wrapper around submain() to catch exceptions (because we 00326 // want to allow PsychoDisplay to shut down cleanly; otherwise if we 00327 // abort while SDL is in fullscreen mode, the X server won't return 00328 // to its original resolution) 00329 try 00330 { 00331 return submain(argc, argv); 00332 } 00333 catch (...) 00334 { 00335 REPORT_CURRENT_EXCEPTION; 00336 } 00337 00338 return 1; 00339 } 00340 00341 // ###################################################################### 00342 /* So things look consistent in everyone's emacs... */ 00343 /* Local Variables: */ 00344 /* indent-tabs-mode: nil */ 00345 /* End: */