00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
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
00096
00097 if (dosubmaps && level1 == level2)
00098 return chan1->numSubmaps() > chan2->numSubmaps();
00099
00100
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;
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
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
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
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];
00161 }
00162 };
00163
00164
00165
00166
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
00211
00212 if (itsUseOlderVersion.getVal() == false)
00213 {
00214 itsOutputRangeMin.setVal(0.0f);
00215 itsOutputRangeMax.setVal(0.0f);
00216 }
00217
00218
00219
00220
00221
00222 if (itsUseOlderVersion.getVal() && itsNormType.getVal() != VCXNORM_MAXNORM)
00223 {
00224 itsOutputRangeMin.setVal(0.0f);
00225 itsOutputRangeMax.setVal(0.0f);
00226 }
00227
00228
00229
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 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
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
00464 if (tagname != 0 && *tagname != '\0')
00465 ch->setTagName(tagname);
00466
00467
00468 this->addSubComponent(ch);
00469
00470
00471 rep->subchans.push_back(SubchanInfo(this, ch, weight));
00472
00473
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
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();
00565
00566
00567 rep->output.freeMem();
00568
00569
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;
00586
00587
00588 uint num_nzw = 0;
00589
00590 for (uint i = 0; i < numChans(); ++i)
00591 {
00592
00593
00594
00595
00596
00597
00598
00599
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
00637
00638 if (!output.initialized())
00639 output = Image<float>(getMapDims(), ZEROS);
00640
00641
00642
00643
00644
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
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
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
00693 for (uint i = 0; i < numChans(); ++i) subChan(i)->saveResults(ofs);
00694 }
00695
00696
00697
00698
00699
00700