00001 /*!@file AppPsycho/videograb.C grab frames and save them to disk */ 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/AppGUI/videograb.C $ 00035 // $Id: videograb.C 9816 2008-06-17 01:42:47Z ilab24 $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Devices/FrameGrabberConfigurator.H" 00040 #include "Image/DrawOps.H" 00041 #include "Image/Image.H" 00042 #include "Image/Image.H" 00043 #include "Image/ImageCache.H" 00044 #include "Image/Pixels.H" 00045 #include "GUI/SDLdisplay.H" 00046 #include "GUI/GUIOpts.H" 00047 #include "Raster/Raster.H" 00048 #include "Transport/FrameIstream.H" 00049 #include "Util/Timer.H" 00050 #include "Util/log.H" 00051 #include "Util/sformat.H" 00052 #include "Video/RgbConversion.H" // for toVideoYUV422() 00053 00054 #include <pthread.h> 00055 00056 //! number of frames over which frame rate is computed 00057 #define NAVG 20 00058 00059 //! Maximum number of frames in queue 00060 #define MAXQLEN 1000 00061 00062 pthread_mutex_t qmutex; 00063 ImageCache< PixRGB<byte> > cache; 00064 std::vector<std::string> base; 00065 00066 static void* saveframes(void *) 00067 { 00068 uint fnb = 0; 00069 while(1) { 00070 Image< PixRGB<byte> > ima; bool havemore = false; 00071 00072 // do we have images ready to go? 00073 pthread_mutex_lock(&qmutex); 00074 if (cache.size()) ima = cache.pop_front(); 00075 if (cache.size()) havemore = true; 00076 pthread_mutex_unlock(&qmutex); 00077 00078 // if we got an image, save it: 00079 if (ima.initialized()) 00080 { 00081 // we save each frame to a different base in a rolling manner: 00082 const char *b = base[fnb % base.size()].c_str(); 00083 Raster::WriteRGB(ima, sformat("%s%06u.ppm", b, fnb++)); 00084 } 00085 00086 if (havemore == false) usleep(1000); 00087 } 00088 return NULL; 00089 } 00090 00091 00092 /*! This simple executable grabs video frames through the video4linux 00093 driver (see V4Lgrabber.H) or the IEEE1394 (firewire) grabber (see 00094 IEEE1394grabber.H). Selection of the grabber type is made via the 00095 --fg-type=XX command-line option. Frames are pushed into a queue and 00096 a second thread then tries to empty the queue as quickly as possible 00097 by writing the frames to disk. In testing, PPM format actually gave 00098 better frame rates than PNG, so that's what is used. Press <SPACE> 00099 to start grabbing and <SPACE> again to stop. */ 00100 static int submain(const int argc, char** argv) 00101 { 00102 // instantiate a model manager: 00103 ModelManager manager("Frame Grabber"); 00104 00105 // Instantiate our various ModelComponents: 00106 nub::soft_ref<FrameGrabberConfigurator> 00107 gbc(new FrameGrabberConfigurator(manager)); 00108 manager.addSubComponent(gbc); 00109 00110 nub::soft_ref<SDLdisplay> d(new SDLdisplay(manager)); 00111 manager.addSubComponent(d); 00112 00113 manager.setOptionValString(&OPT_SDLdisplayPriority, "0"); 00114 00115 // Parse command-line: 00116 if (manager.parseCommandLine(argc, argv, "<basename> ... <basename>", 00117 1, -1) == false) 00118 return(1); 00119 00120 // do post-command-line configs: 00121 nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber(); 00122 if (gb.isInvalid()) 00123 LFATAL("You need to select a frame grabber type via the " 00124 "--fg-type=XX command-line option for this program " 00125 "to be useful"); 00126 00127 // get the basename: 00128 for (uint i = 0; i < manager.numExtraArgs(); i ++) 00129 base.push_back(manager.getExtraArg(i)); 00130 00131 // let's get all our ModelComponent instances started: 00132 manager.start(); 00133 00134 // get ready for main loop: 00135 Timer tim; uint64 t[NAVG]; int frame = 0; 00136 d->clearScreen(PixRGB<byte>(128)); bool doit = true; 00137 00138 int iw = gb->getWidth(), ih = gb->getHeight(); 00139 int dw = d->getDims().w(), dh = d->getDims().h(); 00140 if (iw > dw || ih > dh) 00141 { 00142 LERROR("Grab frame size must be smaller than display size"); 00143 doit = false; 00144 } 00145 00146 int ovlyoff = (dw - iw) / 2 + dw * ((dh - ih) / 2); 00147 int ovluvoff = (dw - iw) / 4 + dw * ((dh - ih) / 8); 00148 int ystride = (dw - iw), uvstride = (dw - iw) / 2; 00149 bool saving = false; float frate = 0.0f; 00150 pthread_t saver; 00151 pthread_create(&saver, NULL, &saveframes, (void *)NULL); 00152 00153 // create an overlay: 00154 d->createYUVoverlay(SDL_YV12_OVERLAY); 00155 00156 // get the frame grabber to start streaming: 00157 gb->startStream(); 00158 00159 // main loop: 00160 while(doit) { 00161 tim.reset(); 00162 00163 // grab a frame: 00164 Image< PixRGB<byte> > ima = gb->readRGB(); 00165 00166 // to measure display time: 00167 uint64 t0 = tim.get(); 00168 00169 // if saving, push image into queue: 00170 if (saving) 00171 { 00172 pthread_mutex_lock(&qmutex); 00173 cache.push_back(ima); 00174 pthread_mutex_unlock(&qmutex); 00175 const std::string msg = sformat(" %.1ffps [%04d] ", frate, cache.size()); 00176 writeText(ima, Point2D<int>(0, 0), msg.c_str(), PixRGB<byte>(255), PixRGB<byte>(0)); 00177 } 00178 else // tell user we are ready to save 00179 { 00180 const std::string msg = sformat(" [SPC] to save [%04d] ", cache.size()); 00181 writeText(ima, Point2D<int>(0, 0), msg.c_str(), PixRGB<byte>(255), PixRGB<byte>(0)); 00182 } 00183 00184 // show the frame: 00185 SDL_Overlay* ovl = d->lockYUVoverlay(); 00186 toVideoYUV422(ima, ovl->pixels[0] + ovlyoff, 00187 ovl->pixels[2] + ovluvoff, 00188 ovl->pixels[1] + ovluvoff, 00189 ystride, uvstride, uvstride); 00190 d->unlockYUVoverlay(); 00191 d->displayYUVoverlay(-1, SDLdisplay::NO_WAIT); 00192 00193 // check for space bar pressed; note: will abort us violently if 00194 // ESC is pressed instead: 00195 if (d->checkForKey() == ' ') saving = ! saving; 00196 00197 t[frame % NAVG] = tim.get(); 00198 t0 = t[frame % NAVG] - t0; 00199 if (t0 > 20000ULL) LINFO("Display took %lluus", t0); 00200 00201 // compute and show framerate over the last NAVG frames: 00202 if (frame % NAVG == 0 && frame > 0) 00203 { 00204 uint64 avg = 0ULL; for (int i = 0; i < NAVG; i ++) avg += t[i]; 00205 frate = 1000.0F / float(avg) * float(NAVG); 00206 } 00207 frame ++; 00208 } 00209 00210 // stop all our ModelComponents 00211 d->destroyYUVoverlay(); 00212 manager.stop(); 00213 00214 // all done! 00215 return 0; 00216 } 00217 00218 extern "C" int main(const int argc, char** argv) 00219 { 00220 try 00221 { 00222 return submain(argc, argv); 00223 } 00224 catch (...) 00225 { 00226 REPORT_CURRENT_EXCEPTION; 00227 } 00228 00229 return 1; 00230 } 00231 00232 // ###################################################################### 00233 /* So things look consistent in everyone's emacs... */ 00234 /* Local Variables: */ 00235 /* indent-tabs-mode: nil */ 00236 /* End: */