00001 /*!@file VFAT/fzvisionTCP3-master.C Grab & process over beowulf w/ pvisionTCP3 */ 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: T. Nathan Mundhenk<mundhenk@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/VFAT/fzvisionTCP3-master.C $ 00035 // $Id: fzvisionTCP3-master.C 9412 2008-03-10 23:10:15Z farhan $ 00036 // 00037 00038 /*! This parallel vision processing master is for use with 00039 pvisionTCP3. See the pvisionTCP3go script in bin/ for how to launch 00040 the slaves. The main difference between the TCP2 and TCP3 versions 00041 is that TCP3 uses load-balancing to send images to the slaves, and 00042 the master node is not only in charge of grabbing the video and 00043 displaying the results, but also of collecting the various 00044 conspicuity maps and assembling them into the saliency map. */ 00045 00046 #include "Beowulf/Beowulf.H" 00047 #include "Component/ModelManager.H" 00048 #include "Devices/DeviceOpts.H" 00049 #include "Devices/FrameGrabberConfigurator.H" 00050 #include "GUI/XWindow.H" 00051 #include "Image/CutPaste.H" // for inplacePaste() 00052 #include "Image/DrawOps.H" 00053 #include "Image/FilterOps.H" 00054 #include "Image/Image.H" 00055 #include "Image/ImageSet.H" 00056 #include "Image/ShapeOps.H" // for decX() etc. 00057 #include "Media/FrameSeries.H" 00058 #include "Neuro/NeuroOpts.H" 00059 #include "Neuro/SaccadeController.H" 00060 #include "Neuro/SaccadeControllerConfigurator.H" 00061 #include "Simulation/SimEventQueue.H" 00062 #include "Simulation/SimEventQueueConfigurator.H" 00063 #include "Transport/FrameIstream.H" 00064 #ifdef HAVE_SDL_SDL_H 00065 #include "Psycho/PsychoDisplay.H" 00066 #endif 00067 #include "Util/Assert.H" 00068 #include "Util/Timer.H" 00069 #include "VFAT/featureClusterVision.H" 00070 #include "Video/RgbConversion.H" // for toVideoYUV422() 00071 00072 #include <signal.h> 00073 #include <unistd.h> 00074 00075 00076 // the following beowulf action definitions are used by pvisionTCP: 00077 #define BEO_RETINA 1 00078 #define BEO_WINNER 2 00079 #define BEO_LUMINANCE 3 00080 #define BEO_REDGREEN 4 00081 #define BEO_BLUEYELLOW 5 00082 #define BEO_ORI0 6 00083 #define BEO_ORI45 7 00084 #define BEO_ORI90 8 00085 #define BEO_ORI135 9 00086 #define BEO_CMAP 10 00087 #define BEO_FLICKER 11 00088 00089 //! Number of conspicuity maps for pvisionTCP 00090 #define NBCMAP 7 00091 00092 //! Number of conspicuity maps fo pvisionTCP2 00093 #define NBCMAP2 8 00094 00095 //! Node offset for the two processing streams: 00096 #define POFFSET 8 00097 00098 //! Number of frames over which average framerate is computed 00099 #define NAVG 20 00100 00101 //! prescale level by which we downsize images before sending them off 00102 #define PRESCALE 2 00103 00104 static bool goforever = true; //!< Will turn false on interrupt signal 00105 00106 //! Signal handler (e.g., for control-C) 00107 void terminate(int s) 00108 { LERROR("*** INTERRUPT ***"); goforever = false; exit(1); } 00109 00110 //! function to receive results from slaves 00111 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, std::vector<Image<float> > *cmap, 00112 int32 *cmapframe); 00113 00114 // ###################################################################### 00115 extern "C" int main(const int argc, char** argv) 00116 { 00117 #ifndef HAVE_SDL_SDL_H 00118 00119 LFATAL("<SDL/SDL.h> must be installed to use this program"); 00120 00121 #else 00122 00123 MYLOGVERB = LOG_INFO; 00124 00125 // instantiate a model manager: 00126 ModelManager manager("Parallel Vision TCP Version 3"); 00127 00128 // Instantiate our various ModelComponents: 00129 nub::soft_ref<SimEventQueueConfigurator> 00130 seqc(new SimEventQueueConfigurator(manager)); 00131 manager.addSubComponent(seqc); 00132 00133 nub::soft_ref<FrameGrabberConfigurator> 00134 gbc(new FrameGrabberConfigurator(manager)); 00135 manager.addSubComponent(gbc); 00136 00137 nub::soft_ref<Beowulf> 00138 beo(new Beowulf(manager, "Beowulf Master", "BeowulfMaster", true)); 00139 manager.addSubComponent(beo); 00140 00141 nub::soft_ref<PsychoDisplay> 00142 d(new PsychoDisplay(manager)); 00143 manager.addSubComponent(d); 00144 00145 // Instantiate our various ModelComponents (for compatability) 00146 nub::soft_ref<InputFrameSeries> ifs(new InputFrameSeries(manager)); 00147 manager.addSubComponent(ifs); 00148 00149 // nub::soft_ref<ThresholdFrictionSaccadeController> 00150 // sc(new ThresholdFrictionSaccadeController(manager)); 00151 nub::soft_ref<SaccadeControllerEyeConfigurator> 00152 scc(new SaccadeControllerEyeConfigurator(manager)); 00153 manager.addSubComponent(scc); 00154 00155 // let's set a bunch of defauls: 00156 manager.setOptionValString(&OPT_SaccadeControllerEyeType, "Threshfric"); 00157 manager.setOptionValString(&OPT_FrameGrabberType, "V4L"); 00158 manager.setOptionValString(&OPT_SCeyeMaxIdleSecs, "1000.0"); 00159 manager.setOptionValString(&OPT_SCeyeThreshMinOvert, "4.0"); 00160 manager.setOptionValString(&OPT_SCeyeThreshMaxCovert, "3.0"); 00161 // manager.setOptionValString(&OPT_SCeyeSpringK, "1000000.0"); 00162 00163 // Parse command-line: 00164 if (manager.parseCommandLine(argc, argv, "", 0, 0) == false) return(1); 00165 00166 // do post-command-line configs: 00167 nub::soft_ref<SimEventQueue> seq = seqc->getQ(); 00168 00169 nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber(); 00170 if (gb.isInvalid()) 00171 LFATAL("You need to select a frame grabber type via the " 00172 "--fg-type=XX command-line option for this program " 00173 "to be useful"); 00174 int w = gb->getWidth(), h = gb->getHeight(); 00175 00176 nub::ref<SaccadeController> sc = scc->getSC(); 00177 00178 int foa_size = std::min(w, h) / 12; 00179 manager.setModelParamVal("InputFrameDims", Dims(w, h), 00180 MC_RECURSE | MC_IGNORE_MISSING); 00181 manager.setModelParamVal("SCeyeStartAtIP", true, 00182 MC_RECURSE | MC_IGNORE_MISSING); 00183 manager.setModelParamVal("SCeyeInitialPosition",Point2D<int>(w/2,h/2), 00184 MC_RECURSE | MC_IGNORE_MISSING); 00185 manager.setModelParamVal("FOAradius", foa_size, 00186 MC_RECURSE | MC_IGNORE_MISSING); 00187 manager.setModelParamVal("FoveaRadius", foa_size, 00188 MC_RECURSE | MC_IGNORE_MISSING); 00189 00190 // catch signals and redirect them to terminate for clean exit: 00191 signal(SIGHUP, terminate); signal(SIGINT, terminate); 00192 signal(SIGQUIT, terminate); signal(SIGTERM, terminate); 00193 signal(SIGALRM, terminate); 00194 00195 // get prepared to grab, communicate, display, etc: 00196 int32 frame = 0; // count the frames 00197 PixRGB<byte> pix(255, 255, 0); // yellow color for fixations 00198 TCPmessage smsg; // buffer to send messages to nodes 00199 00200 uint64 avgtime = 0; int avgn = 0; // for average framerate 00201 float fps = 0.0F; // to display framerate 00202 Timer tim; // for computation of framerate 00203 Timer masterclock; // master clock for simulations 00204 00205 Image<float> bimage; 00206 std::vector<Image<float> > cmap(NBCMAP2,bimage); // vec of conspicuity maps 00207 int32 cmapframe[NBCMAP2]; // array of cmap frame numbers 00208 for (int i = 0; i < NBCMAP2; i ++) cmapframe[i] = -1; 00209 int sml = 4; // pyramid level of saliency map 00210 Image<float> sm(w >> sml, h >> sml, ZEROS); // saliency map 00211 Point2D<int> fixation(-1, -1); // coordinates of eye fixation 00212 const std::string name = "featureCluster"; 00213 const std::string tag = "fCV"; 00214 00215 featureClusterVision<float> fCVout(manager,name,tag,&sm,&cmap,ifs, 00216 manager.getExtraArg(0)); 00217 00218 nub::soft_ref<featureClusterVision<float> > 00219 fCV(&fCVout); 00220 manager.addSubComponent(fCV); 00221 00222 00223 // image buffer for display: 00224 Image<PixRGB<byte> > disp(w * 2, h + 20, ZEROS); 00225 disp += d->getGrey(); 00226 int dw = d->getDims().w(), dh = d->getDims().h(); 00227 ASSERT(dw == w * 2); ASSERT(dh >= disp.getHeight()); 00228 int ovlyoff = (w * 2) * ((dh - disp.getHeight()) / 2); 00229 int ovluvoff = ovlyoff / 4; 00230 00231 char info[1000]; // general text buffer for various info messages 00232 00233 // let's get all our ModelComponent instances started: 00234 manager.start(); 00235 00236 // clear screen 00237 d->clearScreen(); 00238 d->displayText("<SPACE> to start - <SPACE> again to quit"); 00239 while(d->waitForKey() != ' ') ; 00240 d->clearScreen(); 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 // initialize the timers: 00249 00250 tim.reset(); masterclock.reset(); 00251 XWindow win1(Dims(720, 480), 0, 0, "CLASSES"); 00252 Image<PixRGB<float> > fima1; Image<PixRGB<byte> > bima1; 00253 XWindow win2(Dims(720, 480), 0, 0, "CLASSES TEMPORAL"); 00254 Image<PixRGB<float> > fima2; Image<PixRGB<byte> > bima2; 00255 XWindow win3(Dims(720, 480), 0, 0, "TARGETS TEMPORAL"); 00256 Image<PixRGB<float> > fima3; Image<PixRGB<byte> > bima3; 00257 XWindow win4(Dims(720, 480), 0, 0, "SALIENCY MAP"); 00258 Image<float> fima4; Image<PixRGB<byte> > bima4; 00259 00260 // ########## MAIN LOOP: grab, process, display: 00261 while(goforever) 00262 { 00263 // receive conspicuity maps: 00264 receiveCMAPS(beo, &cmap, cmapframe); 00265 00266 // grab an image: 00267 Image< PixRGB<byte> > ima = gb->readRGB(); 00268 std::string myname; 00269 myname = "Beowulf." + frame; 00270 fCVout.fCVuploadImage(ima,myname); 00271 00272 // display image in window: 00273 inplacePaste(disp, ima, Point2D<int>(0, 0)); 00274 Image<float> dispsm(sm); inplaceNormalize(dispsm, 0.0F, 255.0F); 00275 inplacePaste(disp, 00276 Image<PixRGB<byte> >(toRGB(quickInterpolate(dispsm, 1 << sml))), 00277 Point2D<int>(w, 0)); 00278 00279 sc->evolve(*seq); 00280 Point2D<int> eye = sc->getDecision(*seq); 00281 if (eye.i >= 0) fixation = eye; 00282 if (fixation.i >= 0) 00283 { 00284 drawPatch(disp, fixation, 2, pix); 00285 drawCircle(disp, fixation, foa_size, pix, 2); 00286 } 00287 sprintf(info, "%.1ffps", fps); 00288 writeText(disp, Point2D<int>(w, 0), info, PixRGB<byte>(255), PixRGB<byte>(0)); 00289 00290 SDL_Overlay* ovl = d->lockYUVoverlay(); 00291 toVideoYUV422(disp, ovl->pixels[0] + ovlyoff, 00292 ovl->pixels[2] + ovluvoff, 00293 ovl->pixels[1] + ovluvoff); 00294 d->unlockYUVoverlay(); 00295 d->displayYUVoverlay(-1, SDLdisplay::NO_WAIT); 00296 00297 // check for space bar pressed; note: will abort us violently if 00298 // ESC is pressed instead: 00299 if (d->checkForKey() == ' ') goforever = false; 00300 00301 // receive conspicuity maps: 00302 receiveCMAPS(beo, &cmap, cmapframe); 00303 00304 // send every other frame to processing: 00305 if (frame & 1) 00306 { 00307 // prescale image: 00308 Image<PixRGB<byte> > ima2 = 00309 decY(lowPass5y(decX(lowPass5x(ima),1<<PRESCALE)),1<<PRESCALE); 00310 00311 // we do the job of BEO_RETINA on the master to reduce latency: 00312 // compute luminance and send it off: 00313 Image<byte> lum = luminance(ima2); 00314 00315 // first, send off luminance to orientation slaves: 00316 smsg.reset(frame, BEO_ORI0); smsg.addImage(lum); beo->send(smsg); 00317 smsg.setAction(BEO_ORI45); beo->send(smsg); 00318 smsg.setAction(BEO_ORI90); beo->send(smsg); 00319 smsg.setAction(BEO_ORI135); beo->send(smsg); 00320 00321 // and also send to flicker slave: 00322 smsg.setAction(BEO_FLICKER); beo->send(smsg); 00323 00324 // finally, send to luminance slave: 00325 smsg.setAction(BEO_LUMINANCE); beo->send(smsg); 00326 00327 // compute RG and BY and send them off: 00328 Image<byte> r, g, b, y; getRGBY(ima2, r, g, b, y, (byte)25); 00329 smsg.reset(frame, BEO_REDGREEN); 00330 smsg.addImage(r); smsg.addImage(g); beo->send(smsg); 00331 smsg.reset(frame, BEO_BLUEYELLOW); 00332 smsg.addImage(b); smsg.addImage(y); beo->send(smsg); 00333 } 00334 00335 // receive conspicuity maps: 00336 receiveCMAPS(beo, &cmap, cmapframe); 00337 00338 // build our current saliency map: 00339 sprintf(info, "%06d / ", frame); 00340 Image<float> sminput; 00341 for (int i = 0; i < NBCMAP2; i ++) 00342 if (cmap[i].initialized()) 00343 { 00344 if (sminput.initialized()) sminput += cmap[i]; 00345 else sminput = cmap[i]; 00346 char num[10]; sprintf(num, "%06d ", cmapframe[i]); 00347 strcat(info, num); 00348 } 00349 else 00350 strcat(info, "------ "); 00351 writeText(disp, Point2D<int>(0, h), info, 00352 PixRGB<byte>(255), d->getGrey()); 00353 00354 // inject saliency map input into saliency map: 00355 if (sminput.initialized()) sm = sm * 0.7F + sminput * 0.3F; 00356 00357 // evolve our saccade controller up to now: 00358 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00359 sc->evolve(*seq); 00360 00361 // find most salient location and feed saccade controller: 00362 float maxval; Point2D<int> currwin; findMax(sm, currwin, maxval); 00363 WTAwinner newwin = 00364 WTAwinner::buildFromSMcoords(currwin, sml, true, 00365 masterclock.getSimTime(), 00366 maxval, false); 00367 if (newwin.isValid()) sc->setPercept(newwin, *seq); 00368 00369 fCVout.fCVgetClusterImages(&fima1,&fima2,&fima3,&fima4); 00370 bima1 = fima1; bima2 = fima2; bima3 = fima3; 00371 win1.drawImage(bima1); 00372 win2.drawImage(bima2); 00373 win3.drawImage(bima3); 00374 00375 // receive conspicuity maps: 00376 receiveCMAPS(beo, &cmap, cmapframe); 00377 00378 // compute and show framerate and stats over the last NAVG frames: 00379 avgtime += tim.getReset(); avgn ++; 00380 if (avgn == NAVG) 00381 { 00382 fps = 1000.0F / float(avgtime) * float(avgn); 00383 avgtime = 0; avgn = 0; 00384 } 00385 00386 // ready for next frame: 00387 frame++; 00388 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00389 } 00390 00391 // got interrupted; let's cleanup and exit: 00392 d->destroyYUVoverlay(); 00393 LINFO("Normal exit"); 00394 manager.stop(); 00395 return 0; 00396 00397 #endif // HAVE_SDL_SDL_H 00398 00399 } 00400 00401 // ###################################################################### 00402 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, std::vector<Image<float> > *cmap, 00403 int32 *cmapframe) 00404 { 00405 TCPmessage rmsg; // buffer to receive messages from nodes 00406 int32 rframe, raction, rnode = -1, recnb=0; // receive from any node 00407 while(beo->receive(rnode, rmsg, rframe, raction)) // no wait 00408 { 00409 //LINFO("received %d/%d from %d while at %d", 00410 // rframe, raction, rnode, frame); 00411 switch(raction & 0xffff) 00412 { 00413 case BEO_CMAP: // ############################## 00414 { 00415 // get the map: 00416 Image<float> ima = rmsg.getElementFloatIma(); 00417 00418 // the map number is stored in the high 16 bits of the 00419 // raction field: 00420 int32 mapn = raction >> 16; 00421 if (mapn < 0 || mapn >= NBCMAP2) { 00422 LERROR("Bogus cmap number ignored"); 00423 break; 00424 } 00425 00426 // here is a totally asynchronous system example: we 00427 // just update our current value of a given cmap if 00428 // the one we just received is more recent than the 00429 // one we had so far: 00430 if (cmapframe[mapn] < rframe) 00431 { cmap->at(mapn) = ima; cmapframe[mapn] = rframe; } 00432 } 00433 00434 break; 00435 default: // ############################## 00436 LERROR("Bogus action %d -- IGNORING.", raction); 00437 break; 00438 } 00439 // limit number of receives, so we don't hold CPU too long: 00440 recnb ++; if (recnb > NBCMAP2 * 2) break; 00441 } 00442 } 00443 00444 // ###################################################################### 00445 /* So things look consistent in everyone's emacs... */ 00446 /* Local Variables: */ 00447 /* indent-tabs-mode: nil */ 00448 /* End: */