00001 /*!@file Channels/ComplexChannel.C Channel class that pools across multiple 00002 subchannels. */ 00003 00004 // //////////////////////////////////////////////////////////////////// // 00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00006 // University of Southern California (USC) and the iLab at USC. // 00007 // See http://iLab.usc.edu for information about this project. // 00008 // //////////////////////////////////////////////////////////////////// // 00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00011 // in Visual Environments, and Applications'' by Christof Koch and // 00012 // Laurent Itti, California Institute of Technology, 2001 (patent // 00013 // pending; application number 09/912,225 filed July 23, 2001; see // 00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00015 // //////////////////////////////////////////////////////////////////// // 00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00017 // // 00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00019 // redistribute it and/or modify it under the terms of the GNU General // 00020 // Public License as published by the Free Software Foundation; either // 00021 // version 2 of the License, or (at your option) any later version. // 00022 // // 00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00026 // PURPOSE. See the GNU General Public License for more details. // 00027 // // 00028 // You should have received a copy of the GNU General Public License // 00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00031 // Boston, MA 02111-1307 USA. // 00032 // //////////////////////////////////////////////////////////////////// // 00033 // 00034 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu> 00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Channels/ComplexChannel.C $ 00036 // $Id: ComplexChannel.C 14487 2011-02-08 18:57:45Z dberg $ 00037 // 00038 00039 #include "Channels/ComplexChannel.H" 00040 00041 #include "Channels/ChannelOpts.H" 00042 #include "Channels/ChannelVisitor.H" 00043 #include "Channels/ChannelFacets.H" 00044 #include "Component/OptionManager.H" 00045 #include "Component/ParamMap.H" 00046 #include "Image/MathOps.H" 00047 #include "Image/ShapeOps.H" 00048 #include "Image/fancynorm.H" 00049 #include "Transport/FrameInfo.H" 00050 #include "Transport/FrameOstream.H" 00051 #include "Util/Assert.H" 00052 #include "Util/MathFunctions.H" 00053 #include "Util/log.H" 00054 #include "Util/sformat.H" 00055 #include "rutz/shared_ptr.h" 00056 #include "rutz/trace.h" 00057 00058 #include <algorithm> 00059 #include <vector> 00060 00061 namespace dummy_namespace_to_avoid_gcc411_bug_ComplexChannel_C 00062 { 00063 struct SubchanInfo 00064 { 00065 SubchanInfo(ComplexChannel* owner, nub::ref<ChannelBase> c, 00066 const double wt = 1.0) 00067 : 00068 chan(c), 00069 weight(NModelParam<double>::make 00070 (sformat("%s_weight", c->tagName().c_str()), owner, wt)) 00071 {} 00072 00073 nub::ref<ChannelBase> chan; 00074 rutz::shared_ptr<NModelParam<double> > weight; 00075 }; 00076 00077 struct ChannelHierarchySorter 00078 { 00079 ChannelHierarchySorter(bool dosubmaps_) 00080 : 00081 dosubmaps(dosubmaps_) 00082 {} 00083 00084 bool operator()(const SubchanInfo& i1, const SubchanInfo& i2) 00085 { 00086 nub::ref<ChannelBase> chan1 = i1.chan; 00087 nub::ref<ChannelBase> chan2 = i2.chan; 00088 00089 const int level1 = int(featureHierarchyLevel(chan1->visualFeature())); 00090 const int level2 = int(featureHierarchyLevel(chan2->visualFeature())); 00091 00092 if (level1 < level2) 00093 return true; 00094 00095 // else ... 00096 00097 if (dosubmaps && level1 == level2) 00098 return chan1->numSubmaps() > chan2->numSubmaps(); 00099 00100 // else ... 00101 00102 return false; 00103 } 00104 00105 bool dosubmaps; 00106 }; 00107 } 00108 00109 using namespace dummy_namespace_to_avoid_gcc411_bug_ComplexChannel_C; 00110 00111 struct ComplexChannel::Impl 00112 { 00113 Impl() 00114 : 00115 sortChannelsByNumSubmaps(false) 00116 {} 00117 00118 Image<float> output; 00119 rutz::shared_ptr<ChannelVisitor> subchanVisitor; // apply to all newly-added subchannels 00120 00121 std::vector<SubchanInfo> subchans; 00122 00123 bool sortChannelsByNumSubmaps; 00124 00125 SubchanInfo& findSubchanInfo(ComplexChannel::SubchanKey key) 00126 { 00127 if (key.index != uint(-1)) 00128 { 00129 // ok, the index is valid, let's look up based on that: 00130 00131 if (key.index >= subchans.size()) 00132 LFATAL("Ooops, no such sub-channel %u " 00133 "(I have only %"ZU" sub-channels)", 00134 key.index, subchans.size()); 00135 00136 return subchans[key.index]; 00137 } 00138 else if (key.tag != 0) 00139 { 00140 // ok, the tagname is valid, let's look up based on that: 00141 00142 for (uint i = 0; i < subchans.size(); ++i) 00143 if (subchans[i].chan->tagName().compare(key.tag) == 0) 00144 return subchans[i]; 00145 00146 LFATAL("Ooops, no such sub-channel '%s'", key.tag); 00147 } 00148 else if (key.addr != 0) 00149 { 00150 // ok, the address is valid, let's look up based on that: 00151 for (uint i = 0; i < subchans.size(); ++i) 00152 if (subchans[i].chan.get() == key.addr) 00153 return subchans[i]; 00154 00155 LFATAL("no such sub-channel '%s'", key.addr->tagName().c_str()); 00156 } 00157 00158 LFATAL("Ooops, invalid SubchanKey (one of index, tag, or address " 00159 "must be valid)"); 00160 return subchans[0]; // keep compiler happy 00161 } 00162 }; 00163 00164 // ###################################################################### 00165 // ###################################################################### 00166 // ComplexChannel member definitions: 00167 // ###################################################################### 00168 // ###################################################################### 00169 00170 00171 // ###################################################################### 00172 ComplexChannel::ComplexChannel(OptionManager& mgr, 00173 const std::string& descrName, 00174 const std::string& tag, 00175 const VisualFeature vs) : 00176 ChannelBase(mgr, descrName, tag, vs), 00177 itsNormType(&OPT_MaxNormType, this), 00178 itsCombineType(&OPT_ComplexChannelMapCombineType, this), 00179 itsSaveOutputMap(&OPT_ComplexChannelSaveOutputMap, this), 00180 itsUseOlderVersion(&OPT_UseOlderVersion, this), 00181 itsUseSpaceVariantBoundary("UseSpaceVariantBoundary", this, false), 00182 itsOutputRangeMin(&OPT_ChannelOutputRangeMin, this), 00183 itsOutputRangeMax(&OPT_ChannelOutputRangeMax, this), 00184 rep(new Impl) 00185 { 00186 GVX_TRACE(__PRETTY_FUNCTION__); 00187 } 00188 00189 // ###################################################################### 00190 ComplexChannel::~ComplexChannel() 00191 { 00192 GVX_TRACE(__PRETTY_FUNCTION__); 00193 delete rep; 00194 } 00195 00196 // ###################################################################### 00197 void ComplexChannel::start1() 00198 { 00199 GVX_TRACE(__PRETTY_FUNCTION__); 00200 00201 std::stable_sort(rep->subchans.begin(), rep->subchans.end(), 00202 ChannelHierarchySorter(rep->sortChannelsByNumSubmaps)); 00203 } 00204 00205 // ###################################################################### 00206 void ComplexChannel::start2() 00207 { 00208 GVX_TRACE(__PRETTY_FUNCTION__); 00209 00210 // in the new version, leave our output range open rather than 00211 // forcing it to a given range of values: 00212 if (itsUseOlderVersion.getVal() == false) 00213 { 00214 itsOutputRangeMin.setVal(0.0f); 00215 itsOutputRangeMax.setVal(0.0f); 00216 } 00217 00218 // in the older version, we used to set the map range as we would 00219 // also apply spatial competition for salience to the output map, 00220 // only if using the MAXNORM type of competition, and otherwise we 00221 // would not touch the range: 00222 if (itsUseOlderVersion.getVal() && itsNormType.getVal() != VCXNORM_MAXNORM) 00223 { 00224 itsOutputRangeMin.setVal(0.0f); 00225 itsOutputRangeMax.setVal(0.0f); 00226 } 00227 00228 // if we are in space variant mode, make sure our subchannels 00229 // handle those types of images 00230 for (uint i = 0; i < this->numChans(); ++i) 00231 if (itsUseSpaceVariantBoundary.getVal()) 00232 if (this->subChan(i)->hasModelParam("UseSpaceVariantBoundary", MC_IGNORE_MISSING) == false) 00233 LFATAL("All channels must support space variant image boundary " 00234 "conditions when using a retinal type 'RetinaCT'"); 00235 } 00236 00237 // ###################################################################### 00238 void ComplexChannel::stop2() 00239 { 00240 GVX_TRACE(__PRETTY_FUNCTION__); 00241 } 00242 00243 // ###################################################################### 00244 void ComplexChannel::accept(ChannelVisitor& v) 00245 { 00246 GVX_TRACE(__PRETTY_FUNCTION__); 00247 v.visitComplexChannel(*this); 00248 } 00249 00250 // ###################################################################### 00251 bool ComplexChannel::isHomogeneous() const 00252 { 00253 GVX_TRACE("ComplexChannel::isHomogeneous"); 00254 00255 for (uint i = 0; i < this->numChans(); ++i) 00256 { 00257 if (subChan(i)->visualFeature() != this->visualFeature() 00258 || (subChan(i)->isHomogeneous() == false)) 00259 return false; 00260 } 00261 00262 return true; 00263 } 00264 00265 // ###################################################################### 00266 uint ComplexChannel::numChans() const 00267 { 00268 GVX_TRACE(__PRETTY_FUNCTION__); 00269 return rep->subchans.size(); 00270 } 00271 00272 // ###################################################################### 00273 nub::ref<ChannelBase> ComplexChannel::subChan(const uint idx) const 00274 { 00275 GVX_TRACE(__PRETTY_FUNCTION__); 00276 if (idx >= rep->subchans.size()) 00277 LFATAL("Ooops, no such sub-channel %u (I have only %"ZU" sub-channels)", 00278 idx, rep->subchans.size()); 00279 00280 return rep->subchans[idx].chan; 00281 } 00282 00283 // ###################################################################### 00284 nub::ref<ChannelBase> ComplexChannel::subChan(const std::string& tagname) const 00285 { 00286 GVX_TRACE(__PRETTY_FUNCTION__); 00287 00288 for (uint i = 0; i < rep->subchans.size(); ++i) 00289 if (rep->subchans[i].chan->tagName() == tagname) 00290 return rep->subchans[i].chan; 00291 00292 LFATAL("Ooops, no such sub-channel '%s'", tagname.c_str()); 00293 00294 /* can't happen */ return nub::ref<ChannelBase>((ChannelBase*)0); 00295 } 00296 00297 // ###################################################################### 00298 nub::ref<ChannelBase> ComplexChannel:: 00299 subChanFromSubmapNum(const uint oldIdx, 00300 uint& newIdx) const 00301 { 00302 GVX_TRACE(__PRETTY_FUNCTION__); 00303 ASSERT(oldIdx < numSubmaps()); 00304 uint subchan = 0; 00305 lookupSubmap(oldIdx, subchan, newIdx); 00306 00307 return subChan(subchan); 00308 } 00309 00310 // ###################################################################### 00311 void ComplexChannel::readFrom(const ParamMap& pmap) 00312 { 00313 GVX_TRACE(__PRETTY_FUNCTION__); 00314 ChannelBase::readFrom(pmap); 00315 ChannelFacetMap::readFacetsFrom(pmap); 00316 00317 for (uint i = 0; i < numChans(); ++i) 00318 { 00319 const std::string tagname = subChan(i)->tagName(); 00320 if (pmap.hasParam(tagname)) 00321 { 00322 rutz::shared_ptr<ParamMap> submap = pmap.getSubpmap(tagname); 00323 subChan(i)->readFrom(*submap); 00324 00325 double wt = rep->subchans[i].weight->getVal(); 00326 00327 if (submap->queryDoubleParam("weight", wt) == ParamMap::MISSING) 00328 rep->subchans[i].weight->setVal(1.0); 00329 else 00330 rep->subchans[i].weight->setVal(wt); 00331 } 00332 } 00333 } 00334 00335 // ###################################################################### 00336 void ComplexChannel::writeTo(ParamMap& pmap) const 00337 { 00338 GVX_TRACE(__PRETTY_FUNCTION__); 00339 00340 ChannelBase::writeTo(pmap); 00341 ChannelFacetMap::writeFacetsTo(pmap); 00342 00343 for (uint i = 0; i < numChans(); ++i) 00344 { 00345 rutz::shared_ptr<ParamMap> submap(new ParamMap); 00346 subChan(i)->writeTo(*submap); 00347 submap->putDoubleParam("weight", rep->subchans[i].weight->getVal()); 00348 submap->putDoubleParam("subchanindex", i); 00349 pmap.putSubpmap(subChan(i)->tagName(), submap); 00350 } 00351 } 00352 00353 // ###################################################################### 00354 bool ComplexChannel::outputAvailable() const 00355 { 00356 GVX_TRACE(__PRETTY_FUNCTION__); 00357 for (uint i = 0; i < numChans(); ++i) 00358 if (subChan(i)->outputAvailable() == false) return false; 00359 return true; 00360 } 00361 00362 // ###################################################################### 00363 Dims ComplexChannel::getMapDims() const 00364 { 00365 GVX_TRACE(__PRETTY_FUNCTION__); 00366 return subChan(0)->getMapDims(); 00367 } 00368 00369 // ###################################################################### 00370 uint ComplexChannel::numSubmaps() const 00371 { 00372 GVX_TRACE(__PRETTY_FUNCTION__); 00373 uint sum = 0; 00374 for (uint i = 0; i < numChans(); ++i) sum += subChan(i)->numSubmaps(); 00375 return sum; 00376 } 00377 00378 // ###################################################################### 00379 void ComplexChannel::lookupSubmap(const uint idx, uint& subchan, 00380 uint& subidx) const 00381 { 00382 GVX_TRACE(__PRETTY_FUNCTION__); 00383 ASSERT(idx < numSubmaps()); 00384 uint offset = 0; 00385 for (subchan = 0; subchan < numChans(); ++subchan) 00386 { 00387 subidx = idx - offset; 00388 const uint nsub = subChan(subchan)->numSubmaps(); 00389 if (subidx < nsub) 00390 { 00391 // OK, we have found the right subchan+submap combination: 00392 return; 00393 } 00394 else 00395 offset += nsub; 00396 } 00397 LFATAL("invalid submap index: %d", idx); 00398 } 00399 00400 // ###################################################################### 00401 Image<float> ComplexChannel::getSubmap(const uint idx) const 00402 { 00403 GVX_TRACE(__PRETTY_FUNCTION__); 00404 uint subchan = 0, subidx = 0; 00405 lookupSubmap(idx, subchan, subidx); 00406 return subChan(subchan)->getSubmap(subidx); 00407 } 00408 00409 // ###################################################################### 00410 Image<float> ComplexChannel::getRawCSmap(const uint idx) const 00411 { 00412 GVX_TRACE(__PRETTY_FUNCTION__); 00413 uint subchan = 0, subidx = 0; 00414 lookupSubmap(idx, subchan, subidx); 00415 return subChan(subchan)->getRawCSmap(subidx); 00416 } 00417 00418 // ###################################################################### 00419 std::string ComplexChannel::getSubmapName(const uint idx) const 00420 { 00421 GVX_TRACE(__PRETTY_FUNCTION__); 00422 uint subchan = 0, subidx = 0; 00423 lookupSubmap(idx, subchan, subidx); 00424 return subChan(subchan)->getSubmapName(subidx); 00425 } 00426 00427 // ###################################################################### 00428 std::string ComplexChannel::getSubmapNameShort(const uint idx) const 00429 { 00430 GVX_TRACE(__PRETTY_FUNCTION__); 00431 uint subchan = 0, subidx = 0; 00432 lookupSubmap(idx, subchan, subidx); 00433 return subChan(subchan)->getSubmapNameShort(subidx); 00434 } 00435 00436 // ###################################################################### 00437 void ComplexChannel::getFeatures(const Point2D<int>& locn, 00438 std::vector<float>& mean) const 00439 { 00440 GVX_TRACE(__PRETTY_FUNCTION__); 00441 for(uint i = 0; i < numChans(); ++i) 00442 subChan(i)->getFeatures(locn, mean); 00443 } 00444 00445 // ###################################################################### 00446 void ComplexChannel::getFeaturesBatch(std::vector<Point2D<int>*> *locn, 00447 std::vector<std::vector<float> > *mean, 00448 int *count) const 00449 { 00450 GVX_TRACE(__PRETTY_FUNCTION__); 00451 for (uint i = 0; i < numChans(); ++i) 00452 subChan(i)->getFeaturesBatch(locn, mean, count); 00453 } 00454 00455 // ###################################################################### 00456 void ComplexChannel::addSubChan(nub::ref<ChannelBase> ch, 00457 const char* tagname, 00458 const double weight, 00459 bool exportOpts) 00460 { 00461 GVX_TRACE(__PRETTY_FUNCTION__); 00462 00463 // if the user gave us a name for the channel, then let's rename it: 00464 if (tagname != 0 && *tagname != '\0') 00465 ch->setTagName(tagname); 00466 00467 // take ownership of the subchannel: 00468 this->addSubComponent(ch); 00469 00470 // add it to our list of subchannels: 00471 rep->subchans.push_back(SubchanInfo(this, ch, weight)); 00472 00473 // apply our subchannel visitor to the new subchannel: 00474 if (rep->subchanVisitor.is_valid()) 00475 ch->accept(*rep->subchanVisitor); 00476 00477 if (exportOpts) 00478 ch->exportOptions(MC_RECURSE); 00479 } 00480 00481 // ###################################################################### 00482 void ComplexChannel::removeSubChan(nub::ref<ChannelBase> ch) 00483 { 00484 GVX_TRACE(__PRETTY_FUNCTION__); 00485 00486 std::vector<SubchanInfo>::iterator itr = rep->subchans.begin(); 00487 00488 while (itr != rep->subchans.end()) 00489 { 00490 if ((*itr).chan == ch) 00491 { 00492 itr = rep->subchans.erase(itr); 00493 this->removeSubComponent(*ch); 00494 } 00495 else 00496 ++itr; 00497 } 00498 } 00499 00500 // ###################################################################### 00501 void ComplexChannel::removeAllSubChans() 00502 { 00503 GVX_TRACE(__PRETTY_FUNCTION__); 00504 00505 while (rep->subchans.size() > 0) 00506 { 00507 this->removeSubComponent(*(rep->subchans.back().chan)); 00508 rep->subchans.pop_back(); 00509 } 00510 00511 ASSERT(rep->subchans.size() == 0); 00512 } 00513 00514 // ###################################################################### 00515 bool ComplexChannel::hasSubChan(const char* tagname) const 00516 { 00517 GVX_TRACE(__PRETTY_FUNCTION__); 00518 for (uint i = 0; i < rep->subchans.size(); ++i) 00519 if (rep->subchans[i].chan->tagName().compare(tagname) == 0) 00520 return true; 00521 00522 return false; 00523 } 00524 00525 // ###################################################################### 00526 void ComplexChannel::setSubchanVisitor(rutz::shared_ptr<ChannelVisitor> v) 00527 { 00528 GVX_TRACE(__PRETTY_FUNCTION__); 00529 rep->subchanVisitor = v; 00530 00531 // now apply the visitor to any subchannels that we already have: 00532 if (rep->subchanVisitor.is_valid()) 00533 for (uint i = 0; i < this->numChans(); ++i) 00534 subChan(i)->accept(*rep->subchanVisitor); 00535 } 00536 00537 // ###################################################################### 00538 void ComplexChannel::setSubchanTotalWeight(SubchanKey key, const double wt) 00539 { 00540 this->killCaches(); 00541 rep->findSubchanInfo(key).weight->setVal(wt); 00542 } 00543 00544 // ###################################################################### 00545 double ComplexChannel::getSubchanTotalWeight(SubchanKey key) const 00546 { 00547 return rep->findSubchanInfo(key).weight->getVal(); 00548 } 00549 00550 // ###################################################################### 00551 void ComplexChannel::sortChannelsByNumSubmaps(bool dosort) 00552 { 00553 GVX_TRACE(__PRETTY_FUNCTION__); 00554 if (this->started()) 00555 LFATAL("This must be called before start()"); 00556 00557 rep->sortChannelsByNumSubmaps = dosort; 00558 } 00559 00560 // ###################################################################### 00561 void ComplexChannel::killCaches() 00562 { 00563 GVX_TRACE(__PRETTY_FUNCTION__); 00564 ChannelBase::killCaches(); // call our base class's implementation 00565 00566 // free our own output cache: 00567 rep->output.freeMem(); 00568 00569 // propagate to our subchannels: 00570 for (uint i = 0; i < numChans(); ++i) subChan(i)->killCaches(); 00571 } 00572 00573 // ###################################################################### 00574 Image<float> ComplexChannel::getOutput() 00575 { 00576 GVX_TRACE(__PRETTY_FUNCTION__); 00577 if (!rep->output.initialized()) rep->output = combineOutputs(); 00578 return rep->output; 00579 } 00580 00581 // ###################################################################### 00582 Image<float> ComplexChannel::combineOutputs() 00583 { 00584 GVX_TRACE(__PRETTY_FUNCTION__); 00585 Image<float> output; // leave uninitialized so that mapCombine() can 00586 // do the Right Thing on the first call 00587 00588 uint num_nzw = 0; // number of subchans with non-zero weights 00589 00590 for (uint i = 0; i < numChans(); ++i) 00591 { 00592 // Determine the weight for each subchan. This is the product of 00593 // the intrinsic weight of each subchan (which is set either 00594 // through the --vc-chans config string of VisualCortex for 00595 // top-level channels, or subject to static modifications by 00596 // using --load at the start of the entire model and setting the 00597 // NModelParam value associated with each channel), multiplied 00598 // by any possible extra top-down gain if we have a 00599 // ChannelFacetGainComplex: 00600 float w = float(rep->subchans[i].weight->getVal()); 00601 00602 if (w != 0.0F) ++num_nzw; 00603 00604 if (hasFacet<ChannelFacetGainComplex>()) 00605 w *= getFacet<ChannelFacetGainComplex>()->getVal(i); 00606 00607 if (w != 0.0f) 00608 { 00609 Image<float> subChanOut = subChan(i)->getOutput(); 00610 00611 if (subChanOut.getDims() != this->getMapDims()) 00612 LFATAL("Oops! In \"%s\", the output of " 00613 "\"%s\" (sub-channel %u/%u) was expected to be " 00614 "%dx%d, but was actually %dx%d", 00615 this->tagName().c_str(), 00616 subChan(i)->tagName().c_str(), 00617 i+1, this->numChans(), 00618 output.getWidth(), output.getHeight(), 00619 subChanOut.getWidth(), subChanOut.getHeight()); 00620 00621 if (w != 1.0f) subChanOut *= w; 00622 00623 output = mapCombine(itsCombineType.getVal(), 00624 output, subChanOut); 00625 00626 if (MYLOGVERB >= LOG_DEBUG) 00627 { 00628 float mi, ma, av; getMinMaxAvg(subChanOut, mi, ma, av); 00629 LDEBUG("%s: %s weight %f, range %g .. %g (mean %g)", 00630 tagName().c_str(), 00631 subChan(i)->tagName().c_str(), w, mi, ma, av); 00632 } 00633 } 00634 } 00635 00636 // if we didn't have any subchannel outputs, then just fill our 00637 // output with zeros 00638 if (!output.initialized()) 00639 output = Image<float>(getMapDims(), ZEROS); 00640 00641 // apply max-normalization on the output as needed, but only if we 00642 // have more than one subchan with non-zero weight; otherwise, that 00643 // means that we are just a shell for a singlechannel and should not 00644 // add this extra maxnorm: 00645 if (num_nzw > 1) 00646 { 00647 if (itsNormType.getVal() == VCXNORM_LANDMARK) 00648 { 00649 float goodness = pow(goodness_map(output), 0.05); 00650 LINFO("GOODNESS = %f", goodness); 00651 output *= goodness; 00652 } 00653 else 00654 { 00655 LDEBUG("%s: Normalizing output: %s(%f .. %f)", tagName().c_str(), 00656 maxNormTypeName(itsNormType.getVal()), itsOutputRangeMin.getVal(), 00657 itsOutputRangeMax.getVal()); 00658 output = maxNormalize(output, itsOutputRangeMin.getVal(), 00659 itsOutputRangeMax.getVal(), itsNormType.getVal()); 00660 } 00661 } 00662 00663 // print some debug info if in debug mode: 00664 if (MYLOGVERB >= LOG_DEBUG) 00665 { 00666 float mi, ma; getMinMax(output, mi, ma); 00667 LDEBUG("%s: final range [%f .. %f]", tagName().c_str(), mi, ma); 00668 } 00669 00670 LINFO("Computed %s Conspicuity Map", descriptiveName().c_str()); 00671 00672 return output; 00673 } 00674 00675 // ###################################################################### 00676 void ComplexChannel::saveResults(const nub::ref<FrameOstream>& ofs) 00677 { 00678 GVX_TRACE(__PRETTY_FUNCTION__); 00679 // save our own map: 00680 if (itsSaveOutputMap.getVal()) 00681 { 00682 std::string name = sformat("CO%s-", tagName().c_str()); 00683 00684 LINFO("SAVING RESULTS %s",name.c_str()); 00685 00686 ofs->writeFloat(getOutput(), FLOAT_NORM_0_255, name, 00687 FrameInfo(sformat("%s ComplexChannel output", 00688 this->descriptiveName().c_str()), 00689 SRC_POS)); 00690 } 00691 00692 // now do it for our subchannels: 00693 for (uint i = 0; i < numChans(); ++i) subChan(i)->saveResults(ofs); 00694 } 00695 00696 // ###################################################################### 00697 /* So things look consistent in everyone's emacs... */ 00698 /* Local Variables: */ 00699 /* indent-tabs-mode: nil */ 00700 /* End: */