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 #include "Image/OpenCVUtil.H"
00039
00040 #include "Channels/RawVisualCortex.H"
00041
00042 #include "Channels/ChannelOpts.H"
00043 #include "Channels/ChannelFacets.H"
00044 #include "Channels/ColorChannel.H"
00045 #include "Channels/CompositeColorChannel.H"
00046 #include "Channels/ContourChannel.H"
00047 #include "Channels/DepthChannel.H"
00048 #include "Channels/DepthMotionChannel.H"
00049 #include "Channels/DKLcolorChannel.H"
00050 #include "Channels/DummyChannel.H"
00051 #include "Channels/EndPointChannel.H"
00052 #include "Channels/FlickerChannel.H"
00053 #include "Channels/FlickerNBackChannel.H"
00054 #include "Channels/ForegroundDetectionChannel.H"
00055 #include "Channels/H2SVChannel.H"
00056 #include "Channels/CIELabChannel.H"
00057 #include "Channels/ImagizeColorChannel.H"
00058 #include "Channels/InputFrame.H"
00059 #include "Channels/IntensityBandChannel.H"
00060 #include "Channels/IntensityChannel.H"
00061 #include "Channels/Jet.H"
00062 #include "Channels/LJunctionChannel.H"
00063 #include "Channels/MotionChannel.H"
00064 #include "Channels/MotionSpatioTemporalChannel.H"
00065 #include "Channels/MotionOpticalFlowChannel.H"
00066 #include "Channels/MSTChannel.H"
00067 #include "Channels/FoeMSTChannel.H"
00068 #include "Channels/MultiColorBandChannel.H"
00069 #include "Channels/OrientationChannel.H"
00070 #include "Channels/PedestrianChannel.H"
00071 #include "Channels/SIFTChannel.H"
00072 #include "Channels/ObjDetChannel.H"
00073 #include "Channels/SkinHueChannel.H"
00074 #include "Channels/SoxChannel.H"
00075 #include "Channels/TJunctionChannel.H"
00076 #include "Channels/XJunctionChannel.H"
00077 #include "Channels/ZeroChannel.H"
00078 #include "Component/GlobalOpts.H"
00079 #include "Component/OptionManager.H"
00080 #include "Component/ParamMap.H"
00081 #include "Image/ColorOps.H"
00082 #include "Image/MathOps.H"
00083 #include "Image/Pixels.H"
00084 #include "Image/PyramidOps.H"
00085 #include "Image/ShapeOps.H"
00086 #include "Image/Transforms.H"
00087 #include "Media/MgzDecoder.H"
00088 #include "Media/MgzEncoder.H"
00089 #include "Media/MediaSimEvents.H"
00090 #include "Transport/FrameInfo.H"
00091 #include "Transport/FrameOstream.H"
00092 #include "Util/sformat.H"
00093 #include "rutz/mutex.h"
00094 #include "rutz/trace.h"
00095
00096 #include <algorithm>
00097 #include <cctype>
00098 #include <cstdio>
00099 #include <vector>
00100 #include <sstream>
00101
00102
00103 RawVisualCortex::RawVisualCortex(OptionManager& mgr,
00104 const std::string& descrName,
00105 const std::string& tag) :
00106 ComplexChannel(mgr, descrName, tag, UNKNOWN),
00107 itsType(&OPT_RawVisualCortexChans, this),
00108 itsNormType(&OPT_MaxNormType, this),
00109 itsUseRandom(&OPT_UseRandom, this),
00110 itsOutputFactor(&OPT_RawVisualCortexOutputFactor, this),
00111 itsNoise(&OPT_RawVisualCortexNoise, this),
00112 itsUseOlderVersion(&OPT_UseOlderVersion, this),
00113 itsLevelSpec(&OPT_LevelSpec, this),
00114 itsSaveOutTo(&OPT_VCXsaveOutTo, this),
00115 itsLoadOutFrom(&OPT_VCXloadOutFrom, this),
00116 itsSaveOutput(&OPT_RawVisualCortexSaveOutput, this),
00117 itsUseMax(&OPT_VCXuseMax, this),
00118 itsWeightThresh(&OPT_VCXweightThresh, this),
00119 itsOutputMgzIn(),
00120 itsOutputMgzOut()
00121 {
00122 GVX_TRACE(__PRETTY_FUNCTION__);
00123 itsFrame = 0;
00124 }
00125
00126
00127 RawVisualCortex::~RawVisualCortex()
00128 {
00129 GVX_TRACE(__PRETTY_FUNCTION__);
00130 }
00131
00132
00133 namespace
00134 {
00135 struct ChannelSorter
00136 {
00137 ChannelSorter(const RawVisualCortex* vcx_,
00138 bool dosubmaps_)
00139 :
00140 vcx(vcx_), dosubmaps(dosubmaps_)
00141 {}
00142
00143 bool operator()(uint i, uint j)
00144 {
00145 nub::ref<ChannelBase> chan1 = vcx->subChan(i);
00146 nub::ref<ChannelBase> chan2 = vcx->subChan(j);
00147
00148 const int level1 = int(featureHierarchyLevel(chan1->visualFeature()));
00149 const int level2 = int(featureHierarchyLevel(chan2->visualFeature()));
00150
00151 if (level1 < level2)
00152 return true;
00153
00154
00155
00156 if (dosubmaps && level1 == level2)
00157 return chan1->numSubmaps() > chan2->numSubmaps();
00158
00159
00160
00161 return false;
00162 }
00163
00164 const RawVisualCortex* vcx;
00165 bool dosubmaps;
00166 };
00167 }
00168
00169
00170 void RawVisualCortex::start1()
00171 {
00172 GVX_TRACE(__PRETTY_FUNCTION__);
00173
00174 ComplexChannel::start1();
00175
00176
00177 if (itsSaveOutTo.getVal().empty() == false)
00178 itsOutputMgzOut = rutz::make_shared(new MgzEncoder(itsSaveOutTo.getVal(), 9));
00179
00180 if (itsLoadOutFrom.getVal().empty() == false)
00181 itsOutputMgzIn = rutz::make_shared(new MgzDecoder(itsLoadOutFrom.getVal()));
00182
00183 for (uint i = 0; i < this->numChans(); ++i)
00184 {
00185 LINFO("Top-level channel (%u/%u): level=%s feature=%s submaps=%u name=%s",
00186 i+1, this->numChans(),
00187 featureHierarchyName(featureHierarchyLevel(this->subChan(i)->visualFeature())),
00188 featureName(this->subChan(i)->visualFeature()),
00189 this->subChan(i)->numSubmaps(),
00190 this->subChan(i)->descriptiveName().c_str());
00191 }
00192 }
00193
00194
00195 void RawVisualCortex::stop2()
00196 {
00197 GVX_TRACE(__PRETTY_FUNCTION__);
00198
00199 if (itsOutputMgzOut.is_valid()) itsOutputMgzOut->close();
00200
00201 ComplexChannel::stop2();
00202 }
00203
00204
00205 void RawVisualCortex::paramChanged(ModelParamBase* const param,
00206 const bool valueChanged,
00207 ParamClient::ChangeStatus* status)
00208 {
00209 ModelComponent::paramChanged(param, valueChanged, status);
00210 OptionManager& mgr = this->getManager();
00211
00212 if (param == &itsType) {
00213
00214 this->removeAllSubChans();
00215 ASSERT(this->numChans() == 0);
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230 uint i = 0; const std::string& str = itsType.getVal(); const uint len = str.length();
00231 std::vector<double> ww(('z' - 'A')+1, 0.0);
00232
00233 while (i < len) {
00234 char c = str[i];
00235 if (! ((c >= 'A' && c <= 'Z') || (c >= 'a' && c < 'z')) )
00236 LFATAL("Invalid channel character 0x%x (%c)", c, c);
00237 ww[c - 'A'] = 1.0;
00238
00239
00240 if (i < len - 1 && str[i+1] == ':') {
00241 if (i >= len - 2) LFATAL("Missing channel weight value");
00242 uint end = str.find_first_not_of(".0123456789", i+2);
00243 std::stringstream s; s << str.substr(i+2, end); s >> ww[c - 'A'];
00244 i = end;
00245 } else ++i;
00246 }
00247
00248
00249 if (double w = ww['C' - 'A']) {
00250 LINFO("Using a Double Opponent ColorChannel with weight %f", w);
00251 addSubChan(makeSharedComp(new ColorChannel(mgr)), "color", w);
00252 }
00253 if (double w = ww['S' - 'A']) {
00254 LINFO("Using a Composite ColorChannel with weight %f", w);
00255 addSubChan(makeSharedComp(new CompositeColorChannel(mgr)), "composite-color", w);
00256 }
00257 if (double w = ww['F' - 'A']) {
00258 LINFO("Using a FlickerChannel with weight %f", w);
00259 addSubChan(makeSharedComp(new FlickerChannel(mgr)), "flicker", w);
00260 }
00261 if (double w = ww['I' - 'A']) {
00262 LINFO("Using an IntensityChannel with weight %f", w);
00263 addSubChan(makeSharedComp(new IntensityChannel(mgr)), "intensity", w);
00264 }
00265 if (double w = ww['O' - 'A']) {
00266 LINFO("Using an OrientationChannel with weight %f", w);
00267 addSubChan(makeSharedComp(new OrientationChannel(mgr)), "orientation", w);
00268 }
00269 if (double w = ww['M' - 'A']) {
00270 LINFO("Using a MotionChannel with weight %f", w);
00271 addSubChan(makeSharedComp(new MotionChannel(mgr)), "motion", w);
00272 }
00273
00274 if (double w = ww['s' - 'A']) {
00275 LINFO("Using a MotionSpatioTemporalChannel with weight %f", w);
00276 addSubChan(makeSharedComp(new MotionSpatioTemporalChannel(mgr)), "motionSpatioTemporal", w);
00277 }
00278
00279 if (double w = ww['o' - 'A']) {
00280 LINFO("Using a MotionOpticalFlowChannel with weight %f", w);
00281 addSubChan(makeSharedComp(new MotionOpticalFlowChannel(mgr)), "motionOpticalFlow", w);
00282 }
00283
00284 if (double w = ww['B' - 'A']) {
00285 LINFO("Using a FOE MSTChannel with weight %f", w);
00286 if (!this->hasSubChan("motion"))
00287 LFATAL("Must have a Motion Channel to build an FoeMSTChannel; specify 'M' before 'B' in --vc-chans");
00288 addSubChan(makeSharedComp(new FoeMSTChannel(mgr, dynCastWeak<MotionChannel>
00289 (subChan("motion")))), "FoeMST", w);
00290 }
00291
00292 if (double w = ww['L' - 'A']) {
00293 LINFO("Using an L Junction Channel with weight %f", w);
00294 if (!this->hasSubChan("orientation"))
00295 LFATAL("Must have an OrientationChannel to build an LJunctionChannel; specify 'O' before 'L' in --vc-chans");
00296 addSubChan(makeSharedComp(new LJunctionChannel(mgr, dynCastWeak<OrientationChannel>
00297 (subChan("orientation")))), "LJunction", w);
00298 }
00299
00300 if (double w = ww['T' - 'A']) {
00301 LINFO("Using a T Junction Channel with weight %f", w);
00302 if (!this->hasSubChan("orientation"))
00303 LFATAL("Must have an OrientationChannel to build an TJunctionChannel; specify 'O' before 'T' in --vc-chans");
00304 addSubChan(makeSharedComp(new TJunctionChannel(mgr, dynCastWeak<OrientationChannel>
00305 (subChan("orientation")))), "TJunction", w);
00306 }
00307
00308 if (double w = ww['X' - 'A']) {
00309 LINFO("Using an X Junction Channel with weight %f", w);
00310 if (!this->hasSubChan("orientation"))
00311 LFATAL("Must have an OrientationChannel to build an XJunctionChannel; specify 'O' before 'X' in --vc-chans");
00312 addSubChan(makeSharedComp(new XJunctionChannel(mgr, dynCastWeak<OrientationChannel>
00313 (subChan("orientation")))), "XJunction", w);
00314 }
00315
00316 if (double w = ww['E' - 'A']) {
00317 LINFO("Using an EndPoint Channel with weight %f", w);
00318 if (!this->hasSubChan("orientation"))
00319 LFATAL("Must have an OrientationChannel to build an EndPointChannel; specify 'O' before 'E' in --vc-chans");
00320 addSubChan(makeSharedComp(new EndPointChannel(mgr, dynCastWeak<OrientationChannel>
00321 (subChan("orientation")))), "EndPoint", w);
00322 }
00323
00324 if (double w = ww['G' - 'A']) {
00325 LINFO("Using Gaussian multi-color band channels with weight %f", w);
00326 addSubChan(makeSharedComp(new MultiColorBandChannel(mgr)), "MultiColorBand", w);
00327 }
00328 if (double w = ww['D' - 'A']) {
00329 LINFO("Using a Dummy Channel with weight %f", w);
00330 addSubChan(makeSharedComp(new DummyChannel(mgr)), "Dummy", w);
00331 }
00332 if (double w = ww['K' - 'A']) {
00333 LINFO("Using a SkinHue channel with weight %f", w);
00334 addSubChan(makeSharedComp(new SkinHueChannel(mgr)), "SkinHue", w);
00335 }
00336 if (double w = ww['N' - 'A']) {
00337 LINFO("Using an Intensity Band channel with weight %f", w);
00338 addSubChan(makeSharedComp(new IntensityBandChannel(mgr)), "IntensityBand", w);
00339 }
00340 if (double w = ww['P' - 'A']) {
00341 LINFO("Using a SIFT channel with weight %f", w);
00342 addSubChan(makeSharedComp(new SIFTChannel(mgr)), "SIFTBand", w);
00343 }
00344 if (double w = ww['H' - 'A']) {
00345 LINFO("Using a H2SV channel with weight %f", w);
00346 addSubChan(makeSharedComp(new H2SVChannel(mgr)), "H2SVcolor", w);
00347 }
00348 if (double w = ww['Q' - 'A']) {
00349 LINFO("Using a CIELab channel with weight %f", w);
00350 addSubChan(makeSharedComp(new CIELabChannel(mgr)), "CIELabcolor", w);
00351 }
00352 if (double w = ww['Y' - 'A']) {
00353 LINFO("Using a Depth channel with weight %f", w);
00354 addSubChan(makeSharedComp(new DepthChannel(mgr)), "Depth", w);
00355 }
00356 if (double w = ww['y' - 'A']) {
00357 LINFO("Using a Depth Motion channel with weight %f", w);
00358 addSubChan(makeSharedComp(new DepthMotionChannel(mgr)), "DepthMotion", w);
00359 }
00360 if (double w = ww['R' - 'A']) {
00361 LINFO("Using a Pedestrian channel with weight %f", w);
00362 addSubChan(makeSharedComp(new PedestrianChannel(mgr)), "Pedestrian", w);
00363 }
00364 if (double w = ww['V' - 'A']) {
00365 LINFO("Using a SoxChannel with weight %f", w);
00366 addSubChan(makeSharedComp(new SoxChannel(mgr)), "Sox", w);
00367 }
00368 if (double w = ww['W' - 'A']) {
00369 LINFO("Using a ContourChannel with weight %f", w);
00370 addSubChan(makeContourChannel(mgr, "contour"), "Contour", w);
00371 }
00372 if (double w = ww['Z' - 'A']) {
00373 LINFO("Using a dummy Zero Channel with weight %f", w);
00374 addSubChan(makeSharedComp(new ZeroChannel(mgr)), "Zero", w);
00375 }
00376 if (double w = ww['A' - 'A']) {
00377 LINFO("Using an Object Detection channel with weight %f", w);
00378 addSubChan(makeSharedComp(new ObjDetChannel(mgr)), "ObjDet", w);
00379 }
00380 if (double w = ww['J' - 'A']) {
00381 LINFO("Using a DKL Color channel with weight %f", w);
00382 addSubChan(makeSharedComp(new DKLcolorChannel(mgr)), "DKLcolor", w);
00383 }
00384 if (double w = ww['U' - 'A']) {
00385 LINFO("Using Foreground Detection channel with weight %f", w);
00386 addSubChan(makeSharedComp(new ForegroundDetectionChannel(mgr)), "Foreground", w);
00387 }
00388 if (double w = ww['i' - 'A']) {
00389 LINFO("Using an Imagize Composite channel with weight %f", w);
00390 addSubChan(makeSharedComp(new ImagizeColorChannel(mgr)), "ImagizeComposite", w);
00391 }
00392 if (double w = ww['f' - 'A']) {
00393 LINFO("Using a FlickerNBackChannel with weight %f", w);
00394 addSubChan(makeSharedComp(new FlickerNBackChannel(mgr)), "flicker", w);
00395 }
00396 LINFO("RawVisualCortex loaded with %u top-level channels.", numChans());
00397
00398
00399 for (uint i = 0; i < this->numChans(); ++i) this->subChan(i)->exportOptions(MC_RECURSE);
00400 }
00401 }
00402
00403
00404 const Image<float> RawVisualCortex::getVCOutput(const Image<PixRGB<byte> >&rgbin)
00405 {
00406
00407 static SimTime time = SimTime::ZERO();
00408
00409 time += SimTime::MSECS(10);
00410
00411 const InputFrame ifr = InputFrame::fromRgb(&rgbin, time);
00412
00413 this->input(ifr);
00414
00415 return getOutput();
00416 }
00417
00418
00419 Image<float> RawVisualCortex::getChannelOutputMap(const uint idx) const
00420 {
00421 nub::ref<ChannelBase> ch = subChan(idx);
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431 float w = float(this->getSubchanTotalWeight(*ch));
00432
00433 if (itsUseOlderVersion.getVal()) w /= ch->numSubmaps();
00434
00435 if (hasFacet<ChannelFacetGainComplex>())
00436 w *= getFacet<ChannelFacetGainComplex>()->getVal(idx);
00437
00438 if (w == 0.0)
00439 {
00440 LDEBUG("%12s weight is 0.0 (%2u submaps) -- skipped",
00441 ch->tagName().c_str(),ch->numSubmaps());
00442 return Image<float>();
00443 }
00444
00445
00446 Image<float> chanOut = ch->getOutput();
00447
00448 float mi1 = -1.0f, ma1 = -1.0f;
00449 if (MYLOGVERB >= LOG_DEBUG) getMinMax(chanOut, mi1, ma1);
00450
00451 if (w != 1.0f) chanOut *= w;
00452
00453 if (MYLOGVERB >= LOG_DEBUG)
00454 {
00455 float mi, ma; getMinMax(chanOut, mi, ma);
00456 LDEBUG("%12s weight is %.4f (%2u submaps); raw range is "
00457 "%.2g .. %.2g; weighted range is %.2g .. %.2g",
00458 ch->tagName().c_str(), w, ch->numSubmaps(), mi1,ma1,mi,ma);
00459 }
00460
00461 return chanOut;
00462 }
00463
00464
00465 Image<float> RawVisualCortex::postProcessOutputMap(const Image<float>& outmap)
00466 {
00467
00468
00469
00470 Image<float> result = outmap;
00471
00472
00473
00474
00475 uint num_nzw = 0;
00476 for (uint i = 0; i < numChans(); ++i) if (getSubchanTotalWeight(i) > itsWeightThresh.getVal()) ++num_nzw;
00477
00478 if (num_nzw > 1)
00479 {
00480 switch(itsNormType.getVal())
00481 {
00482 case VCXNORM_LANDMARK:
00483 result = maxNormalize(result, 0.0f, 2.0f, VCXNORM_NONE);
00484 break;
00485 case VCXNORM_SURPRISE:
00486 LFATAL("Surprise maxnorm must use RawVisualCortexSurprise");
00487 break;
00488 default:
00489 result = maxNormalize(result, 0.0f, 2.0f, itsNormType.getVal());
00490 LDEBUG("%s(%f .. %f)", maxNormTypeName(itsNormType.getVal()),
00491 0.0f, 2.0f);
00492 break;
00493 }
00494 }
00495
00496 return result;
00497 }
00498
00499
00500 void RawVisualCortex::doInput(const InputFrame& inframe)
00501 {
00502
00503 if (this->numChans() == 0) return;
00504
00505
00506
00507
00508 if (itsLoadOutFrom.getVal().empty() == false)
00509 {
00510 LINFO("Not feeding channels since we will load output from MGZ file.");
00511 return;
00512 }
00513
00514
00515 rutz::mutex_lock_class lock;
00516 if (inframe.pyrCache().get() != 0
00517 && inframe.pyrCache()->gaussian5.beginSet(inframe.grayFloat(), &lock))
00518 {
00519 if (itsUseSpaceVariantBoundary.getVal())
00520 inframe.pyrCache()->gaussian5.endSet
00521 (inframe.grayFloat(),
00522 buildRadialPyrGaussian
00523 (inframe.grayFloat(), 0, itsLevelSpec.getVal().maxDepth()),
00524 &lock);
00525 else
00526 inframe.pyrCache()->gaussian5.endSet
00527 (inframe.grayFloat(),
00528 buildPyrGaussian
00529 (inframe.grayFloat(), 0, itsLevelSpec.getVal().maxDepth(), 5),
00530 &lock);
00531 }
00532
00533
00534 for (uint i = 0; i < this->numChans(); ++i)
00535 {
00536
00537 nub::ref<ChannelBase> chan = this->subChan(i);
00538
00539
00540 chan->input(inframe);
00541 LINFO("Input to %s channel %s ok.",
00542 featureHierarchyName(featureHierarchyLevel(chan->visualFeature())),
00543 chan->tagName().c_str());
00544 }
00545 }
00546
00547
00548 Image<float> RawVisualCortex::combineOutputs()
00549 {
00550 GVX_TRACE(__PRETTY_FUNCTION__);
00551
00552 Image<float> output;
00553
00554
00555 if (itsLoadOutFrom.getVal().empty() == false)
00556 {
00557 LINFO("Bypassing RawVisualCortex output computations.");
00558 LINFO("Loading RawVisualCortex output from '%s'",
00559 itsLoadOutFrom.getVal().c_str());
00560 output = (itsOutputMgzIn->readFrame()).asFloat();
00561 }
00562 else
00563 {
00564
00565 for (uint i = 0; i < this->numChans(); ++i)
00566 {
00567 if (subChan(i)->outputAvailable() == false) continue;
00568
00569 Image<float> chanOut = getChannelOutputMap(i);
00570
00571 if (chanOut.initialized() == false) continue;
00572
00573
00574
00575
00576
00577
00578 if (output.initialized() == false)
00579 output = chanOut;
00580 else
00581 {
00582
00583 Image<float> o = downSizeClean(chanOut, output.getDims());
00584 if (itsUseMax.getVal()) output = takeMax(output, o); else output += o;
00585 }
00586 }
00587 }
00588
00589
00590
00591 if (output.initialized() == false)
00592 {
00593 int sml = itsLevelSpec.getVal().mapLevel();
00594 output = Image<float>(this->getInputDims().w() >> sml,
00595 this->getInputDims().h() >> sml, ZEROS);
00596 }
00597
00598 if (MYLOGVERB >= LOG_DEBUG)
00599 {
00600 float mi, ma; getMinMax(output, mi, ma);
00601 LDEBUG("Raw output range is [%f .. %f]", mi, ma);
00602 }
00603
00604
00605 if (itsSaveOutTo.getVal().empty() == false)
00606 {
00607 LINFO("Saving raw unnormalized RawVisualCortex output to '%s'",
00608 itsSaveOutTo.getVal().c_str());
00609 itsOutputMgzOut->writeFrame(GenericFrame(output, FLOAT_NORM_PRESERVE));
00610 }
00611
00612
00613
00614 output = postProcessOutputMap(output);
00615
00616
00617
00618 output *= itsOutputFactor.getVal();
00619 float mi, ma; getMinMax(output, mi, ma);
00620 LINFO("Salmap input range is [%f .. %f] nA", mi * 1.0e9F, ma * 1.0e9F);
00621
00622
00623
00624 if (itsUseRandom.getVal())
00625 {
00626 LINFO("Adding random background noise to output.");
00627 inplaceAddBGnoise(output, itsNoise.getVal());
00628 output += itsNoise.getVal() * 0.01F;
00629 }
00630
00631
00632
00633 LINFO("Computed RawVisualCortex output.");
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655 return output;
00656 }
00657
00658
00659 void RawVisualCortex::saveResults(const nub::ref<FrameOstream>& ofs)
00660 {
00661 GVX_TRACE(__PRETTY_FUNCTION__);
00662
00663 if (itsSaveOutput.getVal())
00664 ofs->writeFloat(getOutput(), FLOAT_NORM_PRESERVE, "VCO",
00665 FrameInfo("visual cortex output "
00666 "(input to saliency map)", SRC_POS));
00667
00668
00669 for (uint i = 0; i < numChans(); ++i) subChan(i)->saveResults(ofs);
00670 }
00671
00672
00673
00674
00675
00676