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: */