00001 /*!@file Parallel/pvisionTCP3-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: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Parallel/pvisionTCP3-master.C $ 00035 // $Id: pvisionTCP3-master.C 10538 2008-12-17 08:49:27Z itti $ 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 "Image/ColorOps.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/Image.H" 00056 #include "Image/ImageSet.H" 00057 #include "Image/MathOps.H" 00058 #include "Image/Pixels.H" 00059 #include "Image/ShapeOps.H" // for decX() etc. 00060 #include "Image/Transforms.H" 00061 #include "Neuro/NeuroOpts.H" 00062 #include "Neuro/SaccadeControllerConfigurator.H" 00063 #include "Neuro/SaccadeController.H" 00064 #include "Parallel/pvisionTCP-defs.H" 00065 #include "Simulation/SimEventQueue.H" 00066 #include "Simulation/SimEventQueueConfigurator.H" 00067 #include "Transport/FrameIstream.H" 00068 #ifdef HAVE_SDL_SDL_H 00069 #include "GUI/SDLdisplay.H" 00070 #endif 00071 #include "Util/Assert.H" 00072 #include "Util/FpsTimer.H" 00073 #include "Util/Timer.H" 00074 #include "Util/sformat.H" 00075 #include "Video/RgbConversion.H" // for toVideoYUV422() 00076 00077 #include <signal.h> 00078 #include <unistd.h> 00079 00080 00081 //! prescale level by which we downsize images before sending them off 00082 #define PRESCALE 2 00083 00084 static bool goforever = true; //!< Will turn false on interrupt signal 00085 00086 //! Signal handler (e.g., for control-C) 00087 void terminate(int s) 00088 { LERROR("*** INTERRUPT ***"); goforever = false; exit(1); } 00089 00090 //! function to receive results from slaves 00091 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap, 00092 int32 *cmapframe); 00093 00094 // ###################################################################### 00095 int submain(int argc, char** argv) 00096 { 00097 #ifndef HAVE_SDL_SDL_H 00098 LFATAL("<SDL/SDL.h> must be installed to use this program"); 00099 #else 00100 MYLOGVERB = LOG_INFO; 00101 00102 // instantiate a model manager: 00103 ModelManager manager("Parallel Vision TCP Version 3"); 00104 00105 // Instantiate our various ModelComponents: 00106 nub::soft_ref<SimEventQueueConfigurator> 00107 seqc(new SimEventQueueConfigurator(manager)); 00108 manager.addSubComponent(seqc); 00109 00110 nub::soft_ref<FrameGrabberConfigurator> 00111 gbc(new FrameGrabberConfigurator(manager)); 00112 manager.addSubComponent(gbc); 00113 00114 nub::soft_ref<Beowulf> 00115 beo(new Beowulf(manager, "Beowulf Master", "BeowulfMaster", true)); 00116 manager.addSubComponent(beo); 00117 00118 nub::soft_ref<SDLdisplay> 00119 d(new SDLdisplay(manager)); 00120 manager.addSubComponent(d); 00121 00122 // nub::soft_ref<ThresholdFrictionSaccadeController> 00123 // sc(new ThresholdFrictionSaccadeController(manager)); 00124 nub::soft_ref<SaccadeControllerEyeConfigurator> 00125 scc(new SaccadeControllerEyeConfigurator(manager)); 00126 manager.addSubComponent(scc); 00127 00128 // let's set a bunch of defauls: 00129 manager.setOptionValString(&OPT_SaccadeControllerEyeType, "Threshfric"); 00130 manager.setOptionValString(&OPT_FrameGrabberType, "V4L"); 00131 manager.setOptionValString(&OPT_SCeyeMaxIdleSecs, "1000.0"); 00132 manager.setOptionValString(&OPT_SCeyeThreshMinOvert, "4.0"); 00133 manager.setOptionValString(&OPT_SCeyeThreshMaxCovert, "3.0"); 00134 // manager.setOptionValString(&OPT_SCeyeSpringK, "1000000.0"); 00135 00136 // Parse command-line: 00137 if (manager.parseCommandLine(argc, argv, "", 0, 0) == false) return(1); 00138 00139 // do post-command-line configs: 00140 nub::soft_ref<SimEventQueue> seq = seqc->getQ(); 00141 00142 nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber(); 00143 if (gb.isInvalid()) 00144 LFATAL("You need to select a frame grabber type via the " 00145 "--fg-type=XX command-line option for this program " 00146 "to be useful"); 00147 int w = gb->getWidth(), h = gb->getHeight(); 00148 00149 nub::ref<SaccadeController> sc = scc->getSC(); 00150 00151 const int foa_size = 64; 00152 manager.setModelParamVal("InputFrameDims", Dims(w, h), 00153 MC_RECURSE | MC_IGNORE_MISSING); 00154 manager.setModelParamVal("SCeyeStartAtIP", true, 00155 MC_RECURSE | MC_IGNORE_MISSING); 00156 manager.setModelParamVal("SCeyeInitialPosition",Point2D<int>(w/2,h/2), 00157 MC_RECURSE | MC_IGNORE_MISSING); 00158 manager.setModelParamVal("FOAradius", foa_size, 00159 MC_RECURSE | MC_IGNORE_MISSING); 00160 manager.setModelParamVal("FoveaRadius", foa_size, 00161 MC_RECURSE | MC_IGNORE_MISSING); 00162 //manager.setModelParamVal("SimulationTimeStep", SimTime::MSECS(1.0), 00163 // MC_RECURSE | MC_IGNORE_MISSING); 00164 00165 // catch signals and redirect them to terminate for clean exit: 00166 signal(SIGHUP, terminate); signal(SIGINT, terminate); 00167 signal(SIGQUIT, terminate); signal(SIGTERM, terminate); 00168 signal(SIGALRM, terminate); 00169 00170 // get prepared to grab, communicate, display, etc: 00171 PixRGB<byte> pix(255, 255, 0); // yellow color for fixations 00172 TCPmessage smsg; // buffer to send messages to nodes 00173 FpsTimer tim; // for computation of framerate 00174 Timer masterclock; // master clock for simulations 00175 00176 Image<float> cmap[NBCMAP2]; // array of conspicuity maps 00177 int32 cmapframe[NBCMAP2]; // array of cmap frame numbers 00178 for (int i = 0; i < NBCMAP2; i ++) cmapframe[i] = -1; 00179 int sml = 4; // pyramid level of saliency map 00180 Image<float> sm(w >> sml, h >> sml, ZEROS); // saliency map 00181 Point2D<int> fixation(-1, -1); // coordinates of eye fixation 00182 00183 // image buffer for display: 00184 const PixRGB<byte> grey(127); 00185 std::string info; 00186 00187 // adjust SDL display size: 00188 d->setModelParamVal("SDLdisplayDims", gb->peekDims()); 00189 00190 // let's get all our ModelComponent instances started: 00191 manager.start(); 00192 00193 // clear screen 00194 d->clearScreen(grey); 00195 d->displayText("<SPACE> to start - <SPACE> again to quit", false, 00196 PixRGB<byte>(0), grey); 00197 while(d->waitForKey() != ' ') ; 00198 d->clearScreen(grey); 00199 00200 // create an overlay: 00201 d->createYUVoverlay(SDL_YV12_OVERLAY); 00202 00203 // get the frame grabber to start streaming: 00204 gb->startStream(); 00205 00206 // initialize the timers: 00207 masterclock.reset(); 00208 00209 // ##### MAIN LOOP: 00210 while(goforever) 00211 { 00212 // receive conspicuity maps: 00213 receiveCMAPS(beo, cmap, cmapframe); 00214 00215 // grab an image: 00216 Image< PixRGB<byte> > ima = gb->readRGB(); 00217 00218 // display image in window: 00219 Image<float> dispsm(sm); inplaceNormalize(dispsm, 0.0F, 255.0F); 00220 Image<byte> dispsmb = dispsm; dispsmb = quickInterpolate(dispsmb, 2); 00221 inplacePaste(ima, toRGB(dispsmb), Point2D<int>(w-dispsmb.getWidth(), 0)); 00222 drawRect(ima, Rectangle(Point2D<int>(w-dispsmb.getWidth()-1, 0), 00223 dispsmb.getDims()), pix); 00224 00225 sc->evolve(*seq); 00226 Point2D<int> eye = sc->getDecision(*seq); 00227 if (eye.isValid()) fixation = eye; 00228 if (fixation.isValid()) 00229 { 00230 drawPatch(ima, fixation, 2, pix); 00231 drawCircle(ima, fixation, foa_size, pix, 2); 00232 } 00233 writeText(ima, Point2D<int>(0, 0), 00234 sformat(" %.1ffps ", tim.getRecentFps()).c_str(), 00235 PixRGB<byte>(255), grey); 00236 writeText(ima, Point2D<int>(0, h-20), info.c_str(), 00237 PixRGB<byte>(255), grey); 00238 00239 SDL_Overlay* ovl = d->lockYUVoverlay(); 00240 toVideoYUV422(ima, ovl->pixels[0], ovl->pixels[2], ovl->pixels[1]); 00241 d->unlockYUVoverlay(); 00242 d->displayYUVoverlay(-1, SDLdisplay::NO_WAIT); 00243 00244 // check for space bar pressed; note: will abort us violently if 00245 // ESC is pressed instead: 00246 if (d->checkForKey() == ' ') goforever = false; 00247 00248 // receive conspicuity maps: 00249 receiveCMAPS(beo, cmap, cmapframe); 00250 00251 // send every other frame to processing: 00252 if (tim.frameNumber() & 1) 00253 { 00254 const int32 frame = int32(tim.frameNumber()); 00255 00256 // prescale image: 00257 Image<PixRGB<byte> > ima2 = 00258 decY(lowPass5y(decX(lowPass5x(ima),1<<PRESCALE)),1<<PRESCALE); 00259 00260 // we do the job of BEO_RETINA on the master to reduce latency: 00261 // compute luminance and send it off: 00262 Image<byte> lum = luminance(ima2); 00263 00264 // first, send off luminance to orientation slaves: 00265 smsg.reset(frame, BEO_ORI0); smsg.addImage(lum); beo->send(smsg); 00266 smsg.setAction(BEO_ORI45); beo->send(smsg); 00267 smsg.setAction(BEO_ORI90); beo->send(smsg); 00268 smsg.setAction(BEO_ORI135); beo->send(smsg); 00269 00270 // and also send to flicker slave: 00271 smsg.setAction(BEO_FLICKER); beo->send(smsg); 00272 00273 // finally, send to luminance slave: 00274 smsg.setAction(BEO_LUMINANCE); beo->send(smsg); 00275 00276 // compute RG and BY and send them off: 00277 Image<byte> r, g, b, y; getRGBY(ima2, r, g, b, y, (byte)25); 00278 smsg.reset(frame, BEO_REDGREEN); 00279 smsg.addImage(r); smsg.addImage(g); beo->send(smsg); 00280 smsg.reset(frame, BEO_BLUEYELLOW); 00281 smsg.addImage(b); smsg.addImage(y); beo->send(smsg); 00282 } 00283 00284 // receive conspicuity maps: 00285 receiveCMAPS(beo, cmap, cmapframe); 00286 00287 // build our current saliency map: 00288 info = sformat("%06d / ", tim.frameNumber()); 00289 Image<float> sminput; 00290 for (int i = 0; i < NBCMAP2; i ++) 00291 if (cmap[i].initialized()) 00292 { 00293 if (sminput.initialized()) sminput += cmap[i]; 00294 else sminput = cmap[i]; 00295 info += sformat("%06d ", cmapframe[i]); 00296 } 00297 else 00298 info += "------ "; 00299 00300 // inject saliency map input into saliency map: 00301 if (sminput.initialized()) sm = sm * 0.7F + sminput * 0.3F; 00302 00303 // evolve our saccade controller up to now: 00304 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00305 sc->evolve(*seq); 00306 00307 // find most salient location and feed saccade controller: 00308 float maxval; Point2D<int> currwin; findMax(sm, currwin, maxval); 00309 WTAwinner newwin = 00310 WTAwinner::buildFromSMcoords(currwin, sml, true, 00311 masterclock.getSimTime(), 00312 maxval, false); 00313 if (newwin.isValid()) sc->setPercept(newwin, *seq); 00314 00315 // receive conspicuity maps: 00316 receiveCMAPS(beo, cmap, cmapframe); 00317 00318 // ready for next frame: 00319 tim.nextFrame(); 00320 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00321 } 00322 00323 // got interrupted; let's cleanup and exit: 00324 d->destroyYUVoverlay(); 00325 LINFO("Normal exit"); 00326 manager.stop(); 00327 #endif 00328 return 0; 00329 } 00330 00331 // ###################################################################### 00332 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap, 00333 int32 *cmapframe) 00334 { 00335 TCPmessage rmsg; // buffer to receive messages from nodes 00336 int32 rframe, raction, rnode = -1, recnb=0; // receive from any node 00337 while(beo->receive(rnode, rmsg, rframe, raction)) // no wait 00338 { 00339 //LINFO("received %d/%d from %d while at %d", 00340 // rframe, raction, rnode, frame); 00341 switch(raction & 0xffff) 00342 { 00343 case BEO_CMAP: // ############################## 00344 { 00345 // get the map: 00346 Image<float> ima = rmsg.getElementFloatIma(); 00347 00348 // the map number is stored in the high 16 bits of the 00349 // raction field: 00350 int32 mapn = raction >> 16; 00351 if (mapn < 0 || mapn >= NBCMAP2) { 00352 LERROR("Bogus cmap number ignored"); 00353 break; 00354 } 00355 00356 // here is a totally asynchronous system example: we 00357 // just update our current value of a given cmap if 00358 // the one we just received is more recent than the 00359 // one we had so far: 00360 if (cmapframe[mapn] < rframe) 00361 { cmap[mapn] = ima; cmapframe[mapn] = rframe; } 00362 } 00363 00364 break; 00365 default: // ############################## 00366 LERROR("Bogus action %d -- IGNORING.", raction); 00367 break; 00368 } 00369 // limit number of receives, so we don't hold CPU too long: 00370 recnb ++; if (recnb > NBCMAP2 * 2) break; 00371 } 00372 } 00373 00374 // ###################################################################### 00375 extern "C" int main(int argc, char** argv) 00376 { 00377 try 00378 { 00379 return submain(argc, argv); 00380 } 00381 catch (...) 00382 { 00383 REPORT_CURRENT_EXCEPTION; 00384 return 1; 00385 } 00386 return 0; 00387 } 00388 00389 // ###################################################################### 00390 /* So things look consistent in everyone's emacs... */ 00391 /* Local Variables: */ 00392 /* indent-tabs-mode: nil */ 00393 /* End: */