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: */