SingleOpponentChannel.C

Go to the documentation of this file.
00001 /*!@file Channels/SingleOpponentChannel.C */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the 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: Rob Peters <rjpeters at usc dot edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Channels/SingleOpponentChannel.C $
00035 // $Id: SingleOpponentChannel.C 10746 2009-02-03 07:09:00Z itti $
00036 //
00037 
00038 #ifndef CHANNELS_SINGLEOPPONENTCHANNEL_C_DEFINED
00039 #define CHANNELS_SINGLEOPPONENTCHANNEL_C_DEFINED
00040 
00041 #include "Channels/SingleOpponentChannel.H"
00042 
00043 #include "Image/ImageSetOps.H"
00044 #include "Image/PyramidOps.H"
00045 #include "rutz/trace.h"
00046 
00047 // ######################################################################
00048 SingleOpponentChannel::
00049 SingleOpponentChannel(OptionManager& mgr,
00050                       const std::string& descrName,
00051                       const std::string& tagName,
00052                       const VisualFeature vs,
00053                       rutz::shared_ptr<PyrBuilder<float> > pyr)
00054   :
00055   SingleChannel(mgr, descrName, tagName, vs, pyr),
00056   itsPq2()
00057 {}
00058 
00059 // ######################################################################
00060 void SingleOpponentChannel::reset1()
00061 {
00062   itsPq2.clear();
00063   SingleChannel::reset1();
00064 }
00065 
00066 // ######################################################################
00067 Image<float> SingleOpponentChannel::
00068 centerSurround(const uint cntrlev, const uint surrlev) const
00069 {
00070 GVX_TRACE(__PRETTY_FUNCTION__);
00071   ASSERT(itsLevelSpec.getVal().csOK(cntrlev, surrlev));
00072   if (this->hasPyramid() == false) LFATAL("I have no input pyramid yet!");
00073   if (itsPq2.empty()) LFATAL("I have no second input pyramid yet!");
00074 
00075   // in the single-opponent version, we do pyr[cntrlev] - pyr2[surrlev]
00076 
00077   // do basic center-surround for the front (latest) pyramid in queue:
00078   const ImageSet<float>& cpyr = this->pyramid(0);
00079   const ImageSet<float>& spyr = itsPq2.front().pyr;
00080   const double t = this->pyramidTime(0).secs();
00081 
00082   // compute single-opponent center-surround:
00083   Image<float> cs =
00084     ::centerSurroundSingleOpponent(cpyr, spyr, cntrlev, surrlev,
00085                                    itsTakeAbs.getVal(),
00086                                    &(this->clipPyramid()));
00087 
00088   // do additional processing with other pyramids in queue:
00089   for (uint i = 1; i < itsPq2.size(); ++i)
00090     {
00091       const ImageSet<float>& cpyr2 = this->pyramid(i);
00092       const ImageSet<float>& spyr2 = itsPq2[i].pyr;
00093       const double t2 = this->pyramidTime(i).secs();
00094 
00095       // compute a decay factor based on how old the second pyramid is
00096       // compared to the latest one:
00097       float fac = exp( (t2 - t) * itsTimeDecay.getVal());
00098       cs +=
00099         ::centerSurroundDiffSingleOpponent(cpyr, cpyr2, spyr, spyr2,
00100                                            cntrlev, surrlev,
00101                                            itsTakeAbs.getVal(),
00102                                            &(this->clipPyramid()))
00103         * fac;
00104     }
00105   return cs;
00106 }
00107 
00108 // ######################################################################
00109 void SingleOpponentChannel::
00110 centerSurround(const uint cntrlev, const uint surrlev,
00111                Image<float>& pos, Image<float>& neg) const
00112 {
00113 GVX_TRACE(__PRETTY_FUNCTION__);
00114   ASSERT(itsLevelSpec.getVal().csOK(cntrlev, surrlev));
00115   if (this->hasPyramid() == false) LFATAL("I have no output yet!");
00116   if (itsPq2.empty()) LFATAL("I have no second input pyramid yet!");
00117 
00118   // do basic center-surround for the front (latest) pyramid in queue:
00119   const ImageSet<float>& cpyr = this->pyramid(0);
00120   const ImageSet<float>& spyr = itsPq2.front().pyr;
00121   const double t = this->pyramidTime(0).secs();
00122 
00123   // compute center-surround:
00124   ::centerSurroundSingleOpponent(cpyr, spyr, cntrlev, surrlev,
00125                                  pos, neg, &(this->clipPyramid()));
00126 
00127   // do additional processing with other pyramids in queue:
00128   for (uint i = 1; i < itsPq2.size(); ++i)
00129     {
00130       const ImageSet<float>& cpyr2 = this->pyramid(i);
00131       const ImageSet<float>& spyr2 = itsPq2[i].pyr;
00132       double t2 = this->pyramidTime(i).secs();
00133 
00134       // compute a decay factor based on how old the second pyramid is
00135       // compared to the latest one:
00136       float fac = exp( (t2 - t) * itsTimeDecay.getVal());
00137       Image<float> pos2, neg2;
00138       ::centerSurroundDiffSingleOpponent(cpyr, cpyr2, spyr, spyr2,
00139                                          cntrlev, surrlev,
00140                                          pos2, neg2,
00141                                          &(this->clipPyramid()));
00142       pos += pos2 * fac;
00143       neg += neg2 * fac;
00144     }
00145 }
00146 
00147 // ######################################################################
00148 void SingleOpponentChannel::getFeatures(const Point2D<int>& locn,
00149                                         std::vector<float>& mean) const
00150 {
00151 GVX_TRACE(__PRETTY_FUNCTION__);
00152   if (!this->outputAvailable())
00153     {
00154       CLDEBUG("I have no input pyramids yet -- RETURNING ZEROS");
00155       for (uint idx = 0; idx < numSubmaps(); idx ++) mean.push_back(0.0F);
00156       return;
00157     }
00158 
00159   // The coordinates we receive are at the scale of the original
00160   // image, and we will need to rescale them to the size of the
00161   // various submaps we read from. The first image in our first
00162   // pyramid has the dims of the input:
00163   const ImageSet<float>& pyr = this->pyramid(0);
00164   const ImageSet<float>& pyr2 = itsPq2.front().pyr;
00165   const Dims indims = this->getInputDims();
00166 
00167   for (uint idx = 0; idx < numSubmaps(); idx ++)
00168     {
00169       // get center and surround scales for this submap index:
00170       uint clev = 0, slev = 0;
00171       itsLevelSpec.getVal().indexToCS(idx, clev, slev);
00172 
00173       // read center value with bilinear interpolation:
00174       ASSERT(pyr[clev].initialized());
00175       const float cval = pyr[clev].getValInterpScaled(locn, indims);
00176 
00177       // read surround value with bilinear interpolation:
00178       ASSERT(pyr2[slev].initialized());
00179       const float sval = pyr2[slev].getValInterpScaled(locn, indims);
00180 
00181       // compute center - surround and take absolute value if our
00182       // channel wants that:
00183       float val = cval - sval; if (itsTakeAbs.getVal()) val = fabs(val);
00184 
00185       // store the submap value at the chosen location:
00186       mean.push_back(val);
00187     }
00188 }
00189 
00190 // ######################################################################
00191 void SingleOpponentChannel::
00192 getFeaturesBatch(std::vector<Point2D<int>*> *locn,
00193                  std::vector<std::vector<float> > *mean,
00194                  int *count) const
00195 {
00196 GVX_TRACE(__PRETTY_FUNCTION__);
00197   if (!this->outputAvailable())
00198     {
00199       CLDEBUG("I have no input pyramids yet -- RETURNING ZEROS");
00200       for (uint idx = 0; idx < numSubmaps(); idx ++)
00201         {
00202           std::vector<std::vector<float> >::iterator imean = mean->begin();
00203           for(int i = 0; i < *count; i++, ++imean)
00204             imean->push_back(0.0);
00205         }
00206       return;
00207     }
00208 
00209   // The coordinates we receive are at the scale of the original
00210   // image, and we will need to rescale them to the size of the
00211   // various submaps we read from. The first image in our first
00212   // pyramid has the dims of the input:
00213   const ImageSet<float>& pyr = this->pyramid(0);
00214   const ImageSet<float>& pyr2 = itsPq2.front().pyr;
00215   const Dims indims = this->getInputDims();
00216   uint sm = this->numSubmaps();
00217   for (uint idx = 0; idx < sm; ++idx)
00218     {
00219     // get center and surround scales for this submap index:
00220     uint clev = 0, slev = 0;
00221     itsLevelSpec.getVal().indexToCS(idx, clev, slev);
00222     std::vector<Point2D<int>*>::iterator ilocn = locn->begin();
00223     std::vector<std::vector<float> >::iterator imean = mean->begin();
00224 
00225     for(int i = 0; i < *count; ++i, ++ilocn, ++imean)
00226       {
00227         // read center value with bilinear interpolation:
00228         ASSERT(pyr[clev].initialized());
00229         const float cval = pyr[clev].getValInterpScaled(**ilocn, indims);
00230 
00231         // read surround value with bilinear interpolation:
00232         ASSERT(pyr2[slev].initialized());
00233         const float sval = pyr2[slev].getValInterpScaled(**ilocn, indims);
00234 
00235         const float val = cval - sval;
00236 
00237         // store the submap value at the chosen location:
00238         imean->push_back(itsTakeAbs.getVal()
00239                          ? fabs(val)
00240                          : val);
00241     }
00242   }
00243 }
00244 
00245 // ######################################################################
00246 void SingleOpponentChannel::
00247 singleOpponentInput(const Dims& dims,
00248                     const ImageSet<float>& centerPyr,
00249                     const ImageSet<float>& surroundPyr,
00250                     const SimTime& t,
00251                     const Image<byte>& clipMask)
00252 {
00253 GVX_TRACE(__PRETTY_FUNCTION__);
00254   this->killCaches();
00255   this->setClipPyramid(clipMask);
00256   this->storePyramid(centerPyr, t);
00257   this->storePyramid2(surroundPyr, t);
00258   this->setInputDims(dims);
00259 }
00260 
00261 // ######################################################################
00262 void SingleOpponentChannel::storePyramid2(const ImageSet<float>& p,
00263                                           const SimTime& t)
00264 {
00265 GVX_TRACE(__PRETTY_FUNCTION__);
00266 
00267   // check that inputs are not coming in too fast, as we may otherwise
00268   // run into trouble with those channels that care about the
00269   // inter-frame delay:
00270   if (itsPq2.size() && (t - itsPq2.front().t).secs() < 0.002F) // 500fps is the max
00271     CLFATAL("Inputs coming in too fast! -- BAILING OUT");
00272 
00273   // load our pyramid into our front pyramid:
00274   itsPq2.push_front(TPyr(p, t));
00275 
00276   // truncate the pyramid queue if necessary:
00277   while(int(itsPq2.size()) > itsQlen.getVal()) itsPq2.pop_back();
00278 
00279   // We only want dyadic pyramids here:
00280   ASSERT(isDyadic(itsPq2.front().pyr.subSet
00281                   (itsLevelSpec.getVal().levMin(),
00282                    itsLevelSpec.getVal().maxDepth())));
00283 }
00284 
00285 // ######################################################################
00286 /* So things look consistent in everyone's emacs... */
00287 /* Local Variables: */
00288 /* mode: c++ */
00289 /* indent-tabs-mode: nil */
00290 /* End: */
00291 
00292 #endif // CHANNELS_SINGLEOPPONENTCHANNEL_C_DEFINED
Generated on Sun May 8 08:04:41 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3