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: */