00001 /*!@file Neuro/SimulationViewerEyeRegion.C comparison between region data from an ObjRec data (from an XML file) and human eye movements */ 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/SimulationViewerEyeRegion.C $ 00035 // $Id: SimulationViewerEyeRegion.C 14376 2011-01-11 02:44:34Z pez $ 00036 // 00037 00038 #include "Neuro/SimulationViewerEyeRegion.H" 00039 00040 #include "Component/OptionManager.H" 00041 #include "Component/ModelOptionDef.H" 00042 #include "Transport/TransportOpts.H" 00043 #include "Channels/ChannelBase.H" 00044 #include "Image/DrawOps.H" 00045 #include "Image/ColorOps.H" // for toRGB() 00046 #include "Image/CutPaste.H" // for concatX() 00047 #include "Image/FilterOps.H" // for lowPass3() 00048 #include "Image/MathOps.H" // for alphaBlend() 00049 #include "Image/ShapeOps.H" // for rescale() 00050 #include "Image/Transforms.H" // for composite() 00051 #include "Neuro/NeuroOpts.H" 00052 #include "Neuro/NeuroSimEvents.H" 00053 #include "Psycho/EyeData.H" 00054 #include "Simulation/SimEventQueue.H" 00055 #include "Util/sformat.H" 00056 00057 #include "rutz/trace.h" 00058 00059 #include <fstream> 00060 //#include <iostream> 00061 00062 const ModelOptionDef OPT_SimViewXMLInputFile = 00063 { MODOPT_ARG(std::string), "SimViewXMLInputFile", &MOC_INPUT, OPTEXP_CORE, 00064 "XML file to gather region data from.", 00065 "xml-file", '\0', "string", ""}; 00066 00067 const ModelOptionDef OPT_SimViewSelectedObjects = 00068 { MODOPT_ARG(std::string), "SimViewSelectedObjects", &MOC_DISPLAY, OPTEXP_CORE, 00069 "Only display objects matching the given name.", 00070 "objs-filter", '\0', "string", "" }; 00071 00072 const ModelOptionDef OPT_SimViewObjDrawMode = 00073 { MODOPT_ARG(std::string), "SimViewObjDrawMode", &MOC_DISPLAY, OPTEXP_CORE, 00074 "Chooses which objects are displayed. Note that these options currently " 00075 "depend upon --display-patch and --display-foa. \n" 00076 "\t'selected' objects are objects which are selected by the option --objs-filter.\n" 00077 "\t'targeted' objects are objects that saccades are directed towards.\n" 00078 "\t'traced' objects are objects that are followed by an eyetrace.\n" 00079 "\t'none' and 'all' are none and all labeled objects, respectively.", 00080 "obj-drawmode", '\0', "<none|selected|targeted|traced|all>", "all" }; 00081 00082 const ModelOptionDef OPT_SimViewHighlightMode = 00083 { MODOPT_ARG(std::string), "SimViewHighlightMode", &MOC_DISPLAY, OPTEXP_CORE, 00084 "Chooses when objects are highlighted. Note that these options currently " 00085 "depend upon --display-patch and --display-foa. Also, highlighting is currently " 00086 "ambiguous with multiple eye-traces. \n" 00087 "\t'targeted' triggers highlighting only at the FOA.\n" 00088 "\t'traced' highlights the object that is at the point of gaze.", 00089 "obj-highlightmode", '\0', "<off|targeted|traced>", "targeted" }; 00090 00091 // needs updating 00092 const ModelOptionDef OPT_SimViewPrependHeader = 00093 { MODOPT_FLAG, "SimViewPrependHeader", &MOC_DISPLAY, OPTEXP_CORE, 00094 "Determines whether to add a header to svem or region output", 00095 "prepend-header", '\0', "", "false" }; 00096 00097 // Used by: SimulationViewerEyeRegion 00098 const ModelOptionDef OPT_SimViewRegionOutFile = 00099 { MODOPT_ARG_STRING, "SimViewRegionOutFile", &MOC_DISPLAY, OPTEXP_CORE, 00100 "File name for region-based output data (or empty to not save output data).", 00101 "region-out-fname", '\0', "<file>", "" }; 00102 00103 // ###################################################################### 00104 SimulationViewerEyeRegion:: 00105 SimulationViewerEyeRegion(OptionManager& mgr, const std::string& descrName, 00106 const std::string& tagName) : 00107 SimulationViewerEyeMvt(mgr, descrName, tagName), 00108 itsRegionOutFname(&OPT_SimViewRegionOutFile, this), 00109 itsXMLFname(&OPT_SimViewXMLInputFile, this), 00110 itsSelectedObjects(&OPT_SimViewSelectedObjects, this), 00111 itsObjectDrawMode(&OPT_SimViewObjDrawMode, this), 00112 itsHighlightMode(&OPT_SimViewHighlightMode, this), 00113 itsPrependHeader(&OPT_SimViewPrependHeader,this), 00114 itsLineThickness("SVLineThickness", this, 2), 00115 itsHitTransparency("SVHitTransparency", this, 0.65), 00116 itsRandomColoring("SVRandomColoring", this, true), 00117 itsRegionOutFile(0), itsRegions(), itsTargetsMask(), itsNumObjects(0), 00118 itsCurrRegionID(NULL_OBJ), 00119 itsObjectEntry(new EyeData(1,1,1,SACSTATE_FIX,false)), 00120 itsObjectOnset(SimTime::ZERO()), itsObjectFrameOnset(0), itsRandColors(), 00121 itsObjectsNames(), itsRegHeaderWritten(false) 00122 { 00123 GVX_TRACE(__PRETTY_FUNCTION__); 00124 } 00125 00126 // ###################################################################### 00127 SimulationViewerEyeRegion::~SimulationViewerEyeRegion() 00128 { 00129 GVX_TRACE(__PRETTY_FUNCTION__); 00130 } 00131 00132 // ###################################################################### 00133 void SimulationViewerEyeRegion::start1() 00134 { 00135 if (itsXMLFname.getVal().empty()) 00136 LFATAL("No XML file given - use --xml-file option with sv-Type EyeRegion"); 00137 else 00138 { 00139 // load up the objects from the XML file 00140 itsObjectsInfo.reset(new TestImages(itsXMLFname.getVal().c_str(), 00141 TestImages::XMLFILE)); 00142 00143 // "count" the number of distinct objects by ID and get their names 00144 // this works b/c currently our script indexes objects sequentially 00145 // from 0 ... n 00146 Scene thisScene; 00147 std::string nom; 00148 for (uint i = 0; i < itsObjectsInfo->getNumScenes(); i++) 00149 { 00150 thisScene = itsObjectsInfo->getSceneData(i); 00151 for (std::vector<Object>::iterator iObj = thisScene.objects.begin(); 00152 iObj != thisScene.objects.end(); iObj++) 00153 { 00154 //if the array is too small, resize the array 00155 if((*iObj).id >= itsNumObjects) 00156 { 00157 itsNumObjects = (*iObj).id+1; 00158 itsObjectsNames.resize(itsNumObjects,std::string()); 00159 } 00160 00161 // get rid of leading and trailing underscores 00162 nom = (*iObj).name; 00163 if(!nom.empty() && (nom[nom.length()-1] == '_')) 00164 nom.erase(nom.length()-1); 00165 if(!nom.empty() && (nom[0] == '_')) nom.erase(0,1); 00166 00167 //if this is a new name, use the id to write the name 00168 if(itsObjectsNames[(*iObj).id].empty()==true) 00169 { 00170 itsObjectsNames[(*iObj).id] = nom; 00171 LINFO("assigning obj %s to obj id #%d from frame %u", 00172 nom.c_str(),(*iObj).id,i); 00173 } 00174 00175 // if this id is already used for a different object, throw an error 00176 if (itsObjectsNames[(*iObj).id].compare(nom) != 0) 00177 LFATAL("XML file %s has name conflict for object #%u, %s <-> %s", 00178 itsXMLFname.getVal().c_str(), (*iObj).id, 00179 itsObjectsNames[(*iObj).id].c_str(), nom.c_str()); 00180 } 00181 00182 } 00183 } 00184 00185 // open output file: 00186 if (!itsRegionOutFname.getVal().empty()) 00187 { 00188 if (itsRegionOutFile) delete itsRegionOutFile; 00189 itsRegionOutFile = new std::ofstream(itsRegionOutFname.getVal().c_str()); 00190 if (itsRegionOutFile->is_open() == false) 00191 LFATAL("Cannot open '%s' for writing", itsRegionOutFname.getVal().c_str()); 00192 } 00193 00194 // ensure that if this output file is open then --display-patch is also on. 00195 if (!(itsRegionOutFname.getVal().empty()) && itsDisplayPatch.getVal() == false) 00196 LFATAL("Cannot write %s unless --display-patch is set.", 00197 itsRegionOutFname.getVal().c_str()); 00198 00199 // initialize random colors 00200 00201 if(itsRandomColoring.getVal()) 00202 for (uint i = 0; i < itsNumObjects; i++) 00203 itsRandColors.push_back(PixRGB<byte>(randomUpToIncluding(255), 00204 randomUpToIncluding(255), 00205 randomUpToIncluding(255))); 00206 00207 SimulationViewerEyeMvt::start1(); 00208 } 00209 00210 // ###################################################################### 00211 void SimulationViewerEyeRegion::stop1() 00212 { 00213 GVX_TRACE(__PRETTY_FUNCTION__); 00214 if (itsRegionOutFile) { 00215 writeROIOutput(rawToRet(itsObjectEntry->position())); 00216 delete itsRegionOutFile; 00217 itsRegionOutFile = 0; 00218 } 00219 00220 SimulationViewerEyeMvt::stop1(); 00221 } 00222 00223 // ###################################################################### 00224 uint SimulationViewerEyeRegion::findHitRegion(const Point2D<int> pt, uint fNum) 00225 { 00226 // TODO: how can we guarantee that the frame matches the annotation? 00227 // Reasonable assumption 1: the annotation corresponds to the video 00228 // Danger 1: different input frame sequence (e.g. frame rate) 00229 // Danger 2: holdup of frames from Retina 00230 00231 if(itsObjectsInfo.get() == 0) 00232 LFATAL("No objects data - check xml file."); 00233 00234 if(itsFrameNumber >= itsObjectsInfo->getNumScenes()) 00235 { 00236 LINFO("Ran out of XML scene data..."); 00237 return NULL_OBJ; 00238 } 00239 00240 //should check pt must also be looking at current frame w.r.t cropping, etc. 00241 00242 Scene sceneData = 00243 itsObjectsInfo->getSceneData(fNum); 00244 00245 // find which object this saccade target hits 00246 uint pID = NULL_OBJ; 00247 double thisrank, maxrank=-1; 00248 00249 for(std::vector<Object>::iterator itrObject = 00250 sceneData.objects.begin(); itrObject != sceneData.objects.end(); itrObject++) 00251 if(pnpoly((*itrObject).polygon,pt)) // is our object hit by the pt? 00252 { 00253 thisrank = rankForeground(*itrObject); 00254 if(thisrank > maxrank) // is our object in front? 00255 { 00256 pID = (*itrObject).id; 00257 maxrank = thisrank; 00258 } 00259 } 00260 return pID; 00261 } 00262 00263 00264 // ###################################################################### 00265 double SimulationViewerEyeRegion::rankForeground(Object obj) 00266 { 00267 // This is an ad-hoc way to determine which object is most in front. 00268 00269 const int h=1080,w=1920; 00270 const double scrsize= h*w; 00271 const size_t npos = std::string::npos; 00272 00273 // simple way to assign foregroundness 00274 std::string name = toLowerCase(getObjName(obj.id)); 00275 if(name.find("eyes") != npos || name.find("mouth") != npos) return 4.0; 00276 if(name.find("head") != npos || name.find("face") != npos) return 3.0; 00277 if(name.find("body") != npos) return 2.0; 00278 if(name.find("man") != npos || name.find("guy") != npos || 00279 name.find("girl") != npos || name.find("person") != npos || 00280 name.find("people") != npos || name.find("group") != npos) 00281 return area(obj.polygon)/scrsize+1; // weight based on area, normed by screen size - smaller is worse 00282 if(obj.id == NULL_OBJ) return 0; 00283 else return 1-area(obj.polygon)/scrsize; // smaller is better 00284 // none is assigned 00285 } 00286 00287 // ###################################################################### 00288 std::string SimulationViewerEyeRegion::listAllRegionsHit(const Point2D<int> pt, uint fNum) 00289 { 00290 if(itsObjectsInfo.get() == 0) 00291 LFATAL("No objects data - check xml file."); 00292 00293 if(itsFrameNumber >= itsObjectsInfo->getNumScenes()) 00294 { 00295 LINFO("Ran out of XML scene data..."); 00296 return std::string(); 00297 } 00298 00299 //TODO: should check pt is also looking at current frame w.r.t cropping, etc. 00300 00301 Scene sceneData = 00302 itsObjectsInfo->getSceneData(fNum); 00303 00304 // find which images this saccade target hits 00305 std::string hits; 00306 00307 for(std::vector<Object>::iterator itrObject = 00308 sceneData.objects.begin(); itrObject != sceneData.objects.end(); itrObject++) 00309 if(pnpoly((*itrObject).polygon,pt)) 00310 { 00311 if (!hits.empty()) hits += ";"; //delimiter 00312 hits += getObjName((*itrObject).id); 00313 } 00314 00315 if (hits.empty()) 00316 { 00317 LINFO("nothing hit!"); 00318 hits = "none"; 00319 } 00320 return hits; 00321 } 00322 // ###################################################################### 00323 void SimulationViewerEyeRegion::extraSampleProcessing(const rutz::shared_ptr<EyeData> data) 00324 { 00325 Point2D<int> currPos = rawToRet(data->position()); 00326 uint hitObjID = findHitRegion(currPos); 00327 00328 if(hitObjID != itsCurrRegionID) 00329 { 00330 // every time we switch objects we send output to a different file than the one for svem data 00331 // this gives an object by object account instead of a saccade by saccade account 00332 writeROIOutput(currPos); 00333 00334 // TODO: incorporate output into one file, interweave headers? something perhaps more meaningful, less readable though. 00335 // probably not worth it as a separate option.. 00336 // TODO: work into multiple eyetrace streams? 00337 00338 Object oldObj = getSceneObj(itsCurrRegionID, itsObjectFrameOnset), 00339 newObj = getSceneObj(hitObjID); 00340 // CLINFO("**** Object gaze shift found: %s (%0.3f)->%s (%0.3f) at %.1fms **** ", 00341 // getObjName(oldObj.id).c_str(), rankForeground(oldObj), 00342 // getObjName(newObj.id).c_str(), rankForeground(newObj), 00343 // itsCurrTime.msecs()); 00344 00345 // update new information 00346 itsCurrRegionID = hitObjID; 00347 itsObjectOnset = itsCurrTime; 00348 itsObjectFrameOnset = itsFrameNumber; 00349 itsObjectEntry = data; 00350 } 00351 } 00352 00353 // ###################################################################### 00354 void SimulationViewerEyeRegion::writeROIOutput(Point2D<int> currPos) 00355 { 00356 // writes another file with more specific region data, namely: 00357 // distances to different objects 00358 // only works for one file at a time right now 00359 00360 if(itsObjectsInfo.get() == 0) 00361 LFATAL("No objects data - check xml file."); 00362 if(itsFrameNumber >= itsObjectsInfo->getNumScenes()) 00363 { 00364 LINFO("Ran out of XML scene data..."); 00365 return; 00366 } 00367 00368 // if we don't have the name for the file, exit 00369 if (itsRegionOutFname.getVal().empty()) return; 00370 00371 00372 if (!itsRegHeaderWritten) //write the header first 00373 { 00374 itsRegHeaderWritten = true; 00375 00376 std::string header; 00377 header += "objname onset duration "; 00378 for(std::vector<std::string>::iterator iStr = itsObjectsNames.begin(); 00379 iStr != itsObjectsNames.end(); iStr++) 00380 header += sformat("%sDist ", (*iStr).c_str()); 00381 00382 if (itsRegionOutFile) 00383 (*itsRegionOutFile) << header << std::endl; 00384 00385 // return; // don't want to process first object 00386 00387 } 00388 00389 std::string output; 00390 00391 // first, the last region 00392 Object thisObj = 00393 getSceneObj(itsCurrRegionID,itsObjectFrameOnset); 00394 output += sformat("%s ", getObjName(thisObj.id).c_str()); 00395 00396 // second, the time entered to that region 00397 output += sformat("%.1fms ", itsObjectOnset.msecs()); 00398 // third, the time elapsed in that region 00399 output += sformat("%.1fms ", (itsCurrTime-itsObjectOnset).msecs()); 00400 00401 // now, for distances to region. 00402 // we precompute the distances to each region (calculated from edges) 00403 // and sample at N evenly spaced frames to see where the average distance is 00404 const uint N = 5; 00405 uint iFrame; 00406 00407 double dist; 00408 const double NOT_IN_FRAME = 10000; //out of range 00409 std::vector<std::vector<double> > distsNow 00410 (itsNumObjects, std::vector<double>(N,NOT_IN_FRAME)); 00411 00412 for(uint i = 0; i < N; i++) 00413 { 00414 iFrame = itsObjectFrameOnset + uint((itsFrameNumber-itsObjectFrameOnset)*i/N); 00415 Scene sceneData = 00416 itsObjectsInfo->getSceneData(iFrame); 00417 00418 // find the distances to the objects in that frame 00419 std::vector<Object>::iterator itr; 00420 for(itr = sceneData.objects.begin(); itr != sceneData.objects.end(); itr++) 00421 { 00422 std::string objName = getObjName((*itr).id); 00423 std::vector<Point2D<int> > objPoly = (*itr).polygon; //grab its polygon 00424 uint ps = objPoly.size(); 00425 uint objID = (*itr).id; 00426 00427 // find min distance to polygon by iterating around edges 00428 double mindist = 10000; 00429 for (uint ii = 0, jj = ps-1; ii < ps; jj = ii++) 00430 { 00431 dist = currPos.distanceToSegment(objPoly[ii],objPoly[jj]); 00432 if(mindist > dist) mindist = dist; 00433 } 00434 00435 // assign distance as inside or outside a polygon (-/+) 00436 distsNow[objID][i] = (pnpoly(objPoly,currPos)) ? 00437 -mindist : mindist; 00438 } 00439 } 00440 00441 uint nPts; 00442 for(uint i = 0; i < itsNumObjects; i++) // report the average of those distances 00443 { 00444 nPts = 0; 00445 dist = 0; 00446 for(uint j = 0; j < N; j++) 00447 { 00448 // if the object is not in the frame we do not average it 00449 if(distsNow[i][j] != NOT_IN_FRAME) nPts++; 00450 dist += distsNow[i][j]; 00451 } 00452 if (nPts == 0) output += "NaN "; // to be read by matlab 00453 else output += sformat("%0.3f ", dist/nPts); 00454 } 00455 00456 if (itsRegionOutFile) 00457 (*itsRegionOutFile) << output << std::endl; 00458 } 00459 00460 // ###################################################################### 00461 void SimulationViewerEyeRegion::drawEye(const rutz::shared_ptr<EyeData> rawPos, const uint tN) 00462 { 00463 // convert to retinal coordinates (accounting for any shifting, 00464 // embedding, etc): 00465 // Point2D<int> currPos = rawToRet(rawPos->position()); 00466 00467 const PixRGB<byte> col = itsEyeStyles[tN].col; 00468 if(itsFrameNumber >= itsObjectsInfo->getNumScenes()) 00469 { 00470 LINFO("Ran out of XML scene data... (%d/%d)",itsFrameNumber, 00471 itsObjectsInfo->getNumScenes()); 00472 return; 00473 } 00474 00475 // note: these functions are tolerant to 00476 // coordinates outside the actual image area 00477 // this may cause errors if the eye coordinate is outside 00478 00479 if(itsHighlightMode.getVal().compare("traced") == 0) 00480 { 00481 Object currObj = 00482 getSceneObj(itsCurrRegionID); 00483 00484 drawFilledPolygon(itsTargetsMask, currObj.polygon, col); 00485 drawRegions(currObj); 00486 } 00487 if(itsObjectDrawMode.getVal().compare("traced") == 0) 00488 { 00489 Object currObj = 00490 getSceneObj(itsCurrRegionID); 00491 drawRegions(currObj); 00492 } 00493 SimulationViewerEyeMvt::drawEye(rawPos, tN); 00494 } 00495 00496 // ###################################################################### 00497 void SimulationViewerEyeRegion::drawFOA(const Point2D<int> target, const uint tN) 00498 { 00499 const PixRGB<byte> col = itsEyeStyles[tN].col; 00500 if(itsFrameNumber >= itsObjectsInfo->getNumScenes()) 00501 { 00502 LINFO("Ran out of XML scene data..."); 00503 return; 00504 } 00505 00506 if(itsHighlightMode.getVal().compare("targeted") == 0) 00507 { 00508 uint hitObjID = findHitRegion(target); 00509 Object thisObj = 00510 getSceneObj(hitObjID); 00511 drawFilledPolygon(itsTargetsMask, thisObj.polygon, col); 00512 drawRegions(thisObj); 00513 } 00514 if(itsObjectDrawMode.getVal().compare("targeted") == 0) 00515 { 00516 uint hitObjID = findHitRegion(target); 00517 Object thisObj = 00518 getSceneObj(hitObjID); 00519 drawRegions(thisObj); 00520 } 00521 00522 SimulationViewerEyeMvt::drawFOA(target,tN); 00523 } 00524 00525 // ###################################################################### 00526 void SimulationViewerEyeRegion::drawRegions(Object myObj) 00527 { 00528 if(itsObjectsInfo.get() == 0) 00529 LFATAL("No objects data - check xml file."); 00530 00531 00532 if(itsFrameNumber >= itsObjectsInfo->getNumScenes()) 00533 { 00534 LINFO("Ran out of XML scene data..."); 00535 return; 00536 } 00537 00538 Image<PixRGB<byte> > shapes(itsDrawings.getDims(),ZEROS); 00539 00540 Scene sceneData = itsObjectsInfo->getSceneData(itsFrameNumber); 00541 if(myObj.id!=NULL_OBJ || itsObjectDrawMode.getVal().compare("all") != 0) 00542 { 00543 PixRGB<byte> lineCol(0,255,0); 00544 if (itsRandomColoring.getVal()) 00545 lineCol = itsRandColors[myObj.id]; 00546 00547 00548 drawOutlinedPolygon(shapes, myObj.polygon, 00549 lineCol, 00550 Point2D<int>(0,0), 0, 1.0, 0, 0, 00551 itsLineThickness.getVal()); 00552 00553 // find appropriate (i.e. upperleft most) corner to write name on... 00554 // define upper left corner as farthest from 00555 // lowerleft/upperright diagonal of polygon 00556 Point2D<int> cm = centroid(myObj.polygon); 00557 Point2D<int> upperleft = cm - Point2D<int>(-10,10); 00558 00559 double thisDist, maxDist = 0; 00560 Point2D<int> refPt = myObj.polygon[0]; 00561 for(uint i = 0; i < myObj.polygon.size(); i++) { 00562 thisDist = myObj.polygon[i].distanceToLine(cm, upperleft, true); 00563 if (thisDist > maxDist) { 00564 maxDist = thisDist; 00565 refPt = myObj.polygon[i]; 00566 } 00567 } 00568 00569 writeText(itsDrawings, refPt+Point2D<int>(10,5), 00570 getObjName(myObj.id).c_str(), 00571 PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00572 SimpleFont::FIXED(10), true); 00573 } 00574 else { // if no obj, show all objects in this frame 00575 Scene thisScene = itsObjectsInfo->getSceneData(itsFrameNumber); 00576 for (std::vector<Object>::iterator iObj = thisScene.objects.begin(); 00577 iObj != thisScene.objects.end(); iObj++) 00578 drawRegions(*iObj); 00579 } 00580 itsDrawings = composite(itsDrawings,shapes); 00581 } 00582 00583 00584 // ###################################################################### 00585 Object SimulationViewerEyeRegion::getSceneObj(uint objID, uint FrameNum) 00586 { 00587 Object non_object = Object(); 00588 non_object.name = getObjName(NULL_OBJ); 00589 non_object.id = NULL_OBJ; 00590 if(objID == NULL_OBJ) return non_object; // no object targeted 00591 ASSERT(FrameNum < itsObjectsInfo->getNumScenes()); 00592 00593 Scene sceneData = itsObjectsInfo->getSceneData(FrameNum); 00594 for(std::vector<Object>::iterator itrObject = 00595 sceneData.objects.begin(); itrObject != sceneData.objects.end(); itrObject++) 00596 if ((*itrObject).id==objID) return (*itrObject); 00597 00598 return non_object; // object not found 00599 } 00600 00601 // ###################################################################### 00602 std::string SimulationViewerEyeRegion::getObjName(uint objID) 00603 { 00604 if(objID == NULL_OBJ) return "none"; 00605 else if(objID >= itsNumObjects) LFATAL("%d illegal index (max = %d, null = %d)", objID, itsNumObjects, NULL_OBJ); 00606 return itsObjectsNames[objID]; 00607 } 00608 00609 // ###################################################################### 00610 std::string SimulationViewerEyeRegion::craftSVEMOutput(const std::string tfn, 00611 const rutz::shared_ptr<EyeData> data) 00612 { 00613 // Write some measurements to a string. Here, we just put all the stats 00614 // into a text string, and we will send that off to be 00615 // written to disk. 00616 00617 // The first time we run this, we build a header for the file 00618 // to know what stats to read. This is done in the craft submodules. 00619 00620 // Note: This overloads the SVEyeMvt class method craftSVEMOutput. 00621 // It does so to insert a 00622 // In the future we may implement a policy-based method to grab different output modules. 00623 00624 std::string output = SimulationViewerEyeMvt::craftSVEMOutput(tfn,data); 00625 00626 // add which objects are saccaded to/from 00627 output += craftRegionOutput(data); 00628 00629 return output; 00630 } 00631 00632 // ###################################################################### 00633 std::string SimulationViewerEyeRegion:: 00634 craftRegionOutput(const rutz::shared_ptr<EyeData> data) 00635 { 00636 std::string output; 00637 // output region of origin, region of destination 00638 00639 Point2D<int> position = rawToRet(data->position()); 00640 Point2D<int> sacTarget = rawToRet(data->saccadeTarget()); 00641 bool giveAllRegions = true; //TODO: should be ModelParam 00642 if(giveAllRegions) 00643 { 00644 uint destObjID = findHitRegion(sacTarget); 00645 output += sformat(" %s %s %s %s", 00646 getObjName(itsCurrRegionID).c_str(), 00647 listAllRegionsHit(position).c_str(), 00648 getObjName(destObjID).c_str(), 00649 listAllRegionsHit(sacTarget).c_str()); 00650 00651 if(!itsHeaderCrafted) 00652 { 00653 itsOutFields.push_back("bestregion_src"); 00654 itsOutFields.push_back("region_src"); 00655 itsOutFields.push_back("bestregion_target"); 00656 itsOutFields.push_back("region_target"); 00657 } 00658 } 00659 else 00660 { 00661 uint destObjID = findHitRegion(sacTarget); 00662 output += sformat(" %s %s", 00663 getObjName(itsCurrRegionID).c_str(), 00664 00665 getObjName(destObjID).c_str()); 00666 if(!itsHeaderCrafted) 00667 { 00668 itsOutFields.push_back("region_src"); 00669 itsOutFields.push_back("region_target"); 00670 } 00671 00672 } 00673 return output; 00674 } 00675 00676 // ###################################################################### 00677 Image< PixRGB<byte> > SimulationViewerEyeRegion::getTraj(SimEventQueue& q) 00678 { 00679 LDEBUG("Buffering output frame %d...", itsFrameNumber); 00680 00681 GVX_TRACE(__PRETTY_FUNCTION__); 00682 // get the latest retina image: 00683 Image< PixRGB<byte> > input; 00684 if (SeC<SimEventRetinaImage> e = q.check<SimEventRetinaImage>(this, SEQ_ANY)) 00685 { 00686 input = e->frame().colorByte(); 00687 00688 if(itsObjectDrawMode.getVal().compare("all") == 0) 00689 drawRegions(); 00690 else if(itsObjectDrawMode.getVal().compare("selected") == 0 && 00691 !itsSelectedObjects.getVal().empty()) 00692 { 00693 Scene sceneData = itsObjectsInfo->getSceneData(itsFrameNumber); 00694 for(uint i = 0; i < itsObjectsInfo->getNumObj(itsFrameNumber); i++) 00695 { 00696 Object objData = sceneData.objects[i]; 00697 00698 //TODO: write rules to determine which objects to draw 00699 //as external opt 00700 std::string objName = getObjName(objData.id); 00701 //if(objName.compare(itsSelectedObjects.getVal()) == 0) 00702 drawRegions(objData); 00703 } 00704 } 00705 } 00706 else 00707 LFATAL("Could not find required SimEventRetinaImage"); 00708 00709 // draw the highlighted regions if there are any... 00710 if(itsTargetsMask.initialized()) { //TODO: add && itsTargetsMask is not blank for speed 00711 input = alphaBlend(itsTargetsMask, input, 00712 itsHitTransparency.getVal()); 00713 } 00714 00715 // reinit these here, although this should be done in onSimEventClockTick 00716 itsTargetsMask.resize(input.getDims(), true); 00717 00718 // make a composite of the input + the drawings: 00719 Image<PixRGB<byte> > comp = composite(itsDrawings, input); 00720 00721 // return a plain traj? 00722 if (itsSaveTraj.getVal()) return comp; 00723 00724 // let's get the current saliency map (normalized): 00725 const Dims dims = input.getDims(); 00726 Image<float> sm = getMap(q, false); 00727 if (sm.initialized()) sm = rescaleOpt(sm, dims, itsDisplayInterp.getVal()); 00728 else sm.resize(dims, true); // blank 00729 Image< PixRGB<byte> > smc = toRGB(Image<byte>(sm)); 00730 00731 // make a composite of the instantaneous SM + the drawings: 00732 Image< PixRGB<byte> > smcomp = composite(itsDrawings, smc); 00733 00734 // otherwise, return mega combo; we have two formats: if we have a 00735 // max-cache, it will be a 4-image format, otherwise a 2-image 00736 // format: 00737 Image< PixRGB<byte> > ret; 00738 if (itsMaxCacheSize.getVal()) 00739 { 00740 // 4-image format 00741 Image<float> maxsm = 00742 rescaleOpt(itsMaxCache.getMax(), dims, itsDisplayInterp.getVal()); 00743 Image< PixRGB<byte> > maxsmc = toRGB(Image<byte>(maxsm)); 00744 00745 ret = concatX(concatY(input, smcomp), 00746 concatY(comp, composite(itsDrawings, maxsmc))); 00747 00748 drawGrid(ret, dims.w()-1, dims.h()-1, 3, 3, PixRGB<byte>(128, 128, 128)); 00749 } 00750 else 00751 { 00752 // 2-image format: 00753 ret = concatX(comp, smcomp); 00754 drawLine(ret, Point2D<int>(dims.w()-1, 0), Point2D<int>(dims.w()-1, dims.h()-1), 00755 PixRGB<byte>(255, 255, 0), 1); 00756 drawLine(ret, Point2D<int>(dims.w(), 0), Point2D<int>(dims.w(), dims.h()-1), 00757 PixRGB<byte>(255, 255, 0), 1); 00758 } 00759 00760 // make sure image is not unreasonably large: 00761 while (ret.getWidth() > itsMaxComboWidth.getVal()) 00762 ret = decY(lowPass3y(decX(lowPass3x(ret)))); 00763 00764 return ret; 00765 } 00766 00767 // ###################################################################### 00768 /* So things look consistent in everyone's emacs... */ 00769 /* Local Variables: */ 00770 /* indent-tabs-mode: nil */ 00771 /* End: */ 00772