IntegerRawVisualCortex.C

Go to the documentation of this file.
00001 /*!@file Channels/IntegerRawVisualCortex.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/IntegerRawVisualCortex.C $
00035 // $Id: IntegerRawVisualCortex.C 11584 2009-08-12 05:24:46Z itti $
00036 //
00037 
00038 #include "Channels/IntegerRawVisualCortex.H"
00039 
00040 #include "Channels/ChannelOpts.H"
00041 #include "Channels/IntegerColorChannel.H"
00042 #include "Channels/IntegerFlickerChannel.H"
00043 #include "Channels/IntegerIntensityChannel.H"
00044 #include "Channels/IntegerMotionChannel.H"
00045 #include "Channels/IntegerOrientationChannel.H"
00046 #include "Component/GlobalOpts.H"
00047 #include "Component/OptionManager.H"
00048 #include "Component/ParamMap.H"
00049 #include "Image/ColorOps.H"   // for luminance()
00050 #include "Image/IntegerMathOps.H"
00051 #include "Image/MathOps.H"    // for distance()
00052 #include "Image/Pixels.H"
00053 #include "Image/PyramidCache.H"
00054 #include "Image/ShapeOps.H"
00055 #include "Image/Transforms.H" // for chamfer34()
00056 #include "Channels/ChannelOpts.H"
00057 #include "Transport/FrameInfo.H"
00058 #include "Transport/FrameOstream.H"
00059 #include "Util/TextLog.H"
00060 #include "Util/sformat.H"
00061 #include "rutz/mutex.h"
00062 #include "rutz/trace.h"
00063 
00064 #include <algorithm>
00065 #include <cctype>
00066 #include <cstdio>
00067 #include <vector>
00068 #include <sstream>
00069 
00070 // ######################################################################
00071 IntegerRawVisualCortex::
00072 IntegerRawVisualCortex(OptionManager& mgr,
00073                     nub::ref<IntegerMathEngine> eng,
00074                     const std::string& descrName,
00075                     const std::string& tag) :
00076   IntegerComplexChannel(mgr, descrName, tag, UNKNOWN, eng),
00077   itsType(&OPT_IntegerRawVisualCortexChans, this),
00078   itsLogFile(&OPT_TextLogFile, this),
00079   itsNormType(&OPT_MaxNormType, this), // see Channels/ChannelOpts.{H,C}
00080   itsUseRandom(&OPT_UseRandom, this),  // see Component/ModelManager.{H,C}
00081   itsOutputFactor(&OPT_RawVisualCortexOutputFactor, this), // idem
00082   itsUseOlderVersion(&OPT_UseOlderVersion, this), // Channels/ChannelOpts.{H,C}
00083   itsLevelSpec(&OPT_LevelSpec, this),
00084   itsSaveOutput(&OPT_RawVisualCortexSaveOutput, this),
00085   itsUseMax(&OPT_VCXuseMax, this)
00086 {
00087 GVX_TRACE(__PRETTY_FUNCTION__);
00088 }
00089 
00090 // ######################################################################
00091 IntegerRawVisualCortex::~IntegerRawVisualCortex()
00092 {
00093 GVX_TRACE(__PRETTY_FUNCTION__);
00094 }
00095 
00096 // ######################################################################
00097 void IntegerRawVisualCortex::start1()
00098 {
00099 GVX_TRACE(__PRETTY_FUNCTION__);
00100 
00101   IntegerComplexChannel::start1();
00102 
00103   for (uint i = 0; i < this->numChans(); ++i)
00104     LINFO("Top-level channel (%u/%u): level=%s feature=%s submaps=%u name=%s",
00105           i+1, uint(this->numChans()),
00106           featureHierarchyName(featureHierarchyLevel(this->subChan(i)->visualFeature())),
00107           featureName(this->subChan(i)->visualFeature()),
00108           this->subChan(i)->numSubmaps(), this->subChan(i)->descriptiveName().c_str());
00109 }
00110 
00111 // ######################################################################
00112 void IntegerRawVisualCortex::stop2()
00113 {
00114 GVX_TRACE(__PRETTY_FUNCTION__);
00115 
00116   IntegerComplexChannel::stop2();
00117 }
00118 
00119 // ######################################################################
00120 Image<int> IntegerRawVisualCortex::getChannelOutputMap(IntegerChannel& chan) const
00121 {
00122   float total_weight = 0.0;
00123 
00124   for (uint i = 0; i < this->numChans(); ++i)
00125     {
00126       nub::ref<IntegerChannel> c = this->subChan(i);
00127 
00128       if (itsUseOlderVersion.getVal())
00129         {
00130           const float w = float(this->getSubchanTotalWeight(*c) / c->numSubmaps());
00131           total_weight += w;
00132         }
00133       else
00134         {
00135           const float w = float(this->getSubchanTotalWeight(*c));
00136           total_weight += w;
00137         }
00138     }
00139 
00140   /* We want to compute
00141 
00142                   weight
00143          img * ------------
00144                total_weight
00145 
00146 
00147      To do that without overflowing, we compute it as
00148 
00149 
00150                   weight      256
00151          img * ------------ * ---
00152                total_weight   256
00153 
00154           img       weight * 256
00155       = ( --- ) * ( ------------ )
00156           256       total_weight
00157   */
00158 
00159   const int scalebits = 8;
00160 
00161   Image<int> chanOut = chan.getOutputInt();
00162   chanOut >>= scalebits;
00163 
00164   if (itsUseOlderVersion.getVal())
00165     {
00166       const float w = float(this->getSubchanTotalWeight(chan) / chan.numSubmaps());
00167 
00168       const int iw = int( 0.5 + (w*(1<<scalebits)) / total_weight );
00169 
00170       chanOut *= iw;
00171       LINFO("%s weight %d/%d",
00172             chan.tagName().c_str(), iw, (1<<scalebits));
00173     }
00174   else
00175     {
00176       const float w = float(this->getSubchanTotalWeight(chan));
00177 
00178       const int iw = int( 0.5 + (w*(1<<scalebits)) / total_weight );
00179 
00180       chanOut *= iw;
00181       LINFO("%s weight %d/%d", chan.tagName().c_str(), iw, (1<<scalebits));
00182     }
00183 
00184   return chanOut;
00185 }
00186 
00187 // ######################################################################
00188 Image<int> IntegerRawVisualCortex::postProcessOutputMap(const Image<int>& outmap)
00189 {
00190   Image<int> result = outmap;
00191 
00192   // let's apply a last maxNormalization to the map:
00193   switch(itsNormType.getVal())
00194     {
00195     case VCXNORM_LANDMARK:
00196     case VCXNORM_SURPRISE:
00197       LFATAL("Unsupported VCXNORM type");
00198       break;
00199     default:
00200       result = intgMaxNormalize(result, 0, 32768, itsNormType.getVal());
00201       LDEBUG("%s(%d .. %d)", maxNormTypeName(itsNormType.getVal()), 0, 32768);
00202       break;
00203     }
00204 
00205   return result;
00206 }
00207 
00208 // ######################################################################
00209 void IntegerRawVisualCortex::doInputInt(const IntegerInput& inp,
00210                                      const SimTime& t,
00211                                      PyramidCache<int>* cache,
00212                                      const Image<byte>& clipMask)
00213 {
00214   ASSERT(inp.grayInt().initialized());
00215 
00216   // optimization: if we have no channels, then do a quick return:
00217   if (this->numChans() == 0) return;
00218 
00219   rutz::mutex_lock_class lock;
00220   if (cache && cache->gaussian5.beginSet(inp.grayInt(), &lock))
00221     {
00222       cache->gaussian5.endSet
00223         (inp.grayInt(),
00224          intgBuildPyrGaussian
00225          (inp.grayInt(), itsLevelSpec.getVal().maxDepth(),
00226           5, this->getImath()),
00227          &lock);
00228     }
00229 
00230   // process the channels:
00231   for (uint i = 0; i < this->numChans(); ++i)
00232     {
00233       // get a pointer to the channel of interest:
00234       nub::ref<IntegerChannel> chan = this->subChan(i);
00235 
00236       // feed the input to the channel of interest:
00237       chan->inputInt(inp, t, cache, clipMask);
00238       LINFO("Input to %s channel %s ok.", featureHierarchyName(featureHierarchyLevel(chan->visualFeature())),
00239             chan->tagName().c_str());
00240     }
00241 }
00242 
00243 // ######################################################################
00244 Image<int> IntegerRawVisualCortex::combineOutputsInt()
00245 {
00246 GVX_TRACE(__PRETTY_FUNCTION__);
00247 
00248   Image<int> output;
00249 
00250   // ... OK, we have to recompute the output:
00251   for (uint i = 0; i < this->numChans(); ++i)
00252     {
00253       nub::ref<IntegerChannel> ch = subChan(i);
00254 
00255       // CAUTION: if you modify code here, modify input() as well,
00256       // in the section that is triggered by itsConserveMemory:
00257       if (this->getSubchanTotalWeight(*ch) == 0.0) continue;
00258       if (ch->outputAvailable() == false) continue;
00259 
00260       const Image<int> chanOut = getChannelOutputMap(*ch);
00261 
00262       // downSizeClean() gracefully does nothing if chanOut is
00263       // already the right size. (Eventually we might like to have a
00264       // way for IntegerRawVisualCortex to enforce a particular chanOut size
00265       // on its own, rather than just taking the size from the first
00266       // channel in the array.)
00267       if (output.initialized() == false)
00268         output = chanOut;
00269       else
00270         {
00271           // sum or take max across channels?
00272           Image<int> o = intgDownSize(chanOut, output.getDims(), 9, this->getImath());
00273           if (itsUseMax.getVal()) output = takeMax(output, o); else output += o;
00274         }
00275     }
00276 
00277   // Ensure that we still return a valid image even if we have no channels
00278   // that has a valid output in the IntegerRawVisualCortex:
00279   if (output.initialized() == false)
00280     {
00281       int sml = itsLevelSpec.getVal().mapLevel();
00282       output = Image<int>(this->getInputDims().w() >> sml, this->getInputDims().h() >> sml, ZEROS);
00283       return output;
00284     }
00285 
00286   if (MYLOGVERB >= LOG_DEBUG)
00287     {
00288       int mi, ma; getMinMax(output, mi, ma);
00289       LDEBUG("Raw output range is [%d .. %d]", mi, ma);
00290     }
00291 
00292   // post-process the output in a manner than may depend on which
00293   // variant of IntegerRawVisualCortex we may embody:
00294   output = postProcessOutputMap(output);
00295 
00296   LINFO("Computed IntegerRawVisualCortex output.");
00297   return output;
00298 }
00299 
00300 // ######################################################################
00301 Image<float> IntegerRawVisualCortex::getOutput()
00302 {
00303   // using Image::operator*() we will simultenaously convert to float
00304   // and apply our output factor, looping only once over the pixels:
00305   const float fac = itsOutputFactor.getVal() / (1 << this->getMathEngine()->getNbits());
00306   Image<float> output = getOutputInt() * fac;
00307 
00308   float mi, ma; getMinMax(output, mi, ma);
00309   LINFO("Salmap input range is [%f .. %f] nA", mi * 1.0e9F, ma * 1.0e9F);
00310 
00311   return output;
00312 }
00313 
00314 // ######################################################################
00315 void IntegerRawVisualCortex::paramChanged(ModelParamBase* const param,
00316                                           const bool valueChanged,
00317                                           ParamClient::ChangeStatus* status)
00318 {
00319   ModelComponent::paramChanged(param, valueChanged, status);
00320   OptionManager& mgr = this->getManager();
00321   nub::ref<IntegerMathEngine> eng = this->getMathEngine();
00322 
00323   if (param == &itsType) {
00324     // kill any existing channels:
00325     this->removeAllSubChans();
00326     ASSERT(this->numChans() == 0);
00327 
00328     // Parse the param string. format here is a series of "X[:w.w]"
00329     // where X is any of "ICOFM..." and the optional w.w may be
00330     // channel weights. See note in RawVisualCortex for why we here
00331     // first parse the arg string (where letters may be specified in
00332     // various orders), and then implement the channels (in fixed order):
00333     uint i = 0; const std::string& str = itsType.getVal(); const uint len = str.length();
00334     double wc = 0.0, wi = 0.0, wo = 0.0, wf = 0.0, wm = 0.0;
00335 
00336     while (i < len) {
00337       char c = str[i];  // the channel to implement
00338       double weight = 1.0;  // default weight value
00339 
00340       // do we have a weight specification?
00341       if (i < len - 1 && str[i+1] == ':') {
00342         if (i >= len - 2) LFATAL("Missing channel weight value");
00343         uint end = str.find_first_not_of(".0123456789", i+2);
00344         std::stringstream s; s << str.substr(i+2, end); s >> weight;
00345         i = end;  // ready for next one
00346       } else ++i;
00347 
00348       switch(c) {
00349       case 'C': wc = weight; break;
00350       case 'I': wi = weight; break;
00351       case 'O': wo = weight; break;
00352       case 'F': wf = weight; break;
00353       case 'M': wm = weight; break;
00354       default: LFATAL("Unsupported channel type '%c' with weight %f", c, weight);
00355       }
00356     }
00357 
00358       // Create the channel and assign weight:
00359     if (wc) {
00360       LINFO("Using a Double Opponent ColorChannel with weight %f", wc);
00361       addSubChan(makeSharedComp(new IntegerColorChannel(mgr, eng)), "int-color");
00362       setSubchanTotalWeight("int-color", wc);
00363     }
00364     if (wf) {
00365       LINFO("Using a FlickerChannel with weight %f", wf);
00366       addSubChan(makeSharedComp(new IntegerFlickerChannel(mgr, eng)), "int-flicker");
00367       setSubchanTotalWeight("int-flicker", wf);
00368     }
00369     if (wi) {
00370       LINFO("Using an IntensityChannel with weight %f", wi);
00371       addSubChan(makeSharedComp(new IntegerIntensityChannel(mgr, eng)), "int-intensity");
00372       setSubchanTotalWeight("int-intensity", wi);
00373     }
00374     if (wo) {
00375       LINFO("Using an OrientationChannel with weight %f", wo);
00376       addSubChan(makeSharedComp(new IntegerOrientationChannel(mgr, eng)), "int-orientation");
00377       setSubchanTotalWeight("int-orientation", wo);
00378     }
00379     if (wm) {
00380       LINFO("Using a MotionChannel with weight %f", wm);
00381       addSubChan(makeSharedComp(new IntegerMotionChannel(mgr, eng)), "int-motion");
00382       setSubchanTotalWeight("int-motion", wm);
00383     }
00384 
00385     // make sure options are exported for all channels:
00386     for (uint i = 0; i < this->numChans(); ++i) this->subChan(i)->exportOptions(MC_RECURSE);
00387   }
00388 }
00389 
00390 // ######################################################################
00391 void IntegerRawVisualCortex::saveResults(const nub::ref<FrameOstream>& ofs)
00392 {
00393 GVX_TRACE(__PRETTY_FUNCTION__);
00394   // save our own output:
00395   if (itsSaveOutput.getVal())
00396     ofs->writeFloat(Image<float>(getOutputInt()), FLOAT_NORM_0_255, "IVCO",
00397                     FrameInfo("integer visual cortex output (input to saliency map)", SRC_POS));
00398 
00399   // save our channel outputs:
00400   for (uint i = 0; i < numChans(); ++i) subChan(i)->saveResults(ofs);
00401 }
00402 
00403 // ######################################################################
00404 /* So things look consistent in everyone's emacs... */
00405 /* Local Variables: */
00406 /* mode: c++ */
00407 /* indent-tabs-mode: nil */
00408 /* End: */
Generated on Sun May 8 08:40:21 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3