00001 /*!@file Channels/IntegerComplexChannel.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/IntegerComplexChannel.C $ 00035 // $Id: IntegerComplexChannel.C 12820 2010-02-11 05:44:51Z itti $ 00036 // 00037 00038 #ifndef CHANNELS_INTEGERCOMPLEXCHANNEL_C_DEFINED 00039 #define CHANNELS_INTEGERCOMPLEXCHANNEL_C_DEFINED 00040 00041 #include "Channels/IntegerComplexChannel.H" 00042 00043 #include "Channels/ChannelOpts.H" 00044 #include "Channels/ChannelVisitor.H" 00045 #include "Component/OptionManager.H" 00046 #include "Component/ParamMap.H" 00047 #include "Image/IntegerMathOps.H" 00048 #include "Image/MathOps.H" 00049 #include "Image/ShapeOps.H" 00050 #include "Image/fancynorm.H" 00051 #include "Transport/FrameInfo.H" 00052 #include "Transport/FrameOstream.H" 00053 #include "Util/Assert.H" 00054 #include "Util/MathFunctions.H" 00055 #include "Util/log.H" 00056 #include "Util/sformat.H" 00057 #include "nub/ref.h" 00058 #include "rutz/shared_ptr.h" 00059 #include "rutz/trace.h" 00060 00061 #include <algorithm> 00062 #include <vector> 00063 00064 namespace dummy_namespace_to_avoid_gcc411_bug_IntegerComplexChannel_C 00065 { 00066 struct SubchanInfo 00067 { 00068 SubchanInfo(IntegerComplexChannel* owner, nub::ref<IntegerChannel> c) 00069 : 00070 chan(c), 00071 weight(NModelParam<double>::make 00072 (sformat("%s_weight", c->tagName().c_str()), owner, 1.0)) 00073 {} 00074 00075 nub::ref<IntegerChannel> chan; 00076 rutz::shared_ptr<NModelParam<double> > weight; 00077 }; 00078 00079 struct ChannelHierarchySorter 00080 { 00081 ChannelHierarchySorter(bool dosubmaps_) 00082 : 00083 dosubmaps(dosubmaps_) 00084 {} 00085 00086 bool operator()(const SubchanInfo& i1, const SubchanInfo& i2) 00087 { 00088 nub::ref<ChannelBase> chan1 = i1.chan; 00089 nub::ref<ChannelBase> chan2 = i2.chan; 00090 00091 const int level1 = int(featureHierarchyLevel(chan1->visualFeature())); 00092 const int level2 = int(featureHierarchyLevel(chan2->visualFeature())); 00093 00094 if (level1 < level2) 00095 return true; 00096 00097 // else ... 00098 00099 if (dosubmaps && level1 == level2) 00100 return chan1->numSubmaps() > chan2->numSubmaps(); 00101 00102 // else ... 00103 00104 return false; 00105 } 00106 00107 bool dosubmaps; 00108 }; 00109 } 00110 00111 using namespace dummy_namespace_to_avoid_gcc411_bug_IntegerComplexChannel_C; 00112 00113 struct IntegerComplexChannel::Impl 00114 { 00115 Impl() 00116 : 00117 sortChannelsByNumSubmaps(false) 00118 {} 00119 00120 Image<int> output; 00121 rutz::shared_ptr<ChannelVisitor> subchanVisitor; // apply to all newly-added subchannels 00122 00123 std::vector<SubchanInfo> subchans; 00124 00125 bool sortChannelsByNumSubmaps; 00126 00127 SubchanInfo& findSubchanInfo(IntegerComplexChannel::SubchanKey key) 00128 { 00129 if (key.index != uint(-1)) 00130 { 00131 // ok, the index is valid, let's look up based on that: 00132 00133 if (key.index >= subchans.size()) 00134 LFATAL("Ooops, no such sub-channel %u " 00135 "(I have only %"ZU" sub-channels)", 00136 key.index, subchans.size()); 00137 00138 return subchans[key.index]; 00139 } 00140 else if (key.tag != 0) 00141 { 00142 // ok, the tagname is valid, let's look up based on that: 00143 00144 for (uint i = 0; i < subchans.size(); ++i) 00145 if (subchans[i].chan->tagName().compare(key.tag) == 0) 00146 return subchans[i]; 00147 00148 LFATAL("Ooops, no such sub-channel '%s'", key.tag); 00149 } 00150 else if (key.addr != 0) 00151 { 00152 // ok, the address is valid, let's look up based on that: 00153 for (uint i = 0; i < subchans.size(); ++i) 00154 if (subchans[i].chan.get() == key.addr) 00155 return subchans[i]; 00156 00157 LFATAL("no such sub-channel '%s'", key.addr->tagName().c_str()); 00158 } 00159 00160 LFATAL("Ooops, invalid SubchanKey (one of index, tag, or address " 00161 "must be valid)"); 00162 return subchans[0]; // keep compiler happy 00163 } 00164 }; 00165 00166 // ###################################################################### 00167 // ###################################################################### 00168 // IntegerComplexChannel member definitions: 00169 // ###################################################################### 00170 // ###################################################################### 00171 00172 00173 // ###################################################################### 00174 IntegerComplexChannel:: 00175 IntegerComplexChannel(OptionManager& mgr, 00176 const std::string& descrName, 00177 const std::string& tag, 00178 const VisualFeature vs, 00179 nub::ref<IntegerMathEngine> eng) 00180 : 00181 IntegerChannel(mgr, descrName, tag, vs, eng), 00182 itsNormType(&OPT_MaxNormType, this), 00183 itsSaveOutputMap(&OPT_ComplexChannelSaveOutputMap, this), 00184 itsUseOlderVersion(&OPT_UseOlderVersion, this), 00185 itsOutputRangeMin(&OPT_IntChannelOutputRangeMin, this), 00186 itsOutputRangeMax(&OPT_IntChannelOutputRangeMax, this), 00187 rep(new Impl) 00188 { 00189 GVX_TRACE(__PRETTY_FUNCTION__); 00190 } 00191 00192 // ###################################################################### 00193 IntegerComplexChannel::~IntegerComplexChannel() 00194 { 00195 GVX_TRACE(__PRETTY_FUNCTION__); 00196 delete rep; 00197 } 00198 00199 // ###################################################################### 00200 void IntegerComplexChannel::start1() 00201 { 00202 GVX_TRACE(__PRETTY_FUNCTION__); 00203 00204 std::stable_sort(rep->subchans.begin(), rep->subchans.end(), 00205 ChannelHierarchySorter(rep->sortChannelsByNumSubmaps)); 00206 } 00207 00208 // ###################################################################### 00209 void IntegerComplexChannel::start2() 00210 { 00211 GVX_TRACE(__PRETTY_FUNCTION__); 00212 00213 // in the new version, leave our output range open rather than 00214 // forcing it to a given range of values: 00215 if (itsUseOlderVersion.getVal() == false) 00216 { 00217 itsOutputRangeMin.setVal(0); 00218 itsOutputRangeMax.setVal(0); 00219 } 00220 00221 // in the older version, we used to set the map range as we would 00222 // also apply spatial competition for salience to the output map, 00223 // only if using the MAXNORM type of competition, and otherwise we 00224 // would not touch the range: 00225 if (itsUseOlderVersion.getVal() && itsNormType.getVal() != VCXNORM_MAXNORM) 00226 { 00227 itsOutputRangeMin.setVal(0); 00228 itsOutputRangeMax.setVal(0); 00229 } 00230 } 00231 00232 // ###################################################################### 00233 void IntegerComplexChannel::stop2() 00234 { 00235 GVX_TRACE(__PRETTY_FUNCTION__); 00236 } 00237 00238 // ###################################################################### 00239 bool IntegerComplexChannel::isHomogeneous() const 00240 { 00241 GVX_TRACE("IntegerComplexChannel::isHomogeneous"); 00242 00243 for (uint i = 0; i < this->numChans(); ++i) 00244 { 00245 if (subChan(i)->visualFeature() != this->visualFeature() 00246 || (subChan(i)->isHomogeneous() == false)) 00247 return false; 00248 } 00249 00250 return true; 00251 } 00252 00253 // ###################################################################### 00254 uint IntegerComplexChannel::numChans() const 00255 { 00256 GVX_TRACE(__PRETTY_FUNCTION__); 00257 return rep->subchans.size(); 00258 } 00259 00260 // ###################################################################### 00261 nub::ref<IntegerChannel> IntegerComplexChannel::subChan(const uint idx) const 00262 { 00263 GVX_TRACE(__PRETTY_FUNCTION__); 00264 if (idx >= rep->subchans.size()) 00265 LFATAL("Ooops, no such sub-channel %u (I have only %"ZU" sub-channels)", 00266 idx, rep->subchans.size()); 00267 00268 return rep->subchans[idx].chan; 00269 } 00270 00271 // ###################################################################### 00272 nub::ref<IntegerChannel> IntegerComplexChannel::subChan(const std::string& tagname) const 00273 { 00274 GVX_TRACE(__PRETTY_FUNCTION__); 00275 00276 for (uint i = 0; i < rep->subchans.size(); ++i) 00277 if (rep->subchans[i].chan->tagName() == tagname) 00278 return rep->subchans[i].chan; 00279 00280 LFATAL("Ooops, no such sub-channel '%s'", tagname.c_str()); 00281 00282 /* can't happen */ return nub::ref<IntegerChannel>((IntegerChannel*)0); 00283 } 00284 00285 // ###################################################################### 00286 nub::ref<IntegerChannel> IntegerComplexChannel:: 00287 subChanFromSubmapNum(const uint oldIdx, 00288 uint& newIdx) const 00289 { 00290 GVX_TRACE(__PRETTY_FUNCTION__); 00291 ASSERT(oldIdx < numSubmaps()); 00292 uint subchan = 0; 00293 lookupSubmap(oldIdx, subchan, newIdx); 00294 00295 return subChan(subchan); 00296 } 00297 00298 // ###################################################################### 00299 void IntegerComplexChannel::readFrom(const ParamMap& pmap) 00300 { 00301 GVX_TRACE(__PRETTY_FUNCTION__); 00302 IntegerChannel::readFrom(pmap); 00303 ChannelFacetMap::readFacetsFrom(pmap); 00304 00305 for (uint i = 0; i < numChans(); ++i) 00306 { 00307 const std::string tagname = subChan(i)->tagName(); 00308 if (pmap.hasParam(tagname)) 00309 { 00310 rutz::shared_ptr<ParamMap> submap = pmap.getSubpmap(tagname); 00311 subChan(i)->readFrom(*submap); 00312 00313 double wt = rep->subchans[i].weight->getVal(); 00314 00315 if (submap->queryDoubleParam("weight", wt) == ParamMap::MISSING) 00316 rep->subchans[i].weight->setVal(1.0); 00317 else 00318 rep->subchans[i].weight->setVal(wt); 00319 } 00320 } 00321 } 00322 00323 // ###################################################################### 00324 void IntegerComplexChannel::writeTo(ParamMap& pmap) const 00325 { 00326 GVX_TRACE(__PRETTY_FUNCTION__); 00327 IntegerChannel::writeTo(pmap); 00328 ChannelFacetMap::writeFacetsTo(pmap); 00329 00330 for (uint i = 0; i < numChans(); ++i) 00331 { 00332 rutz::shared_ptr<ParamMap> submap(new ParamMap); 00333 subChan(i)->writeTo(*submap); 00334 submap->putDoubleParam("weight", rep->subchans[i].weight->getVal()); 00335 pmap.putSubpmap(subChan(i)->tagName(), submap); 00336 } 00337 } 00338 00339 // ###################################################################### 00340 bool IntegerComplexChannel::outputAvailable() const 00341 { 00342 GVX_TRACE(__PRETTY_FUNCTION__); 00343 for (uint i = 0; i < numChans(); ++i) 00344 if (subChan(i)->outputAvailable() == false) return false; 00345 return true; 00346 } 00347 00348 // ###################################################################### 00349 Dims IntegerComplexChannel::getMapDims() const 00350 { 00351 GVX_TRACE(__PRETTY_FUNCTION__); 00352 return subChan(0)->getMapDims(); 00353 } 00354 00355 // ###################################################################### 00356 uint IntegerComplexChannel::numSubmaps() const 00357 { 00358 GVX_TRACE(__PRETTY_FUNCTION__); 00359 uint sum = 0; 00360 for (uint i = 0; i < numChans(); ++i) sum += subChan(i)->numSubmaps(); 00361 return sum; 00362 } 00363 00364 // ###################################################################### 00365 void IntegerComplexChannel::lookupSubmap(const uint idx, uint& subchan, 00366 uint& subidx) const 00367 { 00368 GVX_TRACE(__PRETTY_FUNCTION__); 00369 ASSERT(idx < numSubmaps()); 00370 uint offset = 0; 00371 for (subchan = 0; subchan < numChans(); ++subchan) 00372 { 00373 subidx = idx - offset; 00374 const uint nsub = subChan(subchan)->numSubmaps(); 00375 if (subidx < nsub) 00376 { 00377 // OK, we have found the right subchan+submap combination: 00378 return; 00379 } 00380 else 00381 offset += nsub; 00382 } 00383 LFATAL("invalid submap index: %d", idx); 00384 } 00385 00386 // ###################################################################### 00387 Image<int> IntegerComplexChannel::getSubmapInt(const uint idx) const 00388 { 00389 GVX_TRACE(__PRETTY_FUNCTION__); 00390 uint subchan = 0, subidx = 0; 00391 lookupSubmap(idx, subchan, subidx); 00392 return subChan(subchan)->getSubmapInt(subidx); 00393 } 00394 00395 // ###################################################################### 00396 Image<int> IntegerComplexChannel::getRawCSmapInt(const uint idx) const 00397 { 00398 GVX_TRACE(__PRETTY_FUNCTION__); 00399 uint subchan = 0, subidx = 0; 00400 lookupSubmap(idx, subchan, subidx); 00401 return subChan(subchan)->getRawCSmapInt(subidx); 00402 } 00403 00404 // ###################################################################### 00405 std::string IntegerComplexChannel::getSubmapName(const uint idx) const 00406 { 00407 GVX_TRACE(__PRETTY_FUNCTION__); 00408 uint subchan = 0, subidx = 0; 00409 lookupSubmap(idx, subchan, subidx); 00410 return subChan(subchan)->getSubmapName(subidx); 00411 } 00412 00413 // ###################################################################### 00414 std::string IntegerComplexChannel::getSubmapNameShort(const uint idx) const 00415 { 00416 GVX_TRACE(__PRETTY_FUNCTION__); 00417 uint subchan = 0, subidx = 0; 00418 lookupSubmap(idx, subchan, subidx); 00419 return subChan(subchan)->getSubmapNameShort(subidx); 00420 } 00421 00422 // ###################################################################### 00423 void IntegerComplexChannel::getFeatures(const Point2D<int>& locn, 00424 std::vector<float>& mean) const 00425 { 00426 LFATAL("not implemented"); 00427 } 00428 00429 // ###################################################################### 00430 void IntegerComplexChannel::getFeaturesBatch(std::vector<Point2D<int>*> *locn, 00431 std::vector<std::vector<float> > *mean, 00432 int *count) const 00433 { 00434 LFATAL("not implemented"); 00435 } 00436 00437 // ###################################################################### 00438 void IntegerComplexChannel::addSubChan(nub::ref<IntegerChannel> ch, 00439 const char* tagname) 00440 { 00441 GVX_TRACE(__PRETTY_FUNCTION__); 00442 00443 // if the user gave us a name for the channel, then let's rename it: 00444 if (tagname != 0 && *tagname != '\0') 00445 ch->setTagName(tagname); 00446 00447 // take ownership of the subchannel: 00448 this->addSubComponent(ch); 00449 00450 // add it to our list of subchannels: 00451 rep->subchans.push_back(SubchanInfo(this, ch)); 00452 00453 // apply our subchannel visitor to the new subchannel: 00454 if (rep->subchanVisitor.is_valid()) 00455 ch->accept(*rep->subchanVisitor); 00456 } 00457 00458 // ###################################################################### 00459 void IntegerComplexChannel::removeSubChan(nub::ref<IntegerChannel> ch) 00460 { 00461 GVX_TRACE(__PRETTY_FUNCTION__); 00462 00463 std::vector<SubchanInfo>::iterator itr = rep->subchans.begin(); 00464 00465 while (itr != rep->subchans.end()) 00466 { 00467 if ((*itr).chan == ch) 00468 { 00469 itr = rep->subchans.erase(itr); 00470 this->removeSubComponent(*ch); 00471 } 00472 else 00473 ++itr; 00474 } 00475 } 00476 00477 // ###################################################################### 00478 void IntegerComplexChannel::removeAllSubChans() 00479 { 00480 GVX_TRACE(__PRETTY_FUNCTION__); 00481 00482 while (rep->subchans.size() > 0) 00483 { 00484 this->removeSubComponent(*(rep->subchans.back().chan)); 00485 rep->subchans.pop_back(); 00486 } 00487 00488 ASSERT(rep->subchans.size() == 0); 00489 } 00490 00491 // ###################################################################### 00492 bool IntegerComplexChannel::hasSubChan(const char* tagname) const 00493 { 00494 GVX_TRACE(__PRETTY_FUNCTION__); 00495 for (uint i = 0; i < rep->subchans.size(); ++i) 00496 if (rep->subchans[i].chan->tagName().compare(tagname) == 0) 00497 return true; 00498 00499 return false; 00500 } 00501 00502 // ###################################################################### 00503 void IntegerComplexChannel::setSubchanVisitor(rutz::shared_ptr<ChannelVisitor> v) 00504 { 00505 GVX_TRACE(__PRETTY_FUNCTION__); 00506 rep->subchanVisitor = v; 00507 00508 // now apply the visitor to any subchannels that we already have: 00509 if (rep->subchanVisitor.is_valid()) 00510 for (uint i = 0; i < this->numChans(); ++i) 00511 subChan(i)->accept(*rep->subchanVisitor); 00512 } 00513 00514 // ###################################################################### 00515 void IntegerComplexChannel::setSubchanTotalWeight(SubchanKey key, const double wt) 00516 { 00517 this->killCaches(); 00518 rep->findSubchanInfo(key).weight->setVal(wt); 00519 } 00520 00521 // ###################################################################### 00522 double IntegerComplexChannel::getSubchanTotalWeight(SubchanKey key) const 00523 { 00524 return rep->findSubchanInfo(key).weight->getVal(); 00525 } 00526 00527 // ###################################################################### 00528 void IntegerComplexChannel::sortChannelsByNumSubmaps(bool dosort) 00529 { 00530 GVX_TRACE(__PRETTY_FUNCTION__); 00531 if (this->started()) 00532 LFATAL("This must be called before start()"); 00533 00534 rep->sortChannelsByNumSubmaps = dosort; 00535 } 00536 00537 // ###################################################################### 00538 void IntegerComplexChannel::killCaches() 00539 { 00540 GVX_TRACE(__PRETTY_FUNCTION__); 00541 IntegerChannel::killCaches(); // call our base class's implementation 00542 00543 // free our own output cache: 00544 rep->output.freeMem(); 00545 00546 // propagate to our subchannels: 00547 for (uint i = 0; i < numChans(); ++i) subChan(i)->killCaches(); 00548 } 00549 00550 // ###################################################################### 00551 Image<int> IntegerComplexChannel::getOutputInt() 00552 { 00553 GVX_TRACE(__PRETTY_FUNCTION__); 00554 if (!rep->output.initialized()) rep->output = combineOutputsInt(); 00555 return rep->output; 00556 } 00557 00558 // ###################################################################### 00559 Image<int> IntegerComplexChannel::combineOutputsInt() 00560 { 00561 GVX_TRACE(__PRETTY_FUNCTION__); 00562 Image<int> output(getMapDims(), ZEROS); 00563 for (uint i = 0; i < numChans(); ++i) 00564 { 00565 const Image<int> subChanOut = subChan(i)->getOutputInt(); 00566 00567 ASSERT(subChanOut.initialized()); 00568 00569 output += (subChanOut / int(numChans())); 00570 } 00571 00572 LDEBUG("%s: Normalizing output: %s(%d .. %d)", tagName().c_str(), 00573 maxNormTypeName(itsNormType.getVal()), 00574 itsOutputRangeMin.getVal(), 00575 itsOutputRangeMax.getVal()); 00576 output = intgMaxNormalize(output, itsOutputRangeMin.getVal(), 00577 itsOutputRangeMax.getVal(), itsNormType.getVal()); 00578 00579 // apply max-normalization on the output as needed: 00580 // print some debug info if in debug mode: 00581 if (MYLOGVERB >= LOG_DEBUG) 00582 { 00583 int mi, ma; getMinMax(output, mi, ma); 00584 LDEBUG("%s: final range [%d .. %d]", tagName().c_str(), mi, ma); 00585 } 00586 00587 LINFO("Computed %s Conspicuity Map", descriptiveName().c_str()); 00588 00589 return output; 00590 } 00591 00592 // ###################################################################### 00593 void IntegerComplexChannel::saveResults(const nub::ref<FrameOstream>& ofs) 00594 { 00595 GVX_TRACE(__PRETTY_FUNCTION__); 00596 // save our own map: 00597 if (itsSaveOutputMap.getVal()) 00598 ofs->writeFloat(Image<float>(getOutputInt()), FLOAT_NORM_0_255, 00599 sformat("ICO%s-", tagName().c_str()), 00600 FrameInfo(sformat("%s IntegerComplexChannel output", 00601 this->descriptiveName().c_str()), 00602 SRC_POS)); 00603 00604 // now do it for our subchannels: 00605 for (uint i = 0; i < numChans(); ++i) subChan(i)->saveResults(ofs); 00606 } 00607 00608 // ###################################################################### 00609 /* So things look consistent in everyone's emacs... */ 00610 /* Local Variables: */ 00611 /* mode: c++ */ 00612 /* indent-tabs-mode: nil */ 00613 /* End: */ 00614 00615 #endif // CHANNELS_INTEGERCOMPLEXCHANNEL_C_DEFINED