00001 /*!@file Channels/RawVisualCortex.C Implementation for visual cortex class */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // 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/RawVisualCortex.C $ 00035 // $Id: RawVisualCortex.C 14559 2011-03-01 22:02:47Z ehu $ 00036 // 00037 00038 #include "Image/OpenCVUtil.H" // must be included first to avoid possible type conflicts 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" // for luminance() 00082 #include "Image/MathOps.H" // for distance() 00083 #include "Image/Pixels.H" 00084 #include "Image/PyramidOps.H" 00085 #include "Image/ShapeOps.H" 00086 #include "Image/Transforms.H" // for chamfer34() 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), // see Channels/ChannelOpts.{H,C} 00109 itsUseRandom(&OPT_UseRandom, this), // see Component/ModelManager.{H,C} 00110 itsOutputFactor(&OPT_RawVisualCortexOutputFactor, this), // idem 00111 itsNoise(&OPT_RawVisualCortexNoise, this), // idem 00112 itsUseOlderVersion(&OPT_UseOlderVersion, this), // Channels/ChannelOpts.{H,C} 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 // else ... 00155 00156 if (dosubmaps && level1 == level2) 00157 return chan1->numSubmaps() > chan2->numSubmaps(); 00158 00159 // else ... 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 // initialize out input or output MGZ handlers if necessary: 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 // kill any existing channels: 00214 this->removeAllSubChans(); 00215 ASSERT(this->numChans() == 0); 00216 00217 // Parse the param string. format here is a series of "X[:w.w]" 00218 // where X is any of "ICOFM..." and the optional w.w may be 00219 // channel weights. NOTE: sadly, the final saliency map is (very 00220 // slightly) affected by the order in which the different channels 00221 // sum into it. If we just create the channel objects as we parse 00222 // the param string here, this will lead to --vc-chans=CIO might 00223 // give a slightly different saliency map than 00224 // --vc-chans=IOC. This seems to be due to rounding during 00225 // floating-point computations. Hence, here we parse the param 00226 // string in two steps: first, parse the user input and find out 00227 // which channels should be built; second, build the channels, in 00228 // a fixed order than does not depend on the ordering of the 00229 // letters in the user input: 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]; // the channel to implement 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; // default weight value 00238 00239 // do we have a weight specification? 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; // ready for next one 00245 } else ++i; 00246 } 00247 00248 // Create the channels and assign weights, creating in a fixed order: 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 // make sure options are exported for all channels: 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 // Determine the weight for each subchan. This is the product of 00424 // the intrinsic weight of each subchan (which is set either 00425 // through the --vc-chans config string of RawVisualCortex for 00426 // top-level channels, or subject to static modifications by 00427 // using --load at the start of the entire model and setting the 00428 // NModelParam value associated with each channel), multiplied 00429 // by any possible extra top-down gain if we have a 00430 // ChannelFacetGainComplex: 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 // get the raw channel output and compute its range (for debug msg): 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 // CAUTION: if you make major changes to the default behavior here, 00468 // make sure you check that this is compatible with the various 00469 // derivatives of RawVisualCortex (e.g., RawVisualCortexBeo, etc). 00470 Image<float> result = outmap; 00471 00472 // let's apply a last maxNormalization to the map, but only if we 00473 // have two or more channels with non-zero weights, otherwise, we 00474 // just pass through: 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 // optimization: if we have no channels, then do a quick return: 00503 if (this->numChans() == 0) return; 00504 00505 // we will not process the input at all if itsLoadOutFrom is set, 00506 // since we will instead read our final output map from disk in 00507 // getOutput(): 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 // build our pyramid cache? 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 // process the channels: 00534 for (uint i = 0; i < this->numChans(); ++i) 00535 { 00536 // get a pointer to the channel of interest: 00537 nub::ref<ChannelBase> chan = this->subChan(i); 00538 00539 // feed the input to the channel of interest: 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 // are we reading our precomputed outputs from an MGZ file? 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 // ... OK, we have to recompute the output: 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 // downSizeClean() gracefully does nothing if chanOut is 00574 // already the right size. (Eventually we might like to have a 00575 // way for RawVisualCortex to enforce a particular chanOut size 00576 // on its own, rather than just taking the size from the first 00577 // channel in the array.) 00578 if (output.initialized() == false) 00579 output = chanOut; 00580 else 00581 { 00582 // sum or take max across channels? 00583 Image<float> o = downSizeClean(chanOut, output.getDims()); 00584 if (itsUseMax.getVal()) output = takeMax(output, o); else output += o; 00585 } 00586 } 00587 } 00588 00589 // Ensure that we still return a valid image even if we have no channels 00590 // that has a valid output in the RawVisualCortex: 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 // before we finalize the output, do we want to save it to an MGZ file? 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 // post-process the output in a manner than may depend on which 00613 // variant of RawVisualCortex we may embody: 00614 output = postProcessOutputMap(output); 00615 00616 // output is now typically in the (0.0..8.0) range; 00617 // typical images are in (0..4) range; we want input current in nA 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 // add a tiny background activity to ensure that the entire image 00623 // will be visited: 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 // uint width = output.getWidth(); 00636 // uint height = output.getHeight(); 00637 // LINFO("w: %d h: %d", width, height); 00638 00639 // if(itsWin.is_invalid()) 00640 // itsWin.reset(new XWinManaged(Dims(width,height), 0, 0, "rVC")); 00641 // itsWin->setDims(Dims(width*16, height*16)); 00642 // itsWin->drawImage(zoomXY(output,16),0,0); 00643 // Raster::waitForKey(); 00644 00645 // std::string folder("/lab/tmpib/u/siagian/neuroscience/Data/FOE/" 00646 // "DARPA_nv2_2011/Germany_K_CIOFM/"); 00647 // Image<float> img = zoomXY(output,16); 00648 // inplaceNormalize(img, 0.0F, 255.0F); 00649 // Image<byte> res(img); 00650 // Raster::WriteRGB(res, sformat("%sSalMap_CIOFM_%06d.ppm", 00651 // folder.c_str(), itsFrame)); 00652 00653 // itsFrame++; 00654 00655 return output; 00656 } 00657 00658 // ###################################################################### 00659 void RawVisualCortex::saveResults(const nub::ref<FrameOstream>& ofs) 00660 { 00661 GVX_TRACE(__PRETTY_FUNCTION__); 00662 // save our own output: 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 // save our channel outputs: 00669 for (uint i = 0; i < numChans(); ++i) subChan(i)->saveResults(ofs); 00670 } 00671 00672 // ###################################################################### 00673 /* So things look consistent in everyone's emacs... */ 00674 /* Local Variables: */ 00675 /* indent-tabs-mode: nil */ 00676 /* End: */