SingleSvChannel.C

Go to the documentation of this file.
00001 /*!@file Channels/SingleSvChannel.C Channel for a single stream of space variant processing. */
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: David J. Berg <dberg@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu:/software/invt/trunk/saliency/src/Channels/SingleSvChannel.C $
00035 // $Id: SingleSvChannel.C 13757 2010-08-04 22:11:39Z siagian $
00036 
00037 #include "Channels/SingleSvChannel.H"
00038 #include "SpaceVariant/SpaceVariantOpts.H"
00039 
00040 // ######################################################################
00041 SingleSvChannel::SingleSvChannel(OptionManager& mgr, const std::string& descrName,
00042                                  const std::string& tag,const VisualFeature vs,
00043                                  const SpaceVariantModule& spacevariantmodule) :
00044   SingleChannel(mgr, descrName, tag, vs, rutz::shared_ptr<PyrBuilder<float> >(/*NULL*/)),
00045   itsChanDims(&OPT_SpaceVariantDims, this),
00046   itsLevels(&OPT_SpaceVariantChanScales, this),
00047   itsTakeSquare("TakeSquareRoot", this, false),
00048   isPolarized("IsPolarized", this, false),
00049   itsUseSpaceVariantBoundary("UseSpaceVariantBoundary", this, true),
00050   itsTransform(new SpaceVariantModule(mgr))
00051 {
00052 GVX_TRACE(__PRETTY_FUNCTION__);
00053 
00054 //hide the options we don't care about from single channel
00055  hideOption(&OPT_LevelSpec);
00056 }
00057 
00058 // ######################################################################
00059 SingleSvChannel::~SingleSvChannel()
00060 {
00061 GVX_TRACE(__PRETTY_FUNCTION__);
00062 }
00063 
00064 // ######################################################################
00065 ImageSet<float> SingleSvChannel::
00066 computePyramid(const Image<float>& bwimg,
00067                const rutz::shared_ptr<PyramidCache<float> >& cache)
00068 {
00069 GVX_TRACE(__PRETTY_FUNCTION__);
00070 // Compute our pyramid:
00071  if (!cache.is_valid())
00072    LFATAL("Space variant channels should always get a cache from the retina");
00073  
00074  ImageSet<float> py;
00075  itsTransform->transformFloatPyramid(bwimg, py, itsLevels.getVal(), cache->get());
00076  
00077  // Eliminate small values if and/or rectify the pyramid desired:
00078  if (itsLowThresh.getVal() > 0.0f)
00079    {
00080      if (itsRectifyPyramid.getVal())
00081        doLowThresh(py, itsLowThresh.getVal());
00082      else
00083        doLowThreshAbs(py, itsLowThresh.getVal());
00084    }
00085  else if (itsRectifyPyramid.getVal())
00086    doRectify(py);
00087  
00088  // return pyramid:
00089  return py;
00090 }
00091 
00092 // ######################################################################
00093 Dims SingleSvChannel::getMapDims() const
00094 {
00095 GVX_TRACE(__PRETTY_FUNCTION__);
00096  return itsChanDims.getVal();
00097 }
00098 
00099 // ######################################################################
00100 Image<float> SingleSvChannel::centerSurround(const uint cntrlev,
00101                                            const uint surrlev) const
00102 {
00103 GVX_TRACE(__PRETTY_FUNCTION__);
00104  LFATAL("We don't do center surround operations for this channel");
00105  return Image<float>();
00106 }
00107 
00108 // ######################################################################
00109 void SingleSvChannel::centerSurround(const uint cntrlev, const uint surrlev,
00110                                    Image<float>& pos, Image<float>& neg) const
00111 {
00112 GVX_TRACE(__PRETTY_FUNCTION__);
00113  LFATAL("We don't do center surround operations for this channel");
00114 }
00115 
00116 // ######################################################################
00117 std::string SingleSvChannel::getSubmapName(const uint idx) const
00118 {
00119 GVX_TRACE(__PRETTY_FUNCTION__);
00120  ASSERT(idx < numSubmaps());
00121  return sformat("%s lev ", descriptiveName().c_str(), idx);
00122 }
00123 
00124 // ######################################################################
00125 std::string SingleSvChannel::getSubmapNameShort(const uint idx) const
00126 {
00127 GVX_TRACE(__PRETTY_FUNCTION__);
00128  ASSERT(idx < numSubmaps());
00129  return sformat("%s(%d)", tagName().c_str(), idx);
00130 }
00131 
00132 // ######################################################################
00133 void SingleSvChannel::getFeatures(const Point2D<int>& locn,
00134                                 std::vector<float>& mean) const
00135 {
00136 GVX_TRACE(__PRETTY_FUNCTION__);
00137 
00138  if (!this->outputAvailable())
00139    {
00140      CLDEBUG("I have no input pyramid yet -- RETURNING ZEROS");
00141      for (uint idx = 0; idx < numSubmaps(); idx ++) mean.push_back(0.0F);
00142      return;
00143    }
00144  
00145  const ImageSet<float>& pyr = itsPq.front().pyr;
00146  const Dims indims = this->getInputDims();
00147 
00148   for (uint idx = 0; idx < numSubmaps(); idx ++)
00149     {
00150       // read center value with bilinear interpolation:
00151       ASSERT(pyr[idx].initialized());
00152       const float val = pyr[idx].getValInterpScaled(locn, indims);
00153 
00154       // store the submap value at the chosen location:
00155       mean.push_back(val);
00156     }
00157 }
00158 
00159 // ######################################################################
00160 void SingleSvChannel::getFeaturesBatch(std::vector<Point2D<int>*> *locn,
00161                                      std::vector<std::vector<float> > *mean,
00162                                      int *count) const
00163 {
00164 GVX_TRACE(__PRETTY_FUNCTION__);
00165 
00166  if (!this->outputAvailable())
00167    {
00168      CLDEBUG("I have no input pyramid yet -- RETURNING ZEROS");
00169      for (uint idx = 0; idx < numSubmaps(); idx ++)
00170        {
00171          std::vector<std::vector<float> >::iterator imean = mean->begin();
00172          for(int i = 0; i < *count; i++, ++imean)
00173            imean->push_back(0.0);
00174        }
00175      return;
00176    }
00177 
00178   // The coordinates we receive are at the scale of the original
00179   // image, and we will need to rescale them to the size of the
00180   // various submaps we read from. The first image in our first
00181   // pyramid has the dims of the input:
00182   const ImageSet<float>& pyr = itsPq.front().pyr;
00183   const Dims indims = this->getInputDims();
00184   const uint sm = numSubmaps();
00185   for (uint idx = 0; idx < sm; ++idx)
00186     {
00187       std::vector<Point2D<int>*>::iterator ilocn = locn->begin();
00188       std::vector<std::vector<float> >::iterator imean = mean->begin();
00189 
00190       for (int i = 0; i < *count; ++i, ++ilocn, ++imean)
00191         {
00192           // read center value with bilinear interpolation:
00193           ASSERT(pyr[clev].initialized());
00194           const float val = pyr[clev].getValInterpScaled(**ilocn, indims);
00195         }
00196     }
00197 }
00198 
00199 // ######################################################################
00200 LevelSpec SingleSvChannel::getLevelSpec() const
00201 {
00202 GVX_TRACE(__PRETTY_FUNCTION__);
00203  LFATAL("Space variant channels do not use a LevelSpec");
00204  return itsLevelSpec.getVal();
00205 }
00206 
00207 // ######################################################################
00208 void SingleSvChannel::setClipPyramid(const Image<byte>& clipMask)
00209 {
00210 GVX_TRACE(__PRETTY_FUNCTION__);
00211   // if necessary create pyramid of clipping masks
00212   if (clipMask.initialized())
00213     {
00214       Image<float> mask = rescale(Image<float>(clipMask)/255.0f, getMapDims());
00215       itsClipPyr = ImageSet<float>(maxIndex());
00216       for (uint ii = 0; ii < maxIndex(); ++ii)
00217         itsClipPyr[ii] = mask;
00218     }
00219   else
00220     itsClipPyr.clear();
00221 }
00222 
00223 // ######################################################################
00224 void SingleSvChannel::storePyramid(const ImageSet<float>& p,const SimTime& t)
00225 {
00226 GVX_TRACE(__PRETTY_FUNCTION__);
00227   // load our pyramid into our front pyramid:
00228   itsPq.push_front(TPyr(p, t));
00229 
00230   // truncate the pyramid queue if necessary:
00231   while(int(itsPq.size()) > itsQlen.getVal()) itsPq.pop_back();
00232 
00233   ASSERT(itsPq.front().pyr.size() < this->getMaxPyrLevel());
00234 }
00235 
00236 // ######################################################################
00237 Image<float> SingleSvChannel::getRawCSmap(const uint idx) const
00238 {
00239 GVX_TRACE(__PRETTY_FUNCTION__);
00240   ASSERT(itsLevelSpec.getVal().indexOK(idx));
00241   if (itsPq.empty()) CLFATAL("I have no input pyramid yet!");
00242 
00243   // determine any gain factor (possibly from a
00244   // ChannelFacetGainSingle). But note that we don't apply that gain
00245   // factor here, rather we just care to see whether we can avoid
00246   // computing the map if its gain factor is zero. Applying the gain
00247   // factors is done in combineSubmaps():
00248   float w = 1.0;
00249   if (hasFacet<ChannelFacetGainSingle>())
00250     w = getFacet<ChannelFacetGainSingle>()->getVal(idx);
00251 
00252   // if we have a zero weight, just return an empty image:
00253   if (w == 0.0f) return Image<float>(getMapDims(), ZEROS);
00254 
00255   Image<float> submap;
00256 
00257   // if using split center-surround model param from SingleChannel, 
00258   // compute both the positive and negative submaps for channels which 
00259   // are polarized, normalize them, sum them and we
00260   // will below further normalize the sum:
00261   if (itsUseSplitCS.getVal() && isPolarized.getVal())
00262     {
00263       Image<float> subpos, subneg;
00264       computeSubChanSplit(idx, subpos, subneg);
00265 
00266       // apply spatial competition for salience, independently to
00267       // both positive and negative parts of the submap,
00268       // preserving their original range rather than first
00269       // normalizing them to [MAXNORMMIN..MAXNORMMAX] as we
00270       // usually do, so that those maps who contain nothing do not
00271       // get artificially amplified:
00272       subpos = maxNormalize(subpos, 0.0f, 0.0f, itsNormType.getVal());
00273       subneg = maxNormalize(subneg, 0.0f, 0.0f, itsNormType.getVal());
00274 
00275       // the raw submap is the sum of the normalized positive and
00276       // negative sides:
00277       submap = subpos + subneg;
00278     }
00279   else    
00280     submap = computeSubChan(idx);
00281 
00282   // print some debug info if in debug mode:
00283   if (MYLOGVERB >= LOG_DEBUG)
00284     {
00285       float mi, ma; getMinMax(submap, mi, ma);
00286       LDEBUG("%s(%d,%d): raw range [%f .. %f]", tagName().c_str(),
00287              clev, slev, mi, ma);
00288     }
00289 
00290   return submap;
00291 }
00292 
00293 // ######################################################################
00294 void SingleSvChannel::computeSubChanSplit(const uint idx, Image<float>& pos, Image<float>& neg) const
00295 {
00296   //grab the front of the que
00297   const Image<float>& img = itsPq.front().pyr[idx];
00298   double t = itsPq.front().t.secs();
00299   
00300   splitPosNeg(img, pos, neg);
00301 
00302   else if (itsTakeSquare.getVal())
00303     {
00304       pos = squared(pos);
00305       neg = squared(neg);
00306     }
00307   
00308   // do additional processing with other pyramids in queue:
00309   for (uint i = 1; i < itsPq.size(); ++i)
00310     {
00311       const Image<float>& img2 = itsPq[i].pyr[idx];
00312       double t2 = itsPq[i].t.secs();
00313 
00314       // compute a decay factor based on how old the second pyramid is
00315       // compared to the latest one:
00316       float fac = exp( (t2 - t) * itsTimeDecay.getVal());
00317       
00318       const Image<float> pos2, neg2;
00319       splitPosNeg(img2, pos2, neg2);
00320       
00321       if (itsTakeSquare.getVal())
00322         {
00323           pos2 = squared(pos2);
00324           neg2 = squared(neg2);
00325         }
00326       pos += pos2 * fac;
00327       neg += neg2 * fac;
00328     }
00329 }
00330 
00331 // ######################################################################
00332 Image<float> SingleSvChannel::computeSubChan(const uint idx) const
00333 {
00334   //grab the front of the que
00335   Image<float> img = itsPq.front().pyr[idx];
00336   double t = itsPq.front().t.secs();
00337   
00338   if (itsTakeAbs.getVal())
00339     img = abs(img);
00340   else if (itsTakeSquare.getVal())
00341     img = squared(img);
00342   
00343   // do additional processing with other pyramids in queue:
00344   for (uint i = 1; i < itsPq.size(); ++i)
00345     {
00346       double t2 = itsPq[i].t.secs();
00347 
00348       // compute a decay factor based on how old the second pyramid is
00349       // compared to the latest one:
00350       float fac = exp( (t2 - t) * itsTimeDecay.getVal());
00351       
00352       if (itsTakeAbs.getVal())
00353         img = abs(itsPq[i].pyr[idx]) * fac;
00354       else if (itsTakeSquare.getVal())
00355         img = squared(itsPq[i].pyr[idx]) * fac;
00356     }
00357   return img;
00358 }
00359 
00360 // ######################################################################
00361 int SingleSvChannel::getMinPyrLevel() const
00362 {
00363   return 0;
00364 }
00365 
00366 // ######################################################################
00367 int SingleSvChannel::getMaxPyrLevel() const
00368 {
00369   return maxIndex();
00370 }
00371 
00372 // ######################################################################
00373 void SingleSvChannel::start1()
00374 {
00375 GVX_TRACE(__PRETTY_FUNCTION__);
00376  SingleSvChannel::start1();
00377 }
00378 
00379 // ######################################################################
00380 uint SingleSvChannel::csToIndex(const uint centerlev, const uint surroundlev) const
00381 {
00382 GVX_TRACE(__PRETTY_FUNCTION__);
00383   return centerlev;
00384 }
00385 
00386 // ######################################################################
00387 void SingleSvChannel::indexToCS(const uint index, uint& centerlev, uint& surroundlev) const
00388 {
00389 GVX_TRACE(__PRETTY_FUNCTION__);
00390   centerlev = idx; surroundlev = maxIndex() + 1;
00391 }
00392 
00393 // ######################################################################
00394 uint SingleSvChannel::maxIndex() const
00395 {
00396 GVX_TRACE(__PRETTY_FUNCTION__);
00397  return itsLevels.getVal().numLevels();
00398 }
00399 
00400 // ######################################################################
00401 /* So things look consistent in everyone's emacs... */
00402 /* Local Variables: */
00403 /* indent-tabs-mode: nil */
00404 /* End: */
Generated on Sun May 8 08:40:22 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3