OrientationChannel.C

Go to the documentation of this file.
00001 /*!@file Channels/OrientationChannel.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:
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Channels/OrientationChannel.C $
00035 // $Id: OrientationChannel.C 12074 2009-11-24 07:51:51Z itti $
00036 //
00037 
00038 #ifndef ORIENTATIONCHANNEL_C_DEFINED
00039 #define ORIENTATIONCHANNEL_C_DEFINED
00040 
00041 #include "Channels/OrientationChannel.H"
00042 
00043 #include "Channels/ChannelOpts.H"
00044 #include "Channels/GaborChannel.H"
00045 #include "Component/OptionManager.H"
00046 #include "Image/ImageSetOps.H"
00047 #include "Image/PyramidOps.H"
00048 #include "Image/PyramidCache.H"
00049 #include "rutz/mutex.h"
00050 #include "rutz/trace.h"
00051 #include "Util/sformat.H"
00052 
00053 #include <cstdio> // for sscanf()
00054 
00055 // ######################################################################
00056 // OrientationChannel member definitions:
00057 // ######################################################################
00058 
00059 // ######################################################################
00060 OrientationChannel::OrientationChannel(OptionManager& mgr) :
00061   ComplexChannel(mgr, "Orientation", "orientation", ORI),
00062   itsNumOrients(&OPT_NumOrientations, this), // see Channels/ChannelOpts.{H,C}
00063   itsInteractString(&OPT_OriInteraction, this),
00064   itsInteractType(NONE),
00065   itsDidInteractions(false),
00066   itsOverideTagName(false)
00067 {
00068 GVX_TRACE(__PRETTY_FUNCTION__);
00069   // let's build our channels; we may have to re-build them if
00070   // itsNumOrient get changed on us before we start():
00071   buildSubChans();
00072 }
00073 
00074 // ######################################################################
00075 OrientationChannel::OrientationChannel(OptionManager& mgr, const char* tag, const char* desc, const char* gabortag) :
00076   ComplexChannel(mgr, "Orientation", "orientation", ORI),
00077   itsNumOrients(&OPT_NumOrientations, this), // see Channels/ChannelOpts.{H,C}
00078   itsInteractString(&OPT_OriInteraction, this),
00079   itsInteractType(NONE),
00080   itsDidInteractions(false),
00081   itsOverideTagName(true)
00082 {
00083 GVX_TRACE(__PRETTY_FUNCTION__);
00084   // let's build our channels; we may have to re-build them if
00085   // itsNumOrient get changed on us before we start():
00086 
00087   setDescriptiveName(sformat("%s", desc));
00088   setTagName(sformat("%s", tag));
00089 
00090   itsGaborOverideTag = gabortag;
00091 
00092   buildSubChans();
00093 
00094 }
00095 // ######################################################################
00096 void OrientationChannel::buildSubChans()
00097 {
00098 GVX_TRACE(__PRETTY_FUNCTION__);
00099   // kill any subchans we may have had...
00100   this->removeAllSubChans();
00101 
00102   // let's instantiate our Gabor subchannels now that we know how many
00103   // we want. They will inherit the current values (typically
00104   // post-command-line parsing) of all their options as they are
00105   // constructed:
00106   if(itsOverideTagName)
00107   {
00108     LINFO("Using %d %s orientations spanning [0..180]deg", itsNumOrients.getVal(),itsGaborOverideTag.c_str());
00109     for (uint ori = 0; ori < itsNumOrients.getVal(); ++ori)
00110     {
00111       nub::ref<GaborChannel> chan =
00112         makeSharedComp
00113         (new GaborChannel(getManager(), ori,
00114                           180.0 * double(ori) /
00115                           double(itsNumOrients.getVal()),
00116                           itsGaborOverideTag.c_str(),
00117                           itsGaborOverideTag.c_str()));
00118       this->addSubChan(chan);
00119 
00120       // let's export options on our newly built channels:
00121       chan->exportOptions(MC_RECURSE);
00122     }
00123   }
00124   else
00125   {
00126     LINFO("Using %d orientations spanning [0..180]deg", itsNumOrients.getVal());
00127     for (uint ori = 0; ori < itsNumOrients.getVal(); ++ori)
00128     {
00129       nub::ref<GaborChannel> chan =
00130         makeSharedComp
00131         (new GaborChannel(getManager(), ori,
00132                           180.0 * double(ori) /
00133                           double(itsNumOrients.getVal())));
00134       this->addSubChan(chan);
00135 
00136       // let's export options on our newly built channels:
00137       chan->exportOptions(MC_RECURSE);
00138     }
00139   }
00140   // Here, we just use GaborChannel's default tag names
00141 }
00142 
00143 // ######################################################################
00144 void OrientationChannel::parseInteractString(const std::string& value)
00145 {
00146 GVX_TRACE(__PRETTY_FUNCTION__);
00147 
00148   // need to clear the coefficients in any case
00149   itsInteractCoeffs.clear();
00150 
00151   if (value.compare("None") == 0)
00152     {
00153       itsInteractType = NONE;
00154       return;
00155     }
00156 
00157   if (value.compare("SubtractMean") == 0)
00158     {
00159       itsInteractType = SUB_MEAN;
00160       return;
00161     }
00162 
00163   itsInteractType = CUSTOM;
00164 
00165   // format here is "c.c,...,c.c"
00166   int curpos = 0, len = value.length();
00167   while (curpos < len)
00168     {
00169       // get end of next number
00170       int nextpos = value.find_first_not_of("-.0123456789eE",curpos);
00171       if (nextpos == -1) nextpos = len;
00172 
00173       // no number characters found -> bummer
00174       if (nextpos == curpos)
00175         LFATAL("Error parsing the OriInteract string '%s' - found '%c' "
00176                "instead of a number.",value.c_str(),value[curpos]);
00177 
00178       // now let's see - can we get a number here?
00179       float coeff;
00180       int nscan = sscanf(value.substr(curpos,nextpos-curpos).c_str(),"%g",&coeff);
00181 
00182       // couldn't read a number -> bummer
00183       if (nscan != 1)
00184         LFATAL("Error parsing OriInteract string '%s' - found '%s' instead of "
00185                "a number.", value.c_str(),
00186                value.substr(curpos,nextpos-curpos).c_str());
00187 
00188       // yeah! found a number -> store it
00189       itsInteractCoeffs.push_back(coeff);
00190 
00191       LDEBUG("coeff = %g; value[nextpos] = '%c'",coeff,value[nextpos]);
00192 
00193       // not a comma -> bummer
00194       if ((nextpos < len) && (value[nextpos] != ','))
00195         LFATAL("Error parsing the OriInteract string '%s' - found '%c' "
00196                "instead of ','.",value.c_str(),value[nextpos]);
00197 
00198       // the character right after the comma should be a number again
00199       curpos = nextpos + 1;
00200     }
00201 
00202   // end of string, done
00203   return;
00204 }
00205 
00206 // ######################################################################
00207 void OrientationChannel::paramChanged(ModelParamBase* const param,
00208                                       const bool valueChanged,
00209                                       ParamClient::ChangeStatus* status)
00210 {
00211 GVX_TRACE(__PRETTY_FUNCTION__);
00212   ComplexChannel::paramChanged(param, valueChanged, status);
00213 
00214   // if the param is our number of orientations and it has become
00215   // different from our number of channels, let's reconfigure:
00216   if (param == &itsNumOrients &&
00217       numChans() != itsNumOrients.getVal())
00218     buildSubChans();
00219 
00220   // if the param is our OriInteraction, then parse the string
00221   else if (param == &itsInteractString)
00222     parseInteractString(itsInteractString.getVal());
00223 }
00224 
00225 // ######################################################################
00226 OrientationChannel::~OrientationChannel()
00227 {
00228 GVX_TRACE(__PRETTY_FUNCTION__);
00229 }
00230 
00231 // ######################################################################
00232 GaborChannel& OrientationChannel::gabor(const uint idx) const
00233 {
00234 GVX_TRACE(__PRETTY_FUNCTION__);
00235   // Since we are dynamic_cast'ing a reference, this operation will either
00236   // succeed or throw an exception.
00237   return *(dynCast<GaborChannel>(subChan(idx)));
00238 }
00239 
00240 // ######################################################################
00241 void OrientationChannel::doInput(const InputFrame& inframe)
00242 {
00243 GVX_TRACE(__PRETTY_FUNCTION__);
00244   ASSERT(inframe.grayFloat().initialized());
00245 
00246   if (numChans() == 0)
00247     return;
00248 
00249   rutz::mutex_lock_class lock;
00250   if (inframe.pyrCache().get() != 0
00251       && inframe.pyrCache()->laplacian9.beginSet(inframe.grayFloat(), &lock))
00252     {
00253       inframe.pyrCache()->laplacian9.endSet
00254         (inframe.grayFloat(),
00255          buildPyrLaplacian
00256          (inframe.grayFloat(), gabor(0).getMinPyrLevel(),
00257           gabor(0).getMaxPyrLevel(), 9),
00258          &lock);
00259     }
00260 
00261   for (uint i = 0; i < numChans(); ++i)
00262     {
00263       gabor(i).input(inframe);
00264       LINFO("Orientation pyramid (%d/%d) ok.", i+1, numChans());
00265     }
00266   itsDidInteractions = false;
00267 }
00268 
00269 // ######################################################################
00270 void OrientationChannel::doInteractions()
00271 {
00272 GVX_TRACE(__PRETTY_FUNCTION__);
00273   LINFO("OriInteractionType is %s.",itsInteractString.getVal().c_str());
00274   switch(itsInteractType)
00275     {
00276     case NONE:
00277       {
00278         // compute oriented gabor pyramids in several basis directions:
00279         break;
00280       }
00281 
00282     case SUB_MEAN:
00283       {
00284         // compute the average and subtract it from each GaborPyramid
00285         // this is actually a special case of the CUSTOM one, but
00286         // this method here is more efficient
00287         ImageSet<float> mean;
00288 
00289         // compute the mean
00290         for (uint i = 0; i < numChans(); ++i)
00291           {
00292             gabor(i).getOutput(); // make sure that gabor(i) has a pyramid
00293             if (mean.isEmpty())
00294               mean = gabor(i).itsPq.back().pyr;
00295             else
00296               mean += gabor(i).itsPq.back().pyr;
00297           }
00298         mean /= (float)numChans();
00299 
00300         // clampdiff each pyramid with the mean and feed them back
00301         // into the channel:
00302         for (uint i = 0; i < numChans(); ++i)
00303           {
00304             gabor(i).getOutput(); // make sure that gabor(i) has a pyramid
00305             gabor(i).itsPq.back().pyr =
00306               clampedDiff(gabor(i).itsPq.back().pyr,mean);
00307             LINFO("Orientation pyramid interactions (%d/%d) ok.",
00308                   i+1, numChans());
00309           }
00310 
00311         // done
00312         break;
00313       }
00314 
00315     case CUSTOM:
00316       {
00317         // In this case we have arbitrary linear interactions between the
00318         // orientations - takes a bit of computation
00319 
00320         // make sure we have the correct number of coefficients
00321         ASSERT(itsInteractCoeffs.size() == numChans());
00322         std::vector< ImageSet<float> > resPyrs(numChans());
00323 
00324         // compute the pyramid interactions
00325         for (uint i = 0; i < numChans(); ++i)
00326           {
00327             ImageSet<float> curPyr = gabor(i).itsPq.back().pyr;
00328             uint coeff_ptr = i;
00329 
00330             // add this new pyramid with the correct coefficients
00331             for (uint j = 0; j < numChans(); ++j)
00332               {
00333                 if (resPyrs[j].isEmpty())
00334                   resPyrs[j] =  (curPyr * itsInteractCoeffs[coeff_ptr]);
00335                 else
00336                   resPyrs[j] += (curPyr * itsInteractCoeffs[coeff_ptr]);
00337 
00338                 // count down the coeff_ptr and wrap it around
00339                 // We count down here, because at parsing the input string
00340                 // we have used push_back and hence reversed the order.
00341                 coeff_ptr = (coeff_ptr - 1) % numChans();
00342               }
00343           }
00344 
00345         // now clamp these pyramids and feed them back to the channel
00346         for (uint i = 0; i < numChans(); ++i)
00347           {
00348             doLowThresh(resPyrs[i],0.0f,0.0f);
00349             gabor(i).itsPq.back().pyr = resPyrs[i];
00350             LINFO("Orientation pyramid interactions (%d/%d) ok.",
00351                   i+1, numChans());
00352           }
00353 
00354         // done
00355         break;
00356       }
00357     default: LFATAL("Unknown orientation interaction type: %d",
00358                     itsInteractType);
00359     } // end switch
00360 }
00361 
00362 // ######################################################################
00363 Image<float> OrientationChannel::getOutput()
00364 {
00365 GVX_TRACE(__PRETTY_FUNCTION__);
00366   if (!itsDidInteractions)
00367     {
00368       doInteractions();
00369       itsDidInteractions = true;
00370     }
00371   return ComplexChannel::getOutput();
00372 }
00373 
00374 // ######################################################################
00375 OrientationChannel::InteractType OrientationChannel::getInteractType()
00376 {
00377 GVX_TRACE(__PRETTY_FUNCTION__);
00378   return itsInteractType;
00379 }
00380 
00381 // ######################################################################
00382 void OrientationChannel::setInteractType(OrientationChannel::InteractType type)
00383 {
00384 GVX_TRACE(__PRETTY_FUNCTION__);
00385   itsInteractType = type;
00386 }
00387 
00388 // ######################################################################
00389 std::vector<float> OrientationChannel::getInteractCoeffs()
00390 {
00391 GVX_TRACE(__PRETTY_FUNCTION__);
00392   return itsInteractCoeffs;
00393 }
00394 
00395 // ######################################################################
00396 void OrientationChannel::setInteractCoeffs(std::vector<float>& coeffs)
00397 {
00398 GVX_TRACE(__PRETTY_FUNCTION__);
00399   itsInteractCoeffs = coeffs;
00400 }
00401 
00402 
00403 
00404 // ######################################################################
00405 /* So things look consistent in everyone's emacs... */
00406 /* Local Variables: */
00407 /* indent-tabs-mode: nil */
00408 /* End: */
00409 
00410 #endif // ORIENTATIONCHANNEL_C_DEFINED
Generated on Sun May 8 08:40:22 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3