pvisionTCP3-nerdcam.C

Go to the documentation of this file.
00001 /*!@file Parallel/pvisionTCP3-nerdcam.C Grab, process over beowulf, move camera  */
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-nerdcam.C $
00035 // $Id: pvisionTCP3-nerdcam.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. The
00045   difference between this and pvisionTCP3-master is that here we also
00046   move a pan/tilt camera towards the most salient location in the
00047   display. */
00048 
00049 #include "Beowulf/Beowulf.H"
00050 #include "Component/ModelManager.H"
00051 #include "Devices/BeoChip.H"
00052 #include "Devices/DeviceOpts.H"
00053 #include "Devices/FrameGrabberConfigurator.H"
00054 #include "Image/ColorOps.H"
00055 #include "Image/CutPaste.H"  // for inplacePaste()
00056 #include "Image/DrawOps.H"
00057 #include "Image/FilterOps.H"
00058 #include "Image/Image.H"
00059 #include "Image/ImageSet.H"
00060 #include "Image/MathOps.H"
00061 #include "Image/ShapeOps.H"  // for decX() etc.
00062 #include "Neuro/NeuroOpts.H"
00063 #include "Neuro/SaccadeController.H"
00064 #include "Neuro/SaccadeControllerConfigurator.H"
00065 #include "Parallel/pvisionTCP-defs.H"
00066 #include "Simulation/SimEventQueue.H"
00067 #include "Simulation/SimEventQueueConfigurator.H"
00068 #include "Transport/FrameIstream.H"
00069 #ifdef HAVE_SDL_SDL_H
00070 #include "Psycho/PsychoDisplay.H"
00071 #endif
00072 #include "Util/Assert.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 //! servo to use for pan
00082 #define SERVO_PAN 6
00083 
00084 //! servo to use for tilt
00085 #define SERVO_TILT 7
00086 
00087 //! Number of frames over which average framerate is computed
00088 #define NAVG 20
00089 
00090 //! prescale level by which we downsize images before sending them off
00091 #define PRESCALE 2
00092 
00093 static bool goforever = true;  //!< Will turn false on interrupt signal
00094 
00095 //! Signal handler (e.g., for control-C)
00096 void terminate(int s)
00097 { LERROR("*** INTERRUPT ***"); goforever = false; exit(1); }
00098 
00099 //! function to receive results from slaves
00100 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap,
00101                   int32 *cmapframe);
00102 
00103 // ######################################################################
00104 extern "C" int main(const int argc, char** argv)
00105 {
00106 #ifndef HAVE_SDL_SDL_H
00107 
00108   LFATAL("<SDL/SDL.h> must be installed to use this program");
00109 
00110 #else
00111 
00112   MYLOGVERB = LOG_INFO;
00113 
00114   // instantiate a model manager:
00115   ModelManager manager("Parallel Vision TCP Version 3");
00116 
00117   // Instantiate our various ModelComponents:
00118   nub::soft_ref<SimEventQueueConfigurator>
00119     seqc(new SimEventQueueConfigurator(manager));
00120   manager.addSubComponent(seqc);
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<PsychoDisplay>
00131     d(new PsychoDisplay(manager));
00132   manager.addSubComponent(d);
00133 
00134   nub::soft_ref<SaccadeControllerEyeConfigurator>
00135     scc(new SaccadeControllerEyeConfigurator(manager));
00136   manager.addSubComponent(scc);
00137 
00138   nub::soft_ref<BeoChip>
00139     bc(new BeoChip(manager));
00140   manager.addSubComponent(bc);
00141 
00142   // let's set a bunch of defauls:
00143   manager.exportOptions(MC_RECURSE);
00144   manager.setOptionValString(&OPT_SaccadeControllerEyeType, "Threshfric");
00145   manager.setOptionValString(&OPT_FrameGrabberType, "V4L");
00146   manager.setOptionValString(&OPT_SCeyeMaxIdleSecs, "1000.0");
00147   manager.setOptionValString(&OPT_SCeyeThreshMinOvert, "4.0");
00148   manager.setOptionValString(&OPT_SCeyeThreshMaxCovert, "3.0");
00149   //  manager.setOptionValString(&OPT_SCeyeSpringK, "1000000.0");
00150   bc->calibrateServo(SERVO_PAN, 128, 0, 255);
00151   bc->calibrateServo(SERVO_TILT, 128, 0, 255);
00152   bc->setServo(SERVO_PAN, 0.0f);
00153   bc->setServo(SERVO_TILT, 0.0f);
00154 
00155   // Parse command-line:
00156   if (manager.parseCommandLine(argc, argv, "", 0, 0) == false) return(1);
00157 
00158   // do post-command-line configs:
00159   nub::soft_ref<SimEventQueue> seq = seqc->getQ();
00160 
00161   nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber();
00162   if (gb.isInvalid())
00163     LFATAL("You need to select a frame grabber type via the "
00164            "--fg-type=XX command-line option for this program "
00165            "to be useful");
00166   int w = gb->getWidth(), h = gb->getHeight();
00167 
00168   nub::ref<SaccadeController> sc = scc->getSC();
00169 
00170   int foa_size = std::min(w, h) / 12;
00171   manager.setModelParamVal("InputFrameDims", Dims(w, h),
00172                            MC_RECURSE | MC_IGNORE_MISSING);
00173   manager.setModelParamVal("SCeyeStartAtIP", true,
00174                            MC_RECURSE | MC_IGNORE_MISSING);
00175   manager.setModelParamVal("SCeyeInitialPosition",Point2D<int>(w/2,h/2),
00176                            MC_RECURSE | MC_IGNORE_MISSING);
00177   manager.setModelParamVal("FOAradius", foa_size,
00178                            MC_RECURSE | MC_IGNORE_MISSING);
00179   manager.setModelParamVal("FoveaRadius", foa_size,
00180                            MC_RECURSE | MC_IGNORE_MISSING);
00181 
00182   // catch signals and redirect them to terminate for clean exit:
00183   signal(SIGHUP, terminate); signal(SIGINT, terminate);
00184   signal(SIGQUIT, terminate); signal(SIGTERM, terminate);
00185   signal(SIGALRM, terminate);
00186 
00187   // get prepared to grab, communicate, display, etc:
00188   int32 frame = 0;                  // count the frames
00189   PixRGB<byte> pix(255, 255, 0);    // yellow color for fixations
00190   TCPmessage smsg;                  // buffer to send messages to nodes
00191 
00192   uint64 avgtime = 0; int avgn = 0; // for average framerate
00193   float fps = 0.0F;                 // to display framerate
00194   Timer tim;                        // for computation of framerate
00195   Timer masterclock;                // master clock for simulations
00196 
00197   Image<float> cmap[NBCMAP2];       // array of conspicuity maps
00198   int32 cmapframe[NBCMAP2];         // array of cmap frame numbers
00199   for (int i = 0; i < NBCMAP2; i ++) cmapframe[i] = -1;
00200   int sml = 4;                      // pyramid level of saliency map
00201   Image<float> sm(w >> sml, h >> sml, ZEROS); // saliency map
00202   Point2D<int> fixation(-1, -1);         // coordinates of eye fixation
00203 
00204   // image buffer for display:
00205   Image<PixRGB<byte> > disp(w * 2, h + 20, ZEROS);
00206   disp += d->getGrey();
00207   int dw = d->getDims().w(), dh = d->getDims().h();
00208   ASSERT(dw == w * 2); ASSERT(dh >= disp.getHeight());
00209   int ovlyoff = (w * 2) * ((dh - disp.getHeight()) / 2);
00210   int ovluvoff = ovlyoff / 4;
00211 
00212   // let's get all our ModelComponent instances started:
00213   manager.start();
00214 
00215   // get our BeoChip going:
00216   bc->lcdClear();    //01234567890123456789
00217   bc->lcdPrintf(0, 0, "pvisionTCP3-nerdcam ");
00218   bc->lcdPrintf(0, 1, "Pan: ---    Tilt:---");
00219   bc->lcdPrintf(0, 2, "xFOA:---    yFOA:---");
00220   bc->lcdPrintf(0, 3, "xFOV:---    yFOV:---");
00221 
00222   // clear screen
00223   d->clearScreen();
00224   d->displayText("<SPACE> to start - <SPACE> again to quit");
00225   while(d->waitForKey() != ' ') ;
00226   d->clearScreen();
00227 
00228   // create an overlay:
00229   d->createYUVoverlay(SDL_YV12_OVERLAY);
00230 
00231   // get the frame grabber to start streaming:
00232   gb->startStream();
00233 
00234   // initialize the timers:
00235   tim.reset(); masterclock.reset();
00236 
00237   // ########## MAIN LOOP: grab, process, display:
00238   while(goforever)
00239     {
00240       // receive conspicuity maps:
00241       receiveCMAPS(beo, cmap, cmapframe);
00242 
00243       // grab an image:
00244       Image< PixRGB<byte> > ima = gb->readRGB();
00245 
00246       // display image in window:
00247       inplacePaste(disp, ima, Point2D<int>(0, 0));
00248       Image<float> dispsm(sm); inplaceNormalize(dispsm, 0.0F, 255.0F);
00249       inplacePaste(disp,
00250                    Image<PixRGB<byte> >(toRGB(quickInterpolate(dispsm, 1 << sml))),
00251                    Point2D<int>(w, 0));
00252 
00253       sc->evolve(*seq);
00254       Point2D<int> eye = sc->getDecision(*seq);
00255       if (eye.i >= 0) fixation = eye;
00256       if (fixation.i >= 0)
00257         {
00258           drawPatch(disp, fixation, 2, pix);
00259           drawCircle(disp, fixation, foa_size, pix, 2);
00260         }
00261       writeText(disp, Point2D<int>(w, 0), sformat("%.1ffps", fps).c_str(),
00262                 PixRGB<byte>(255), PixRGB<byte>(0));
00263 
00264       SDL_Overlay* ovl = d->lockYUVoverlay();
00265       toVideoYUV422(disp, ovl->pixels[0] + ovlyoff,
00266                     ovl->pixels[2] + ovluvoff,
00267                     ovl->pixels[1] + ovluvoff);
00268       d->unlockYUVoverlay();
00269       d->displayYUVoverlay(-1, SDLdisplay::NO_WAIT);
00270 
00271       // check for space bar pressed; note: will abort us violently if
00272       // ESC is pressed instead:
00273       if (d->checkForKey() == ' ') goforever = false;
00274 
00275       // receive conspicuity maps:
00276       receiveCMAPS(beo, cmap, cmapframe);
00277 
00278       // send every other frame to processing:
00279       if (frame & 1)
00280         {
00281           // prescale image:
00282           Image<PixRGB<byte> > ima2 =
00283             decY(lowPass5y(decX(lowPass5x(ima),1<<PRESCALE)),1<<PRESCALE);
00284 
00285           // we do the job of BEO_RETINA on the master to reduce latency:
00286           // compute luminance and send it off:
00287           Image<byte> lum = luminance(ima2);
00288 
00289           // first, send off luminance to orientation slaves:
00290           smsg.reset(frame, BEO_ORI0); smsg.addImage(lum); beo->send(smsg);
00291           smsg.setAction(BEO_ORI45); beo->send(smsg);
00292           smsg.setAction(BEO_ORI90); beo->send(smsg);
00293           smsg.setAction(BEO_ORI135); beo->send(smsg);
00294 
00295           // and also send to flicker slave:
00296           smsg.setAction(BEO_FLICKER); beo->send(smsg);
00297 
00298           // finally, send to luminance slave:
00299           smsg.setAction(BEO_LUMINANCE); beo->send(smsg);
00300 
00301           // compute RG and BY and send them off:
00302           Image<byte> r, g, b, y; getRGBY(ima2, r, g, b, y, (byte)25);
00303           smsg.reset(frame, BEO_REDGREEN);
00304           smsg.addImage(r); smsg.addImage(g); beo->send(smsg);
00305           smsg.reset(frame, BEO_BLUEYELLOW);
00306           smsg.addImage(b); smsg.addImage(y); beo->send(smsg);
00307         }
00308 
00309       // receive conspicuity maps:
00310       receiveCMAPS(beo, cmap, cmapframe);
00311 
00312       // build our current saliency map:
00313       std::string info = sformat("%06d / ", frame);
00314       Image<float> sminput;
00315       for (int i = 0; i < NBCMAP2; i ++)
00316         if (cmap[i].initialized())
00317           {
00318             if (sminput.initialized()) sminput += cmap[i];
00319             else sminput = cmap[i];
00320             info += sformat("%06d ", cmapframe[i]);
00321           }
00322         else
00323           info += "------ ";
00324       writeText(disp, Point2D<int>(0, h), info.c_str(),
00325                 PixRGB<byte>(255), d->getGrey());
00326 
00327       // inject saliency map input into saliency map:
00328       if (sminput.initialized()) sm = sm * 0.7F + sminput * 0.3F;
00329 
00330       // evolve our saccade controller up to now:
00331       while(seq->now() < masterclock.getSimTime()) seq->evolve();
00332       sc->evolve(*seq);
00333 
00334       // find most salient location and feed saccade controller:
00335       float maxval; Point2D<int> currwin; findMax(sm, currwin, maxval);
00336       WTAwinner newwin =
00337         WTAwinner::buildFromSMcoords(currwin, sml, true,
00338                                      masterclock.getSimTime(),
00339                                      maxval, false);
00340       if (newwin.isValid()) sc->setPercept(newwin, *seq);
00341 
00342       // receive conspicuity maps:
00343       receiveCMAPS(beo, cmap, cmapframe);
00344 
00345       // compute and show framerate and stats over the last NAVG frames:
00346       avgtime += tim.getReset(); avgn ++;
00347       if (avgn == NAVG)
00348         {
00349           fps = 1000.0F / float(avgtime) * float(avgn);
00350           avgtime = 0; avgn = 0;
00351         }
00352 
00353       // update BeoChip display every other frame:
00354       if ((frame & 1) == 0)
00355         {
00356           bc->lcdPrintf(0, 1, "Pan: %03d    Tilt:%03d",
00357                         bc->getServoRaw(SERVO_PAN),
00358                         bc->getServoRaw(SERVO_TILT));
00359           bc->lcdPrintf(0, 2, "xFOA:%03d    yFOA:%03d",
00360                         newwin.p.i, newwin.p.j);
00361           bc->lcdPrintf(0, 3, "xFOV:%03d    yFOV:%03d",
00362                         fixation.i, fixation.j);
00363         }
00364 
00365       // move the nerdcam so as to bring eye to the center:
00366       float pan = bc->getServo(SERVO_PAN) +
00367         float(fixation.i)/float(w) - 0.5f;
00368       float tilt = bc->getServo(SERVO_TILT) +
00369         float(fixation.j)/float(h) - 0.5f;
00370       if (pan < -1.0f) pan = -1.0f; else if (pan > 1.0f) pan = 1.0f;
00371       if (tilt < -1.0f) tilt = -1.0f; else if (tilt > 1.0f) tilt = 1.0f;
00372 
00373       bc->setServo(SERVO_PAN, pan);
00374       bc->setServo(SERVO_TILT, tilt);
00375 
00376       // ready for next frame:
00377       frame++;
00378       while(seq->now() < masterclock.getSimTime()) seq->evolve();
00379     }
00380 
00381   // got interrupted; let's cleanup and exit:
00382   d->destroyYUVoverlay();
00383   LINFO("Normal exit");
00384   manager.stop();
00385   return 0;
00386 
00387 #endif
00388 
00389 }
00390 
00391 // ######################################################################
00392 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap,
00393                   int32 *cmapframe)
00394 {
00395   TCPmessage rmsg;      // buffer to receive messages from nodes
00396   int32 rframe, raction, rnode = -1, recnb=0; // receive from any node
00397   while(beo->receive(rnode, rmsg, rframe, raction)) // no wait
00398     {
00399       //LINFO("received %d/%d from %d while at %d",
00400       //    rframe, raction, rnode, frame);
00401       switch(raction & 0xffff)
00402         {
00403         case BEO_CMAP: // ##############################
00404           {
00405             // get the map:
00406             Image<float> ima = rmsg.getElementFloatIma();
00407 
00408             // the map number is stored in the high 16 bits of the
00409             // raction field:
00410             int32 mapn = raction >> 16;
00411             if (mapn < 0 || mapn >= NBCMAP2) {
00412               LERROR("Bogus cmap number ignored");
00413               break;
00414             }
00415 
00416             // here is a totally asynchronous system example: we
00417             // just update our current value of a given cmap if
00418             // the one we just received is more recent than the
00419             // one we had so far:
00420             if (cmapframe[mapn] < rframe)
00421               { cmap[mapn] = ima; cmapframe[mapn] = rframe; }
00422           }
00423 
00424           break;
00425         default: // ##############################
00426           LERROR("Bogus action %d -- IGNORING.", raction);
00427           break;
00428         }
00429       // limit number of receives, so we don't hold CPU too long:
00430       recnb ++; if (recnb > NBCMAP2 * 2) break;
00431     }
00432 }
00433 
00434 // ######################################################################
00435 /* So things look consistent in everyone's emacs... */
00436 /* Local Variables: */
00437 /* indent-tabs-mode: nil */
00438 /* End: */
Generated on Sun May 8 08:41:08 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3