BeobotVisualCortex.C

Go to the documentation of this file.
00001 /*!@file Beobot/BeobotVisualCortex.C Implementation of navigation algorithm */
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/Beobot/BeobotVisualCortex.C $
00035 // $Id: BeobotVisualCortex.C 9412 2008-03-10 23:10:15Z farhan $
00036 //
00037 
00038 #include "Beobot/BeobotVisualCortex.H"
00039 
00040 #include "Beobot/beobot-defs.H"
00041 #include "Channels/Jet.H"
00042 #include "Image/ColorOps.H" // for luminance(), getRGBY(), etc.
00043 #include "Image/MathOps.H"
00044 #include "Image/Pixels.H"
00045 #include "Image/PyramidOps.H"
00046 #include "Image/ShapeOps.H" // for downSize()
00047 #include "Image/Transforms.H"
00048 #include "Image/fancynorm.H" // for maxNormalize()
00049 #include "Util/Assert.H"
00050 #include "Util/Timer.H"
00051 
00052 #include <cmath> // for sqrt()
00053 #include <limits> // for numeric_limits<float>::max() instead of FLOATMAX
00054 
00055 
00056 //######################################################################
00057 BeobotVisualCortex::BeobotVisualCortex()
00058 { initialized = false; }
00059 
00060 // ######################################################################
00061 void BeobotVisualCortex::init(const int imgw, const int imgh,
00062                               const int lev_min, const int lev_max,
00063                               const int delta_min, const int delta_max,
00064                               const int smlev, const int nborient,
00065                               const MaxNormType normtype, const int jlev,
00066                               const int jdepth, const int nbneig,
00067                               nub::soft_ref<Beowulf> beow)
00068 {
00069   iw = imgw; ih = imgh;
00070   lmin = lev_min; lmax = lev_max; dmin = delta_min; dmax = delta_max;
00071   sml = smlev; nori = nborient; nortyp = normtype; beo = beow;
00072   jetlevel = jlev; jetdepth = jdepth; nbneigh = nbneig;
00073 
00074   scene.resize(iw, ih, true);
00075 
00076   JetSpec *js = new JetSpec;
00077   js->addIndexRange(RG, RAW, jlev, jlev + jdepth - 1);
00078   js->addIndexRange(BY, RAW, jlev, jlev + jdepth - 1);
00079   js->addIndexRange(INTENS, RAW, jlev, jlev + jdepth - 1);
00080   js->addIndexRange(ORI, RAW, 0, nborient - 1);  // orientations
00081   js->addIndexRange(ORI, RAW, jlev, jlev + jdepth - 1);
00082   js->print();
00083   jetSpec = rutz::make_shared(js);
00084 
00085   jets.init(iw >> jetlevel, ih >> jetlevel, nbneigh);
00086   ImageSpring<Jet <float> >::iterator itr = jets.beginw(), stop = jets.endw();
00087   while (itr != stop) { itr->init(jetSpec); itr ++; }
00088 
00089   sminput.resize(iw >> sml, ih >> sml, true);
00090   initialized = true;
00091 }
00092 
00093 //#######################################################################
00094 void BeobotVisualCortex::newVisualInput(Image< PixRGB<byte> >& newscene)
00095 { scene = newscene; iw = scene.getWidth(); ih = scene.getHeight(); }
00096 
00097 // ######################################################################
00098 Image< PixRGB<byte> >* BeobotVisualCortex::getScenePtr()
00099 { return &scene; }
00100 
00101 // ######################################################################
00102 void BeobotVisualCortex::process(const int frame)
00103 {
00104   currframe = frame;
00105   sminput.clear(); nbcmap = 0; nbjmap = 0;
00106 
00107   if (beo.get())  // use parallel version (we are master)
00108     {
00109       masterProcess(frame);
00110 
00111       while (nbcmap != NBCMAP || nbjmap != NBJMAP) masterCollect();
00112     }
00113   else      // process everything on current CPU
00114     singleCPUprocess(frame);
00115 
00116   // finalize computation of saliency map input:
00117   sminput = maxNormalize(sminput, 0.0f, 9.0f, nortyp);
00118 
00119   // find most salient location:
00120   float maxval;
00121   findMax(sminput, winner, maxval);
00122 
00123   // rescale those coordinates to scale of original image:
00124   winner.i <<= sml; winner.i += 1 << (sml - 1);
00125   winner.j <<= sml; winner.j += 1 << (sml - 1);
00126 }
00127 
00128 // ######################################################################
00129 void BeobotVisualCortex::processStart(const int frame)
00130 {
00131   if (beo.isInvalid()) LFATAL("This is for parallel processing only!");
00132   currframe = frame;
00133   masterProcess(frame);
00134 }
00135 
00136 // ######################################################################
00137 void BeobotVisualCortex::processEnd(const int frame)
00138 {
00139   if (beo.isInvalid()) LFATAL("This is for parallel processing only!");
00140   sminput.clear(); nbcmap = 0; nbjmap = 0; currframe = frame; Timer tim;
00141   while((nbcmap != NBCMAP || nbjmap != NBJMAP) && tim.get() < 80)
00142     {
00143       masterCollect();
00144       //LINFO("frame %d: nc=%d nj=%d",frame,nbcmap,nbjmap);
00145     }
00146 
00147   // finalize computation of saliency map input:
00148   sminput = maxNormalize(sminput, 0.0f, 9.0f, nortyp);
00149 
00150   // find most salient location:
00151   float maxval;
00152   findMax(sminput, winner, maxval);
00153 
00154   // rescale those coordinates to scale of original image:
00155   winner.i <<= sml; winner.i += 1 << (sml - 1);
00156   winner.j <<= sml; winner.j += 1 << (sml - 1);
00157 }
00158 
00159 // ######################################################################
00160 void BeobotVisualCortex::singleCPUprocess(const int frame)
00161 {
00162   currframe = frame;
00163 
00164   Image<float> lumf = luminance(scene); // from Image_ColorOps.H
00165 
00166   Image<float> rgf, byf;
00167   getRGBY(scene, rgf, byf, byte(25)); // from Image_ColorOps.H
00168 
00169   // compute intensity:
00170   ImageSet<float> intensPyr = buildPyrGaussian(lumf, 0, sml + dmax + 1, 5);
00171 
00172   // compute colors:
00173   ImageSet<float> rgPyr = buildPyrGaussian(rgf, 0, sml + dmax + 1, 5);
00174   ImageSet<float> byPyr = buildPyrGaussian(byf, 0, sml + dmax + 1, 5);
00175 
00176   // compute orientations:
00177   ImageSet<float> oriPyr[nori];
00178   for (int i = 0; i < nori; i ++)
00179     {
00180       oriPyr[i] = buildPyrOriented(lumf, 0, sml + dmax + 1, 5,
00181                                    float(i) * 180.0f / float(nori));
00182     }
00183 
00184   // compute flicker:
00185   if (prevlum.initialized() == false) prevlum = lumf;
00186   prevlum -= lumf;
00187   ImageSet<float> flickPyr = buildPyrGaussian(prevlum, 0, sml + dmax + 1, 5);
00188   prevlum = lumf;
00189 
00190   // compute comspicuity maps and accumulate into sminput:
00191   Image<float> cmap;
00192   sminput.resize(intensPyr[sml].getDims(), true);
00193   computeCmap(intensPyr, cmap); sminput += cmap;
00194   computeCmap(rgPyr, cmap); sminput += cmap;
00195   computeCmap(byPyr, cmap); sminput += cmap;
00196   computeCmap(flickPyr, cmap); sminput += cmap;
00197   for (int i = 0; i < nori; i++)
00198     { computeCmap(oriPyr[i], cmap); sminput += cmap; }
00199 
00200   // fill-in the jets -- IGNORE FLICKER CHANNEL:
00201   ImageSpring< Jet<float> >::iterator jt = jets.beginw();
00202   Point2D<int> p; int step = 1 << jetlevel;
00203   int jmax = jets.getHeight() * step, imax = jets.getWidth() * step;
00204   for (p.j = 0; p.j < jmax; p.j += step)
00205     for (p.i = 0; p.i < imax; p.i += step)
00206       {
00207         for (int k = jetlevel; k < jetlevel + jetdepth; k ++)
00208           {
00209             float rgval = getPyrPixel(rgPyr, p, k); jt->setVal(rgval, RG, RAW, k);
00210             float byval = getPyrPixel(byPyr, p, k); jt->setVal(byval, BY, RAW, k);
00211             float ival = getPyrPixel(intensPyr, p, k); jt->setVal(ival, INTENS, RAW, k);
00212             for (int o = 0; o < nori; o ++)
00213               {
00214                 float oval = getPyrPixel(oriPyr[o], p, k);
00215                 jt->setVal(oval, ORI, RAW, o, k);
00216               }
00217           }
00218         jt ++;
00219       }
00220 
00221   nbcmap = NBCMAP; nbjmap = NBJMAP;
00222 }
00223 
00224 //######################################################################
00225 void BeobotVisualCortex::masterProcess(const int frame)
00226 {
00227   ASSERT(initialized);
00228 
00229   TCPmessage smsg;
00230   currframe = frame; nbcmap = 0;
00231 
00232   // compute luminance and send it off:
00233   Image<byte> lum = luminance(scene);
00234   smsg.reset(frame, BEO_LUMFLICK);
00235   smsg.addImage(lum);
00236   beo->send(0, smsg);  // send off to intensity/RG/BY/flick slave
00237   smsg.setAction(BEO_ORI0_45);
00238   beo->send(1, smsg);  // send off to ori0/ori45 slave
00239   smsg.setAction(BEO_ORI90_135);
00240   beo->send(2, smsg);  // send off to ori90/ori135 slave
00241 
00242   // compute RG and BY and send them off:
00243   Image<byte> r, g, b, y; getRGBY(scene, r, g, b, y, (byte)25);
00244   smsg.reset(frame, BEO_REDGREEN);
00245   smsg.addImage(r); smsg.addImage(g);
00246   beo->send(0, smsg);  // send off to intensity/RG/BY/flick slave
00247   smsg.reset(frame, BEO_BLUEYELLOW);
00248   smsg.addImage(b); smsg.addImage(y);
00249   beo->send(0, smsg);  // send off to intensity/RG/BY/flick slave
00250 }
00251 
00252 // ######################################################################
00253 void BeobotVisualCortex::slaveProcess()
00254 {
00255   TCPmessage rmsg; int rframe, raction, rnode = -1;  // receive from any node
00256   int nbrec = 0;
00257   while (beo->receive(rnode, rmsg, rframe, raction, 5)) // wait up to 5ms
00258     {
00259       //LINFO("GOT frame %d, action %d from node %d", rframe, raction, rnode);
00260       //Timer tim;
00261       switch(raction)
00262         {
00263         case BEO_INIT:       // ##############################
00264           {
00265             // ooops, someone wants to re-initialize us!
00266             // reinitialization of beowulf is handled automatically.
00267           }
00268           break;
00269         case BEO_LUMFLICK:     // ##############################
00270           {
00271             // get the luminance image out of the message:
00272             Image<byte> ima = rmsg.getElementByteIma();
00273             Image<float> fima = ima;  // convert to float
00274 
00275             // compute intensity maps and send to collector:
00276             computeFeature(fima, Gaussian5, 0.0, rframe, BEO_FMAP_I);
00277 
00278             // compute flicker maps and send to collector:
00279             if (prevlum.initialized() == false) prevlum = fima;
00280             prevlum -= fima;
00281             computeFeature(prevlum, Gaussian5, 0.0, rframe, BEO_FMAP_F);
00282             prevlum = fima;
00283           }
00284           break;
00285         case BEO_REDGREEN:   // ##############################
00286           computeFeature2(rmsg, Gaussian5, 0.0, BEO_FMAP_RG);
00287           break;
00288         case BEO_BLUEYELLOW: // ##############################
00289           computeFeature2(rmsg, Gaussian5, 0.0, BEO_FMAP_BY);
00290           break;
00291         case BEO_ORI0_45:    // ##############################
00292           {
00293             // get the luminance image out of the message:
00294             Image<byte> ima = rmsg.getElementByteIma();
00295             Image<float> fima = ima;  // convert to float
00296 
00297             // compute 0deg orientation maps and send to collector:
00298             computeFeature(fima, Oriented5, 0.0, rframe, BEO_FMAP_O0);
00299 
00300             // compute 45deg orientation maps and send to collector:
00301             computeFeature(fima, Oriented5, 45.0, rframe, BEO_FMAP_O45);
00302           }
00303           break;
00304         case BEO_ORI90_135:  // ##############################
00305           {
00306             // get the luminance image out of the message:
00307             Image<byte> ima = rmsg.getElementByteIma();
00308             Image<float> fima = ima;  // convert to float
00309 
00310             // compute 90deg orientation maps and send to collector:
00311             computeFeature(fima, Oriented5, 90.0, rframe, BEO_FMAP_O90);
00312 
00313             // compute 135deg orientation maps and send to collector:
00314             computeFeature(fima, Oriented5, 135.0, rframe, BEO_FMAP_O135);
00315           }
00316           break;
00317         default: // ##############################
00318           LERROR("Bogus action %d -- IGNORING.", raction);
00319           break;
00320         }
00321       //LINFO("Job %d/%d from %d completed in %dms",
00322       //      rframe, raction, rnode, tim.get());
00323 
00324       // limit number of receives, so we don't hold CPU too long:
00325       nbrec ++; if (nbrec > 3) break;
00326     }
00327 }
00328 
00329 // ######################################################################
00330 void BeobotVisualCortex::masterCollect()
00331 {
00332   // receive various conspicuity maps
00333   int32 rframe, raction, rnode = -1, recnb = 0;  // receive from any node
00334   TCPmessage rmsg;
00335   while(beo->receive(rnode, rmsg, rframe, raction, 5)) // wait up to 5ms
00336     {
00337       //LINFO("received %d/%d from %d while at %d",
00338       //      rframe, raction, rnode, currframe);
00339       if (rframe != currframe)
00340         {
00341           LERROR("Dropping old map, type %d for frame %d while at frame %d",
00342                  raction, rframe, currframe);
00343           continue;
00344         }
00345 
00346       // collect conspicuity maps:
00347       if (raction == BEO_CMAP)
00348         {
00349           nbcmap ++;
00350 
00351           // get the map:
00352           Image<float> ima = rmsg.getElementFloatIma();
00353 
00354           // add received cmap to saliency map input:
00355           sminput += ima;
00356         }
00357 
00358       // collect feature maps:
00359       if (raction >= BEO_FMAP_RG && raction <= BEO_FMAP_O135)
00360         {
00361           nbjmap ++;
00362 
00363           VisualFeature jf = COLOR;
00364           std::vector<int> v;
00365           switch(raction)
00366             {
00367             case BEO_FMAP_RG: jf = RG; break;
00368             case BEO_FMAP_BY: jf = BY; break;
00369             case BEO_FMAP_I: jf = INTENS; break;
00370             case BEO_FMAP_F: jf = FLICKER; break;
00371             case BEO_FMAP_O0: jf = ORI; v.push_back(0); break;
00372             case BEO_FMAP_O45: jf = ORI; v.push_back(1); break;
00373             case BEO_FMAP_O90: jf = ORI; v.push_back(2); break;
00374             case BEO_FMAP_O135: jf = ORI; v.push_back(3); break;
00375             default: LFATAL("Bogus feature map type %d", raction);
00376             }
00377           v.push_back(0);  // add an index for the scale
00378 
00379           // get the maps:
00380           Image<float> ima[jetdepth];
00381           for (int i = 0; i < jetdepth; i ++)
00382             {
00383               ima[i] = rmsg.getElementFloatIma();
00384             }
00385 
00386           // fill in the jets -- IGNORE FLICKER CHANNEL:
00387           if (jf != FLICKER) {
00388             ImageSpring< Jet<float> >::iterator jet_itr = jets.beginw();
00389 
00390             for (int j = 0; j < jets.getHeight(); j ++)
00391               for (int i = 0; i < jets.getWidth(); i ++)
00392                 {
00393                   float ii = float(i), jj = float(j);
00394                   for (int k = 0; k < jetdepth; k ++)
00395                     {
00396                       float ii2 = ii, jj2 = jj;
00397                       if (ii2 > ima[k].getWidth()-1)
00398                         ii2 = ima[k].getWidth()-1;
00399                       if (jj2 > ima[k].getHeight()-1)
00400                         jj2 = ima[k].getHeight()-1;
00401 
00402                       v[v.size() - 1] = k + jetlevel;
00403 
00404                       // set the jet, using bilinear interpolation:
00405                       jet_itr->setValV(ima[k].getValInterp(ii2, jj2),
00406                                        jf, RAW, v);
00407 
00408                       // ready for next scale:
00409                       ii *= 0.5f; jj *= 0.5f;
00410                     }
00411                 }
00412           }
00413         }
00414       //LINFO("frame: %d nbcmap=%d nbjmap=%d", currframe, nbcmap, nbjmap);
00415 
00416       // limit number of receives, so we don't hold CPU for too long:
00417       recnb ++; if (recnb > 20) break;
00418     }
00419 }
00420 
00421 // ######################################################################
00422 void BeobotVisualCortex::getWinner(Point2D<int>& win) const
00423 { win.i = winner.i; win.j = winner.j; }
00424 
00425 //######################################################################
00426 void BeobotVisualCortex::initSprings(bool initPosMasses)
00427 { jets.initClustering(initPosMasses); }
00428 
00429 
00430 // ######################################################################
00431 void BeobotVisualCortex::iterateSprings(const float dt)
00432 { jets.computePos(dt); }
00433 
00434 //######################################################################
00435 void BeobotVisualCortex::getClusteredImage(Image< PixRGB<byte> >
00436                                            &clusteredImage,
00437                                            Point2D<int> &supposedTrackCentroid,
00438                                            const Point2D<int>&
00439                                            previousTrackCentroid)
00440 {
00441   jets.getClusteredImage(scene, clusteredImage, supposedTrackCentroid,
00442                          previousTrackCentroid);
00443 }
00444 
00445 // ######################################################################
00446 void BeobotVisualCortex::getPositions(Image< PixRGB<byte> > &img,
00447                                       const int zoom)
00448 { jets.getPositions(img, zoom); }
00449 
00450 // ######################################################################
00451 void BeobotVisualCortex::computeFeature(TCPmessage &rmsg,
00452                                         const PyramidType ptyp,
00453                                         const float ori,
00454                                         const int maptype)
00455 {
00456   // get the image out of the message:
00457   Image<byte> ima = rmsg.getElementByteIma();
00458   Image<float> fima = ima;  // convert to float
00459 
00460   // compute maps and send to collector:
00461   computeFeature(fima, ptyp, ori, rmsg.getID(), maptype);
00462 }
00463 
00464 // ######################################################################
00465 void BeobotVisualCortex::computeFeature2(TCPmessage &rmsg,
00466                                         const PyramidType ptyp,
00467                                         const float ori,
00468                                         const int maptype)
00469 {
00470   // get the two images out of the message:
00471   Image<byte> ima1 = rmsg.getElementByteIma();
00472   Image<byte> ima2 = rmsg.getElementByteIma();
00473   Image<float> fima = ima1 - ima2;
00474 
00475   // compute maps and send to collector:
00476   computeFeature(fima, ptyp, ori, rmsg.getID(), maptype);
00477 }
00478 
00479 // ######################################################################
00480 void BeobotVisualCortex::computeFeature(const Image<float>& fima,
00481                                         const PyramidType ptyp,
00482                                         const float ori,
00483                                         const int32 id, const int32 maptype)
00484 {
00485   // compute pyramid:
00486   ImageSet<float> pyr = buildPyrGeneric(fima, 0, lmax + dmax + 1,
00487                                         ptyp, ori);
00488 
00489   // now send off a message with the raw features, for the jets:
00490   TCPmessage smsg(id, maptype);
00491   for (int i = 0; i < jetdepth; i ++)
00492     smsg.addImage(pyr.getImage(i + jetlevel));
00493   beo->send(-1, smsg);
00494 
00495   // compute conspicuity map:
00496   Image<float> cmap;
00497   computeCmap(pyr, cmap);
00498 
00499   // send cmap off to master:
00500   smsg.reset(id, BEO_CMAP);
00501   smsg.addImage(cmap);
00502   beo->send(-1, smsg);
00503 }
00504 
00505 // ######################################################################
00506 void BeobotVisualCortex::computeCmap(const ImageSet<float>& pyr,
00507                                      Image<float>& cmap)
00508 {
00509   // clear conspicuity map:
00510   cmap.resize(pyr[sml].getDims(), true);
00511 
00512   // compute conspicuity map from feature maps:
00513   for (int delta = dmin; delta <= dmax; delta ++)
00514     for (int lev = lmin; lev <= lmax; lev ++)
00515       {
00516         Image<float> tmp = centerSurround(pyr, lev, lev + delta, true);
00517         tmp = downSize(tmp, cmap.getWidth(), cmap.getHeight());
00518         inplaceAddBGnoise(tmp, 255.0);
00519         tmp = maxNormalize(tmp, MAXNORMMIN, MAXNORMMAX, nortyp);
00520         cmap += tmp;
00521       }
00522   if (nortyp == VCXNORM_MAXNORM)
00523     cmap = maxNormalize(cmap, MAXNORMMIN, MAXNORMMAX, nortyp);
00524   else
00525     cmap = maxNormalize(cmap, 0.0f, 0.0f, nortyp);
00526 }
00527 
00528 // ######################################################################
00529 /* So things look consistent in everyone's emacs... */
00530 /* Local Variables: */
00531 /* indent-tabs-mode: nil */
00532 /* End: */
Generated on Sun May 8 08:40:12 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3