00001 /*!@file Channels/SingleOpponentChannel.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/SingleOpponentChannel.C $ 00035 // $Id: SingleOpponentChannel.C 10746 2009-02-03 07:09:00Z itti $ 00036 // 00037 00038 #ifndef CHANNELS_SINGLEOPPONENTCHANNEL_C_DEFINED 00039 #define CHANNELS_SINGLEOPPONENTCHANNEL_C_DEFINED 00040 00041 #include "Channels/SingleOpponentChannel.H" 00042 00043 #include "Image/ImageSetOps.H" 00044 #include "Image/PyramidOps.H" 00045 #include "rutz/trace.h" 00046 00047 // ###################################################################### 00048 SingleOpponentChannel:: 00049 SingleOpponentChannel(OptionManager& mgr, 00050 const std::string& descrName, 00051 const std::string& tagName, 00052 const VisualFeature vs, 00053 rutz::shared_ptr<PyrBuilder<float> > pyr) 00054 : 00055 SingleChannel(mgr, descrName, tagName, vs, pyr), 00056 itsPq2() 00057 {} 00058 00059 // ###################################################################### 00060 void SingleOpponentChannel::reset1() 00061 { 00062 itsPq2.clear(); 00063 SingleChannel::reset1(); 00064 } 00065 00066 // ###################################################################### 00067 Image<float> SingleOpponentChannel:: 00068 centerSurround(const uint cntrlev, const uint surrlev) const 00069 { 00070 GVX_TRACE(__PRETTY_FUNCTION__); 00071 ASSERT(itsLevelSpec.getVal().csOK(cntrlev, surrlev)); 00072 if (this->hasPyramid() == false) LFATAL("I have no input pyramid yet!"); 00073 if (itsPq2.empty()) LFATAL("I have no second input pyramid yet!"); 00074 00075 // in the single-opponent version, we do pyr[cntrlev] - pyr2[surrlev] 00076 00077 // do basic center-surround for the front (latest) pyramid in queue: 00078 const ImageSet<float>& cpyr = this->pyramid(0); 00079 const ImageSet<float>& spyr = itsPq2.front().pyr; 00080 const double t = this->pyramidTime(0).secs(); 00081 00082 // compute single-opponent center-surround: 00083 Image<float> cs = 00084 ::centerSurroundSingleOpponent(cpyr, spyr, cntrlev, surrlev, 00085 itsTakeAbs.getVal(), 00086 &(this->clipPyramid())); 00087 00088 // do additional processing with other pyramids in queue: 00089 for (uint i = 1; i < itsPq2.size(); ++i) 00090 { 00091 const ImageSet<float>& cpyr2 = this->pyramid(i); 00092 const ImageSet<float>& spyr2 = itsPq2[i].pyr; 00093 const double t2 = this->pyramidTime(i).secs(); 00094 00095 // compute a decay factor based on how old the second pyramid is 00096 // compared to the latest one: 00097 float fac = exp( (t2 - t) * itsTimeDecay.getVal()); 00098 cs += 00099 ::centerSurroundDiffSingleOpponent(cpyr, cpyr2, spyr, spyr2, 00100 cntrlev, surrlev, 00101 itsTakeAbs.getVal(), 00102 &(this->clipPyramid())) 00103 * fac; 00104 } 00105 return cs; 00106 } 00107 00108 // ###################################################################### 00109 void SingleOpponentChannel:: 00110 centerSurround(const uint cntrlev, const uint surrlev, 00111 Image<float>& pos, Image<float>& neg) const 00112 { 00113 GVX_TRACE(__PRETTY_FUNCTION__); 00114 ASSERT(itsLevelSpec.getVal().csOK(cntrlev, surrlev)); 00115 if (this->hasPyramid() == false) LFATAL("I have no output yet!"); 00116 if (itsPq2.empty()) LFATAL("I have no second input pyramid yet!"); 00117 00118 // do basic center-surround for the front (latest) pyramid in queue: 00119 const ImageSet<float>& cpyr = this->pyramid(0); 00120 const ImageSet<float>& spyr = itsPq2.front().pyr; 00121 const double t = this->pyramidTime(0).secs(); 00122 00123 // compute center-surround: 00124 ::centerSurroundSingleOpponent(cpyr, spyr, cntrlev, surrlev, 00125 pos, neg, &(this->clipPyramid())); 00126 00127 // do additional processing with other pyramids in queue: 00128 for (uint i = 1; i < itsPq2.size(); ++i) 00129 { 00130 const ImageSet<float>& cpyr2 = this->pyramid(i); 00131 const ImageSet<float>& spyr2 = itsPq2[i].pyr; 00132 double t2 = this->pyramidTime(i).secs(); 00133 00134 // compute a decay factor based on how old the second pyramid is 00135 // compared to the latest one: 00136 float fac = exp( (t2 - t) * itsTimeDecay.getVal()); 00137 Image<float> pos2, neg2; 00138 ::centerSurroundDiffSingleOpponent(cpyr, cpyr2, spyr, spyr2, 00139 cntrlev, surrlev, 00140 pos2, neg2, 00141 &(this->clipPyramid())); 00142 pos += pos2 * fac; 00143 neg += neg2 * fac; 00144 } 00145 } 00146 00147 // ###################################################################### 00148 void SingleOpponentChannel::getFeatures(const Point2D<int>& locn, 00149 std::vector<float>& mean) const 00150 { 00151 GVX_TRACE(__PRETTY_FUNCTION__); 00152 if (!this->outputAvailable()) 00153 { 00154 CLDEBUG("I have no input pyramids yet -- RETURNING ZEROS"); 00155 for (uint idx = 0; idx < numSubmaps(); idx ++) mean.push_back(0.0F); 00156 return; 00157 } 00158 00159 // The coordinates we receive are at the scale of the original 00160 // image, and we will need to rescale them to the size of the 00161 // various submaps we read from. The first image in our first 00162 // pyramid has the dims of the input: 00163 const ImageSet<float>& pyr = this->pyramid(0); 00164 const ImageSet<float>& pyr2 = itsPq2.front().pyr; 00165 const Dims indims = this->getInputDims(); 00166 00167 for (uint idx = 0; idx < numSubmaps(); idx ++) 00168 { 00169 // get center and surround scales for this submap index: 00170 uint clev = 0, slev = 0; 00171 itsLevelSpec.getVal().indexToCS(idx, clev, slev); 00172 00173 // read center value with bilinear interpolation: 00174 ASSERT(pyr[clev].initialized()); 00175 const float cval = pyr[clev].getValInterpScaled(locn, indims); 00176 00177 // read surround value with bilinear interpolation: 00178 ASSERT(pyr2[slev].initialized()); 00179 const float sval = pyr2[slev].getValInterpScaled(locn, indims); 00180 00181 // compute center - surround and take absolute value if our 00182 // channel wants that: 00183 float val = cval - sval; if (itsTakeAbs.getVal()) val = fabs(val); 00184 00185 // store the submap value at the chosen location: 00186 mean.push_back(val); 00187 } 00188 } 00189 00190 // ###################################################################### 00191 void SingleOpponentChannel:: 00192 getFeaturesBatch(std::vector<Point2D<int>*> *locn, 00193 std::vector<std::vector<float> > *mean, 00194 int *count) const 00195 { 00196 GVX_TRACE(__PRETTY_FUNCTION__); 00197 if (!this->outputAvailable()) 00198 { 00199 CLDEBUG("I have no input pyramids yet -- RETURNING ZEROS"); 00200 for (uint idx = 0; idx < numSubmaps(); idx ++) 00201 { 00202 std::vector<std::vector<float> >::iterator imean = mean->begin(); 00203 for(int i = 0; i < *count; i++, ++imean) 00204 imean->push_back(0.0); 00205 } 00206 return; 00207 } 00208 00209 // The coordinates we receive are at the scale of the original 00210 // image, and we will need to rescale them to the size of the 00211 // various submaps we read from. The first image in our first 00212 // pyramid has the dims of the input: 00213 const ImageSet<float>& pyr = this->pyramid(0); 00214 const ImageSet<float>& pyr2 = itsPq2.front().pyr; 00215 const Dims indims = this->getInputDims(); 00216 uint sm = this->numSubmaps(); 00217 for (uint idx = 0; idx < sm; ++idx) 00218 { 00219 // get center and surround scales for this submap index: 00220 uint clev = 0, slev = 0; 00221 itsLevelSpec.getVal().indexToCS(idx, clev, slev); 00222 std::vector<Point2D<int>*>::iterator ilocn = locn->begin(); 00223 std::vector<std::vector<float> >::iterator imean = mean->begin(); 00224 00225 for(int i = 0; i < *count; ++i, ++ilocn, ++imean) 00226 { 00227 // read center value with bilinear interpolation: 00228 ASSERT(pyr[clev].initialized()); 00229 const float cval = pyr[clev].getValInterpScaled(**ilocn, indims); 00230 00231 // read surround value with bilinear interpolation: 00232 ASSERT(pyr2[slev].initialized()); 00233 const float sval = pyr2[slev].getValInterpScaled(**ilocn, indims); 00234 00235 const float val = cval - sval; 00236 00237 // store the submap value at the chosen location: 00238 imean->push_back(itsTakeAbs.getVal() 00239 ? fabs(val) 00240 : val); 00241 } 00242 } 00243 } 00244 00245 // ###################################################################### 00246 void SingleOpponentChannel:: 00247 singleOpponentInput(const Dims& dims, 00248 const ImageSet<float>& centerPyr, 00249 const ImageSet<float>& surroundPyr, 00250 const SimTime& t, 00251 const Image<byte>& clipMask) 00252 { 00253 GVX_TRACE(__PRETTY_FUNCTION__); 00254 this->killCaches(); 00255 this->setClipPyramid(clipMask); 00256 this->storePyramid(centerPyr, t); 00257 this->storePyramid2(surroundPyr, t); 00258 this->setInputDims(dims); 00259 } 00260 00261 // ###################################################################### 00262 void SingleOpponentChannel::storePyramid2(const ImageSet<float>& p, 00263 const SimTime& t) 00264 { 00265 GVX_TRACE(__PRETTY_FUNCTION__); 00266 00267 // check that inputs are not coming in too fast, as we may otherwise 00268 // run into trouble with those channels that care about the 00269 // inter-frame delay: 00270 if (itsPq2.size() && (t - itsPq2.front().t).secs() < 0.002F) // 500fps is the max 00271 CLFATAL("Inputs coming in too fast! -- BAILING OUT"); 00272 00273 // load our pyramid into our front pyramid: 00274 itsPq2.push_front(TPyr(p, t)); 00275 00276 // truncate the pyramid queue if necessary: 00277 while(int(itsPq2.size()) > itsQlen.getVal()) itsPq2.pop_back(); 00278 00279 // We only want dyadic pyramids here: 00280 ASSERT(isDyadic(itsPq2.front().pyr.subSet 00281 (itsLevelSpec.getVal().levMin(), 00282 itsLevelSpec.getVal().maxDepth()))); 00283 } 00284 00285 // ###################################################################### 00286 /* So things look consistent in everyone's emacs... */ 00287 /* Local Variables: */ 00288 /* mode: c++ */ 00289 /* indent-tabs-mode: nil */ 00290 /* End: */ 00291 00292 #endif // CHANNELS_SINGLEOPPONENTCHANNEL_C_DEFINED