00001 /*!@file AppPsycho/psycho-movie-rating.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-movie-rating.C $ 00035 // $Id: psycho-movie-rating.C 13712 2010-07-28 21:00:40Z itti $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Image/Image.H" 00040 #include "GUI/GUIOpts.H" 00041 #include "Media/MrawvInputStream.H" 00042 #include "Media/MediaOpts.H" 00043 #include "Psycho/PsychoDisplay.H" 00044 #include "Psycho/EyeTrackerConfigurator.H" 00045 #include "Psycho/EyeTracker.H" 00046 #include "Psycho/PsychoOpts.H" 00047 #include "Component/EventLog.H" 00048 #include "Component/ComponentOpts.H" 00049 #include "Util/MathFunctions.H" 00050 #include "Util/Types.H" 00051 #include "Video/VideoFrame.H" 00052 #include "rutz/time.h" 00053 #include "Util/sformat.H" 00054 00055 #include <deque> 00056 00057 #define CACHELEN 350 00058 00059 //! Like psycho-movie, but also asks for a subjective rating of each movie 00060 00061 // ###################################################################### 00062 static bool cacheFrame(nub::soft_ref<MrawvInputStream>& mp, 00063 std::deque<VideoFrame>& cache) 00064 { 00065 const VideoFrame frame = mp->readFrame().asVideo(); 00066 if (!frame.initialized()) return false; // end of stream 00067 00068 cache.push_front(frame); 00069 return true; 00070 } 00071 00072 // ###################################################################### 00073 static int submain(const int argc, char** argv) 00074 { 00075 MYLOGVERB = LOG_INFO; // suppress debug messages 00076 00077 // Instantiate a ModelManager: 00078 ModelManager manager("Psycho Movie"); 00079 00080 // Instantiate our various ModelComponents: 00081 nub::soft_ref<MrawvInputStream> mp 00082 (new MrawvInputStream(manager, "Input Raw Video Stream", 00083 "MrawvInputStream")); 00084 manager.addSubComponent(mp); 00085 00086 nub::soft_ref<EventLog> el(new EventLog(manager)); 00087 manager.addSubComponent(el); 00088 00089 nub::soft_ref<EyeTrackerConfigurator> 00090 etc(new EyeTrackerConfigurator(manager)); 00091 manager.addSubComponent(etc); 00092 00093 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00094 manager.addSubComponent(d); 00095 00096 //manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true"); 00097 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00098 manager.setOptionValString(&OPT_EyeTrackerType, "None"); 00099 manager.setOptionValString(&OPT_SDLdisplayDims, "1280x1024"); 00100 00101 // Parse command-line: 00102 if (manager.parseCommandLine(argc, argv, 00103 "<movie1.mraw> ... <movieN.mraw>", 00104 1, -1) == false) 00105 return(1); 00106 00107 // hook our various babies up and do post-command-line configs: 00108 nub::soft_ref<EyeTracker> et = etc->getET(); 00109 d->setEyeTracker(et); 00110 d->setEventLog(el); 00111 et->setEventLog(el); 00112 00113 // EyeLink opens the screen for us, so make sure SDLdisplay is slave: 00114 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0) 00115 d->setModelParamVal("SDLslaveMode", true); 00116 00117 // let's get all our ModelComponent instances started: 00118 manager.start(); 00119 00120 // let's do an eye tracker calibration: 00121 et->calibrate(d); 00122 00123 // ok, let's get going: 00124 d->clearScreen(); 00125 d->displayText("<SPACE> for random play; other key for ordered"); 00126 int c = d->waitForKey(); 00127 00128 // setup array of movie indices: 00129 uint nbmovies = manager.numExtraArgs(); int index[nbmovies]; 00130 for (uint i = 0; i < nbmovies; i ++) index[i] = i; 00131 if (c == ' ') { LINFO("Randomizing movies..."); randShuffle(index,nbmovies);} 00132 00133 // main loop: 00134 for (uint i = 0; i < nbmovies; i ++) 00135 { 00136 // cache initial movie frames: 00137 d->clearScreen(); 00138 bool streaming = true; 00139 LINFO("Buffering '%s'...", manager.getExtraArg(index[i]).c_str()); 00140 mp->setFileName(manager.getExtraArg(index[i])); 00141 00142 std::deque<VideoFrame> cache; 00143 for (uint j = 0; j < CACHELEN; j ++) 00144 { 00145 streaming = cacheFrame(mp, cache); 00146 if (streaming == false) break; // all movie frames got cached! 00147 } 00148 LINFO("'%s' ready.", manager.getExtraArg(index[i]).c_str()); 00149 00150 // give a chance to other processes (useful on single-CPU machines): 00151 sleep(1); if (system("/bin/sync")) LERROR("error in sync"); 00152 00153 // display fixation to indicate that we are ready: 00154 d->displayFixation(); 00155 00156 // ready to go whenever the user is ready: 00157 d->waitForKey(); int frame = 0; 00158 d->waitNextRequestedVsync(false, true); 00159 d->pushEvent(std::string("===== Playing movie: ") + 00160 manager.getExtraArg(index[i]) + " ====="); 00161 00162 // start the eye tracker: 00163 et->track(true); 00164 00165 // blink the fixation: 00166 d->displayFixationBlink(); 00167 00168 // create an overlay: 00169 if (cache.size() == 0) LFATAL("Zero-frame movie?"); 00170 const int ovw = cache[0].getDims().w(); 00171 const int ovh = cache[0].getDims().h(); 00172 d->createVideoOverlay(VIDFMT_YUV420P, ovw, ovh); 00173 const int ovx = (d->getWidth() - ovw) / 2; 00174 const int ovy = (d->getHeight() - ovh) / 2; 00175 00176 // play the movie: 00177 rutz::time start = rutz::time::wall_clock_now(); 00178 while(cache.size()) 00179 { 00180 // let's first cache one more frame: 00181 if (streaming) streaming = cacheFrame(mp, cache); 00182 00183 // get next frame to display and put it into our overlay: 00184 VideoFrame vidframe = cache.back(); 00185 d->displayVideoOverlay_pos(vidframe, frame, 00186 SDLdisplay::NEXT_VSYNC, 00187 ovx, ovy, ovw, ovh); 00188 cache.pop_back(); 00189 00190 ++frame; 00191 } 00192 rutz::time stop = rutz::time::wall_clock_now(); 00193 const double secs = (stop-start).sec(); 00194 LINFO("%d frames in %.02f sec (~%.02f fps)", frame, secs, frame/secs); 00195 00196 // destroy the overlay. Somehow, mixing overlay displays and 00197 // normal displays does not work. With a single overlay created 00198 // before this loop and never destroyed, the first movie plays 00199 // ok but the other ones don't show up: 00200 d->destroyYUVoverlay(); 00201 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00202 00203 // stop the eye tracker: 00204 usleep(50000); 00205 et->track(false); 00206 00207 // collect subjective rating data (it will be in the psycho log): 00208 d->displayText("Please score this movie (1=bad .. 5=excellent)"); 00209 do { c = d->waitForKey(); } while (c < '1' || c > '5'); 00210 d->pushEvent(sformat("== Rating for %s = %d ==", 00211 manager.getExtraArg(index[i]).c_str(), c - '0')); 00212 00213 // let's do a quickie eye tracker recalibration: 00214 et->recalibrate(d); 00215 } 00216 00217 d->clearScreen(); 00218 d->displayText("Experiment complete. Thank you!"); 00219 d->waitForKey(); 00220 00221 // stop all our ModelComponents 00222 manager.stop(); 00223 00224 // all done! 00225 return 0; 00226 } 00227 00228 // ###################################################################### 00229 extern "C" int main(const int argc, char** argv) 00230 { 00231 // simple wrapper around submain() to catch exceptions (because we 00232 // want to allow PsychoDisplay to shut down cleanly; otherwise if we 00233 // abort while SDL is in fullscreen mode, the X server won't return 00234 // to its original resolution) 00235 try 00236 { 00237 return submain(argc, argv); 00238 } 00239 catch (...) 00240 { 00241 REPORT_CURRENT_EXCEPTION; 00242 } 00243 00244 return 1; 00245 } 00246 00247 // ###################################################################### 00248 /* So things look consistent in everyone's emacs... */ 00249 /* Local Variables: */ 00250 /* indent-tabs-mode: nil */ 00251 /* End: */