00001 /*!@file AppPsycho/psycho-video.C Psychophysics display and grabbing of video feeds */ 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-video.C $ 00035 // $Id: psycho-video.C 13539 2010-06-09 22:43:26Z ilab19 $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Devices/DeviceOpts.H" 00040 #include "Devices/DiskDataStream.H" 00041 #include "Devices/FrameGrabberConfigurator.H" 00042 #include "Image/Image.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 "GUI/SDLdisplay.H" 00050 #include "GUI/GUIOpts.H" 00051 #include "Raster/GenericFrame.H" 00052 #include "Transport/FrameIstream.H" 00053 #include "Util/FileUtil.H" 00054 #include "Util/StringUtil.H" // for split() and join() 00055 #include "Util/Types.H" 00056 #include "Util/csignals.H" 00057 #include "Util/sformat.H" 00058 #include "Video/VideoFrame.H" 00059 00060 #include <SDL/SDL.h> 00061 #include <iterator> // for back_inserter() 00062 #include <unistd.h> // for sync() 00063 #include <time.h> 00064 00065 namespace 00066 { 00067 class DiskDataStreamListener : public FrameListener 00068 { 00069 public: 00070 DiskDataStreamListener(nub::ref<DiskDataStream> stream) 00071 : 00072 itsStream(stream) 00073 {} 00074 00075 virtual ~DiskDataStreamListener() {} 00076 00077 virtual void onRawFrame(const GenericFrame& frame) 00078 { 00079 itsStream->writeFrame(frame, "frame"); 00080 } 00081 00082 private: 00083 nub::ref<DiskDataStream> itsStream; 00084 }; 00085 } 00086 00087 //! Psychophysics display and grabbing of video feeds 00088 /*! This displays and grabs video feeds to disk, with added machinery 00089 for eye-tracking. To save time, we grab raw data (in whatever native 00090 grab mode has been selected, without converting to RGB) then feed 00091 them to an SDL YUV overlay and save them to disk. For this to work, 00092 you need to configure your grabber in YUV420P mode, which is what is 00093 used for displays. The saved files are pure raw YUV420P data and will 00094 need to be converted, e.g., to RGB, later and in a different 00095 program (or you can use the YUV input mode of mpeg_encode to create 00096 MPEG movies from your saved files). */ 00097 int submain(const int argc, char** argv) 00098 { 00099 MYLOGVERB = LOG_DEBUG; // suppress debug messages 00100 00101 // 'volatile' because we will modify this from signal handlers 00102 volatile int signum = 0; 00103 catchsignals(&signum); 00104 00105 // Instantiate a ModelManager: 00106 ModelManager manager("Psycho Video"); 00107 00108 // Instantiate our various ModelComponents: 00109 nub::ref<FrameGrabberConfigurator> 00110 fgc(new FrameGrabberConfigurator(manager)); 00111 manager.addSubComponent(fgc); 00112 00113 nub::ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00114 manager.addSubComponent(d); 00115 00116 nub::ref<DiskDataStream> ds(new DiskDataStream(manager)); 00117 manager.addSubComponent(ds); 00118 00119 nub::soft_ref<EyeTrackerConfigurator> 00120 etc(new EyeTrackerConfigurator(manager)); 00121 manager.addSubComponent(etc); 00122 00123 nub::soft_ref<EventLog> el(new EventLog(manager)); 00124 manager.addSubComponent(el); 00125 00126 // select a V4L grabber in 640x480 YUV420P by default: 00127 manager.exportOptions(MC_RECURSE); 00128 manager.setOptionValString(&OPT_FrameGrabberType, "V4L"); 00129 manager.setOptionValString(&OPT_FrameGrabberMode, "UYVY"); 00130 manager.setOptionValString(&OPT_FrameGrabberDims, "640x480"); 00131 manager.setOptionValString(&OPT_DeinterlacerType, "Bob"); 00132 00133 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00134 00135 manager.setOptionValString(&OPT_DiskDataStreamSleepUsecs, "10000"); 00136 manager.setOptionValString(&OPT_SDLdisplayDims, "640x480" ); 00137 00138 // Parse command-line: 00139 if (manager.parseCommandLine 00140 (argc, argv, "<dir1:dir2:dir3> <nframes> <subject>", 3, 3) 00141 == false) 00142 return(1); 00143 00144 std::string Sub=manager.getExtraArg(2); 00145 manager.setOptionValString(&OPT_EventLogFileName, Sub); 00146 00147 // hook our various babies up and do post-command-line configs: 00148 nub::soft_ref<EyeTracker> et = etc->getET(); 00149 d->setEyeTracker(et); 00150 d->setEventLog(el); 00151 et->setEventLog(el); 00152 00153 const std::string dirpath = manager.getExtraArg(0); 00154 const int maxframe = manager.getExtraArgAs<int>(1); 00155 00156 std::vector<std::string> stems; 00157 split(dirpath, ":", std::back_inserter(stems)); 00158 00159 if (stems.size() == 0) 00160 LFATAL("expected at least one entry in the directory path"); 00161 00162 el->setModelParamString("EventLogFileName", 00163 stems[0] + "/" + Sub); 00164 00165 for (size_t i = 0; i < stems.size(); ++i) 00166 { 00167 makeDirectory(stems[i]); 00168 makeDirectory(stems[i] + "/frames"); 00169 00170 stems[i] = stems[i] + "/frames/"; 00171 } 00172 00173 ds->setModelParamString("DiskDataStreamSavePath", 00174 join(stems.begin(), stems.end(), ",")); 00175 00176 nub::ref<FrameIstream> gb = fgc->getFrameGrabber(); 00177 00178 gb->setListener(rutz::shared_ptr<FrameListener>(new DiskDataStreamListener(ds))); 00179 00180 // let's get all our ModelComponent instances started: 00181 manager.start(); 00182 00183 d->setDesiredRefreshDelayUsec(gb->getNaturalFrameTime().usecs(), 0.2F); 00184 00185 // let's display an ISCAN calibration grid: 00186 d->clearScreen(); 00187 d->displayISCANcalib(); 00188 d->waitForKey(); 00189 00190 // let's do an eye tracker calibration: 00191 d->displayText("<SPACE> to calibrate; other key to skip"); 00192 int c = d->waitForKey(); 00193 if (c == ' ') d->displayEyeTrackerCalibration(3, 3, 2); 00194 d->clearScreen(); 00195 00196 // give a chance to other processes (useful on single-CPU machines): 00197 sleep(1); 00198 sync(); 00199 00200 // ready for action: 00201 d->displayText("<SPACE> to start experiment"); 00202 d->waitForKey(); 00203 00204 // display fixation to indicate that we are ready: 00205 d->clearScreen(); 00206 d->displayFixation(); 00207 00208 // create an overlay: 00209 d->createVideoOverlay(gb->peekFrameSpec().videoFormat); 00210 00211 00212 00213 // ready to go whenever the user is ready: 00214 d->waitForKey(); 00215 d->waitNextRequestedVsync(false, true); 00216 d->pushEvent("===== START ====="); 00217 00218 // get wall time: 00219 struct timeval tv; 00220 struct timezone tz; 00221 struct tm *tm; 00222 gettimeofday(&tv, &tz); 00223 tm = localtime(&tv.tv_sec); char msg[300]; 00224 snprintf(msg, 300, "== WALL TIME: %d:%02d:%02d.%03"ZU".%03"ZU" ==", 00225 tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec / 1000, 00226 tv.tv_usec % 1000); 00227 d->pushEvent(msg); 00228 00229 d->pushEvent(std::string("===== Playing movie: ") + Sub.c_str() + " ====="); 00230 00231 // start the eye tracker: 00232 et->track(true); 00233 00234 // blink the fixation: 00235 d->displayFixationBlink(); 00236 00237 // grab, display and save: 00238 int framenum = 0; 00239 while (framenum < maxframe) 00240 { 00241 if (signum != 0) 00242 { 00243 LINFO("quitting because %s was caught", signame(signum)); 00244 return -1; 00245 } 00246 00247 // grab a raw buffer: 00248 const VideoFrame frame = gb->readFrame().asVideo(); 00249 00250 // display the frame as an overlay 00251 d->displayVideoOverlay(frame, framenum, SDLdisplay::NO_WAIT); 00252 00253 ++framenum; 00254 00255 // check for a keypress to see if the user wants to quit the 00256 // experiment; pressing '.' will give a graceful exit and normal 00257 // shutdown, while pressing <ESC> will trigger an LFATAL() and 00258 // an urgent shutdown: 00259 if (d->checkForKey() == '.') 00260 break; 00261 } 00262 00263 // destroy the overlay. Somehow, mixing overlay displays and 00264 // normal displays does not work. With a single overlay created 00265 // before this loop and never destroyed, the first movie plays 00266 // ok but the other ones don't show up: 00267 d->destroyYUVoverlay(); 00268 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00269 d->clearScreen(); // sometimes 2 clearScreen() are necessary 00270 00271 // stop the eye tracker: 00272 usleep(50000); 00273 et->track(false); 00274 00275 d->clearScreen(); 00276 00277 // no need to explicit clear DiskDataStream's queue here; that 00278 // happens automatically in DiskDataStream::stop(), called from 00279 // manager.stop() 00280 00281 d->displayText("Experiment complete. Thank you!"); 00282 d->waitForKey(); 00283 00284 // stop all our ModelComponents 00285 manager.stop(); 00286 00287 // all done! 00288 return 0; 00289 } 00290 00291 extern "C" int main(const int argc, char** argv) 00292 { 00293 try 00294 { 00295 return submain(argc, argv); 00296 } 00297 catch (...) 00298 { 00299 REPORT_CURRENT_EXCEPTION; 00300 } 00301 00302 return 1; 00303 } 00304 00305 // ###################################################################### 00306 /* So things look consistent in everyone's emacs... */ 00307 /* Local Variables: */ 00308 /* indent-tabs-mode: nil */ 00309 /* End: */