RawVisualCortex.C

Go to the documentation of this file.
00001 /*!@file Channels/RawVisualCortex.C Implementation for visual cortex class */
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/Channels/RawVisualCortex.C $
00035 // $Id: RawVisualCortex.C 14559 2011-03-01 22:02:47Z ehu $
00036 //
00037 
00038 #include "Image/OpenCVUtil.H" // must be included first to avoid possible type conflicts
00039 
00040 #include "Channels/RawVisualCortex.H"
00041 
00042 #include "Channels/ChannelOpts.H"
00043 #include "Channels/ChannelFacets.H"
00044 #include "Channels/ColorChannel.H"
00045 #include "Channels/CompositeColorChannel.H"
00046 #include "Channels/ContourChannel.H"
00047 #include "Channels/DepthChannel.H"
00048 #include "Channels/DepthMotionChannel.H"
00049 #include "Channels/DKLcolorChannel.H"
00050 #include "Channels/DummyChannel.H"
00051 #include "Channels/EndPointChannel.H"
00052 #include "Channels/FlickerChannel.H"
00053 #include "Channels/FlickerNBackChannel.H"
00054 #include "Channels/ForegroundDetectionChannel.H"
00055 #include "Channels/H2SVChannel.H"
00056 #include "Channels/CIELabChannel.H"
00057 #include "Channels/ImagizeColorChannel.H"
00058 #include "Channels/InputFrame.H"
00059 #include "Channels/IntensityBandChannel.H"
00060 #include "Channels/IntensityChannel.H"
00061 #include "Channels/Jet.H"
00062 #include "Channels/LJunctionChannel.H"
00063 #include "Channels/MotionChannel.H"
00064 #include "Channels/MotionSpatioTemporalChannel.H"
00065 #include "Channels/MotionOpticalFlowChannel.H"
00066 #include "Channels/MSTChannel.H"
00067 #include "Channels/FoeMSTChannel.H"
00068 #include "Channels/MultiColorBandChannel.H"
00069 #include "Channels/OrientationChannel.H"
00070 #include "Channels/PedestrianChannel.H"
00071 #include "Channels/SIFTChannel.H"
00072 #include "Channels/ObjDetChannel.H"
00073 #include "Channels/SkinHueChannel.H"
00074 #include "Channels/SoxChannel.H"
00075 #include "Channels/TJunctionChannel.H"
00076 #include "Channels/XJunctionChannel.H"
00077 #include "Channels/ZeroChannel.H"
00078 #include "Component/GlobalOpts.H"
00079 #include "Component/OptionManager.H"
00080 #include "Component/ParamMap.H"
00081 #include "Image/ColorOps.H"   // for luminance()
00082 #include "Image/MathOps.H"    // for distance()
00083 #include "Image/Pixels.H"
00084 #include "Image/PyramidOps.H"
00085 #include "Image/ShapeOps.H"
00086 #include "Image/Transforms.H" // for chamfer34()
00087 #include "Media/MgzDecoder.H"
00088 #include "Media/MgzEncoder.H"
00089 #include "Media/MediaSimEvents.H"
00090 #include "Transport/FrameInfo.H"
00091 #include "Transport/FrameOstream.H"
00092 #include "Util/sformat.H"
00093 #include "rutz/mutex.h"
00094 #include "rutz/trace.h"
00095 
00096 #include <algorithm>
00097 #include <cctype>
00098 #include <cstdio>
00099 #include <vector>
00100 #include <sstream>
00101 
00102 // ######################################################################
00103 RawVisualCortex::RawVisualCortex(OptionManager& mgr,
00104                            const std::string& descrName,
00105                            const std::string& tag) :
00106   ComplexChannel(mgr, descrName, tag, UNKNOWN),
00107   itsType(&OPT_RawVisualCortexChans, this),
00108   itsNormType(&OPT_MaxNormType, this), // see Channels/ChannelOpts.{H,C}
00109   itsUseRandom(&OPT_UseRandom, this),  // see Component/ModelManager.{H,C}
00110   itsOutputFactor(&OPT_RawVisualCortexOutputFactor, this), // idem
00111   itsNoise(&OPT_RawVisualCortexNoise, this), // idem
00112   itsUseOlderVersion(&OPT_UseOlderVersion, this), // Channels/ChannelOpts.{H,C}
00113   itsLevelSpec(&OPT_LevelSpec, this),
00114   itsSaveOutTo(&OPT_VCXsaveOutTo, this),
00115   itsLoadOutFrom(&OPT_VCXloadOutFrom, this),
00116   itsSaveOutput(&OPT_RawVisualCortexSaveOutput, this),
00117   itsUseMax(&OPT_VCXuseMax, this),
00118   itsWeightThresh(&OPT_VCXweightThresh, this),
00119   itsOutputMgzIn(),
00120   itsOutputMgzOut()
00121 {
00122 GVX_TRACE(__PRETTY_FUNCTION__);
00123  itsFrame = 0;
00124 }
00125 
00126 // ######################################################################
00127 RawVisualCortex::~RawVisualCortex()
00128 {
00129 GVX_TRACE(__PRETTY_FUNCTION__);
00130 }
00131 
00132 // ######################################################################
00133 namespace
00134 {
00135   struct ChannelSorter
00136   {
00137     ChannelSorter(const RawVisualCortex* vcx_,
00138                   bool dosubmaps_)
00139       :
00140       vcx(vcx_), dosubmaps(dosubmaps_)
00141     {}
00142 
00143     bool operator()(uint i, uint j)
00144     {
00145       nub::ref<ChannelBase> chan1 = vcx->subChan(i);
00146       nub::ref<ChannelBase> chan2 = vcx->subChan(j);
00147 
00148       const int level1 = int(featureHierarchyLevel(chan1->visualFeature()));
00149       const int level2 = int(featureHierarchyLevel(chan2->visualFeature()));
00150 
00151       if (level1 < level2)
00152         return true;
00153 
00154       // else ...
00155 
00156       if (dosubmaps && level1 == level2)
00157         return chan1->numSubmaps() > chan2->numSubmaps();
00158 
00159       // else ...
00160 
00161       return false;
00162     }
00163 
00164     const RawVisualCortex* vcx;
00165     bool dosubmaps;
00166   };
00167 }
00168 
00169 // ######################################################################
00170 void RawVisualCortex::start1()
00171 {
00172 GVX_TRACE(__PRETTY_FUNCTION__);
00173 
00174   ComplexChannel::start1();
00175 
00176   // initialize out input or output MGZ handlers if necessary:
00177   if (itsSaveOutTo.getVal().empty() == false)
00178     itsOutputMgzOut = rutz::make_shared(new MgzEncoder(itsSaveOutTo.getVal(), 9));
00179 
00180   if (itsLoadOutFrom.getVal().empty() == false)
00181     itsOutputMgzIn = rutz::make_shared(new MgzDecoder(itsLoadOutFrom.getVal()));
00182 
00183   for (uint i = 0; i < this->numChans(); ++i)
00184     {
00185       LINFO("Top-level channel (%u/%u): level=%s feature=%s submaps=%u name=%s",
00186             i+1, this->numChans(),
00187             featureHierarchyName(featureHierarchyLevel(this->subChan(i)->visualFeature())),
00188             featureName(this->subChan(i)->visualFeature()),
00189             this->subChan(i)->numSubmaps(),
00190             this->subChan(i)->descriptiveName().c_str());
00191     }
00192 }
00193 
00194 // ######################################################################
00195 void RawVisualCortex::stop2()
00196 {
00197 GVX_TRACE(__PRETTY_FUNCTION__);
00198 
00199   if (itsOutputMgzOut.is_valid()) itsOutputMgzOut->close();
00200 
00201   ComplexChannel::stop2();
00202 }
00203 
00204 // ######################################################################
00205 void RawVisualCortex::paramChanged(ModelParamBase* const param,
00206                                    const bool valueChanged,
00207                                    ParamClient::ChangeStatus* status)
00208 {
00209   ModelComponent::paramChanged(param, valueChanged, status);
00210   OptionManager& mgr = this->getManager();
00211 
00212   if (param == &itsType) {
00213     // kill any existing channels:
00214     this->removeAllSubChans();
00215     ASSERT(this->numChans() == 0);
00216 
00217     // Parse the param string. format here is a series of "X[:w.w]"
00218     // where X is any of "ICOFM..." and the optional w.w may be
00219     // channel weights. NOTE: sadly, the final saliency map is (very
00220     // slightly) affected by the order in which the different channels
00221     // sum into it. If we just create the channel objects as we parse
00222     // the param string here, this will lead to --vc-chans=CIO might
00223     // give a slightly different saliency map than
00224     // --vc-chans=IOC. This seems to be due to rounding during
00225     // floating-point computations. Hence, here we parse the param
00226     // string in two steps: first, parse the user input and find out
00227     // which channels should be built; second, build the channels, in
00228     // a fixed order than does not depend on the ordering of the
00229     // letters in the user input:
00230     uint i = 0; const std::string& str = itsType.getVal(); const uint len = str.length();
00231     std::vector<double> ww(('z' - 'A')+1, 0.0);
00232 
00233     while (i < len) {
00234       char c = str[i];  // the channel to implement
00235       if (! ((c >= 'A' && c <= 'Z') || (c >= 'a' && c < 'z')) )
00236         LFATAL("Invalid channel character 0x%x (%c)", c, c);
00237       ww[c - 'A'] =  1.0;   // default weight value
00238 
00239       // do we have a weight specification?
00240       if (i < len - 1 && str[i+1] == ':') {
00241         if (i >= len - 2) LFATAL("Missing channel weight value");
00242         uint end = str.find_first_not_of(".0123456789", i+2);
00243         std::stringstream s; s << str.substr(i+2, end); s >> ww[c - 'A'];
00244         i = end;  // ready for next one
00245       } else ++i;
00246     }
00247 
00248     // Create the channels and assign weights, creating in a fixed order:
00249     if (double w = ww['C' - 'A']) {
00250       LINFO("Using a Double Opponent ColorChannel with weight %f", w);
00251       addSubChan(makeSharedComp(new ColorChannel(mgr)), "color", w);
00252     }
00253     if (double w = ww['S' - 'A']) {
00254       LINFO("Using a Composite ColorChannel with weight %f", w);
00255       addSubChan(makeSharedComp(new CompositeColorChannel(mgr)), "composite-color", w);
00256     }
00257     if (double w = ww['F' - 'A']) {
00258       LINFO("Using a FlickerChannel with weight %f", w);
00259       addSubChan(makeSharedComp(new FlickerChannel(mgr)), "flicker", w);
00260     }
00261     if (double w = ww['I' - 'A']) {
00262       LINFO("Using an IntensityChannel with weight %f", w);
00263       addSubChan(makeSharedComp(new IntensityChannel(mgr)), "intensity", w);
00264     }
00265     if (double w = ww['O' - 'A']) {
00266       LINFO("Using an OrientationChannel with weight %f", w);
00267       addSubChan(makeSharedComp(new OrientationChannel(mgr)), "orientation", w);
00268     }
00269     if (double w = ww['M' - 'A']) {
00270       LINFO("Using a MotionChannel with weight %f", w);
00271       addSubChan(makeSharedComp(new MotionChannel(mgr)), "motion", w);
00272     }
00273 
00274     if (double w = ww['s' - 'A']) {
00275      LINFO("Using a MotionSpatioTemporalChannel with weight %f", w);
00276      addSubChan(makeSharedComp(new MotionSpatioTemporalChannel(mgr)), "motionSpatioTemporal", w);
00277     }
00278 
00279     if (double w = ww['o' - 'A']) {
00280      LINFO("Using a MotionOpticalFlowChannel with weight %f", w);
00281      addSubChan(makeSharedComp(new MotionOpticalFlowChannel(mgr)), "motionOpticalFlow", w);
00282     }
00283 
00284     if (double w = ww['B' - 'A']) {
00285      LINFO("Using a FOE MSTChannel with weight %f", w);
00286       if (!this->hasSubChan("motion"))
00287         LFATAL("Must have a Motion Channel to build an FoeMSTChannel; specify 'M' before 'B' in --vc-chans");
00288       addSubChan(makeSharedComp(new FoeMSTChannel(mgr, dynCastWeak<MotionChannel>
00289                                                      (subChan("motion")))), "FoeMST", w);
00290     }
00291 
00292     if (double w = ww['L' - 'A']) {
00293       LINFO("Using an L Junction Channel with weight %f", w);
00294       if (!this->hasSubChan("orientation"))
00295         LFATAL("Must have an OrientationChannel to build an LJunctionChannel; specify 'O' before 'L' in --vc-chans");
00296       addSubChan(makeSharedComp(new LJunctionChannel(mgr, dynCastWeak<OrientationChannel>
00297                                                      (subChan("orientation")))), "LJunction", w);
00298     }
00299 
00300     if (double w = ww['T' - 'A']) {
00301       LINFO("Using a T Junction Channel with weight %f", w);
00302       if (!this->hasSubChan("orientation"))
00303         LFATAL("Must have an OrientationChannel to build an TJunctionChannel; specify 'O' before 'T' in --vc-chans");
00304       addSubChan(makeSharedComp(new TJunctionChannel(mgr, dynCastWeak<OrientationChannel>
00305                                                      (subChan("orientation")))), "TJunction", w);
00306     }
00307 
00308     if (double w = ww['X' - 'A']) {
00309       LINFO("Using an X Junction Channel with weight %f", w);
00310       if (!this->hasSubChan("orientation"))
00311         LFATAL("Must have an OrientationChannel to build an XJunctionChannel; specify 'O' before 'X' in --vc-chans");
00312       addSubChan(makeSharedComp(new XJunctionChannel(mgr, dynCastWeak<OrientationChannel>
00313                                                      (subChan("orientation")))), "XJunction", w);
00314     }
00315 
00316     if (double w = ww['E' - 'A']) {
00317       LINFO("Using an EndPoint Channel with weight %f", w);
00318       if (!this->hasSubChan("orientation"))
00319         LFATAL("Must have an OrientationChannel to build an EndPointChannel; specify 'O' before 'E' in --vc-chans");
00320       addSubChan(makeSharedComp(new EndPointChannel(mgr, dynCastWeak<OrientationChannel>
00321                                                     (subChan("orientation")))), "EndPoint", w);
00322     }
00323 
00324     if (double w = ww['G' - 'A']) {
00325       LINFO("Using Gaussian multi-color band channels with weight %f", w);
00326       addSubChan(makeSharedComp(new MultiColorBandChannel(mgr)), "MultiColorBand", w);
00327     }
00328     if (double w = ww['D' - 'A']) {
00329       LINFO("Using a Dummy Channel with weight %f", w);
00330       addSubChan(makeSharedComp(new DummyChannel(mgr)), "Dummy", w);
00331     }
00332     if (double w = ww['K' - 'A']) {
00333       LINFO("Using a SkinHue channel with weight %f", w);
00334       addSubChan(makeSharedComp(new SkinHueChannel(mgr)), "SkinHue", w);
00335     }
00336     if (double w = ww['N' - 'A']) {
00337       LINFO("Using an Intensity Band channel with weight %f", w);
00338       addSubChan(makeSharedComp(new IntensityBandChannel(mgr)), "IntensityBand", w);
00339     }
00340     if (double w = ww['P' - 'A']) {
00341       LINFO("Using a SIFT channel with weight %f", w);
00342       addSubChan(makeSharedComp(new SIFTChannel(mgr)), "SIFTBand", w);
00343     }
00344     if (double w = ww['H' - 'A']) {
00345       LINFO("Using a H2SV channel with weight %f", w);
00346       addSubChan(makeSharedComp(new H2SVChannel(mgr)), "H2SVcolor", w);
00347     }
00348     if (double w = ww['Q' - 'A']) {
00349       LINFO("Using a CIELab channel with weight %f", w);
00350       addSubChan(makeSharedComp(new CIELabChannel(mgr)), "CIELabcolor", w);
00351     }
00352     if (double w = ww['Y' - 'A']) {
00353       LINFO("Using a Depth channel with weight %f", w);
00354       addSubChan(makeSharedComp(new DepthChannel(mgr)), "Depth", w);
00355     }
00356     if (double w = ww['y' - 'A']) {
00357       LINFO("Using a Depth Motion channel with weight %f", w);
00358       addSubChan(makeSharedComp(new DepthMotionChannel(mgr)), "DepthMotion", w);
00359     }
00360     if (double w = ww['R' - 'A']) {
00361       LINFO("Using a Pedestrian channel with weight %f", w);
00362       addSubChan(makeSharedComp(new PedestrianChannel(mgr)), "Pedestrian", w);
00363     }
00364     if (double w = ww['V' - 'A']) {
00365       LINFO("Using a SoxChannel with weight %f", w);
00366       addSubChan(makeSharedComp(new SoxChannel(mgr)), "Sox", w);
00367     }
00368     if (double w = ww['W' - 'A']) {
00369       LINFO("Using a ContourChannel with weight %f", w);
00370       addSubChan(makeContourChannel(mgr, "contour"), "Contour", w);
00371     }
00372     if (double w = ww['Z' - 'A']) {
00373       LINFO("Using a dummy Zero Channel with weight %f", w);
00374       addSubChan(makeSharedComp(new ZeroChannel(mgr)), "Zero", w);
00375     }
00376     if (double w = ww['A' - 'A']) {
00377       LINFO("Using an Object Detection channel with weight %f", w);
00378       addSubChan(makeSharedComp(new ObjDetChannel(mgr)), "ObjDet", w);
00379     }
00380     if (double w = ww['J' - 'A']) {
00381       LINFO("Using a DKL Color channel with weight %f", w);
00382       addSubChan(makeSharedComp(new DKLcolorChannel(mgr)), "DKLcolor", w);
00383     }
00384     if (double w = ww['U' - 'A']) {
00385       LINFO("Using Foreground Detection channel with weight %f", w);
00386       addSubChan(makeSharedComp(new ForegroundDetectionChannel(mgr)), "Foreground", w);
00387     }
00388     if (double w = ww['i' - 'A']) {
00389       LINFO("Using an Imagize Composite channel with weight %f", w);
00390       addSubChan(makeSharedComp(new ImagizeColorChannel(mgr)), "ImagizeComposite", w);
00391     }
00392     if (double w = ww['f' - 'A']) {
00393       LINFO("Using a FlickerNBackChannel with weight %f", w);
00394       addSubChan(makeSharedComp(new FlickerNBackChannel(mgr)), "flicker", w);
00395     }
00396     LINFO("RawVisualCortex loaded with %u top-level channels.", numChans());
00397 
00398     // make sure options are exported for all channels:
00399     for (uint i = 0; i < this->numChans(); ++i) this->subChan(i)->exportOptions(MC_RECURSE);
00400   }
00401 }
00402 
00403 // ######################################################################
00404 const Image<float> RawVisualCortex::getVCOutput(const Image<PixRGB<byte> >&rgbin)
00405 {
00406 
00407   static SimTime time = SimTime::ZERO();
00408 
00409   time += SimTime::MSECS(10);
00410 
00411   const InputFrame ifr = InputFrame::fromRgb(&rgbin, time);
00412 
00413   this->input(ifr);
00414 
00415   return getOutput();
00416 }
00417 
00418 // ######################################################################
00419 Image<float> RawVisualCortex::getChannelOutputMap(const uint idx) const
00420 {
00421   nub::ref<ChannelBase> ch = subChan(idx);
00422 
00423   // Determine the weight for each subchan. This is the product of
00424   // the intrinsic weight of each subchan (which is set either
00425   // through the --vc-chans config string of RawVisualCortex for
00426   // top-level channels, or subject to static modifications by
00427   // using --load at the start of the entire model and setting the
00428   // NModelParam value associated with each channel), multiplied
00429   // by any possible extra top-down gain if we have a
00430   // ChannelFacetGainComplex:
00431   float w = float(this->getSubchanTotalWeight(*ch));
00432 
00433   if (itsUseOlderVersion.getVal()) w /= ch->numSubmaps();
00434 
00435   if (hasFacet<ChannelFacetGainComplex>())
00436     w *= getFacet<ChannelFacetGainComplex>()->getVal(idx);
00437 
00438   if (w == 0.0)
00439     {
00440       LDEBUG("%12s weight is 0.0 (%2u submaps) -- skipped",
00441              ch->tagName().c_str(),ch->numSubmaps());
00442       return Image<float>();
00443     }
00444 
00445   // get the raw channel output and compute its range (for debug msg):
00446   Image<float> chanOut = ch->getOutput();
00447 
00448   float mi1 = -1.0f, ma1 = -1.0f;
00449   if (MYLOGVERB >= LOG_DEBUG) getMinMax(chanOut, mi1, ma1);
00450 
00451   if (w != 1.0f) chanOut *= w;
00452 
00453   if (MYLOGVERB >= LOG_DEBUG)
00454     {
00455       float mi, ma; getMinMax(chanOut, mi, ma);
00456       LDEBUG("%12s weight is %.4f (%2u submaps); raw range is "
00457              "%.2g .. %.2g; weighted range is %.2g .. %.2g",
00458              ch->tagName().c_str(), w, ch->numSubmaps(), mi1,ma1,mi,ma);
00459     }
00460 
00461   return chanOut;
00462 }
00463 
00464 // ######################################################################
00465 Image<float> RawVisualCortex::postProcessOutputMap(const Image<float>& outmap)
00466 {
00467   // CAUTION: if you make major changes to the default behavior here,
00468   // make sure you check that this is compatible with the various
00469   // derivatives of RawVisualCortex (e.g., RawVisualCortexBeo, etc).
00470   Image<float> result = outmap;
00471 
00472   // let's apply a last maxNormalization to the map, but only if we
00473   // have two or more channels with non-zero weights, otherwise, we
00474   // just pass through:
00475   uint num_nzw = 0;
00476   for (uint i = 0; i < numChans(); ++i) if (getSubchanTotalWeight(i) > itsWeightThresh.getVal()) ++num_nzw;
00477 
00478   if (num_nzw > 1)
00479     {
00480       switch(itsNormType.getVal())
00481         {
00482         case VCXNORM_LANDMARK:
00483           result = maxNormalize(result, 0.0f, 2.0f, VCXNORM_NONE);
00484           break;
00485         case VCXNORM_SURPRISE:
00486           LFATAL("Surprise maxnorm must use RawVisualCortexSurprise");
00487           break;
00488         default:
00489           result = maxNormalize(result, 0.0f, 2.0f, itsNormType.getVal());
00490           LDEBUG("%s(%f .. %f)", maxNormTypeName(itsNormType.getVal()),
00491                  0.0f, 2.0f);
00492           break;
00493         }
00494     }
00495 
00496   return result;
00497 }
00498 
00499 // ######################################################################
00500 void RawVisualCortex::doInput(const InputFrame& inframe)
00501 {
00502   // optimization: if we have no channels, then do a quick return:
00503   if (this->numChans() == 0) return;
00504 
00505   // we will not process the input at all if itsLoadOutFrom is set,
00506   // since we will instead read our final output map from disk in
00507   // getOutput():
00508   if (itsLoadOutFrom.getVal().empty() == false)
00509     {
00510       LINFO("Not feeding channels since we will load output from MGZ file.");
00511       return;
00512     }
00513 
00514   // build our pyramid cache?
00515   rutz::mutex_lock_class lock;
00516   if (inframe.pyrCache().get() != 0
00517       && inframe.pyrCache()->gaussian5.beginSet(inframe.grayFloat(), &lock))
00518     {
00519       if (itsUseSpaceVariantBoundary.getVal())
00520         inframe.pyrCache()->gaussian5.endSet
00521           (inframe.grayFloat(),
00522            buildRadialPyrGaussian
00523            (inframe.grayFloat(), 0, itsLevelSpec.getVal().maxDepth()),
00524            &lock);
00525       else
00526         inframe.pyrCache()->gaussian5.endSet
00527           (inframe.grayFloat(),
00528            buildPyrGaussian
00529            (inframe.grayFloat(), 0, itsLevelSpec.getVal().maxDepth(), 5),
00530            &lock);
00531     }
00532 
00533   // process the channels:
00534   for (uint i = 0; i < this->numChans(); ++i)
00535     {
00536       // get a pointer to the channel of interest:
00537       nub::ref<ChannelBase> chan = this->subChan(i);
00538 
00539       // feed the input to the channel of interest:
00540       chan->input(inframe);
00541       LINFO("Input to %s channel %s ok.",
00542             featureHierarchyName(featureHierarchyLevel(chan->visualFeature())),
00543             chan->tagName().c_str());
00544     }
00545 }
00546 
00547 // ######################################################################
00548 Image<float> RawVisualCortex::combineOutputs()
00549 {
00550 GVX_TRACE(__PRETTY_FUNCTION__);
00551 
00552   Image<float> output;
00553 
00554   // are we reading our precomputed outputs from an MGZ file?
00555   if (itsLoadOutFrom.getVal().empty() == false)
00556     {
00557       LINFO("Bypassing RawVisualCortex output computations.");
00558       LINFO("Loading RawVisualCortex output from '%s'",
00559             itsLoadOutFrom.getVal().c_str());
00560       output = (itsOutputMgzIn->readFrame()).asFloat();
00561     }
00562   else
00563     {
00564       // ... OK, we have to recompute the output:
00565       for (uint i = 0; i < this->numChans(); ++i)
00566         {
00567           if (subChan(i)->outputAvailable() == false) continue;
00568 
00569           Image<float> chanOut = getChannelOutputMap(i);
00570 
00571           if (chanOut.initialized() == false) continue;
00572 
00573           // downSizeClean() gracefully does nothing if chanOut is
00574           // already the right size. (Eventually we might like to have a
00575           // way for RawVisualCortex to enforce a particular chanOut size
00576           // on its own, rather than just taking the size from the first
00577           // channel in the array.)
00578           if (output.initialized() == false)
00579             output = chanOut;
00580           else
00581             {
00582               // sum or take max across channels?
00583               Image<float> o = downSizeClean(chanOut, output.getDims());
00584               if (itsUseMax.getVal()) output = takeMax(output, o); else output += o;
00585             }
00586         }
00587     }
00588 
00589   // Ensure that we still return a valid image even if we have no channels
00590   // that has a valid output in the RawVisualCortex:
00591   if (output.initialized() == false)
00592     {
00593       int sml = itsLevelSpec.getVal().mapLevel();
00594       output = Image<float>(this->getInputDims().w() >> sml,
00595                             this->getInputDims().h() >> sml, ZEROS);
00596     }
00597 
00598   if (MYLOGVERB >= LOG_DEBUG)
00599     {
00600       float mi, ma; getMinMax(output, mi, ma);
00601       LDEBUG("Raw output range is [%f .. %f]", mi, ma);
00602     }
00603 
00604   // before we finalize the output, do we want to save it to an MGZ file?
00605   if (itsSaveOutTo.getVal().empty() == false)
00606     {
00607       LINFO("Saving raw unnormalized RawVisualCortex output to '%s'",
00608             itsSaveOutTo.getVal().c_str());
00609       itsOutputMgzOut->writeFrame(GenericFrame(output, FLOAT_NORM_PRESERVE));
00610     }
00611 
00612   // post-process the output in a manner than may depend on which
00613   // variant of RawVisualCortex we may embody:
00614   output = postProcessOutputMap(output);
00615 
00616   // output is now typically in the (0.0..8.0) range;
00617   // typical images are in (0..4) range; we want input current in nA
00618   output *= itsOutputFactor.getVal();
00619   float mi, ma; getMinMax(output, mi, ma);
00620   LINFO("Salmap input range is [%f .. %f] nA", mi * 1.0e9F, ma * 1.0e9F);
00621 
00622   // add a tiny background activity to ensure that the entire image
00623   // will be visited:
00624   if (itsUseRandom.getVal())
00625     {
00626       LINFO("Adding random background noise to output.");
00627       inplaceAddBGnoise(output, itsNoise.getVal());
00628       output += itsNoise.getVal() * 0.01F;
00629     }
00630 
00631 
00632 
00633   LINFO("Computed RawVisualCortex output.");
00634   
00635   // uint width  = output.getWidth();
00636   // uint height = output.getHeight();
00637   // LINFO("w: %d h: %d", width, height);
00638   
00639   // if(itsWin.is_invalid())
00640   //   itsWin.reset(new XWinManaged(Dims(width,height), 0, 0, "rVC"));
00641   // itsWin->setDims(Dims(width*16, height*16));
00642   // itsWin->drawImage(zoomXY(output,16),0,0);
00643   // Raster::waitForKey();
00644 
00645  // std::string folder("/lab/tmpib/u/siagian/neuroscience/Data/FOE/"
00646  //                     "DARPA_nv2_2011/Germany_K_CIOFM/");
00647  // Image<float> img = zoomXY(output,16);
00648  // inplaceNormalize(img, 0.0F, 255.0F);
00649  // Image<byte>  res(img);
00650  // Raster::WriteRGB(res, sformat("%sSalMap_CIOFM_%06d.ppm", 
00651  //                               folder.c_str(), itsFrame));
00652 
00653    // itsFrame++;
00654 
00655   return output;
00656 }
00657 
00658 // ######################################################################
00659 void RawVisualCortex::saveResults(const nub::ref<FrameOstream>& ofs)
00660 {
00661 GVX_TRACE(__PRETTY_FUNCTION__);
00662   // save our own output:
00663   if (itsSaveOutput.getVal())
00664     ofs->writeFloat(getOutput(), FLOAT_NORM_PRESERVE, "VCO",
00665                     FrameInfo("visual cortex output "
00666                               "(input to saliency map)", SRC_POS));
00667 
00668   // save our channel outputs:
00669   for (uint i = 0; i < numChans(); ++i) subChan(i)->saveResults(ofs);
00670 }
00671 
00672 // ######################################################################
00673 /* So things look consistent in everyone's emacs... */
00674 /* Local Variables: */
00675 /* indent-tabs-mode: nil */
00676 /* End: */
Generated on Sun May 8 08:40:22 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3