00001 /*!@file AppPsycho/psycho-monkey.C Psychophysics display of movies for monkeys */ 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-monkey.C $ 00035 // $Id: psycho-monkey.C 13795 2010-08-17 16:47:41Z beobot $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Component/ModelOptionDef.H" 00040 #include "Transport/TransportOpts.H" 00041 #include "Image/Image.H" 00042 #include "Image/Range.H" 00043 #include "Media/MPEGStream.H" 00044 #include "Media/MediaOpts.H" 00045 #include "Psycho/PsychoDisplay.H" 00046 #include "Psycho/EyeTrackerConfigurator.H" 00047 #include "Psycho/EyeTracker.H" 00048 #include "Psycho/PsychoOpts.H" 00049 #include "Component/EventLog.H" 00050 #include "Component/ComponentOpts.H" 00051 #include "GUI/SDLdisplay.H" 00052 #include "Util/MathFunctions.H" 00053 #include "Util/Types.H" 00054 #include "Util/SimTime.H" 00055 #include "Video/VideoFrame.H" 00056 #include "rutz/rand.h" 00057 00058 #include <deque> 00059 #include <sys/time.h> 00060 #include <unistd.h> 00061 00062 #define CACHELEN 500 00063 00064 00065 // ###################################################################### 00066 static bool cacheFrame(nub::soft_ref<InputMPEGStream>& mp, 00067 std::deque<VideoFrame>& cache, 00068 const bool flip) 00069 { 00070 const VideoFrame frame = mp->readVideoFrame(); 00071 if (!frame.initialized()) return false; // end of stream 00072 00073 if (flip) cache.push_front(frame.getFlippedHoriz()); 00074 else cache.push_front(frame); 00075 00076 return true; 00077 } 00078 00079 // ###################################################################### 00080 extern "C" int main(const int argc, char** argv) 00081 { 00082 MYLOGVERB = LOG_INFO; // suppress debug messages 00083 00084 // Instantiate a ModelManager: 00085 ModelManager manager("Psycho Monkey"); 00086 OModelParam<bool> hflip(&OPT_Hflip, &manager); 00087 OModelParam<bool> keepfix(&OPT_KeepFix, &manager); 00088 OModelParam<float> fixsize(&OPT_FixSize, &manager); 00089 OModelParam<float> ppd(&OPT_Ppd, &manager); 00090 OModelParam<Range<int> > itsWait(&OPT_TrialWait, &manager); 00091 OModelParam<bool> testrun(&OPT_Testrun, &manager); 00092 OModelParam<int> itsPercent(&OPT_GrayFramePrcnt, &manager); 00093 OModelParam<Range<int> > itsRange(&OPT_GrayFrameRange, &manager); 00094 00095 // Instantiate our various ModelComponents: 00096 nub::soft_ref<InputMPEGStream> mp 00097 (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream")); 00098 manager.addSubComponent(mp); 00099 00100 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00101 manager.addSubComponent(d); 00102 00103 nub::soft_ref<EyeTrackerConfigurator> 00104 etc(new EyeTrackerConfigurator(manager)); 00105 manager.addSubComponent(etc); 00106 00107 nub::soft_ref<EventLog> el(new EventLog(manager)); 00108 manager.addSubComponent(el); 00109 00110 manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true"); 00111 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00112 manager.setOptionValString(&OPT_EyeTrackerType, "DML"); 00113 00114 // Parse command-line: 00115 if (manager.parseCommandLine(argc, argv, 00116 "<movie1.mpg> ... <movieN.mpg>", 1, -1)==false) 00117 return(1); 00118 00119 // hook our various babies up and do post-command-line configs: 00120 nub::soft_ref<EyeTracker> et = etc->getET(); 00121 d->setEyeTracker(et); 00122 d->setEventLog(el); 00123 et->setEventLog(el); 00124 00125 //screen center and fixation point 00126 int fixrad = int(ppd.getVal() * fixsize.getVal()); 00127 if ((fixrad % 2) != 0) 00128 --fixrad; 00129 d->setFixationSize(fixrad*2); 00130 00131 const int cx = d->getWidth() /2 - 1 - (fixrad/2 -1); 00132 const int cy = d->getHeight()/2 - 1 - (fixrad/2 -1); 00133 00134 //create a fixation patch 00135 Image<PixRGB<byte> > patch(fixrad,fixrad,ZEROS); 00136 patch += PixRGB<byte>(255,0,0); 00137 00138 std::deque<VideoFrame> cache; 00139 initRandomNumbers(); 00140 00141 // let's get all our ModelComponent instances started: 00142 manager.start(); 00143 d->clearScreen(); 00144 00145 // setup array of movie indices: 00146 const uint nbmovies = manager.numExtraArgs(); 00147 const float percentage = (float)itsPercent.getVal() * 0.01F; 00148 const float factor = percentage/(1 - percentage); 00149 const uint graycount = uint(factor * (float)nbmovies); 00150 const uint nbtotal = nbmovies + graycount; 00151 00152 LINFO("Randomizing movies..."); 00153 int index[nbtotal]; 00154 std::string fnames[nbtotal]; 00155 for (uint i = 0; i < nbmovies; ++i) index[i] = i; 00156 for (uint i = nbmovies; i < nbtotal; ++i) index[i] = -1; 00157 randShuffle(index, nbtotal); 00158 for (uint i = 0; i < nbtotal; ++i) 00159 fnames[i] = (index[i] < 0) ? "Gray image" : manager.getExtraArg(index[i]); 00160 00161 try { 00162 // main loop: 00163 for (uint i = 0; i < nbtotal; i ++) 00164 { 00165 // cache initial movie frames: 00166 d->clearScreen(); 00167 if (cache.size()) LFATAL("ooops, cache not empty?"); 00168 bool streaming = true; 00169 LINFO("Buffering '%s'...", fnames[i].c_str()); 00170 00171 // NOTE: we now specify --preload-mpeg=true with a 00172 // setOptionValString() a few lines up from here 00173 if (index[i] > -1) 00174 { 00175 mp->setFileName(fnames[i]); 00176 for (uint j = 0; j < CACHELEN; j ++) 00177 { 00178 streaming = cacheFrame(mp, cache, hflip.getVal()); 00179 if (streaming == false) break; // all movie frames got cached! 00180 } 00181 } 00182 00183 LINFO("'%s' ready.", fnames[i].c_str()); 00184 00185 //wait for requested time till next trial 00186 usleep(rutz::rand_range<int>(itsWait.getVal().min(), 00187 itsWait.getVal().max()) * 1000); 00188 00189 00190 // give a chance to other processes (useful on single-CPU machines): 00191 sleep(1); if (system("/bin/sync")) LERROR("Cannot sync()"); 00192 LINFO("/bin/sync complete"); 00193 00194 // eat up any extra fixation codes and keys before we start 00195 // the next fixation round: 00196 while(et->isFixating()) ; 00197 while(d->checkForKey() != -1) ; 00198 00199 // display fixation to indicate that we are ready: 00200 d->displayRedDotFixation(); 00201 00202 // give us a chance to abort 00203 d->checkForKey(); 00204 00205 // ready to go whenever the monkey is ready (pulse on parallel port): 00206 if (!testrun.getVal()) 00207 while(true) 00208 { 00209 if (et->isFixating()) break; 00210 if (d->checkForKey() != -1) break; // allow force start by key 00211 } 00212 00213 int frame = 0; 00214 if (index[i] > -1) d->waitNextRequestedVsync(false, true); 00215 d->pushEvent(std::string("===== Playing movie: ") + 00216 fnames[i] + " ====="); 00217 00218 // start the eye tracker: 00219 et->track(true); 00220 00221 if (index[i] > -1) 00222 { 00223 // create an overlay: 00224 d->createVideoOverlay(VIDFMT_YUV420P); // mpeg stream uses YUV420P 00225 00226 // play the movie: 00227 while(cache.size()) 00228 { 00229 // let's first cache one more frame: 00230 if (streaming) streaming = cacheFrame(mp, cache, hflip.getVal()); 00231 00232 // get next frame to display and put it into our overlay: 00233 VideoFrame vidframe = cache.back(); 00234 if (keepfix.getVal()) 00235 d->displayVideoOverlay_patch(vidframe, frame, 00236 SDLdisplay::NEXT_VSYNC, cx, cy, 00237 patch); 00238 else 00239 d->displayVideoOverlay(vidframe, frame, 00240 SDLdisplay::NEXT_VSYNC); 00241 00242 cache.pop_back(); 00243 ++frame; 00244 } 00245 00246 // destroy the overlay. Somehow, mixing overlay displays and 00247 // normal displays does not work. With a single overlay created 00248 // before this loop and never destroyed, the first movie plays 00249 // ok but the other ones don't show up: 00250 d->destroyYUVoverlay(); 00251 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00252 } 00253 else 00254 { 00255 d->clearScreen(); 00256 usleep(rutz::rand_range<int>(itsRange.getVal().min(), 00257 itsRange.getVal().max()) * 1000); 00258 } 00259 00260 00261 // stop the eye tracker: 00262 usleep(50000); 00263 et->track(false); 00264 } 00265 00266 d->clearScreen(); 00267 LINFO("Experiment complete"); 00268 } 00269 catch (...) 00270 { 00271 REPORT_CURRENT_EXCEPTION; 00272 }; 00273 00274 // stop all our ModelComponents 00275 manager.stop(); 00276 00277 // all done! 00278 return 0; 00279 } 00280 00281 // ###################################################################### 00282 /* So things look consistent in everyone's emacs... */ 00283 /* Local Variables: */ 00284 /* indent-tabs-mode: nil */ 00285 /* End: */