00001 /*!@file AppPsycho/psycho-video-replay.C */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the 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: Rob Peters <rjpeters at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-video-replay.C $ 00035 // $Id: psycho-video-replay.C 9412 2008-03-10 23:10:15Z farhan $ 00036 // 00037 00038 #ifndef APPPSYCHO_PSYCHO_VIDEO_REPLAY_C_DEFINED 00039 #define APPPSYCHO_PSYCHO_VIDEO_REPLAY_C_DEFINED 00040 00041 #include "Component/ModelManager.H" 00042 #include "Image/DrawOps.H" 00043 #include "Image/Image.H" 00044 #include "Media/BufferedInputFrameSeries.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 "Raster/GenericFrame.H" 00052 #include "Util/FileUtil.H" 00053 #include "Util/StringUtil.H" 00054 #include "Util/Types.H" 00055 #include "Util/csignals.H" 00056 #include "Video/VideoFrame.H" 00057 00058 #include <SDL/SDL.h> 00059 #include <fstream> 00060 #include <iterator> // for back_inserter() 00061 #include <unistd.h> // for sync() 00062 #include <vector> 00063 00064 namespace 00065 { 00066 struct Question 00067 { 00068 Question(const std::string& p, const std::string& a) 00069 : prompt(p), valid_answers(a) 00070 {} 00071 00072 std::string prompt; 00073 std::string valid_answers; 00074 }; 00075 00076 void parseQuestions(const std::string& fname, 00077 std::vector<Question>& qv) 00078 { 00079 std::ifstream questions(fname.c_str()); 00080 00081 if (!questions.is_open()) 00082 LFATAL("couldn't open %s for reading", fname.c_str()); 00083 00084 std::string line; 00085 while (std::getline(questions, line)) 00086 { 00087 std::vector<std::string> toks; 00088 split(line, "|", std::back_inserter(toks)); 00089 00090 if (toks.size() != 2) 00091 LFATAL("invalid line in %s (expected 2 tokens, got %d:\n%s", 00092 fname.c_str(), int(toks.size()), line.c_str()); 00093 00094 qv.push_back(Question(toks[0], toks[1])); 00095 } 00096 } 00097 00098 void doQuestion(PsychoDisplay& d, const Question& q, 00099 std::ostream& dest) 00100 { 00101 while (d.checkForKey() != -1) 00102 { /* discard any outstanding keypresses */ } 00103 00104 const PixRGB<byte> bgcol(0,0,0); 00105 const PixRGB<byte> msgcol(255,255,255); 00106 const PixRGB<byte> anscol(0,255,0); 00107 00108 const Dims dims = d.getDims(); 00109 00110 Image<PixRGB<byte> > screen(d.getDims(), NO_INIT); 00111 screen.clear(bgcol); 00112 00113 // write prompt: 00114 Point2D<int> p; p.i = dims.w() / 2 - (10 * q.prompt.length()) / 2; 00115 p.j = dims.h() / 2 - 10; 00116 if (p.i < 0) 00117 LFATAL("Text '%s' does not fit on screen!", q.prompt.c_str()); 00118 00119 writeText(screen, p, q.prompt.c_str(), msgcol, bgcol); 00120 00121 d.displayImage(screen); 00122 00123 // get answer: 00124 int answer_id; 00125 std::string answer; 00126 while (true) 00127 { 00128 const int c = toupper(d.waitForKey()); 00129 00130 std::string::size_type p = q.valid_answers.find(char(c)); 00131 00132 if (p != q.valid_answers.npos) 00133 { 00134 answer_id = int(p); 00135 answer = char(c); 00136 break; 00137 } 00138 } 00139 00140 p.i = dims.w() / 2 - 5; 00141 p.j += 25; 00142 writeText(screen, p, answer.c_str(), anscol, bgcol); 00143 00144 d.displayImage(screen); 00145 00146 // write answer to file: 00147 dest << answer_id << " % '" << answer 00148 << "' of '" << q.valid_answers << "' (" 00149 << q.prompt << ")\n"; 00150 00151 LINFO("A: %d Q: %s", answer_id, q.prompt.c_str()); 00152 00153 usleep(750000); 00154 00155 d.SDLdisplay::clearScreen(bgcol); 00156 00157 usleep(250000); 00158 } 00159 } 00160 00161 //! Psychophysics display of video frames from disk 00162 /*! This displays video frames that have already been saved to disk, 00163 with added machinery for eye-tracking. */ 00164 int submain(const int argc, char** argv) 00165 { 00166 MYLOGVERB = LOG_INFO; // suppress debug messages 00167 00168 // 'volatile' because we will modify this from signal handlers 00169 volatile int signum = 0; 00170 catchsignals(&signum); 00171 00172 // Instantiate a ModelManager: 00173 ModelManager manager("Psycho Video Replay"); 00174 00175 // Instantiate our various ModelComponents: 00176 00177 nub::ref<BufferedInputFrameSeries> bifs 00178 (new BufferedInputFrameSeries(manager, 256)); 00179 manager.addSubComponent(bifs); 00180 00181 nub::ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00182 manager.addSubComponent(d); 00183 00184 nub::ref<EyeTrackerConfigurator> 00185 etc(new EyeTrackerConfigurator(manager)); 00186 manager.addSubComponent(etc); 00187 00188 nub::ref<EventLog> el(new EventLog(manager)); 00189 manager.addSubComponent(el); 00190 00191 manager.exportOptions(MC_RECURSE); 00192 00193 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00194 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00195 00196 // Parse command-line: 00197 if (manager.parseCommandLine(argc, argv, "<outdir> <questions>", 00198 2, 2) == false) 00199 return(1); 00200 00201 // hook our various babies up and do post-command-line configs: 00202 nub::soft_ref<EyeTracker> et = etc->getET(); 00203 d->setEyeTracker(et); 00204 d->setEventLog(el); 00205 et->setEventLog(el); 00206 00207 const std::string outdir = manager.getExtraArg(0); 00208 00209 makeDirectory(outdir); 00210 el->setModelParamString("EventLogFileName", 00211 outdir + "/psychodata.psy"); 00212 00213 std::ofstream answers((outdir + "/answers").c_str()); 00214 if (!answers.is_open()) 00215 LFATAL("couldn't open %s/answers for writing", outdir.c_str()); 00216 00217 std::vector<Question> questions; 00218 parseQuestions(manager.getExtraArg(1), questions); 00219 00220 // let's get all our ModelComponent instances started: 00221 manager.start(); 00222 00223 const VideoFormat vf = bifs->peekFrameSpec().videoFormat; 00224 00225 d->setDesiredRefreshDelayUsec(1000000.0/59.94, 0.2F); 00226 00227 // let's display an ISCAN calibration grid: 00228 d->clearScreen(); 00229 d->displayISCANcalib(); 00230 d->waitForKey(); 00231 00232 // let's do an eye tracker calibration: 00233 d->displayText("<SPACE> to calibrate; other key to skip"); 00234 int c = d->waitForKey(); 00235 if (c == ' ') d->displayEyeTrackerCalibration(3, 3, 3); 00236 d->clearScreen(); 00237 00238 // give a chance to other processes (useful on single-CPU machines): 00239 sleep(1); 00240 sync(); 00241 00242 // ready for action: 00243 d->displayText("<SPACE> to start experiment"); 00244 d->waitForKey(); 00245 00246 // display fixation to indicate that we are ready: 00247 d->clearScreen(); 00248 d->displayFixation(); 00249 00250 // create an overlay: 00251 d->createVideoOverlay(vf); 00252 00253 // ready to go whenever the user is ready: 00254 d->waitForKey(); 00255 d->waitNextRequestedVsync(false, true); 00256 d->pushEvent("===== START ====="); 00257 00258 // start the eye tracker: 00259 et->track(true); 00260 00261 // blink the fixation: 00262 d->displayFixationBlink(); 00263 00264 // grab, display and save: 00265 int framenum = 0; 00266 bool did_underflow = false; 00267 while (true) 00268 { 00269 if (signum != 0) 00270 { 00271 LINFO("quitting because %s was caught", signame(signum)); 00272 return -1; 00273 } 00274 00275 // check for a keypress to see if the user wants to quit the 00276 // experiment; pressing '.' will give a graceful exit and normal 00277 // shutdown, while pressing <ESC> will trigger an LFATAL() and 00278 // an urgent shutdown: 00279 if (d->checkForKey() == '.') 00280 break; 00281 00282 // grab a raw buffer: 00283 const GenericFrame frame = bifs->get(&did_underflow); 00284 if (!frame.initialized()) 00285 break; 00286 00287 // display the frame as an overlay 00288 d->displayVideoOverlay(frame.asVideo(), framenum, 00289 SDLdisplay::NEXT_FRAMETIME); 00290 00291 ++framenum; 00292 } 00293 00294 LINFO("displayed %d frames", framenum); 00295 00296 if (did_underflow) 00297 LERROR("input ended due to premature underflow"); 00298 00299 // destroy the overlay. Somehow, mixing overlay displays and 00300 // normal displays does not work. With a single overlay created 00301 // before this loop and never destroyed, the first movie plays 00302 // ok but the other ones don't show up: 00303 d->destroyYUVoverlay(); 00304 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00305 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00306 00307 // stop the eye tracker: 00308 usleep(50000); 00309 et->track(false); 00310 00311 d->clearScreen(); 00312 00313 for (size_t i = 0; i < questions.size(); ++i) 00314 doQuestion(*d, questions[i], answers); 00315 00316 d->displayText("Experiment complete. Thank you!"); 00317 d->waitForKey(); 00318 00319 // stop all our ModelComponents 00320 manager.stop(); 00321 00322 // all done! 00323 return 0; 00324 } 00325 00326 extern "C" int main(const int argc, char** argv) 00327 { 00328 try 00329 { 00330 return submain(argc, argv); 00331 } 00332 catch (...) 00333 { 00334 REPORT_CURRENT_EXCEPTION; 00335 } 00336 00337 return 1; 00338 } 00339 00340 // ###################################################################### 00341 /* So things look consistent in everyone's emacs... */ 00342 /* Local Variables: */ 00343 /* mode: c++ */ 00344 /* indent-tabs-mode: nil */ 00345 /* End: */ 00346 00347 #endif // APPPSYCHO_PSYCHO_VIDEO_REPLAY_C_DEFINED