00001 /*!@file Channels/IntegerSimpleChannel.C IntegerSimpleChannel is like SingleChannel, but avoids floating-point arithmetic */ 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/IntegerSimpleChannel.C $ 00035 // $Id: IntegerSimpleChannel.C 10983 2009-03-05 07:19:14Z itti $ 00036 // 00037 00038 #ifndef CHANNELS_INTEGERSIMPLECHANNEL_C_DEFINED 00039 #define CHANNELS_INTEGERSIMPLECHANNEL_C_DEFINED 00040 00041 #include "Channels/IntegerSimpleChannel.H" 00042 00043 #include "Channels/ChannelOpts.H" 00044 #include "Component/GlobalOpts.H" 00045 #include "Component/ModelOptionDef.H" 00046 #include "Image/Image.H" 00047 #include "Image/ImageSetOps.H" 00048 #include "Image/IntegerMathOps.H" 00049 #include "Image/MathOps.H" // for binaryReverse() 00050 #include "Image/PyrBuilder.H" 00051 #include "Transport/FrameInfo.H" 00052 #include "Transport/FrameOstream.H" 00053 #include "Util/Assert.H" 00054 #include "Util/log.H" 00055 #include "Util/sformat.H" 00056 #include "rutz/trace.h" 00057 00058 #include "Image/CutPaste.H" 00059 #include <cmath> 00060 #include <limits> 00061 00062 const int INTMAXNORMMIN = 0; 00063 const int INTMAXNORMMAX = 32768; 00064 00065 // ###################################################################### 00066 IntegerSimpleChannel:: 00067 IntegerSimpleChannel(OptionManager& mgr, const std::string& descrName, 00068 const std::string& tag, 00069 const VisualFeature vs, 00070 rutz::shared_ptr<PyrBuilder<int> > pbuild, 00071 nub::ref<IntegerMathEngine> eng) : 00072 IntegerChannel(mgr, descrName, tag, vs, eng), 00073 itsTakeAbs("IntegerSimpleChannelTakeAbs", this, false), 00074 itsNormalizeOutput("IntegerSimpleChannelNormalizeOutput", this, false), 00075 itsScaleNoiseToMax("IntegerSimpleChannelScaleNoiseToMax", this, false), 00076 itsLowThresh("IntegerSimpleChannelLowThresh", this, 0), 00077 itsRectifyPyramid("IntegerSimpleChannelRectifyPyramid", this, false), 00078 itsUseRandom(&OPT_UseRandom, this), // see Component/ModelManager.{H,C} 00079 itsLevelSpec(&OPT_LevelSpec, this), // see Channels/ChannelOpts.{H,C} 00080 itsNormType(&OPT_MaxNormType, this), // see Channels/ChannelOpts.{H,C} 00081 itsQlen(&OPT_SingleChannelQueueLen, this), // see Channels/ChannelOpts.{H,C} 00082 itsUseOlderVersion(&OPT_UseOlderVersion, this), // see Channels/ChannelOpts.{H,C} 00083 itsSaveRawMaps(&OPT_SingleChannelSaveRawMaps, this), 00084 itsSaveFeatureMaps(&OPT_SingleChannelSaveFeatureMaps, this), 00085 itsSaveOutputMap(&OPT_SingleChannelSaveOutputMap, this), 00086 itsOutputRangeMin(&OPT_IntChannelOutputRangeMin, this), 00087 itsOutputRangeMax(&OPT_IntChannelOutputRangeMax, this), 00088 itsPyr(), 00089 itsT(SimTime::ZERO()), 00090 itsOutputCache(), 00091 itsSubmapCache(NULL), 00092 itsPyrBuilder(pbuild), 00093 itsClipPyr() 00094 { 00095 GVX_TRACE(__PRETTY_FUNCTION__); 00096 } 00097 00098 // ###################################################################### 00099 IntegerSimpleChannel::~IntegerSimpleChannel() 00100 { 00101 GVX_TRACE(__PRETTY_FUNCTION__); 00102 } 00103 00104 // ###################################################################### 00105 void IntegerSimpleChannel::start1() 00106 { 00107 GVX_TRACE(__PRETTY_FUNCTION__); 00108 const int maxind = itsLevelSpec.getVal().maxIndex(); 00109 itsSubmapCache = new Image<int>[maxind]; 00110 00111 // in the new version, leave our output range open rather than 00112 // forcing it to a given range of values: 00113 if (itsUseOlderVersion.getVal() == false) 00114 { 00115 itsOutputRangeMin.setVal(0); 00116 itsOutputRangeMax.setVal(0); 00117 } 00118 00119 // in the older version, we used to set the map range as we would 00120 // also apply spatial competition for salience to the output map, 00121 // only if using the MAXNORM type of competition, and otherwise we 00122 // would not touch the range: 00123 if (itsUseOlderVersion.getVal() && itsNormType.getVal() != VCXNORM_MAXNORM) 00124 { 00125 itsOutputRangeMin.setVal(0); 00126 itsOutputRangeMax.setVal(0); 00127 } 00128 00129 if (itsPyrBuilder.get() == 0) 00130 LFATAL("Oops! I have no PyrBuilder!"); 00131 } 00132 00133 // ###################################################################### 00134 void IntegerSimpleChannel::stop2() 00135 { 00136 GVX_TRACE(__PRETTY_FUNCTION__); 00137 delete [] itsSubmapCache; itsSubmapCache = 0; 00138 } 00139 00140 // ###################################################################### 00141 void IntegerSimpleChannel::reset1() 00142 { 00143 GVX_TRACE(__PRETTY_FUNCTION__); 00144 // reset some stuff for IntegerSimpleChannel 00145 itsPyrBuilder->reset(); 00146 itsPyr.clear(); 00147 itsClipPyr.clear(); 00148 killCaches(); 00149 00150 // propagate to our base class: 00151 IntegerChannel::reset1(); 00152 } 00153 00154 // ###################################################################### 00155 void IntegerSimpleChannel::readFrom(const ParamMap& pmap) 00156 { 00157 GVX_TRACE(__PRETTY_FUNCTION__); 00158 00159 if (!this->started()) 00160 CLFATAL("must be start()-ed before using readFrom()"); 00161 00162 IntegerChannel::readFrom(pmap); 00163 ChannelFacetMap::readFacetsFrom(pmap); 00164 } 00165 00166 // ###################################################################### 00167 void IntegerSimpleChannel::writeTo(ParamMap& pmap) const 00168 { 00169 GVX_TRACE(__PRETTY_FUNCTION__); 00170 00171 if (!this->started()) 00172 CLFATAL("must be start()-ed before using readFrom()"); 00173 00174 IntegerChannel::writeTo(pmap); 00175 ChannelFacetMap::writeFacetsTo(pmap); 00176 } 00177 00178 // ###################################################################### 00179 bool IntegerSimpleChannel::outputAvailable() const 00180 { 00181 GVX_TRACE(__PRETTY_FUNCTION__); 00182 00183 return (itsPyr.isEmpty() == false) || itsOutputCache.initialized(); 00184 } 00185 00186 // ###################################################################### 00187 Dims IntegerSimpleChannel::getMapDims() const 00188 { 00189 GVX_TRACE(__PRETTY_FUNCTION__); 00190 const int lev = itsLevelSpec.getVal().mapLevel(); 00191 00192 return Dims(this->getInputDims().w() / (1 << lev), 00193 this->getInputDims().h() / (1 << lev)); 00194 } 00195 00196 // ###################################################################### 00197 uint IntegerSimpleChannel::numSubmaps() const 00198 { 00199 GVX_TRACE(__PRETTY_FUNCTION__); 00200 return itsLevelSpec.getVal().maxIndex(); 00201 } 00202 00203 // ###################################################################### 00204 Image<int> IntegerSimpleChannel::getSubmapInt(const uint i) const 00205 { 00206 GVX_TRACE(__PRETTY_FUNCTION__); 00207 // cache the results for later use if we don't already have cached it: 00208 if (itsSubmapCache[i].initialized() == false) 00209 { 00210 itsSubmapCache[i] = this->getRawCSmapInt(i); 00211 uint clev = 0, slev = 0; 00212 itsLevelSpec.getVal().indexToCS(i, clev, slev); 00213 00214 00215 // resize itsSubmapCache[i] to fixed scale if necessary: 00216 if (itsSubmapCache[i].getWidth() > getMapDims().w()) 00217 itsSubmapCache[i] = intgDownSize(itsSubmapCache[i], getMapDims(), 00218 9, this->getImath()); 00219 else if (itsSubmapCache[i].getWidth() < getMapDims().w()) 00220 itsSubmapCache[i] = intgRescale(itsSubmapCache[i], getMapDims()); 00221 00222 // add noise if wanted: 00223 if (itsUseRandom.getVal()) 00224 { 00225 // check if we want to scale noise to maximum of image 00226 if (itsScaleNoiseToMax.getVal()) 00227 { 00228 int fMin, fMax; getMinMax(itsSubmapCache[i], fMin, fMax); 00229 intgInplaceAddBGnoise(itsSubmapCache[i], fMax); 00230 } 00231 else 00232 intgInplaceAddBGnoise(itsSubmapCache[i], 00233 (1 << this->getImath()->nbits)); 00234 } 00235 00236 // if using the older version: first normalize the submap to a 00237 // fixed dynamic range and then apply spatial competition for 00238 // salience to the submap; otherwise, just apply competition: 00239 if (itsUseOlderVersion.getVal()) 00240 { 00241 LDEBUG("%s(%d,%d): applying %s(%f .. %f)", tagName().c_str(), clev, slev, 00242 maxNormTypeName(itsNormType.getVal()), MAXNORMMIN, MAXNORMMAX); 00243 itsSubmapCache[i] = 00244 intgMaxNormalize(itsSubmapCache[i], INTMAXNORMMIN, INTMAXNORMMAX, 00245 itsNormType.getVal()); 00246 } 00247 else 00248 { 00249 LDEBUG("%s(%d,%d): applying %s(0.0 .. 0.0)", tagName().c_str(), 00250 clev, slev, maxNormTypeName(itsNormType.getVal())); 00251 itsSubmapCache[i] = 00252 intgMaxNormalize(itsSubmapCache[i], 0, 0, 00253 itsNormType.getVal()); 00254 } 00255 00256 // print some debug info if in debug mode: 00257 if (MYLOGVERB >= LOG_DEBUG) 00258 { 00259 int mi, ma; getMinMax(itsSubmapCache[i], mi, ma); 00260 LDEBUG("%s(%d,%d): final range [%d .. %d]", 00261 tagName().c_str(), clev, slev, mi, ma); 00262 } 00263 } 00264 00265 return itsSubmapCache[i]; 00266 } 00267 00268 // ###################################################################### 00269 Image<int> IntegerSimpleChannel::getRawCSmapInt(const uint idx) const 00270 { 00271 GVX_TRACE(__PRETTY_FUNCTION__); 00272 ASSERT(itsLevelSpec.getVal().indexOK(idx)); 00273 if (itsPyr.isEmpty()) CLFATAL("I have no input pyramid yet!"); 00274 00275 // otherwise, compute center-surround map: 00276 uint clev = 0, slev = 0; 00277 itsLevelSpec.getVal().indexToCS(idx, clev, slev); 00278 00279 // submap is computed from a center-surround difference: 00280 const Image<int> submap = 00281 intgCenterSurround(itsPyr, clev, slev, itsTakeAbs.getVal(), &itsClipPyr); 00282 00283 // print some debug info if in debug mode: 00284 if (MYLOGVERB >= LOG_DEBUG) 00285 { 00286 int mi, ma; getMinMax(submap, mi, ma); 00287 LDEBUG("%s(%d,%d): raw range [%d .. %d]", tagName().c_str(), 00288 clev, slev, mi, ma); 00289 } 00290 00291 return submap; 00292 } 00293 00294 // ###################################################################### 00295 std::string IntegerSimpleChannel::getSubmapName(const uint idx) const 00296 { 00297 GVX_TRACE(__PRETTY_FUNCTION__); 00298 ASSERT( itsLevelSpec.getVal().indexOK(idx) ); 00299 00300 uint clev = 0, slev = 0; 00301 itsLevelSpec.getVal().indexToCS(idx, clev, slev); 00302 00303 return sformat("%s lev: %d delta: %d", 00304 descriptiveName().c_str(), clev, slev-clev); 00305 } 00306 00307 // ###################################################################### 00308 std::string IntegerSimpleChannel::getSubmapNameShort(const uint idx) const 00309 { 00310 GVX_TRACE(__PRETTY_FUNCTION__); 00311 ASSERT( itsLevelSpec.getVal().indexOK(idx) ); 00312 00313 uint clev = 0, slev = 0; 00314 itsLevelSpec.getVal().indexToCS(idx, clev, slev); 00315 00316 return sformat("%s(%d,%d)", tagName().c_str(), clev, slev); 00317 } 00318 00319 // ###################################################################### 00320 void IntegerSimpleChannel::getFeatures(const Point2D<int>& locn, 00321 std::vector<float>& mean) const 00322 { 00323 CLFATAL("not implemented"); 00324 } 00325 00326 // ###################################################################### 00327 void IntegerSimpleChannel::getFeaturesBatch(std::vector<Point2D<int>*> *locn, 00328 std::vector<std::vector<float> > *mean, 00329 int *count) const 00330 { 00331 CLFATAL("not implemented"); 00332 } 00333 00334 // ###################################################################### 00335 void IntegerSimpleChannel::killCaches() 00336 { 00337 GVX_TRACE(__PRETTY_FUNCTION__); 00338 IntegerChannel::killCaches(); // call our base class's implementation 00339 00340 itsOutputCache.freeMem(); 00341 00342 // our caches exist only when we are in started() state: 00343 if (started()) 00344 for (uint i = 0; i < itsLevelSpec.getVal().maxIndex(); ++i) 00345 itsSubmapCache[i] = Image<int>(); 00346 } 00347 00348 // ###################################################################### 00349 void IntegerSimpleChannel::doInputInt(const IntegerInput& inp, 00350 const SimTime& t, 00351 PyramidCache<int>* cache, 00352 const Image<byte>& clipMask) 00353 { 00354 ASSERT(inp.grayInt().initialized()); 00355 00356 // if necessary create pyramid of clipping masks 00357 if (clipMask.initialized()) 00358 { 00359 itsClipPyr = intgBuildPyrGaussian 00360 (intgScaleFromByte(&clipMask, this->getImath()->nbits), 00361 itsLevelSpec.getVal().maxDepth(), 9, 00362 this->getImath()); 00363 intgDoLowThresh(itsClipPyr, 255, 0); 00364 } 00365 else 00366 itsClipPyr.clear(); 00367 00368 // Compute our pyramid: 00369 itsPyr = itsPyrBuilder-> 00370 build(inp.grayInt(), 00371 this->getMinPyrLevel(), this->getMaxPyrLevel(), cache); 00372 00373 // Eliminate small values if and/or rectify the pyramid desired: 00374 if (itsLowThresh.getVal() > 0) 00375 { 00376 if (itsRectifyPyramid.getVal()) 00377 intgDoLowThresh(itsPyr, itsLowThresh.getVal()); 00378 else 00379 intgDoLowThreshAbs(itsPyr, itsLowThresh.getVal()); 00380 } 00381 else if (itsRectifyPyramid.getVal()) 00382 intgDoRectify(itsPyr); 00383 00384 itsT = t; 00385 00386 // We only want dyadic pyramids here: 00387 ASSERT(isDyadic(itsPyr.subSet 00388 (this->getMinPyrLevel(), this->getMaxPyrLevel()))); 00389 } 00390 00391 // ###################################################################### 00392 LevelSpec IntegerSimpleChannel::getLevelSpec() const 00393 { 00394 GVX_TRACE(__PRETTY_FUNCTION__); 00395 return itsLevelSpec.getVal(); 00396 } 00397 00398 // ###################################################################### 00399 Image<int> IntegerSimpleChannel::getOutputInt() 00400 { 00401 GVX_TRACE(__PRETTY_FUNCTION__); 00402 00403 if (!this->hasInput()) 00404 // if you think this LFATAL() has been triggered incorrectly, then 00405 // first make sure that somebody has called setInputDims() 00406 CLFATAL("Oops! can't get output -- I don't even have any input yet"); 00407 00408 if (!this->outputAvailable()) 00409 { 00410 // it's possible that we have input but don't yet have output in 00411 // the case of a channel that requires several input frames 00412 // before it can start generating output (such as a flicker or 00413 // motion channel); in that case we just return an empty image 00414 // of the appropriate size 00415 00416 LERROR("No %s channel yet! -- IGNORING.", this->tagName().c_str()); 00417 00418 return Image<int>(this->getMapDims(), ZEROS); 00419 } 00420 00421 if (!itsOutputCache.initialized()) 00422 { 00423 itsOutputCache = Image<int>(getMapDims(), ZEROS); 00424 00425 // compute max-normalized weighted sum of center-surround at all levels: 00426 for (uint idx = 0; idx < itsLevelSpec.getVal().maxIndex(); ++idx) 00427 { 00428 const Image<int> submap = getSubmapInt(idx); // get the unweighted map 00429 00430 // add submap to our sum 00431 itsOutputCache += (submap / int(itsLevelSpec.getVal().maxIndex())); 00432 00433 if (MYLOGVERB >= LOG_DEBUG) 00434 { 00435 uint clev = 0, slev = 0; 00436 itsLevelSpec.getVal().indexToCS(idx, clev, slev); 00437 LDEBUG("%s(%d,%d): weight %f", tagName().c_str(), clev, slev, 1.0f); 00438 } 00439 } 00440 00441 00442 // apply max-normalization on the output as needed: 00443 if (itsNormalizeOutput.getVal()) 00444 { 00445 LDEBUG("%s: Normalizing output: %s(%d .. %d)", tagName().c_str(), 00446 maxNormTypeName(itsNormType.getVal()), itsOutputRangeMin.getVal(), 00447 itsOutputRangeMax.getVal()); 00448 00449 itsOutputCache = 00450 intgMaxNormalize(itsOutputCache, itsOutputRangeMin.getVal(), 00451 itsOutputRangeMax.getVal(), itsNormType.getVal()); 00452 } 00453 00454 // print some debug info if in debug mode: 00455 if (MYLOGVERB >= LOG_DEBUG) 00456 { 00457 int mi, ma; getMinMax(itsOutputCache, mi, ma); 00458 LDEBUG("%s: final range [%d .. %d]", tagName().c_str(), mi, ma); 00459 } 00460 00461 LINFO("Computed %s Conspicuity Map", descriptiveName().c_str()); 00462 } 00463 00464 return itsOutputCache; 00465 } 00466 00467 // ###################################################################### 00468 uint IntegerSimpleChannel::csToIndex(const uint centerlev, 00469 const uint surroundlev) const 00470 { 00471 GVX_TRACE(__PRETTY_FUNCTION__); 00472 return itsLevelSpec.getVal().csToIndex(centerlev, surroundlev); 00473 } 00474 00475 // ###################################################################### 00476 void IntegerSimpleChannel::saveResults(const nub::ref<FrameOstream>& ofs) 00477 { 00478 GVX_TRACE(__PRETTY_FUNCTION__); 00479 00480 if (itsPyr.isEmpty() == false) 00481 { 00482 // save raw pyramid levels? 00483 if (itsSaveRawMaps.getVal()) { 00484 for (uint i = 0; i < itsPyr.size(); i ++) 00485 ofs->writeFloat(Image<float>(itsPyr[i]), FLOAT_NORM_0_255, 00486 sformat("ISR%s-%d-", tagName().c_str(),i), 00487 FrameInfo(sformat("%s IntegerSimpleChannel raw map (%u of %u)", 00488 this->descriptiveName().c_str(), 00489 i, itsPyr.size()), 00490 SRC_POS)); 00491 } 00492 00493 // save center-surround feature submaps? 00494 if (itsSaveFeatureMaps.getVal()) 00495 for (uint i = 0; i < numSubmaps(); i ++) { 00496 uint clev = 0, slev = 0; 00497 itsLevelSpec.getVal().indexToCS(i, clev, slev); 00498 ofs->writeFloat(Image<float>(getSubmapInt(i)), 00499 FLOAT_NORM_0_255, 00500 sformat("ISF%s-%d-%d-", tagName().c_str(),clev, slev), 00501 FrameInfo(sformat("%s IntegerSimpleChannel center-surround map (c=%u s=%u)", 00502 this->descriptiveName().c_str(), 00503 clev, slev), 00504 SRC_POS)); 00505 } 00506 } 00507 00508 // save output map? 00509 if (itsSaveOutputMap.getVal()) 00510 ofs->writeFloat(getOutput(), FLOAT_NORM_0_255, 00511 sformat("ISO%s-", tagName().c_str()), 00512 FrameInfo(sformat("%s IntegerSimpleChannel output", 00513 this->descriptiveName().c_str()), 00514 SRC_POS)); 00515 } 00516 00517 // ###################################################################### 00518 void IntegerSimpleChannel::setPyrBuilder(rutz::shared_ptr<PyrBuilder<int> > pbuild) 00519 { 00520 GVX_TRACE(__PRETTY_FUNCTION__); 00521 00522 ASSERT(pbuild.get() != 0); 00523 this->killCaches(); 00524 itsPyrBuilder = pbuild; 00525 } 00526 00527 // ###################################################################### 00528 /* So things look consistent in everyone's emacs... */ 00529 /* Local Variables: */ 00530 /* mode: c++ */ 00531 /* indent-tabs-mode: nil */ 00532 /* End: */ 00533 00534 #endif // CHANNELS_INTEGERSIMPLECHANNEL_C_DEFINED