XCgrab.C

00001 /*!@file AppPsycho/videograb.C grab frames and save the debayered color
00002   images to disk. Use it like: XCgrab <name>*/
00003 
00004 // //////////////////////////////////////////////////////////////////// //
00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00006 // University of Southern California (USC) and the iLab at USC.         //
00007 // See http://iLab.usc.edu for information about this project.          //
00008 // //////////////////////////////////////////////////////////////////// //
00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00011 // in Visual Environments, and Applications'' by Christof Koch and      //
00012 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00013 // pending; application number 09/912,225 filed July 23, 2001; see      //
00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00015 // //////////////////////////////////////////////////////////////////// //
00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00017 //                                                                      //
00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00019 // redistribute it and/or modify it under the terms of the GNU General  //
00020 // Public License as published by the Free Software Foundation; either  //
00021 // version 2 of the License, or (at your option) any later version.     //
00022 //                                                                      //
00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00026 // PURPOSE.  See the GNU General Public License for more details.       //
00027 //                                                                      //
00028 // You should have received a copy of the GNU General Public License    //
00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00031 // Boston, MA 02111-1307 USA.                                           //
00032 // //////////////////////////////////////////////////////////////////// //
00033 //
00034 // Primary maintainer for this file: Zhicheng Li <zhicheng@usc.edu>
00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppDevices/XCgrab.C $
00036 // $Id: XCgrab.C 12962 2010-03-06 02:13:53Z irock $
00037 //
00038 
00039 #include "Component/ModelManager.H"
00040 #include "Devices/FrameGrabberConfigurator.H"
00041 #include "Devices/DeviceOpts.H"
00042 #include "Image/DrawOps.H"
00043 #include "Image/Image.H"
00044 #include "Image/ShapeOps.H"
00045 #include "Image/CutPaste.H"
00046 #include "Image/ImageCache.H"
00047 #include "Image/Pixels.H"
00048 #include "GUI/SDLdisplay.H"
00049 #include "GUI/GUIOpts.H"
00050 #include "Raster/Raster.H"
00051 #include "Transport/FrameIstream.H"
00052 #include "Util/Timer.H"
00053 #include "Util/log.H"
00054 #include "Util/sformat.H"
00055 #include "Video/RgbConversion.H" // for toVideoYUV422()
00056 #include "Raster/DeBayer.H" // for debayer()
00057 
00058 #include <pthread.h>
00059 
00060 //! number of frames over which frame rate is computed
00061 #define NAVG 20
00062 
00063 #define MAXSAVETHREAD 4
00064 
00065 //! Enumerate the video preview type
00066 enum PreviewType
00067   {
00068     PrevAll,
00069     PrevTopLeft,
00070     PrevTopRight,
00071     PrevBotLeft,
00072     PrevBotRight,
00073     PrevCenter
00074   };
00075 
00076 //! counter for saved frame number
00077 uint fnb = 0;
00078 pthread_mutex_t qmutex_cache, qmutex_cacheRGB, qmutex_imaShow;
00079 pthread_mutex_t qmutex_mgz;
00080 ImageCache<byte> cache;
00081 ImageCache<PixRGB<byte> > cacheRGB;
00082 std::vector<std::string> base;
00083 bool saving = false;
00084 Image<PixRGB<byte> > imaShow;
00085 PreviewType prevType = PrevCenter;
00086 Dims showDim;
00087 
00088 //save the debayered frames in disk as "ppm" format
00089 static void* saveframes(void *)
00090 {
00091   while(1) {
00092     Image<PixRGB<byte> > imaRGB; bool havemore = false;
00093     uint cntTmp = 0;
00094 
00095     // do we have images ready to go?
00096     pthread_mutex_lock(&qmutex_cacheRGB);
00097     if (cacheRGB.size()){
00098       imaRGB = cacheRGB.pop_front();
00099       fnb++;
00100       cntTmp = fnb;
00101     }
00102     if (cacheRGB.size()) havemore = true;
00103     pthread_mutex_unlock(&qmutex_cacheRGB);
00104 
00105     // if we got an image, save it:
00106     if (imaRGB.initialized())
00107       {
00108         // we save each frame to a different base in a rolling manner:
00109         const char *b = base[cntTmp % base.size()].c_str();
00110         Raster::WriteRGB(imaRGB, sformat("%s%06u.ppm", b, fnb));
00111       }
00112 
00113     if (havemore == false) usleep(200);
00114   }
00115   return NULL;
00116 }
00117 
00118 //debayer the captured image and display a smaller size on the screen
00119 static void* debayerframes(void *)
00120 {
00121   while(1){
00122     Image<byte> ima; bool havemore = false;
00123     Image<PixRGB<byte> > imaRGB;
00124 
00125     pthread_mutex_lock(&qmutex_cache);
00126     if(cache.size()) ima = cache.pop_front();
00127     if(cache.size()) havemore = true;
00128     pthread_mutex_unlock(&qmutex_cache);
00129 
00130     if( ima.initialized())
00131       {
00132         imaRGB = deBayer(ima, BAYER_GBRG);
00133         pthread_mutex_lock(&qmutex_imaShow);
00134 
00135         switch(prevType){
00136         case PrevAll:  // scaled size of the whole image
00137           imaShow = rescale(imaRGB, showDim.w(), showDim.h(),
00138                             RESCALE_SIMPLE_NOINTERP);
00139           break;
00140         case PrevTopLeft:  // top left
00141           imaShow = crop(imaRGB, Point2D<int>(0,0), showDim);
00142           break;
00143         case PrevTopRight:  // top right
00144           imaShow = crop(imaRGB, Point2D<int>
00145                          (imaRGB.getWidth()-showDim.w(),0), showDim);
00146           break;
00147         case PrevBotLeft:  // bottom left
00148           imaShow = crop(imaRGB, Point2D<int>
00149                          (0,imaRGB.getHeight()-showDim.h()), showDim);
00150           break;
00151         case PrevBotRight:  // bottom right
00152           imaShow = crop(imaRGB, Point2D<int>
00153                          (imaRGB.getWidth()-showDim.w(),
00154                           imaRGB.getHeight()-showDim.h()), showDim);
00155           break;
00156         case PrevCenter:  // center
00157           imaShow = crop(imaRGB, Point2D<int>
00158                          ((imaRGB.getWidth()-showDim.w())/2,
00159                           (imaRGB.getHeight()-showDim.h())/2),showDim);
00160           break;
00161         default:
00162           LFATAL("the preview type should between 0 and 5, now is %d", prevType);
00163         }
00164 
00165         pthread_mutex_unlock(&qmutex_imaShow);
00166 
00167         if(saving)
00168           {
00169             pthread_mutex_lock(&qmutex_cacheRGB);
00170             cacheRGB.push_back(imaRGB);
00171             pthread_mutex_unlock(&qmutex_cacheRGB);
00172           }
00173       }
00174 
00175     if(havemore == false) usleep(20);
00176   }
00177   return NULL;
00178 }
00179 
00180 
00181 /*! This simple executable grabs video frames through the EPIX XC HD
00182   camera link grabber (see XCgrabber.H). Selection of the grabber type
00183   is made via the  --fg-type=XC command-line option. Frames are pushed
00184   into a queue and  a second thread then tries to empty the queue as
00185   quickly as possible by writing the frames to disk. In testing, PPM
00186   format actually gave better frame rates than PNG, so that's what is
00187   used.  Press <SPACE> to start grabbing and <SPACE> again to stop. */
00188 static int submain(const int argc, char** argv)
00189 {
00190   // instantiate a model manager:
00191   ModelManager manager("Frame Grabber");
00192 
00193   // Instantiate our various ModelComponents:
00194   nub::soft_ref<FrameGrabberConfigurator>
00195     gbc(new FrameGrabberConfigurator(manager));
00196   manager.addSubComponent(gbc);
00197 
00198   nub::soft_ref<SDLdisplay> d(new SDLdisplay(manager));
00199   manager.addSubComponent(d);
00200 
00201   manager.setOptionValString(&OPT_SDLdisplayPriority, "0");
00202   manager.setOptionValString(&OPT_FrameGrabberType, "XC");
00203   manager.setOptionValString(&OPT_SDLdisplayFullscreen,"false");
00204   manager.setOptionValString(&OPT_SDLdisplayDims, "960x640");
00205 
00206   // Parse command-line:
00207   if (manager.parseCommandLine(argc, argv, "<basename> ... <basename>",
00208                                1, MAXSAVETHREAD) == false)
00209     return(1);
00210 
00211   // do post-command-line configs:
00212   nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber();
00213   if (gb.isInvalid())
00214     LFATAL("You need to have XC camera and XClibrary");
00215 
00216   // get the basename:
00217   for (uint i = 0; i < manager.numExtraArgs(); i ++)
00218     base.push_back(manager.getExtraArg(i));
00219 
00220   // let's get all our ModelComponent instances started:
00221   manager.start();
00222 
00223   // get ready for main loop:
00224   Timer tim,timer; uint64 t[NAVG]; int frame = 0;
00225   d->clearScreen(PixRGB<byte>(128)); bool doit = true;
00226 
00227   showDim = d->getDims();
00228   int iw = showDim.w(), ih = showDim.h();
00229   int dw = d->getDims().w(), dh = d->getDims().h();
00230 
00231   int ovlyoff = (dw - iw) / 2 + dw * ((dh - ih) / 2);
00232   int ovluvoff = (dw - iw) / 4 + dw * ((dh - ih) / 8);
00233   int ystride = (dw - iw), uvstride = (dw - iw) / 2;
00234 
00235   pthread_t saver[MAXSAVETHREAD];
00236   for(int ii = 0; ii<(int)base.size(); ii++)
00237     pthread_create(saver+ii, NULL, &saveframes, (void *)NULL);
00238 
00239   pthread_t debayer1;
00240   pthread_create(&debayer1, NULL, &debayerframes, (void*) NULL);
00241 
00242   // create an overlay:
00243   d->createYUVoverlay(SDL_YV12_OVERLAY);
00244 
00245   // get the frame grabber to start streaming:
00246   gb->startStream();
00247 
00248   // main loop:
00249   // if we type space to record then next space will stop the first record section
00250   // the next section will be begin when the third space typed and so on..
00251   //int section_cnt = 0;
00252   float frate = 0.0f;
00253   while(doit) {
00254     tim.reset();
00255 
00256     // grab a frame:
00257     Image<byte> ima = gb->readGray();
00258     pthread_mutex_lock(&qmutex_cache);
00259     cache.push_back(ima);
00260     pthread_mutex_unlock(&qmutex_cache);
00261 
00262     // to measure display time:
00263     uint64 t0 = tim.get();
00264 
00265     // if saving, push image into queue:
00266     pthread_mutex_lock(&qmutex_imaShow);
00267     if (saving)
00268       {
00269         const std::string msg =
00270           sformat(" %.1ffps [%04d] ", frate, cacheRGB.size());
00271         writeText(imaShow, Point2D<int>(0, 0), msg.c_str());
00272       }
00273     else // tell user we are ready to save
00274       {
00275         const std::string msg =
00276           sformat(" [SPC] to save %.1ffp [%04d] ", frate, cacheRGB.size());
00277         writeText(imaShow, Point2D<int>(0, 0), msg.c_str());
00278       }
00279 
00280     // show the frame:
00281     SDL_Overlay* ovl = d->lockYUVoverlay();
00282     toVideoYUV422(imaShow, ovl->pixels[0] + ovlyoff,
00283                   ovl->pixels[2] + ovluvoff,
00284                   ovl->pixels[1] + ovluvoff,
00285              ystride, uvstride, uvstride);
00286 
00287     d->unlockYUVoverlay();
00288     d->displayYUVoverlay(-1, SDLdisplay::NO_WAIT);
00289     pthread_mutex_unlock(&qmutex_imaShow);
00290 
00291     // check for space bar pressed; note: will abort us violently if
00292     // ESC is pressed instead:
00293     int ii = (d->checkForKey());
00294     if(ii == ' ')
00295       { saving = ! saving;
00296         prevType = PrevAll;
00297       }
00298     else if(ii == 'q')
00299       prevType = PrevTopLeft;
00300     else if(ii == 'e')
00301       prevType = PrevTopRight;
00302     else if(ii == 'a')
00303       prevType = PrevBotLeft;
00304     else if(ii == 'd')
00305       prevType = PrevBotRight;
00306     else if(ii == 's')
00307       prevType = PrevCenter;
00308     else if(ii == 'w')
00309       prevType = PrevAll;
00310 
00311     t[frame % NAVG] = tim.get();
00312     t0 = t[frame % NAVG] - t0;
00313     if (t0 > 20000ULL) LINFO("Display took %lluus", t0);
00314 
00315     // compute and show framerate over the last NAVG frames:
00316     if (frame % NAVG == 0 && frame > 0)
00317       {
00318         uint64 avg = 0ULL; for (int i = 0; i < NAVG; i ++) avg += t[i];
00319         frate = 1000.0F / float(avg) * float(NAVG);
00320         LINFO("Frame rate %f fps, buf size %u, time %f", frate,
00321               cacheRGB.size(), timer.getSecs());
00322       }
00323     frame ++;
00324   }
00325   d->destroyYUVoverlay();
00326   manager.stop();
00327 
00328   // all done!
00329   return 0;
00330 }
00331 
00332 extern "C" int main(const int argc, char** argv)
00333 {
00334   try
00335     {
00336       return submain(argc, argv);
00337     }
00338   catch (...)
00339     {
00340       REPORT_CURRENT_EXCEPTION;
00341     }
00342 
00343   return 1;
00344 }
00345 
00346 // ######################################################################
00347 /* So things look consistent in everyone's emacs... */
00348 /* Local Variables: */
00349 /* indent-tabs-mode: nil */
00350 /* End: */
Generated on Sun May 8 08:04:10 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3