00001 /*!@file Channels/DirectFeedChannel.C the source for DirectFeedChannel and 00002 MultiDirectFeedChannel */ 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2002 // 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: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Channels/DirectFeedChannel.C $ 00035 // $Id: DirectFeedChannel.C 10746 2009-02-03 07:09:00Z itti $ 00036 // 00037 00038 #include "Channels/DirectFeedChannel.H" 00039 00040 #include "Channels/ChannelOpts.H" 00041 #include "Component/OptionManager.H" 00042 #include "Image/ShapeOps.H" 00043 #include "Util/Assert.H" 00044 #include "Util/MathFunctions.H" 00045 #include "Util/StringConversions.H" 00046 #include "Util/sformat.H" 00047 00048 // ###################################################################### 00049 DirectFeedChannel::DirectFeedChannel(OptionManager& mgr, int id) 00050 : ChannelBase(mgr, "DirectFeed", "DirectFeed", DIRECTFEED), 00051 itsMapLevel(&OPT_MapLevel, this), 00052 itsNormType(&OPT_MaxNormType, this), 00053 itsUseOlderVersion(&OPT_UseOlderVersion, this), // see Channels/ChannelOpts.{H,C} 00054 itsOutputRangeMin(&OPT_ChannelOutputRangeMin, this), 00055 itsOutputRangeMax(&OPT_ChannelOutputRangeMax, this) 00056 { 00057 if (id >= 0) 00058 { 00059 std::string name("DirectFeed_No_"); 00060 name += toStr(id); 00061 setTagName(name); 00062 setDescriptiveName(name); 00063 } 00064 } 00065 00066 // ###################################################################### 00067 DirectFeedChannel::~DirectFeedChannel() 00068 {} 00069 00070 // ###################################################################### 00071 void DirectFeedChannel::start1() 00072 { 00073 // in the new version, leave our output range open rather than 00074 // forcing it to a given range of values: 00075 if (itsUseOlderVersion.getVal() == false) 00076 { 00077 itsOutputRangeMin.setVal(0.0f); 00078 itsOutputRangeMax.setVal(0.0f); 00079 } 00080 00081 // in the older version, we used to set the map range as we would 00082 // also apply spatial competition for salience to the output map, 00083 // only if using the MAXNORM type of competition, and otherwise we 00084 // would not touch the range: 00085 if (itsUseOlderVersion.getVal() && itsNormType.getVal() != VCXNORM_MAXNORM) 00086 { 00087 itsOutputRangeMin.setVal(0.0f); 00088 itsOutputRangeMax.setVal(0.0f); 00089 } 00090 } 00091 00092 // ###################################################################### 00093 void DirectFeedChannel::reset1() 00094 { 00095 itsMapDims = Dims(); 00096 itsPyr.reset(); 00097 itsInputTime = SimTime::ZERO(); 00098 itsPyrTime = SimTime::SECS(-1.0); 00099 itsCoeff.clear(); 00100 itsOutputCache.freeMem(); 00101 00102 ChannelBase::reset1(); 00103 } 00104 00105 // ###################################################################### 00106 void DirectFeedChannel::readFrom(const ParamMap& pmap) 00107 { 00108 //FIXME 00109 ChannelBase::readFrom(pmap); 00110 } 00111 00112 // ###################################################################### 00113 void DirectFeedChannel::writeTo(ParamMap& pmap) const 00114 { 00115 //FIXME 00116 ChannelBase::writeTo(pmap); 00117 } 00118 00119 // ###################################################################### 00120 void DirectFeedChannel::clampCoeffs(const double cmin, const double cmax) 00121 { 00122 killCaches(); 00123 for (uint i = 0; i < itsCoeff.size(); ++i) 00124 itsCoeff[i] = clampValue(itsCoeff[i], cmin, cmax); 00125 } 00126 00127 // ###################################################################### 00128 double DirectFeedChannel::absSumCoeffs() const 00129 { 00130 double sum = 0.0; 00131 for (uint i = 0; i < itsCoeff.size(); ++i) 00132 sum += std::abs(itsCoeff[i]); 00133 return sum; 00134 } 00135 00136 // ###################################################################### 00137 void DirectFeedChannel::normalizeCoeffs(const double div) 00138 { 00139 killCaches(); 00140 for (uint i = 0; i < itsCoeff.size(); ++i) 00141 itsCoeff[i] /=div; 00142 } 00143 00144 // ###################################################################### 00145 void DirectFeedChannel::setCoeff(const uint idx, const double val) 00146 { 00147 if (itsPyr.isEmpty()) initializeCoeffs(idx+1,1.0); 00148 else ASSERT(idx < numSubmaps()); 00149 00150 killCaches(); 00151 itsCoeff[idx] = val; 00152 } 00153 00154 // ###################################################################### 00155 double DirectFeedChannel::getCoeff(const uint idx) const 00156 { 00157 ASSERT(idx < numSubmaps()); 00158 return itsCoeff[idx]; 00159 } 00160 00161 // ###################################################################### 00162 void DirectFeedChannel::initializeCoeffs(const uint numCoeffs, 00163 const double val) 00164 { 00165 itsCoeff.resize(numCoeffs,val); 00166 } 00167 00168 // ###################################################################### 00169 void DirectFeedChannel::inputPyramid(const ImageSet<float>& pyramid, 00170 const SimTime& t) 00171 { 00172 if (itsCoeff.empty()) initializeCoeffs(pyramid.size()); 00173 else ASSERT(itsCoeff.size() == pyramid.size()); 00174 00175 itsPyr = pyramid; 00176 itsPyrTime = t; 00177 LDEBUG("itsPyrTime: %fms", itsPyrTime.msecs()); 00178 } 00179 00180 // ###################################################################### 00181 void DirectFeedChannel::doInput(const InputFrame& inframe) 00182 { 00183 if (inframe.grayFloat().initialized()) LINFO("using bwimg"); 00184 else if (inframe.colorFloat().initialized()) LINFO("using colimg"); 00185 else LFATAL("Need to have either colimg or bwimg as input!"); 00186 00187 itsInputTime = inframe.time(); 00188 00189 LDEBUG("itsInputTime: %fms", itsInputTime.msecs()); 00190 00191 if (itsInputTime != itsPyrTime) 00192 LFATAL("I don't have any direct-feed input for time=%fms " 00193 "(last input was at time=%fms)", 00194 itsInputTime.msecs(), itsPyrTime.msecs()); 00195 00196 const float fac = pow(0.5f,float(itsMapLevel.getVal())); 00197 itsMapDims = Dims(int(this->getInputDims().w()*fac), 00198 int(this->getInputDims().h()*fac)); 00199 LDEBUG("itsMapDims = %s; itsInputDims = %s",toStr(itsMapDims).c_str(), 00200 toStr(this->getInputDims()).c_str()); 00201 } 00202 00203 // ###################################################################### 00204 bool DirectFeedChannel::outputAvailable() const 00205 { 00206 LDEBUG("itsPyrTime: %fms; itsInputTime: %fms", 00207 itsPyrTime.msecs(), itsInputTime.msecs()); 00208 return (itsInputTime == itsPyrTime); 00209 } 00210 00211 // ###################################################################### 00212 Dims DirectFeedChannel::getMapDims() const 00213 { return itsMapDims; } 00214 00215 // ###################################################################### 00216 uint DirectFeedChannel::numSubmaps() const 00217 { return itsPyr.size(); } 00218 00219 // ###################################################################### 00220 Image<float> DirectFeedChannel::getSubmap(const uint index) const 00221 { 00222 ASSERT(index < numSubmaps()); 00223 return itsPyr[index]; 00224 } 00225 00226 // ###################################################################### 00227 std::string DirectFeedChannel::getSubmapName(const uint index) const 00228 { 00229 ASSERT(index < numSubmaps()); 00230 return sformat("%s lev: %d", descriptiveName().c_str(),index); 00231 } 00232 00233 // ###################################################################### 00234 void DirectFeedChannel::getFeatures(const Point2D<int>& locn, 00235 std::vector<float>& mean) const 00236 { 00237 //FIXME 00238 LFATAL("Not implemented yet."); 00239 } 00240 00241 // ###################################################################### 00242 Image<float> DirectFeedChannel::getOutput() 00243 { 00244 ASSERT(itsInputTime == itsPyrTime); 00245 if (!itsOutputCache.initialized()) computeOutput(); 00246 return itsOutputCache; 00247 } 00248 00249 00250 // ###################################################################### 00251 void DirectFeedChannel::computeOutput() 00252 { 00253 ASSERT(itsInputTime == itsPyrTime); 00254 Image<float> output(getMapDims(),ZEROS); 00255 00256 for (uint i = 0; i < itsPyr.size(); ++i) 00257 { 00258 Image<float> submap = rescale(itsPyr[i], getMapDims()); 00259 applyMaxNorm(submap); 00260 output += (submap * itsCoeff[i]); 00261 } 00262 itsOutputCache = applyMaxNorm(output); 00263 } 00264 00265 00266 // ###################################################################### 00267 Image<float> DirectFeedChannel::applyMaxNorm(Image<float>& map) 00268 { 00269 ASSERT(map.initialized()); 00270 map = maxNormalize(map, itsOutputRangeMin.getVal(), 00271 itsOutputRangeMax.getVal(), 00272 itsNormType.getVal()); 00273 return map; 00274 } 00275 00276 // ###################################################################### 00277 void DirectFeedChannel::killCaches() 00278 { 00279 itsOutputCache.freeMem(); 00280 itsPyrTime = SimTime::SECS(-1.0); 00281 } 00282 00283 00284 // ###################################################################### 00285 // ##### MultiDirecFeedChannel 00286 // ###################################################################### 00287 00288 // ###################################################################### 00289 MultiDirectFeedChannel::MultiDirectFeedChannel(OptionManager& mgr, 00290 uint num) 00291 : ComplexChannel(mgr,"MultiDirectFeed","MultiDirectFeed",MULTIDIRECTFEED) 00292 { 00293 setNumChans(num); 00294 } 00295 00296 MultiDirectFeedChannel::~MultiDirectFeedChannel() 00297 {} 00298 00299 // ###################################################################### 00300 void MultiDirectFeedChannel::setNumChans(uint num) 00301 { 00302 if (num == 0) LFATAL("Need at least one subchannel."); 00303 00304 // Same number that we already have? Nothing to do then 00305 if (num == numChans()) return; 00306 00307 // Need to add additional channels? 00308 if (num > numChans()) 00309 for (uint i = numChans(); i < num; ++i) 00310 addSubChan(makeSharedComp(new DirectFeedChannel(getManager(),i))); 00311 00312 // Need to remove channels? 00313 else 00314 for (uint i = numChans()-1; i >= num; --i) 00315 removeSubComponent(i); 00316 } 00317 00318 // ###################################################################### 00319 void MultiDirectFeedChannel::inputPyramid(uint chanNum, 00320 const ImageSet<float>& pyramid, 00321 const SimTime& t) 00322 { 00323 ASSERT(chanNum < numChans()); 00324 directFeed(chanNum).inputPyramid(pyramid,t); 00325 } 00326 00327 // ###################################################################### 00328 void MultiDirectFeedChannel::inputPyramidVector(const PyrVec& pvec, 00329 const SimTime& t) 00330 { 00331 ASSERT (pvec.size() == numChans()); 00332 for (uint i = 0; i < numChans(); ++i) 00333 inputPyramid(i,pvec[i],t); 00334 } 00335 00336 // ###################################################################### 00337 DirectFeedChannel& MultiDirectFeedChannel::directFeed(uint idx) 00338 { 00339 ASSERT(idx < numChans()); 00340 return *(dynCast<DirectFeedChannel>(subChan(idx))); 00341 } 00342 00343 00344 // ###################################################################### 00345 Image<float> MultiDirectFeedChannel::combineOutputs() 00346 { 00347 ASSERT(numChans() > 0); 00348 00349 if (numChans() == 1) return directFeed(0).getOutput(); 00350 00351 Image<float> output(getMapDims(),ZEROS); 00352 00353 for (uint i = 0; i < numChans(); ++i) 00354 output += directFeed(i).getOutput(); 00355 00356 return maxNormalize(output, itsOutputRangeMin.getVal(), 00357 itsOutputRangeMax.getVal(), 00358 itsNormType.getVal()); 00359 } 00360 00361 00362 // ###################################################################### 00363 void MultiDirectFeedChannel::doInput(const InputFrame& inframe) 00364 { 00365 ASSERT(inframe.colorFloat().initialized() 00366 || inframe.grayFloat().initialized()); 00367 for (uint i = 0; i < numChans(); ++i) 00368 directFeed(i).input(inframe); 00369 } 00370 00371 00372 // ###################################################################### 00373 /* So things look consistent in everyone's emacs... */ 00374 /* Local Variables: */ 00375 /* indent-tabs-mode: nil */ 00376 /* End: */