00001 /*!@file Parallel/pvisionTCP4-master.C Grab & process over beowulf w/ pvisionTCP3. Like pvisionTCP3 but with input and output FrameSeries */ 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/pvisionTCP4-master.C $ 00035 // $Id: $ 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 #include "Media/FrameSeries.H" 00077 #include "Transport/FrameInfo.H" 00078 00079 #include <signal.h> 00080 #include <unistd.h> 00081 00082 00083 //! prescale level by which we downsize images before sending them off 00084 #define PRESCALE 2 00085 00086 //! averaging window 00087 #define NAVG 20 00088 #define TOTAL_FRAMES 100 00089 00090 static bool goforever = true; //!< Will turn false on interrupt signal 00091 00092 //! Signal handler (e.g., for control-C) 00093 void terminate(int s) 00094 { LERROR("*** INTERRUPT ***"); goforever = false; }// exit(1); } 00095 00096 //! function to receive results from slaves 00097 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap, 00098 int32 *cmapframe); 00099 uint nCMAPSreceived; 00100 // ###################################################################### 00101 int submain(int argc, char** argv) 00102 { 00103 #ifndef HAVE_SDL_SDL_H 00104 LFATAL("<SDL/SDL.h> must be installed to use this program"); 00105 #else 00106 MYLOGVERB = LOG_INFO; 00107 00108 // instantiate a model manager: 00109 ModelManager manager("Parallel Vision TCP Version 3"); 00110 00111 // Instantiate our various ModelComponents: 00112 nub::soft_ref<SimEventQueueConfigurator> 00113 seqc(new SimEventQueueConfigurator(manager)); 00114 manager.addSubComponent(seqc); 00115 00116 nub::soft_ref<InputFrameSeries> ifs(new InputFrameSeries(manager)); 00117 manager.addSubComponent(ifs); 00118 00119 nub::soft_ref<OutputFrameSeries> ofs(new OutputFrameSeries(manager)); 00120 manager.addSubComponent(ofs); 00121 00122 // nub::soft_ref<FrameGrabberConfigurator> 00123 // gbc(new FrameGrabberConfigurator(manager)); 00124 // manager.addSubComponent(gbc); 00125 00126 nub::soft_ref<Beowulf> 00127 beo(new Beowulf(manager, "Beowulf Master", "BeowulfMaster", true)); 00128 manager.addSubComponent(beo); 00129 00130 // nub::soft_ref<SDLdisplay> 00131 // d(new SDLdisplay(manager)); 00132 // manager.addSubComponent(d); 00133 00134 // nub::soft_ref<ThresholdFrictionSaccadeController> 00135 // sc(new ThresholdFrictionSaccadeController(manager)); 00136 nub::soft_ref<SaccadeControllerEyeConfigurator> 00137 scc(new SaccadeControllerEyeConfigurator(manager)); 00138 manager.addSubComponent(scc); 00139 00140 // let's set a bunch of defauls: 00141 manager.setOptionValString(&OPT_SaccadeControllerEyeType, "Threshfric"); 00142 manager.setOptionValString(&OPT_FrameGrabberType, "V4L"); 00143 manager.setOptionValString(&OPT_SCeyeMaxIdleSecs, "1000.0"); 00144 manager.setOptionValString(&OPT_SCeyeThreshMinOvert, "4.0"); 00145 manager.setOptionValString(&OPT_SCeyeThreshMaxCovert, "3.0"); 00146 // manager.setOptionValString(&OPT_SCeyeSpringK, "1000000.0"); 00147 00148 // Parse command-line: 00149 if (manager.parseCommandLine(argc, argv, "", 0, 0) == false) return(1); 00150 00151 // do post-command-line configs: 00152 nub::soft_ref<SimEventQueue> seq = seqc->getQ(); 00153 00154 // nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber(); 00155 // if (gb.isInvalid()) 00156 // LFATAL("You need to select a frame grabber type via the " 00157 // "--fg-type=XX command-line option for this program " 00158 // "to be useful"); 00159 // int w = gb->getWidth(), h = gb->getHeight(); 00160 00161 int w = ifs->getWidth(), h = ifs->getHeight(); 00162 std::string dims = convertToString(Dims(w, h)); 00163 LINFO("image size: [%dx%d]", w, h); 00164 //manager.setOptionValString(&OPT_InputFrameDims, dims); 00165 00166 manager.setModelParamVal("InputFrameDims", Dims(w, h), 00167 MC_RECURSE | MC_IGNORE_MISSING); 00168 00169 nub::ref<SaccadeController> sc = scc->getSC(); 00170 00171 const int foa_size = 64; 00172 manager.setModelParamVal("InputFrameDims", Dims(w, h), 00173 MC_RECURSE | MC_IGNORE_MISSING); 00174 manager.setModelParamVal("SCeyeStartAtIP", true, 00175 MC_RECURSE | MC_IGNORE_MISSING); 00176 manager.setModelParamVal("SCeyeInitialPosition",Point2D<int>(w/2,h/2), 00177 MC_RECURSE | MC_IGNORE_MISSING); 00178 manager.setModelParamVal("FOAradius", foa_size, 00179 MC_RECURSE | MC_IGNORE_MISSING); 00180 manager.setModelParamVal("FoveaRadius", foa_size, 00181 MC_RECURSE | MC_IGNORE_MISSING); 00182 //manager.setModelParamVal("SimulationTimeStep", SimTime::MSECS(1.0), 00183 // MC_RECURSE | MC_IGNORE_MISSING); 00184 00185 // catch signals and redirect them to terminate for clean exit: 00186 signal(SIGHUP, terminate); signal(SIGINT, terminate); 00187 signal(SIGQUIT, terminate); signal(SIGTERM, terminate); 00188 signal(SIGALRM, terminate); 00189 00190 // get prepared to grab, communicate, display, etc: 00191 PixRGB<byte> pix(255, 255, 0); // yellow color for fixations 00192 TCPmessage smsg; // buffer to send messages to nodes 00193 FpsTimer tim; // for computation of framerate 00194 Timer masterclock; // master clock for simulations 00195 00196 Image<float> cmap[NBCMAP2]; // array of conspicuity maps 00197 int32 cmapframe[NBCMAP2]; // array of cmap frame numbers 00198 for (int i = 0; i < NBCMAP2; i ++) cmapframe[i] = -1; 00199 int sml = 4; // pyramid level of saliency map 00200 Image<float> sm(w >> sml, h >> sml, ZEROS); // saliency map 00201 Point2D<int> fixation(-1, -1); // coordinates of eye fixation 00202 00203 nCMAPSreceived = 0; 00204 00205 // image buffer for display: 00206 const PixRGB<byte> grey(127); 00207 std::string info; 00208 00209 // adjust SDL display size: 00210 //d->setModelParamVal("SDLdisplayDims", gb->peekDims()); 00211 00212 // let's get all our ModelComponent instances started: 00213 manager.start(); 00214 00215 // clear screen 00216 //d->clearScreen(grey); 00217 //d->displayText("<SPACE> to start - <SPACE> again to quit", false, 00218 // PixRGB<byte>(0), grey); 00219 //while(d->waitForKey() != ' ') ; 00220 //d->clearScreen(grey); 00221 00222 // create an overlay: 00223 //d->createYUVoverlay(SDL_YV12_OVERLAY); 00224 00225 // get the frame grabber to start streaming: 00226 //gb->startStream(); 00227 00228 // initialize the timers: 00229 masterclock.reset(); 00230 00231 Timer timer(1000000); 00232 // ##### MAIN LOOP: 00233 uint fNum = 0; 00234 ifs->updateNext(); Image< PixRGB<byte> > ima = ifs->readRGB(); 00235 if(!ima.initialized()) { goforever = false; } 00236 uint nCMAPSsent = 0; 00237 //while(goforever) 00238 while(nCMAPSreceived < TOTAL_FRAMES*8) 00239 { 00240 timer.reset(); 00241 // receive conspicuity maps: 00242 receiveCMAPS(beo, cmap, cmapframe); 00243 uint64 t1 = timer.get(); 00244 LINFO("time1[%d]: %f", fNum, t1/1000.0F); 00245 00246 // grab an image: 00247 //Image< PixRGB<byte> > ima = gb->readRGB(); 00248 //ifs->updateNext(); Image< PixRGB<byte> > ima = ifs->readRGB(); 00249 //if(!ima.initialized()) { goforever = false; } 00250 00251 // display image in window: 00252 //Image<float> dispsm(sm); inplaceNormalize(dispsm, 0.0F, 255.0F); 00253 //Image<byte> dispsmb = dispsm; dispsmb = quickInterpolate(dispsmb, 2); 00254 //inplacePaste(ima, toRGB(dispsmb), Point2D<int>(w-dispsmb.getWidth(), 0)); 00255 //drawRect(ima, Rectangle(Point2D<int>(w-dispsmb.getWidth()-1, 0), 00256 // dispsmb.getDims()), pix); 00257 00258 sc->evolve(*seq); 00259 Point2D<int> eye = sc->getDecision(*seq); 00260 if (eye.isValid()) fixation = eye; 00261 uint64 t2 = timer.get(); 00262 LINFO("time2[%d]: %f", fNum, (t2-t1)/1000.0F); 00263 00264 //if (fixation.isValid()) 00265 // { 00266 // drawPatch(ima, fixation, 2, pix); 00267 // drawCircle(ima, fixation, foa_size, pix, 2); 00268 // } 00269 //writeText(ima, Point2D<int>(0, 0), 00270 // sformat(" %.1ffps ", tim.getRecentFps()).c_str(), 00271 // PixRGB<byte>(255), grey); 00272 //writeText(ima, Point2D<int>(0, h-20), info.c_str(), 00273 // PixRGB<byte>(255), grey); 00274 00275 // SDL_Overlay* ovl = d->lockYUVoverlay(); 00276 // toVideoYUV422(ima, ovl->pixels[0], ovl->pixels[2], ovl->pixels[1]); 00277 // d->unlockYUVoverlay(); 00278 //d->displayYUVoverlay(-1, SDLdisplay::NO_WAIT); 00279 //ofs->writeRGB(ima, "display", FrameInfo("display",SRC_POS)); 00280 LINFO(" %.1ffps ", tim.getRecentFps()); 00281 00282 // check for space bar pressed; note: will abort us violently if 00283 // ESC is pressed instead: 00284 //if (d->checkForKey() == ' ') goforever = false; 00285 00286 // receive conspicuity maps: 00287 receiveCMAPS(beo, cmap, cmapframe); 00288 00289 // send every other frame to processing: 00290 //if (tim.frameNumber() & 1) 00291 if (nCMAPSsent < TOTAL_FRAMES) 00292 { 00293 nCMAPSsent++; 00294 00295 const int32 frame = int32(tim.frameNumber()); 00296 00297 // prescale image: 00298 //Image<PixRGB<byte> > ima2 = 00299 // decY(lowPass5y(decX(lowPass5x(ima),1<<PRESCALE)),1<<PRESCALE); 00300 Image<PixRGB<byte> > ima2 = 00301 decY(decX(ima,1<<PRESCALE),1<<PRESCALE); 00302 00303 uint64 t3a = timer.get(); 00304 LINFO("time3a[%d]: %f", fNum, (t3a-t2)/1000.0F); 00305 00306 // we do the job of BEO_RETINA on the master to reduce latency: 00307 // compute luminance and send it off: 00308 Image<byte> lum = luminance(ima2); 00309 //Image<byte> lum = luminance(ima); 00310 00311 uint64 t3b = timer.get(); 00312 LINFO("time3b[%d]: %f", fNum, (t3b-t3a)/1000.0F); 00313 00314 // first, send off luminance to orientation slaves: 00315 smsg.reset(frame, BEO_ORI0); smsg.addImage(lum); beo->send(smsg); 00316 smsg.setAction(BEO_ORI15); beo->send(smsg); 00317 smsg.setAction(BEO_ORI30); beo->send(smsg); 00318 smsg.setAction(BEO_ORI45); beo->send(smsg); 00319 smsg.setAction(BEO_ORI60); beo->send(smsg); 00320 smsg.setAction(BEO_ORI75); beo->send(smsg); 00321 smsg.setAction(BEO_ORI90); beo->send(smsg); 00322 smsg.setAction(BEO_ORI105); beo->send(smsg); 00323 smsg.setAction(BEO_ORI120); beo->send(smsg); 00324 smsg.setAction(BEO_ORI135); beo->send(smsg); 00325 smsg.setAction(BEO_ORI150); beo->send(smsg); 00326 smsg.setAction(BEO_ORI165); beo->send(smsg); 00327 00328 // and also send to flicker slave: 00329 smsg.setAction(BEO_FLICKER); beo->send(smsg); 00330 00331 // finally, send to luminance slave: 00332 smsg.setAction(BEO_LUMINANCE); beo->send(smsg); 00333 00334 uint64 t3c = timer.get(); 00335 LINFO("time3c[%d]: %f", fNum, (t3c-t3b)/1000.0F); 00336 00337 // compute RG and BY and send them off: 00338 Image<byte> r, g, b, y; getRGBY(ima2, r, g, b, y, (byte)25); 00339 //Image<byte> r, g, b, y; getRGBY(ima, r, g, b, y, (byte)25); 00340 smsg.reset(frame, BEO_REDGREEN); 00341 smsg.addImage(r); smsg.addImage(g); beo->send(smsg); 00342 smsg.reset(frame, BEO_BLUEYELLOW); 00343 smsg.addImage(b); smsg.addImage(y); beo->send(smsg); 00344 00345 uint64 t3d = timer.get(); 00346 LINFO("time3d[%d]: %f", fNum, (t3d-t3c)/1000.0F); 00347 } 00348 00349 uint64 t3 = timer.get(); 00350 LINFO("time3[%d]: %f", fNum, (t3-t2)/1000.0F); 00351 00352 // receive conspicuity maps: 00353 receiveCMAPS(beo, cmap, cmapframe); 00354 00355 // build our current saliency map: 00356 info = sformat("%06d / ", tim.frameNumber()); 00357 Image<float> sminput; 00358 for (int i = 0; i < NBCMAP2; i ++) 00359 if (cmap[i].initialized()) 00360 { 00361 if (sminput.initialized()) sminput += cmap[i]; 00362 else sminput = cmap[i]; 00363 info += sformat("%06d ", cmapframe[i]); 00364 } 00365 else 00366 info += "------ "; 00367 00368 // inject saliency map input into saliency map: 00369 if (sminput.initialized()) sm = sm * 0.7F + sminput * 0.3F; 00370 00371 // evolve our saccade controller up to now: 00372 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00373 sc->evolve(*seq); 00374 00375 // find most salient location and feed saccade controller: 00376 float maxval; Point2D<int> currwin; findMax(sm, currwin, maxval); 00377 WTAwinner newwin = 00378 WTAwinner::buildFromSMcoords(currwin, sml, true, 00379 masterclock.getSimTime(), 00380 maxval, false); 00381 if (newwin.isValid()) sc->setPercept(newwin, *seq); 00382 00383 uint64 t4 = timer.get(); 00384 LINFO("time4[%d]: %f", fNum, (t4-t3)/1000.0F); 00385 00386 // receive conspicuity maps: 00387 receiveCMAPS(beo, cmap, cmapframe); 00388 00389 // ready for next frame: 00390 tim.nextFrame(); 00391 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00392 00393 uint64 t5 = timer.get(); 00394 LINFO("time5[%d]: %f", fNum, (t5-t4)/1000.0F); 00395 00396 fNum++; 00397 } 00398 00399 // got interrupted; let's cleanup and exit: 00400 //d->destroyYUVoverlay(); 00401 //double frate = tim.frameNumber() / (tim.getElapsedTime()).sec(); 00402 //LINFO("Fr: %6.3f fps -> %8.3f ms/f\n", frate, 1000.0/frate); 00403 double time = (tim.getElapsedTime()).sec(); 00404 double frate = TOTAL_FRAMES/time; 00405 LINFO("[%6d]: %15.6fsec: %6.3f fps -> %8.3f ms/f\n", 00406 fNum, time, frate, 1000/frate); 00407 00408 LINFO("Normal exit"); 00409 manager.stop(); 00410 #endif 00411 return 0; 00412 } 00413 00414 // ###################################################################### 00415 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap, 00416 int32 *cmapframe) 00417 { 00418 TCPmessage rmsg; // buffer to receive messages from nodes 00419 int32 rframe, raction, rnode = -1, recnb=0; // receive from any node 00420 while(beo->receive(rnode, rmsg, rframe, raction)) // no wait 00421 { 00422 nCMAPSreceived++; 00423 LINFO("--------------> nCMAPSreceived: %6d", nCMAPSreceived); 00424 //LINFO("received %d/%d from %d while at %d", 00425 // rframe, raction, rnode, frame); 00426 switch(raction & 0xffff) 00427 { 00428 case BEO_CMAP: // ############################## 00429 { 00430 // get the map: 00431 Image<float> ima = rmsg.getElementFloatIma(); 00432 00433 // the map number is stored in the high 16 bits of the 00434 // raction field: 00435 int32 mapn = raction >> 16; 00436 if (mapn < 0 || mapn >= NBCMAP4) { 00437 LERROR("Bogus cmap number ignored"); 00438 break; 00439 } 00440 00441 // here is a totally asynchronous system example: we 00442 // just update our current value of a given cmap if 00443 // the one we just received is more recent than the 00444 // one we had so far: 00445 if (cmapframe[mapn] < rframe) 00446 { cmap[mapn] = ima; cmapframe[mapn] = rframe; } 00447 } 00448 00449 break; 00450 default: // ############################## 00451 LERROR("Bogus action %d -- IGNORING.", raction); 00452 break; 00453 } 00454 // limit number of receives, so we don't hold CPU too long: 00455 recnb ++; if (recnb > NBCMAP4 * 2) break; 00456 } 00457 } 00458 00459 // ###################################################################### 00460 extern "C" int main(int argc, char** argv) 00461 { 00462 try 00463 { 00464 return submain(argc, argv); 00465 } 00466 catch (...) 00467 { 00468 REPORT_CURRENT_EXCEPTION; 00469 return 1; 00470 } 00471 return 0; 00472 } 00473 00474 // ###################################################################### 00475 /* So things look consistent in everyone's emacs... */ 00476 /* Local Variables: */ 00477 /* indent-tabs-mode: nil */ 00478 /* End: */