SimulationViewerStd.C

Go to the documentation of this file.
00001 /*!@file Neuro/SimulationViewerStd.C visualize various model simulations */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003   //
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: Laurent Itti <itti@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/SimulationViewerStd.C $
00035 // $Id: SimulationViewerStd.C 14503 2011-02-14 00:47:49Z dberg $
00036 //
00037 
00038 #include "Neuro/SimulationViewerStd.H"
00039 
00040 #include "Channels/ChannelMaps.H"
00041 #include "Channels/ChannelOpts.H"
00042 #include "Component/OptionManager.H"
00043 #include "Image/ColorOps.H"    // for normalizeC()
00044 #include "Image/DrawOps.H"
00045 #include "Image/MathOps.H"   // for takeMax()
00046 #include "Image/PyramidOps.H" // for foveate()
00047 #include "Image/ShapeOps.H"  // for crop()
00048 #include "Image/Transforms.H"  // for contour2D()
00049 #include "Neuro/AttentionGuidanceMap.H"
00050 #include "Neuro/Brain.H"
00051 #include "Neuro/NeuroOpts.H"
00052 #include "Neuro/NeuroSimEvents.H"
00053 #include "Neuro/SaliencyMap.H"
00054 #include "Neuro/SpatialMetrics.H"
00055 #include "Neuro/TaskRelevanceMap.H"
00056 #include "Neuro/VisualCortex.H"
00057 #include "Simulation/SimEventQueue.H"
00058 #include "Simulation/SimulationOpts.H"
00059 #include "Transport/FrameInfo.H"
00060 #include "Transport/FrameOstream.H"
00061 #include "Util/StringUtil.H"
00062 #include "Util/TransientStatus.H"
00063 #include "Util/sformat.H"
00064 
00065 // ######################################################################
00066 SimulationViewerStd::SimulationViewerStd(OptionManager& mgr,
00067                                          const std::string& descrName,
00068                                          const std::string& tagName) :
00069   SimulationViewerAdapter(mgr, descrName, tagName),
00070   SIMCALLBACK_INIT(SimEventSaccadeStatusEye),
00071   SIMCALLBACK_INIT(SimEventSaccadeStatusHead),
00072   SIMCALLBACK_INIT(SimEventSaveOutput),
00073   SIMCALLBACK_INIT(SimEventITOutput),
00074   itsTimeStep(&OPT_SimulationTimeStep, this),
00075   itsLevelSpec(&OPT_LevelSpec, this),
00076   itsMetrics(new SpatialMetrics(mgr)),
00077   itsFontSize(&OPT_SVfontSize, this),
00078   itsSaveTraj(&OPT_SVsaveTraj, this),
00079   itsSaveXcombo(&OPT_SVsaveXcombo, this),
00080   itsSaveYcombo(&OPT_SVsaveYcombo, this),
00081   itsSaveTRMXcombo(&OPT_SVsaveTRMXcombo, this),
00082   itsSaveTRMYcombo(&OPT_SVsaveTRMYcombo, this),
00083   itsSaveTRMmegaCombo(&OPT_SVsaveTRMmegaCombo, this),
00084   itsWarp3D(&OPT_SVwarp3D, this),
00085   itsMegaCombo(&OPT_SVmegaCombo, this),
00086   itsMegaComboZoom(&OPT_SVmegaComboZoom, this),
00087   itsMegaComboTopCMapsOnly(&OPT_SVmegaComboTopCMapsOnly, this),
00088   itsCropFOA(&OPT_SVcropFOA, this),
00089   itsFoveateTraj(&OPT_SVfoveateTraj, this),
00090   itsDisplayFOA(&OPT_SVdisplayFOA, this),
00091   itsDisplayPatch(&OPT_SVdisplayPatch, this),
00092   itsDisplayFOAnum(&OPT_SVdisplayFOAnum, this),
00093   itsDisplayFOALinks(&OPT_SVdisplayFOALinks, this),
00094   itsDisplayEye(&OPT_SVdisplayEye, this),
00095   itsDisplayEyeLinks(&OPT_SVdisplayEyeLinks, this),
00096   itsDisplayHead(&OPT_SVdisplayHead, this),
00097   itsDisplayHeadLinks(&OPT_SVdisplayHeadLinks, this),
00098   itsDisplayTime(&OPT_SVdisplayTime, this),
00099   itsDisplayAdditive(&OPT_SVdisplayAdditive, this),
00100   itsDisplayHighlights(&OPT_SVdisplayHighlights, this),
00101   itsDisplaySMmodulate(&OPT_SVdisplaySMmodulate, this),
00102   itsDisplayBoring(&OPT_SVdisplayBoring, this),
00103   itsDisplayShapeEstimator("SVdisplayShapeEstimator", this, false),
00104   itsColorNormal("SVcolorNormal", this, PixRGB<byte>(255, 255, 0)),
00105   itsColorBoring("SVcolorBoring", this, PixRGB<byte>(127, 0, 0)),
00106   itsColorBlink("SVcolorBlink", this, PixRGB<byte>(0, 128, 255)),
00107   itsColorSaccade("SVcolorSaccade", this, PixRGB<byte>(255, 128, 255)),
00108   itsColorSmoothPursuit("SVcolorSmoothPursuit", this,
00109                         PixRGB<byte>(128, 255, 255)),
00110   itsColorLink("SVcolorLink", this, PixRGB<byte>(255, 0, 0)),
00111   itsColorText("SVcolorText", this, PixRGB<byte>(192, 255, 64)),
00112   itsHighlightMax("SVhighlightMax", this, 320),
00113   itsShapeEstimatorBaseContrast("SVShapeEstimatorBaseContrast", this, 0.1F),
00114   itsShapeEstimatorBaseBright("SVShapeEstimatorBaseBright", this, 128),
00115   itsWarp3Dpitch("SVwarp3DInitialPitch", this, -25.0F),
00116   itsWarp3Dyaw("SVwarp3DInitialYaw", this, -15.0F),
00117   itsWarp3DpitchRate("SVwarp3DpitchRate", this, 0.0F),
00118   itsWarp3DyawRate("SVwarp3DyawRate", this, 15.0F),
00119   itsWarp3DpitchMax("SVwarp3DpitchMax", this, 0.0F),
00120   itsWarp3DyawMax("SVwarp3DyawMax", this, 20.0F),
00121   itsHeadRadius(&OPT_HeadMarkerRadius, this),
00122   itsMultiRetinaDepth(&OPT_MultiRetinaDepth, this),
00123   itsFOApsiz("SVfoapsiz", this, 3),
00124   itsFOAthick("SVfoathick", this, 3),
00125   itsFOAlinkThick("SVfoalinkthick", this, 2),
00126   itsFOVpsiz("SVfovpsiz", this, 3),
00127   itsFOVpthick("SVfovpthick", this, 1),
00128   itsFOVthick("SVfovthick", this, 2),
00129   itsFOVlinkThick("SVfovlinkthick", this, 1),
00130   itsHEDpsiz("SVhedpsiz", this, 9),
00131   itsHEDpthick("SVhedpthick", this, 1),
00132   itsHEDthick("SVhedthick", this, 1),
00133   itsHEDlinkThick("SVhedlinkthick", this, 1),
00134   itsUseLargerDrawings(&OPT_SVuseLargerDrawings, this),
00135   itsObsolete1(&OPT_SVxwindow, this),
00136   itsTraj(),
00137   itsFOAshiftNum(0U),
00138   itsCurrFOAmask(),
00139   itsCumFOAmask(),
00140   itsCurrEye(-1, -1),
00141   itsCurrFoveaMask(),
00142   itsCumFoveaMask(),
00143   itsCurrHead(-1, -1),
00144   itsCurrHeadMask(),
00145   itsCumHeadMask(),
00146   itsPrevEye(-1, -1),
00147   itsPrevHead(-1, -1),
00148   itsCurrTime(),
00149   itsEyeSaccade(false),
00150   itsEyeSmoothPursuit(false),
00151   itsHeadSaccade(false),
00152   itsHeadSmoothPursuit(false),
00153   itsEyeBlink(false),
00154   itsMultiTraj(),
00155   itsDims3D(),
00156   itsPitch3D(-1.0e10F),
00157   itsYaw3D(-1.0e10F),
00158   itsHasNewInput(false),
00159   itsFont(SimpleFont::FIXED(10))
00160 {
00161   this->addSubComponent(itsMetrics);
00162 }
00163 
00164 // ######################################################################
00165 SimulationViewerStd::~SimulationViewerStd()
00166 { }
00167 
00168 // ######################################################################
00169 void SimulationViewerStd::reset1()
00170 {
00171   // reset some stuff for SimulationViewerStd
00172   itsTraj.freeMem();
00173   itsFOAshiftNum = 0U;
00174   itsCurrFOAmask.freeMem();
00175   itsCumFOAmask.freeMem();
00176   itsCurrEye = Point2D<int>(-1, -1);
00177   itsCurrFoveaMask.freeMem();
00178   itsCumFoveaMask.freeMem();
00179   itsCurrHead = Point2D<int>(-1, -1);
00180   itsCurrHeadMask.freeMem();
00181   itsCumHeadMask.freeMem();
00182   itsPrevEye = Point2D<int>(-1, -1);
00183   itsPrevHead = Point2D<int>(-1, -1);
00184   itsCurrTime = SimTime::ZERO();
00185   itsEyeSaccade = false;
00186   itsEyeSmoothPursuit = false;
00187   itsHeadSaccade = false;
00188   itsHeadSmoothPursuit = false;
00189   itsEyeBlink = false;
00190   itsMultiTraj.reset();
00191   itsDims3D = Dims();
00192   itsPitch3D = -1.0e10F;
00193   itsYaw3D = -1.0e10F;
00194   itsHasNewInput = false;
00195 
00196   // propagate to our base class:
00197   SimulationViewerAdapter::reset1();
00198 
00199 }
00200 
00201 // ######################################################################
00202 void SimulationViewerStd::start2()
00203 {
00204   // do we wnat larger drawings?
00205   if (itsUseLargerDrawings.getVal())
00206     {
00207       itsFOApsiz.setVal(5);
00208       itsFOVpsiz.setVal(5);
00209       itsFOVpthick.setVal(2);
00210       itsHEDpsiz.setVal(12);
00211       itsHEDpthick.setVal(2);
00212     }
00213 
00214   itsFont = SimpleFont::fixedMaxWidth(itsFontSize.getVal());
00215 }
00216 
00217 // ######################################################################
00218 void SimulationViewerStd::
00219 doEventRetinaImage(SimEventQueue& q, rutz::shared_ptr<SimEventRetinaImage>& e)
00220 {
00221   //itsInput set by base class SimulationViewerAdapter
00222 
00223   // erase old trajectory:
00224   itsTraj.resize(itsInput.getDims(), true);
00225 
00226   // if foveating the traj, get a foveation pyramid ready:
00227   if (itsFoveateTraj.getVal())
00228     itsMultiTraj = buildPyrGaussian(itsInput, 0, itsMultiRetinaDepth.getVal(), 9);
00229 
00230   // we have a new input; this will force redrawing various things on
00231   // the trajectory in case people request it before a new shift of
00232   // attention or other event occurrs:
00233   itsHasNewInput = true;
00234 }
00235 
00236 // ######################################################################
00237 void SimulationViewerStd::
00238 doEventWTAwinner(SimEventQueue& q, rutz::shared_ptr<SimEventWTAwinner>& e)
00239 {
00240   // Any output from the ShapeEstimator?
00241   Image<byte> foaMask;
00242   if (SeC<SimEventShapeEstimatorOutput> ee = q.check<SimEventShapeEstimatorOutput>(this))
00243     {
00244       foaMask = Image<byte>(ee->smoothMask() * 255.0F);
00245       foaMask = inverseRetinal(foaMask);//will inverse map if necessary
00246       if (foaMask.isSameSize(itsInput) == false)
00247         LFATAL("Dimensions of FOAmask must match those of input");
00248     }
00249   
00250   // update our FOA shift number
00251   itsFOAshiftNum = e->shiftNum();
00252   
00253   if (foaMask.initialized())
00254     itsCurrFOAmask = foaMask;  // keep a copy of the FOA mask
00255   else
00256     {
00257       // draw a disk at current foa position:
00258       itsCurrFOAmask.resize(itsInput.getDims(), true);
00259       if (itsCurrFOA.isValid())
00260         drawDisk(itsCurrFOAmask, itsCurrFOA.p,
00261                  itsMetrics->getFOAradius(), byte(255));
00262     }
00263 
00264   // update cumulative FOA mask:
00265   if (itsDisplayAdditive.getVal() &&            // cumulative display?
00266       itsCumFOAmask.initialized() &&            // not first frame?
00267       itsCumFOAmask.isSameSize(itsCurrFOAmask)) // not changing frame dims?
00268     itsCumFOAmask = takeMax(itsCumFOAmask, itsCurrFOAmask);
00269   else
00270     itsCumFOAmask = itsCurrFOAmask;
00271 
00272   // forget it if we don't have a traj yet:
00273   if (itsTraj.initialized() == false) return;
00274 
00275   // draw the FOA:
00276   if (itsDisplayFOA.getVal() || itsDisplayPatch.getVal()) drawFOA();
00277 
00278   // link the last two FOAs:
00279   if (itsDisplayFOALinks.getVal()) linkFOAs();
00280 }
00281 
00282 // ######################################################################
00283 void SimulationViewerStd::
00284 onSimEventSaccadeStatusEye(SimEventQueue& q, rutz::shared_ptr<SimEventSaccadeStatusEye>& e)
00285 {
00286   // update our saccade/blink/etc status:
00287   itsEyeSaccade = transientStatusIsOn(e->saccadeStatus());
00288   itsEyeSmoothPursuit = transientStatusIsOn(e->smoothPursuitStatus());
00289   itsEyeBlink = transientStatusIsOn(e->blinkStatus());
00290 
00291   // do nothing else if eye did not move
00292   if (e->position() == itsCurrEye) return;
00293 
00294   // update our internals:
00295   itsPrevEye = itsCurrEye; itsCurrEye = e->position();
00296 
00297   // draw a disk at current eye position:
00298   itsCurrFoveaMask.resize(itsInput.getDims(), true);
00299   if (itsCurrEye.isValid())
00300     drawDisk(itsCurrFoveaMask, itsCurrEye,
00301              itsMetrics->getFoveaRadius(), byte(255));
00302 
00303   // update cumulative fovea mask:
00304   if (itsDisplayAdditive.getVal() &&
00305       itsCumFoveaMask.initialized() &&
00306       itsCumFoveaMask.isSameSize(itsCurrFoveaMask))
00307     itsCumFoveaMask = takeMax(itsCumFoveaMask, itsCurrFoveaMask);
00308   else
00309     itsCumFoveaMask = itsCurrFoveaMask;
00310 
00311   // forget it if we don't have a traj yet:
00312   if (itsTraj.initialized() == false) return;
00313 
00314   // draw the eye position:
00315   if (itsDisplayEye.getVal()) drawEye();
00316 
00317   // link the last two eye positions:
00318   if (itsDisplayEyeLinks.getVal()) linkEyes();
00319 }
00320 
00321 // ######################################################################
00322 void SimulationViewerStd::
00323 onSimEventSaccadeStatusHead(SimEventQueue& q, rutz::shared_ptr<SimEventSaccadeStatusHead>& e)
00324 {
00325   // update our saccade/blink/etc status:
00326   itsHeadSaccade = transientStatusIsOn(e->saccadeStatus());
00327   itsHeadSmoothPursuit = transientStatusIsOn(e->smoothPursuitStatus());
00328 
00329   // do nothing else if head did not move
00330   if (e->position() == itsCurrHead) return;
00331 
00332   // update our internals:
00333   itsPrevHead = itsCurrHead; itsCurrHead = e->position();
00334 
00335   // draw a disk at current head position:
00336   itsCurrHeadMask.resize(itsInput.getDims(), true);
00337   if (itsCurrHead.isValid())
00338     drawDisk(itsCurrHeadMask, itsCurrHead, itsHeadRadius.getVal(), byte(255));
00339 
00340   // update cumulative head mask:
00341   if (itsDisplayAdditive.getVal() &&
00342       itsCumHeadMask.initialized() &&
00343       itsCumHeadMask.isSameSize(itsCurrHeadMask))
00344     itsCumHeadMask = takeMax(itsCumHeadMask, itsCurrHeadMask);
00345   else
00346     itsCumHeadMask = itsCurrHeadMask;
00347 
00348   // forget it if we don't have a traj yet:
00349   if (itsTraj.initialized() == false) return;
00350 
00351   // draw the head position:
00352   if (itsDisplayHead.getVal()) drawHead();
00353 
00354   // link the last two head positions:
00355   if (itsDisplayHeadLinks.getVal()) linkHeads();
00356 }
00357 
00358 // ######################################################################
00359 void SimulationViewerStd::
00360 onSimEventITOutput(SimEventQueue& q, rutz::shared_ptr<SimEventITOutput>& e)
00361 {
00362   LINFO("The match found is: %s", e->getObjData()->name.c_str());
00363 // forget it if we don't have a traj yet:
00364   if (itsTraj.initialized() == false) return;
00365   else
00366     {
00367      drawOutlinedPolygon(itsTraj, e->getObjData()->polygon, PixRGB<byte>(255,128,128), Point2D<int>(0, 0), 0 , 1, 0, 0, 5 );
00368      Point2D<int> postxtobj (e->getObjData()->polygon[3].i, e->getObjData()->polygon[3].j+1);
00369      writeText(itsTraj, postxtobj, e->getObjData()->name.c_str(), PixRGB<byte>(1,1,1),
00370                PixRGB<byte>(255,255,255), SimpleFont::FIXED(10), false, ANCHOR_BOTTOM_LEFT);
00371     }
00372  }
00373 
00374 // ######################################################################
00375 Image< PixRGB<byte> > SimulationViewerStd::getTraj(SimEventQueue& q)
00376 {
00377   // update our 3D rotations if necessary:
00378   if (itsWarp3D.getVal()) {
00379     const SimTime t = q.now();
00380     const SimTime dt = SimTime::computeDeltaT((t - itsCurrTime), itsTimeStep.getVal());
00381     const double dts = dt.secs();
00382 
00383     for (SimTime tt = itsCurrTime; tt < t; tt += dt) {
00384       if (itsPitch3D == -1.0e10F) itsPitch3D = itsWarp3Dpitch.getVal();
00385       if (itsYaw3D == -1.0e10F) itsYaw3D = itsWarp3Dyaw.getVal();
00386 
00387       itsPitch3D += itsWarp3DpitchRate.getVal() * dts;
00388       itsYaw3D += itsWarp3DyawRate.getVal() * dts;
00389 
00390       if (itsYaw3D >= itsWarp3DyawMax.getVal() || itsYaw3D <= -itsWarp3DyawMax.getVal())
00391         itsWarp3DyawRate.setVal(- itsWarp3DyawRate.getVal());
00392       if (itsPitch3D >= itsWarp3DpitchMax.getVal() || itsPitch3D <= -itsWarp3DpitchMax.getVal())
00393         itsWarp3DpitchRate.setVal(- itsWarp3DpitchRate.getVal());
00394     }
00395   }
00396 
00397   itsCurrTime = q.now();
00398   if (itsTraj.initialized() == false) return itsTraj;
00399 
00400   // ##### if not doing additive displays, clear traj and redraw only
00401   // ##### current foa/eye/head data:
00402   if (itsDisplayAdditive.getVal() == false)
00403     itsTraj.resize(itsInput.getDims(), true);
00404 
00405   // ##### What do we want to use as backdrop image under our
00406   // ##### trajectory drawings? Is it just the original input image,
00407   // ##### or something fancier?
00408   Image< PixRGB<byte> > ret;
00409 
00410   // show ShapeEstimator masking (at the location(s) of covert attention):
00411   if (itsDisplayShapeEstimator.getVal())
00412     ret = contrastModulate(itsInput, itsCumFOAmask,
00413                            itsShapeEstimatorBaseContrast.getVal(),
00414                            itsShapeEstimatorBaseBright.getVal());
00415 
00416   // display FOA highlights using mask of eye positions:
00417   if (itsDisplayHighlights.getVal() && itsCumFoveaMask.initialized())
00418     ret = highlightRegions(itsInput, itsCumFoveaMask,
00419                            itsHighlightMax.getVal());
00420 
00421   // modulate contrast by saliency map:
00422   if (itsDisplaySMmodulate.getVal())
00423     {
00424       const float fac = 0.001F;
00425       Image<float> sm = rescaleOpt(getMap(q) * fac,
00426                                 itsTraj.getDims(),
00427                                 itsDisplayInterp.getVal());
00428       float mi, ma; getMinMax(sm, mi, ma);
00429       LINFO("SM-based contrast modulation range: [%.3f .. %.3f]", mi, ma);
00430 
00431       ret = contrastModulate(itsInput, sm, 0.5F, byte(127));
00432     }
00433 
00434   // show foveated masking, at the location of overt attention:
00435   if (itsFoveateTraj.getVal())
00436     ret = foveate(itsCumFoveaMask, itsMultiTraj);
00437 
00438   // ##### at this point, if we have something in ret, then let's use
00439   // ##### that as backdrop image under our drawings, otherwise let's
00440   // ##### just use the original input:
00441   if (ret.initialized() == false) ret = itsInput;
00442 
00443   // ##### Update our trajectory drawings. We only care about current
00444   // ##### attention/eye/head position and not about any previous ones
00445   // ##### or links:
00446   if (itsHasNewInput)
00447     {
00448       if (itsDisplayFOA.getVal() || itsDisplayPatch.getVal() ||
00449           itsDisplayFOAnum.getVal()) drawFOA();
00450       if (itsDisplayEye.getVal()) drawEye();
00451       if (itsDisplayHead.getVal()) drawHead();
00452       itsHasNewInput = false;
00453     }
00454 
00455   // ##### now slap our FOA, FOA links, etc drawings on top of the backdrop:
00456   ret = composite(itsTraj, ret, PixRGB<byte>(0, 0, 0)); // black is transparent
00457 
00458   // if we want a megacombo, let's return it now:
00459   if (itsMegaCombo.getVal()) ret = drawMegaCombo(q, ret);
00460 
00461   // if cropping let's crop now:
00462   // resizing and other normalizations are taken care of by FrameOstream
00463   else if (itsCropFOA.getVal().isNonEmpty())
00464     {
00465       Dims crop_dims = itsCropFOA.getVal();
00466       Rectangle crect =
00467         Rectangle::tlbrI(itsCurrFOA.p.j - crop_dims.h() / 2,
00468                          itsCurrFOA.p.i - crop_dims.w() / 2,
00469                          itsCurrFOA.p.j + crop_dims.h() / 2 - 1,
00470                          itsCurrFOA.p.i + crop_dims.w() / 2 - 1);
00471       ret = crop(ret, crect, true);
00472     }
00473 
00474   // return a 3D traj warped onto saliency map?
00475   else if (itsWarp3D.getVal()) {
00476     drawGrid(ret, ret.getWidth() / 6, ret.getHeight() / 6, 3, 3,
00477              itsColorNormal.getVal());
00478     drawRect(ret, Rectangle::tlbrI(1, 1, ret.getHeight()-2, ret.getWidth()-2),
00479              itsColorNormal.getVal(), 3);
00480     ret = warp3Dmap(ret, getMap(q), itsPitch3D, itsYaw3D, itsDims3D);
00481   }
00482 
00483   // display combo traj + salmap?
00484   else if ((itsSaveXcombo.getVal() || itsSaveYcombo.getVal()))
00485     ret = colGreyCombo(ret, getMap(q),
00486                        itsSaveXcombo.getVal(), itsDisplayInterp.getVal());
00487 
00488   // display combo traj + salmap + highlighted trm?
00489   else if ((itsSaveTRMXcombo.getVal() || itsSaveTRMYcombo.getVal()))
00490     {
00491       ret = colGreyCombo(ret, getMap(q),
00492                          itsSaveTRMXcombo.getVal(), itsDisplayInterp.getVal());
00493 
00494       Image<float> trm;
00495       if (SeC<SimEventTaskRelevanceMapOutput> e =
00496           q.check<SimEventTaskRelevanceMapOutput>(this, SEQ_ANY))
00497         trm = e->trm(1.0F);
00498       else LFATAL("Cannot find a TRM!");
00499       //inplaceNormalize(trm, 0.0f, 255.0f);
00500 
00501       trm = inverseMap(trm);//pu map back in cartesian coords if necessary
00502       Image<PixRGB<float> > highlight_trm = toRGB(trm);
00503       highlight_trm = rescaleOpt(trm, itsInput.getDims(), itsDisplayInterp.getVal());
00504       // highlight_trm = (Image<PixRGB<float> >)itsInput *
00505       // rescaleOpt(trm, itsInput.getDims(), itsDisplayInterp.getVal());
00506       ret = colColCombo(ret, (Image<PixRGB<byte> >)highlight_trm,
00507                         itsSaveTRMXcombo.getVal(), itsDisplayInterp.getVal());
00508     }
00509 
00510   // display combo traj + salmap + trm + agm?
00511   else if (itsSaveTRMmegaCombo.getVal())
00512     {
00513       Image<float> sm;
00514       if (SeC<SimEventSaliencyMapOutput> e =
00515           q.check<SimEventSaliencyMapOutput>(this, SEQ_ANY))
00516         sm = e->sm(0.0F);
00517       else LFATAL("Cannot find a SM!");
00518       sm = inverseMap(sm);//pu map back in cartesian coords if necessary
00519       Image<PixRGB<float> > highlight_sm = toRGB(sm);
00520       highlight_sm = rescaleOpt(highlight_sm,
00521                                  itsInput.getDims(), itsDisplayInterp.getVal());
00522       Image<PixRGB<float> > xcombo1 = colColCombo(ret, highlight_sm,
00523                                                   1, itsDisplayInterp.getVal());
00524 
00525       Image<float> trm;
00526       if (SeC<SimEventTaskRelevanceMapOutput> e =
00527           q.check<SimEventTaskRelevanceMapOutput>(this, SEQ_ANY))
00528         trm = e->trm(1.0F);
00529       else LFATAL("Cannot find a TRM!");
00530       trm = inverseMap(trm);//pu map back in cartesian coords if necessary
00531       Image<PixRGB<float> > highlight_trm = toRGB(trm);
00532       highlight_trm = rescaleOpt(highlight_trm,
00533                                   itsInput.getDims(), itsDisplayInterp.getVal());
00534 
00535       Image<float> agm;
00536       if (SeC<SimEventAttentionGuidanceMapOutput> e =
00537           q.check<SimEventAttentionGuidanceMapOutput>(this, SEQ_ANY))
00538         agm = e->agm(0.0F);
00539       else LFATAL("Cannot find a AGM!");
00540       agm = inverseMap(agm);//pu map back in cartesian coords if necessary
00541       Image<PixRGB<float> > highlight_agm = toRGB(agm);
00542       highlight_agm = rescaleOpt(highlight_agm,
00543                                  itsInput.getDims(), itsDisplayInterp.getVal());
00544 
00545       Image<PixRGB<float> > xcombo2 = colColCombo(highlight_trm, highlight_agm,
00546                                          1, itsDisplayInterp.getVal());
00547       ret = colColCombo(xcombo1, xcombo2, 0, itsDisplayInterp.getVal());
00548     }
00549 
00550   // ##### do a bunch of last-minute drawings:
00551   if (itsDisplayTime.getVal()) drawTime(ret);
00552 
00553   // ##### return final transformed traj:
00554   return ret;
00555 }
00556 
00557 // ######################################################################
00558 void SimulationViewerStd::
00559 onSimEventSaveOutput(SimEventQueue& q, rutz::shared_ptr<SimEventSaveOutput>& e)
00560 {
00561   this->save1(e->sinfo());
00562 }
00563 
00564 // ######################################################################
00565 void SimulationViewerStd::save1(const ModelComponentSaveInfo& sinfo)
00566 {
00567   // get the OFS to save to, assuming sinfo is of type
00568   // SimModuleSaveInfo (will throw a fatal exception otherwise):
00569   nub::ref<FrameOstream> ofs =
00570     dynamic_cast<const SimModuleSaveInfo&>(sinfo).ofs;
00571 
00572   // also get the SimEventQueue:
00573   SimEventQueue *q = dynamic_cast<const SimModuleSaveInfo&>(sinfo).q;
00574 
00575   // update the trajectory:
00576   Image< PixRGB<byte> > res = getTraj(*q);
00577 
00578   // save results?
00579   if (itsSaveTraj.getVal() || itsSaveXcombo.getVal() ||
00580       itsSaveYcombo.getVal() || itsSaveTRMXcombo.getVal() ||
00581       itsSaveTRMYcombo.getVal() || itsSaveTRMmegaCombo.getVal() ||
00582       itsWarp3D.getVal() || itsMegaCombo.getVal())
00583     {
00584       ofs->writeRGB(res, "T",
00585                     FrameInfo("SimulationViewerStd trajectory", SRC_POS));
00586     }
00587 }
00588 
00589 // ######################################################################
00590 void SimulationViewerStd::drawFOA()
00591 {
00592   if (itsCurrFOA.isValid() == false) return;
00593 
00594   // select a drawing color:
00595   PixRGB<byte> col(itsColorNormal.getVal());
00596   if (itsCurrFOA.boring) col -= itsColorBoring.getVal();
00597 
00598   // display focus of attention, possibly object-shaped:
00599   if (itsDisplayFOA.getVal())
00600     drawMaskOutline(itsTraj, itsCurrFOAmask, col, itsFOAthick.getVal(), itsCurrFOA.p, itsMetrics->getFOAradius());
00601 
00602   // display attention shift number:
00603   if (itsDisplayFOAnum.getVal())
00604     {
00605       const PixRGB<byte> col2(itsColorText.getVal());
00606       const int rad = 2 + int(itsMetrics->getFOAradius() / M_SQRT2 + 0.5);
00607       const std::string txt = sformat("%d", itsFOAshiftNum);
00608       Point2D<int> pp = itsCurrFOA.p + Point2D<int>(rad + 3 + itsFOAthick.getVal(), rad + 3 + itsFOAthick.getVal());
00609       drawLine(itsTraj, itsCurrFOA.p, pp, col2);
00610       drawLine(itsTraj, pp, pp + Point2D<int>(txt.size() * itsFont.w() + 2, 0), col2);
00611 
00612       writeText(itsTraj, pp + Point2D<int>(1, 2), txt.c_str(), col2, PixRGB<byte>(0), itsFont, true);
00613     }
00614 
00615   // draw patch at current eye position:
00616   if (itsDisplayPatch.getVal()) drawPatch(itsTraj, itsCurrFOA.p, itsFOApsiz.getVal(), col);
00617 }
00618 
00619 // ######################################################################
00620 void SimulationViewerStd::linkFOAs()
00621 {
00622   if (itsCurrFOA.isValid() == false) return;
00623 
00624   PixRGB<byte> col = itsColorLink.getVal();
00625   if (itsPrevFOA.isValid())
00626     {
00627       int d = int(itsPrevFOA.p.distance(itsCurrFOA.p));
00628       if (d > 0) drawArrow(itsTraj, itsPrevFOA.p, itsCurrFOA.p,
00629                            col, itsFOAlinkThick.getVal());
00630     }
00631 }
00632 
00633 // ######################################################################
00634 void SimulationViewerStd::drawEye()
00635 {
00636   if (itsCurrEye.isValid() == false) return;
00637 
00638   int psiz = itsFOVpsiz.getVal();    // half-size of patch
00639 
00640   // select a drawing color:
00641   PixRGB<byte> col(itsColorNormal.getVal());
00642   if (itsEyeSmoothPursuit) col = itsColorSmoothPursuit.getVal();
00643   if (itsEyeSaccade) { col = itsColorSaccade.getVal(); psiz += 2; }
00644   if (itsEyeBlink) { col = itsColorBlink.getVal(); psiz += 4; }
00645   if (itsCurrFOA.boring) col -= itsColorBoring.getVal();
00646 
00647   // draw patch (thin hollow square):
00648   Rectangle r =
00649     Rectangle::tlbrI(itsCurrEye.j - psiz, itsCurrEye.i - psiz,
00650                     itsCurrEye.j + psiz, itsCurrEye.i + psiz);
00651   r = r.getOverlap(itsTraj.getBounds());
00652   drawRect(itsTraj, r, col, itsFOVpthick.getVal());
00653 
00654   // draw fovea, possibly mask-shaped:
00655   if (itsDisplayFOA.getVal())
00656     drawMaskOutline(itsTraj, itsCurrFoveaMask, col,
00657                     itsFOVthick.getVal(), itsCurrEye,
00658                     itsMetrics->getFoveaRadius());
00659 }
00660 
00661 // ######################################################################
00662 void SimulationViewerStd::linkEyes()
00663 {
00664   if (itsCurrEye.isValid() == false) return;
00665 
00666   PixRGB<byte> col = itsColorLink.getVal();
00667 
00668   // draw a line segment:
00669   if (itsPrevEye.isValid())
00670     drawLine(itsTraj, itsPrevEye, itsCurrEye, col,
00671              itsFOVlinkThick.getVal());
00672 }
00673 
00674 // ######################################################################
00675 void SimulationViewerStd::drawHead()
00676 {
00677   if (itsCurrHead.isValid() == false) return;
00678 
00679   int psiz = itsHEDpsiz.getVal();   // half-size of patch
00680 
00681   // select a drawing color:
00682   PixRGB<byte> col(itsColorNormal.getVal());
00683   if (itsHeadSmoothPursuit) col = itsColorSmoothPursuit.getVal();
00684   if (itsHeadSaccade) { col = itsColorSaccade.getVal(); psiz += 3; }
00685   if (itsCurrFOA.boring) col -= itsColorBoring.getVal();
00686 
00687   // draw square at current head position:
00688   Rectangle r =
00689     Rectangle::tlbrI(itsCurrHead.j - psiz, itsCurrHead.i - psiz,
00690                     itsCurrHead.j + psiz, itsCurrHead.i + psiz);
00691   r = r.getOverlap(itsTraj.getBounds());
00692   drawRect(itsTraj, r, col, itsHEDpthick.getVal());
00693 
00694   // draw head marker, possibly object-shaped:
00695   if (itsDisplayFOA.getVal())
00696     drawMaskOutline(itsTraj, itsCurrHeadMask, col, itsHEDthick.getVal(),
00697                     itsCurrHead, itsHeadRadius.getVal());
00698 }
00699 
00700 // ######################################################################
00701 void SimulationViewerStd::linkHeads()
00702 {
00703   if (itsCurrHead.isValid() == false) return;
00704 
00705   PixRGB<byte> col = itsColorLink.getVal();
00706 
00707   // draw a line segment:
00708   if (itsPrevHead.isValid())
00709     drawLine(itsTraj, itsPrevHead, itsCurrHead, col,
00710              itsHEDlinkThick.getVal());
00711 }
00712 
00713 // ######################################################################
00714 void SimulationViewerStd::drawTime(Image<PixRGB<byte> >& image)
00715 {
00716   const std::string txt = sformat(" %dms ", int(itsCurrTime.msecs() + 0.4999));
00717   writeText(image, Point2D<int>(0, 0), txt.c_str(),
00718             PixRGB<byte>(0), PixRGB<byte>(255), itsFont);
00719 }
00720 
00721 // ######################################################################
00722 Image< PixRGB<byte> > SimulationViewerStd::drawMegaCombo(SimEventQueue& q,
00723                                                          const Image< PixRGB<byte> >& in)
00724 {
00725   Image<byte> rr, gg, bb;
00726   std::string rng;
00727   if (itsMapType.getVal().compare("AGM") == 0)
00728     {
00729       // get the non-normalized AGM:
00730       Image<float> agm;
00731       if (SeC<SimEventAttentionGuidanceMapOutput> e = q.check<SimEventAttentionGuidanceMapOutput>(this, SEQ_ANY))
00732         agm = e->agm(1.0F);
00733       else LFATAL("Cannot find an AGM!");
00734       agm = inverseMap(agm);//pu map back in cartesian coords if necessary
00735 
00736       // create a string with the map's range of values, then normalize the map:
00737       float mini, maxi; getMinMax(agm, mini, maxi);
00738       rng = sformat("AGM=[%.03f .. %.03f]mV", mini * 1000.0F, maxi * 1000.0F);
00739       if (itsMapFactor.getVal() == 0.0F) inplaceNormalize(agm, 0.0F, 255.0F);
00740       else if (itsMapFactor.getVal() != 1.0F) agm *= itsMapFactor.getVal();
00741 
00742       // get the non-normalized TRM (neutral is 1.0):
00743       Image<float> trm;
00744       if (SeC<SimEventTaskRelevanceMapOutput> ee = q.check<SimEventTaskRelevanceMapOutput>(this, SEQ_ANY))
00745         trm = ee->trm(1.0F);
00746       else
00747         {
00748           LINFO("WARNING: Cannot find a TRM, assuming blank.");
00749           trm.resize(agm.getDims(), false); trm.clear(1.0F);
00750         }
00751 
00752       trm = inverseMap(trm);//pu map back in cartesian coords if necessary
00753 
00754       // create a string with the map's range of values:
00755       getMinMax(trm, mini, maxi);
00756       rng += sformat(", TRM=[%.03f .. %.03f]", mini, maxi);
00757 
00758       // useful range is 0.0 .. 3.0; let's show values smaller
00759       // than 1 as red and those larger that 1 as green:
00760       Image<float> trmfac(trm);
00761       inplaceClamp(trmfac, 0.0F, 1.0F);
00762       trmfac *= -1.0F; trmfac += 1.0F;
00763       rr = trmfac * 255.0F; // the redder, the lower the relevance
00764       trmfac = trm;
00765       inplaceClamp(trmfac, 1.0F, 3.0F);
00766       trmfac -= 1.0F;
00767       gg = trmfac * 127.5F; // the greener, the higher the relevance
00768       bb = agm;  // in blue is the AGM
00769     }
00770   else
00771     {
00772       // just get whatever map and display in greyscale:
00773       Image<float> m = getMap(q);
00774       rr = m; gg = m; bb = m;
00775     }
00776 
00777   // interpolate the attention map:
00778   Image<PixRGB<byte> > cbtmp = rescaleOpt(makeRGB(rr, gg, bb), in.getDims(), itsDisplayInterp.getVal());
00779 
00780   // add our drawings on top of it, a rectangle, and text labels:
00781   cbtmp = composite(itsTraj, cbtmp, PixRGB<byte>(0)); // pure black is transparent
00782   drawRect(cbtmp, Rectangle(Point2D<int>(0, 0), cbtmp.getDims()), PixRGB<byte>(128, 128, 255));
00783   writeText(cbtmp, Point2D<int>(1,1), " Attention Map ", itsColorText.getVal(), PixRGB<byte>(0), itsFont, true);
00784   writeText(cbtmp, Point2D<int>(3, cbtmp.getHeight() - 3), rng.c_str(), itsColorText.getVal(),
00785             PixRGB<byte>(0), SimpleFont::FIXED(6), true, ANCHOR_BOTTOM_LEFT);
00786 
00787   // get our output Layout started:
00788   Layout< PixRGB<byte> > output = hcat(in, cbtmp);
00789 
00790   // grab all the VisualCortex maps:
00791   rutz::shared_ptr<SimReqVCXmaps> vcxm(new SimReqVCXmaps(this));
00792   q.request(vcxm); // VisualCortex is now filling-in the maps...
00793   rutz::shared_ptr<ChannelMaps> chm = vcxm->channelmaps();
00794 
00795   // Display all the conspicuity maps in a nice array:
00796   ImageSet< PixRGB<byte> > cmaps;
00797   getMegaComboMaps(chm, cmaps);
00798 
00799   // all right, assemble the final output:
00800   if (cmaps.size() > 0)
00801     {
00802       Layout< PixRGB<byte> > lmaps = arrcat(cmaps, output.getWidth() / cmaps[0].getWidth());
00803       return vcat(output, lmaps).render();
00804     }
00805   else
00806     return output.render();
00807 }
00808 
00809 // ######################################################################
00810 void SimulationViewerStd::getMegaComboMaps(const rutz::shared_ptr<ChannelMaps>& chm,
00811                                            ImageSet< PixRGB<byte> >& cmaps, const uint depth)
00812 {
00813   for (uint i = 0; i < chm->numSubchans(); ++i)
00814     {
00815       rutz::shared_ptr<ChannelMaps> subchm = chm->subChanMaps(i);
00816 
00817       NamedImage<float> m = subchm->getMap();
00818       if (m.initialized() == false) m.resize(chm->getMap().getDims(), true); // some channels may not have outputs yet
00819       m = inverseMap(m);//pu map back in cartesian coords if necessary
00820       Image< PixRGB<byte> > mm = prepMapForDisplay(m);
00821       cmaps.push_back(mm);
00822 
00823       if (itsMegaComboTopCMapsOnly.getVal() == false && subchm->numSubchans() > 0)
00824         getMegaComboMaps(subchm, cmaps, depth + 1);
00825     }
00826 }
00827 
00828 // ######################################################################
00829 Image< PixRGB<byte> > SimulationViewerStd::prepMapForDisplay(const NamedImage<float>& m) const
00830 {
00831   const uint zoomfac = itsMegaComboZoom.getVal();
00832 
00833   // create a string with the map's range of values:
00834   float mini, maxi; getMinMax(m, mini, maxi);
00835   const std::string rng = sformat("[%.03f .. %.03f]", mini, maxi);
00836 
00837   // split the name into several tokens so that we can write them on
00838   // different lines:
00839   std::vector<std::string> tokens;
00840   split(m.name(), ":", std::back_inserter(tokens));
00841 
00842   // rescale, normalize and colorize the map:
00843   Image<float> ftmp = rescaleOpt(m, m.getWidth() * zoomfac, m.getHeight() * zoomfac, itsDisplayInterp.getVal());
00844   inplaceNormalize(ftmp, 0.0f, 255.0f); Image<byte> btmp = ftmp; // convert to byte
00845   Image< PixRGB<byte> > cbtmp = btmp; // convert to color
00846 
00847   // add a rectangle and the text labels:
00848   drawRect(cbtmp, Rectangle(Point2D<int>(0, 0), cbtmp.getDims()), PixRGB<byte>(128, 128, 255));
00849   for (size_t i = 1; i < tokens.size(); ++i) // note that we start at 1 to ignore "VisualCortex:" label
00850     writeText(cbtmp, Point2D<int>(1, 1+(i-1) * itsFont.h()), tokens[i].c_str(), itsColorText.getVal(),
00851               PixRGB<byte>(0), itsFont, true, ANCHOR_TOP_LEFT);
00852   writeText(cbtmp, Point2D<int>(1, cbtmp.getHeight() - 2), rng.c_str(), itsColorText.getVal(),
00853             PixRGB<byte>(0), SimpleFont::FIXED(6), true, ANCHOR_BOTTOM_LEFT);
00854 
00855   return cbtmp;
00856 }
00857 
00858 // ######################################################################
00859 void SimulationViewerStd::drawMaskOutline(Image< PixRGB<byte> >& traj,
00860                                           const Image<byte> mask,
00861                                           const PixRGB<byte>& col,
00862                                           const int thick,
00863                                           const Point2D<int>& pos,
00864                                           const int radius)
00865 {
00866   if (traj.initialized() == false) return; // can't draw...
00867 
00868 
00869   // object-shaped drawing
00870   Image<byte> om(mask);
00871   inplaceLowThresh(om, byte(128)); // cut off fuzzy (interpolated) object boundaries
00872   om = contour2D(om);       // compute binary contour image
00873   int w = traj.getWidth(), h = traj.getHeight();
00874   Point2D<int> ppp;
00875   for (ppp.j = 0; ppp.j < h; ppp.j ++)
00876     for (ppp.i = 0; ppp.i < w; ppp.i ++)
00877       if (om.getVal(ppp.i, ppp.j))  // got a contour point -> draw here
00878         drawDisk(traj, ppp, thick, col);  // small disk for each point
00879   // OBSOLETE: drawCircle(traj, pos, radius, col, thick);
00880 }
00881 // ######################################################################
00882 /* So things look consistent in everyone's emacs... */
00883 /* Local Variables: */
00884 /* indent-tabs-mode: nil */
00885 /* End: */
Generated on Sun May 8 08:05:26 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3