00001 /*! @file Beobot/app-egomotion.C application to demonstrate egomotion from 00002 visual data - input is MPEG or list (in gistlist format) of image files 00003 (assume .ppm files) 00004 Note: reading the velocity: 00005 right hand rule, x-positive to the left, y-positive to the north, 00006 z-positive forward */ 00007 // //////////////////////////////////////////////////////////////////// // 00008 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00009 // by the University of Southern California (USC) and the iLab at USC. // 00010 // See http://iLab.usc.edu for information about this project. // 00011 // //////////////////////////////////////////////////////////////////// // 00012 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00013 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00014 // in Visual Environments, and Applications'' by Christof Koch and // 00015 // Laurent Itti, California Institute of Technology, 2001 (patent // 00016 // pending; application number 09/912,225 filed July 23, 2001; see // 00017 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00018 // //////////////////////////////////////////////////////////////////// // 00019 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00022 // redistribute it and/or modify it under the terms of the GNU General // 00023 // Public License as published by the Free Software Foundation; either // 00024 // version 2 of the License, or (at your option) any later version. // 00025 // // 00026 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00027 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00028 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00029 // PURPOSE. See the GNU General Public License for more details. // 00030 // // 00031 // You should have received a copy of the GNU General Public License // 00032 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00033 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00034 // Boston, MA 02111-1307 USA. // 00035 // //////////////////////////////////////////////////////////////////// // 00036 // 00037 // Primary maintainer for this file: Christian Siagian <siagian@usc.edu> 00038 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Beobot/app-egomotion.C $ 00039 // $Id: app-egomotion.C 14535 2011-02-18 22:40:51Z siagian $ 00040 // 00041 00042 #include "Beobot/Landmark.H" 00043 #include "Channels/ChannelOpts.H" 00044 #include "Component/GlobalOpts.H" 00045 #include "Component/ModelManager.H" 00046 #include "Component/ModelOptionDef.H" 00047 #include "Component/OptionManager.H" 00048 #include "GUI/XWinManaged.H" 00049 #include "Gist/FFN.H" 00050 #include "Gist/trainUtils.H" 00051 #include "Image/ColorOps.H" 00052 #include "Image/CutPaste.H" 00053 #include "Image/DrawOps.H" 00054 #include "Image/MathOps.H" 00055 #include "Image/Pixels.H" 00056 #include "Image/ShapeOps.H" 00057 #include "Image/Transforms.H" 00058 #include "Media/MPEGStream.H" 00059 #include "Media/MediaOpts.H" 00060 #include "Media/MediaSimEvents.H" 00061 #include "Neuro/GistEstimator.H" 00062 #include "Neuro/InferoTemporal.H" 00063 #include "Neuro/NeuroOpts.H" 00064 #include "Neuro/NeuroSimEvents.H" 00065 #include "Neuro/Retina.H" 00066 #include "Neuro/ShapeEstimator.H" 00067 #include "Neuro/ShapeEstimatorModes.H" 00068 #include "Neuro/SpatialMetrics.H" 00069 #include "Neuro/StdBrain.H" 00070 #include "Neuro/gistParams.H" 00071 #include "Neuro/VisualCortex.H" 00072 #include "Raster/Raster.H" 00073 #include "SIFT/CameraIntrinsicParam.H" 00074 #include "SIFT/Histogram.H" 00075 #include "SIFT/Keypoint.H" 00076 #include "SIFT/VisualObject.H" 00077 #include "SIFT/VisualObjectDB.H" 00078 #include "Simulation/SimEventQueueConfigurator.H" 00079 #include "Util/Timer.H" 00080 00081 #include <iostream> 00082 #include <fstream> 00083 00084 00085 #include "SIFT/SIFTegomotion.H" 00086 00087 #define DB_NAME "out_database" 00088 00089 #define W_ASPECT_RATIO 320 // ideal minimum width for display 00090 #define H_ASPECT_RATIO 240 // ideal minimum height for display 00091 00092 FeedForwardNetwork *ffn_place; 00093 double **gistW = NULL; 00094 00095 CloseButtonListener wList; 00096 rutz::shared_ptr<XWinManaged> salWin; 00097 rutz::shared_ptr<XWinManaged> objWin; 00098 rutz::shared_ptr<XWinManaged> trajWin; 00099 00100 int wDisp, hDisp, sDisp, scaleDisp; 00101 int wDispWin, hDispWin; 00102 00103 // gist display 00104 int pcaW = 16, pcaH = 5; 00105 int winBarW = 5, winBarH = 25; 00106 00107 // number of landmarks produced 00108 int numObj = 0; 00109 00110 // ###################################################################### 00111 void setupDispWin (int w, int h); 00112 Image< PixRGB<byte> > getSalDispImg (Image< PixRGB<byte> > img, Image<float> roiImg, 00113 Image< PixRGB<byte> > objImg, Image< PixRGB<byte> > objImg2); 00114 void processSalCue (Image<PixRGB<byte> > inputImg, 00115 nub::soft_ref<StdBrain> brain, Point2D<int> winner, int fNum, 00116 std::vector< rutz::shared_ptr<Landmark> >& landmarks, 00117 const Image<float>& semask, const std::string& selabel); 00118 void getGistFileList (std::string fName, std::vector<std::string>& tag, 00119 std::vector<int>& start, std::vector<int>& num); 00120 Image< PixRGB<byte> > getTrajImg (std::vector<Image <double> > traj, int w, int h); 00121 00122 // ###################################################################### 00123 00124 // Main function 00125 /*! Load a database, enrich it with new VisualObject entities 00126 extracted from the given images, and save it back. */ 00127 int main(const int argc, const char **argv) 00128 { 00129 MYLOGVERB = LOG_INFO; // suppress debug messages 00130 00131 // Instantiate a ModelManager: 00132 ModelManager manager("Egomotion Model"); 00133 00134 // we cannot use saveResults() on our various ModelComponent objects 00135 // here, so let's not export the related command-line options. 00136 manager.allowOptions(OPTEXP_ALL & (~OPTEXP_SAVE)); 00137 00138 // Instantiate our various ModelComponents: 00139 nub::soft_ref<SimEventQueueConfigurator> 00140 seqc(new SimEventQueueConfigurator(manager)); 00141 manager.addSubComponent(seqc); 00142 00143 nub::soft_ref<InputMPEGStream> 00144 ims(new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream")); 00145 manager.addSubComponent(ims); 00146 00147 nub::soft_ref<StdBrain> brain(new StdBrain(manager)); 00148 manager.addSubComponent(brain); 00149 00150 nub::ref<SpatialMetrics> metrics(new SpatialMetrics(manager)); 00151 manager.addSubComponent(metrics); 00152 00153 manager.exportOptions(true); 00154 metrics->setFOAradius(30); // FIXME 00155 metrics->setFoveaRadius(30); // FIXME 00156 manager.setOptionValString(&OPT_MaxNormType, "FancyOne"); 00157 manager.setOptionValString(&OPT_UseRandom, "false"); 00158 00159 manager.setOptionValString(&OPT_IORtype, "Disc"); 00160 manager.setOptionValString(&OPT_RawVisualCortexChans,"OIC"); 00161 00162 // customize the region considered part of the "object" 00163 // manager.setOptionValString("ShapeEstimatorMode","SaliencyMap"); 00164 // manager.setOptionValString(&OPT_ShapeEstimatorMode,"ConspicuityMap"); 00165 manager.setOptionValString(&OPT_ShapeEstimatorMode, "FeatureMap"); 00166 manager.setOptionValString(&OPT_ShapeEstimatorSmoothMethod, "Chamfer"); 00167 //manager.setOptionValString(&OPT_ShapeEstimatorSmoothMethod, "Gaussian"); 00168 00169 // DO NOT set up the INFEROTEMPORAL 00170 //manager.setOptionValString(&OPT_InferoTemporalType,"Std"); 00171 //manager.setOptionValString(&OPT_AttentionObjRecog,"yes"); 00172 //manager.setOptionValString(&OPT_MatchObjects,"false"); 00173 00174 // Request a bunch of option aliases (shortcuts to lists of options): 00175 REQUEST_OPTIONALIAS_NEURO(manager); 00176 00177 // Parse command-line: 00178 if (manager.parseCommandLine(argc, argv, "<*.mpg or *_gistList.txt>", 00179 1, 1) == false) 00180 return(1); 00181 00182 nub::soft_ref<SimEventQueue> seq = seqc->getQ(); 00183 00184 // if the file passed ends with _gistList.txt 00185 // we have a different protocol 00186 bool isGistListInput = false; 00187 int ifLen = manager.getExtraArg(0).length(); 00188 if(ifLen > 13 && 00189 manager.getExtraArg(0).find("_gistList.txt",ifLen - 13) != std::string::npos) 00190 isGistListInput = true; 00191 00192 // NOTE: this could now be controlled by a command-line option 00193 // --preload-mpeg=true 00194 manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true"); 00195 00196 // do post-command-line configs: 00197 std::vector<std::string> tag; 00198 std::vector<int> start; 00199 std::vector<int> num; 00200 if(isGistListInput) 00201 { 00202 LINFO("we have a gistList input"); 00203 getGistFileList(manager.getExtraArg(0).c_str(), tag, start, num); 00204 } 00205 else 00206 { 00207 LINFO("we have an mpeg input"); 00208 ims->setFileName(manager.getExtraArg(0)); 00209 manager.setOptionValString(&OPT_InputFrameDims, 00210 convertToString(ims->peekDims())); 00211 } 00212 00213 // frame delay in seconds 00214 double rtdelay = 33.3667/1000.0; // real time 00215 double fdelay = rtdelay * 3; // 3 times slower than real time 00216 (void)fdelay; 00217 00218 // let's get all our ModelComponent instances started: 00219 manager.start(); 00220 00221 // create a landmark covering the whole scene 00222 rutz::shared_ptr<Landmark> scene(new Landmark()); 00223 scene->setMatchWin(objWin); 00224 00225 // we HARD CODE the camera intrinsic parameter FOR BEOBOT 00226 rutz::shared_ptr<CameraIntrinsicParam> 00227 cip(new CameraIntrinsicParam(435.806712867904707, 438.523234664943345, 00228 153.585228257964644, 83.663180940275609, 0.0)); 00229 scene->setCameraIntrinsicParam(cip); 00230 00231 // main loop: 00232 SimTime prevstime = SimTime::ZERO(); uint fNum = 0; 00233 Image< PixRGB<byte> > inputImg; 00234 Image< PixRGB<byte> > dispImg; 00235 int w = 0; // 320 or iDims.w() - 50 + 1; 00236 int h = 0; // 240 or iDims.h(); 00237 unsigned int cLine = 0; 00238 int cIndex = start[0]; 00239 std::string folder = ""; 00240 std::string::size_type sPos = manager.getExtraArg(0).rfind("/",ifLen); 00241 if(sPos != std::string::npos) 00242 folder = manager.getExtraArg(0).substr(0,sPos+1); 00243 std::vector<Image<double> > traj; 00244 while(1) 00245 { 00246 // has the time come for a new frame? 00247 // If we want to SLOW THINGS DOWN change fdelay 00248 if (fNum == 0 || 00249 (seq->now() - 0.5 * (prevstime - seq->now())).secs() - fNum * fdelay > fdelay) 00250 { 00251 // load new frame 00252 std::string fName; 00253 if(isGistListInput) 00254 { 00255 if (cLine >= tag.size()) break; // end of input list 00256 00257 // open the current file 00258 char tNumStr[100]; sprintf(tNumStr,"%06d",cIndex); 00259 fName = folder + tag[cLine] + std::string(tNumStr) + ".ppm"; 00260 00261 inputImg = Raster::ReadRGB(fName); 00262 cIndex++; 00263 00264 if(cIndex >= start[cLine] + num[cLine]) 00265 { 00266 cLine++; 00267 if (cLine < tag.size()) cIndex = start[cLine]; 00268 } 00269 00270 // reformat the file name to a gist name 00271 int fNameLen = fName.length(); 00272 unsigned int uPos = fName.rfind("_",fNameLen); 00273 fName = fName.substr(0,uPos)+ ".ppm"; 00274 } 00275 else 00276 { 00277 fName = manager.getExtraArg(0); 00278 inputImg = ims->readRGB(); 00279 if (inputImg.initialized() == false) break; // end of input stream 00280 // format new frame 00281 inputImg = crop(inputImg, 00282 Rectangle::tlbrI(0,25,inputImg.getHeight()-1, inputImg.getWidth()-25)); 00283 cIndex = fNum+1; 00284 } 00285 00286 // setup display at the start of stream 00287 // NOTE: wDisp, hDisp, and sDisp are modified 00288 if (fNum == 0) 00289 { 00290 w = inputImg.getWidth(); h = inputImg.getHeight(); 00291 setupDispWin(w, h); LINFO("w: %d, h: %d",w, h); 00292 } 00293 00294 dispImg = inputImg; 00295 salWin->drawImage(dispImg,0,0); 00296 LINFO("\nnew frame :%d",fNum); 00297 00298 // take out frame borders NOTE: ONLY FOR SONY CAMCORDER 00299 //inputImg = crop(inputImg, Rectangle::tlbrI(0, 25, h-1, 25 + w - 1)); 00300 00301 // pass input to brain: 00302 rutz::shared_ptr<SimEventInputFrame> 00303 e(new SimEventInputFrame(brain.get(), GenericFrame(inputImg), 0)); 00304 seq->post(e); //post the image to the brain 00305 00306 // track the view 00307 std::string viewName(sformat("view%07d", fNum)); 00308 rutz::shared_ptr<VisualObject> 00309 cv(new VisualObject(viewName, "", inputImg)); 00310 rutz::shared_ptr<VisualObjectMatch> cmatch = scene->build(cv, fNum); 00311 00312 // CHECK EGOMOTION 00313 if(fNum != 0) 00314 { 00315 rutz::shared_ptr<SIFTegomotion> 00316 egm(new SIFTegomotion(cmatch, cip, objWin)); 00317 00318 // reading the velocity: 00319 // right hand rule, x-positive to the left, 00320 // y-positive to the north, z-positive forward 00321 traj.push_back(egm->getItsVel()); 00322 egm->print(traj[traj.size() -1] ,"final velocity"); 00323 trajWin->drawImage(getTrajImg(traj, 5*w, 2*h),0,0); 00324 //Raster::waitForKey(); 00325 } 00326 00327 // increment frame count 00328 fNum++; 00329 } 00330 00331 // evolve brain: 00332 prevstime = seq->now(); // time before current step 00333 const SimStatus status = seq->evolve(); 00334 00335 // process SALIENT location found 00336 if (SeC<SimEventWTAwinner> e = seq->check<SimEventWTAwinner>(0)) 00337 { 00338 const Point2D<int> winner = e->winner().p; 00339 LINFO("Frame: %d, winner: (%d,%d)", fNum, winner.i, winner.j); 00340 00341 Image<float> semask; std::string selabel; 00342 if (SeC<SimEventShapeEstimatorOutput> 00343 e = seq->check<SimEventShapeEstimatorOutput>(0)) 00344 { semask = e->smoothMask(); selabel = e->winningLabel(); } 00345 00346 //processSalCue(inputImg, brain, winner, fNum, landmarks, 00347 // semask, selabel); 00348 00349 if (SIM_BREAK == status) // Brain decided it's time to quit 00350 break; 00351 } 00352 } 00353 00354 // save the resulting database: 00355 //if(vdb->numObjects() != 0) 00356 // vdb->saveTo(DB_NAME); 00357 00358 // stop all our ModelComponents 00359 manager.stop(); 00360 00361 // all done! 00362 return 0; 00363 } 00364 00365 // ###################################################################### 00366 // process salient cues 00367 void processSalCue(const Image<PixRGB<byte> > inputImg, 00368 nub::soft_ref<StdBrain> brain, Point2D<int> winner, int fNum, 00369 std::vector< rutz::shared_ptr<Landmark> >& landmarks, 00370 const Image<float>& semask, const std::string& selabel) 00371 { 00372 // segment out the object -> maybe port to infero-temporal later 00373 // ---------------------------------------------- 00374 00375 // use Shape estimator to focus on the attended region 00376 Image<float> roiImg; 00377 Image<PixRGB<byte> > objImgSE; 00378 Point2D<int> objOffsetSE; 00379 if (semask.initialized()) 00380 { 00381 roiImg = semask * luminance(inputImg); 00382 00383 float mn, mx; getMinMax(semask,mn,mx); 00384 Rectangle r = findBoundingRect(semask, mx*.05f); 00385 Image<PixRGB<byte> > objImgSE = crop(inputImg, r); 00386 objOffsetSE = Point2D<int>(r.left(),r.top()); 00387 } 00388 else 00389 { 00390 LINFO("SE Smooth Mask not yet initialized"); 00391 roiImg = luminance(inputImg); 00392 objImgSE = inputImg; 00393 objOffsetSE = Point2D<int>(0,0); 00394 } 00395 00396 // ---------------------------------------------- 00397 // or with pre-set window 00398 Rectangle roi = 00399 Rectangle::tlbrI(winner.j - 50, winner.i - 50, 00400 winner.j + 50, winner.i + 50); 00401 roi = roi.getOverlap(inputImg.getBounds()); 00402 LINFO("[%d,%d,%d,%d]",roi.top(),roi.left(),roi.bottomI(),roi.rightI()); 00403 Image<PixRGB<byte> > objImgWIN = crop(inputImg, roi); 00404 Point2D<int> objOffsetWIN(roi.left(),roi.top()); 00405 00406 // ---------------------------------------------- 00407 00408 LINFO("TOP LEFT at: SE:(%d,%d) WIN:(%d,%d)", 00409 objOffsetSE.i , objOffsetSE.j, 00410 objOffsetWIN.i , objOffsetWIN.j); 00411 00412 // draw the results 00413 drawCircle(roiImg, winner, 10, 0.0f, 1); 00414 drawPoint(roiImg, winner.i, winner.j, 0.0f); 00415 LINFO("Frame: %d, winner: (%d,%d) in %s", fNum, winner.i, winner.j, 00416 selabel.c_str()); 00417 salWin->drawImage(getSalDispImg(inputImg,roiImg,objImgWIN,objImgSE),0,0); 00418 //salWin->drawImage(Image<PixRGB<byte> >(inputImg.getDims() * 2, ZEROS) ,0,0); 00419 Raster::waitForKey(); 00420 00421 // WE CHOOSE: SE 00422 Image<PixRGB<byte> > objImg(objImgSE); 00423 Point2D<int> objOffset(objOffsetSE); 00424 00425 // need a Visual Cortex to obtain the feature vector 00426 LFATAL("fixme using a SimReq"); 00427 //////////nub::soft_ref<VisualCortex> vc = brain->getVC(); 00428 std::vector<float> fvec; ////////vc->getFeatures(winner, fvec); 00429 00430 // create a new VisualObject (a set of SIFT keypoints) 00431 // with the top-left coordinate of the window 00432 rutz::shared_ptr<VisualObject> 00433 obj(new VisualObject("NewObject", "NewObject", 00434 objImg, winner - objOffset, fvec)); 00435 00436 std::string objName(sformat("obj%07d", numObj)); 00437 obj->setName(objName); 00438 obj->setImageFname(objName + ".png"); 00439 numObj++; 00440 00441 // check with the salient regions DB before adding 00442 int trackAccepted = 0; 00443 LINFO("we have: %"ZU" landmarks to match", landmarks.size()); 00444 for(uint i = 0; i < landmarks.size(); i++) 00445 { 00446 LINFO("tracking landmark number: %d",i); 00447 rutz::shared_ptr<VisualObjectMatch> cmatch = 00448 landmarks[i]->build(obj, objOffset, fNum); 00449 if(cmatch.is_valid() && cmatch->getScore() > 3.0) 00450 trackAccepted++; 00451 } 00452 00453 // if it's not used by any of the existing landmarks entry 00454 if(trackAccepted == 0) 00455 { 00456 // create a new one 00457 LINFO("create a new Landmark number %"ZU,landmarks.size()); 00458 std::string lmName(sformat("landmark%07"ZU, landmarks.size())); 00459 rutz::shared_ptr<Landmark> 00460 newlm(new Landmark(obj, objOffset, fNum, lmName)); 00461 newlm->setMatchWin(objWin); 00462 landmarks.push_back(newlm); 00463 Raster::waitForKey(); 00464 } 00465 else if(trackAccepted > 1) 00466 { 00467 LINFO("May have: %d objects jumbled together", trackAccepted); 00468 } 00469 } 00470 00471 // ###################################################################### 00472 void getGistFileList(std::string fName, std::vector<std::string>& tag, 00473 std::vector<int>& start, std::vector<int>& num) 00474 { 00475 char comment[200]; FILE *fp; char inLine[100]; 00476 00477 // open the file 00478 if((fp = fopen(fName.c_str(),"rb")) == NULL) 00479 LFATAL("samples file: %s not found",fName.c_str()); 00480 LINFO("fName: %s",fName.c_str()); 00481 00482 // get number of samples 00483 int nSamples; if (fgets(inLine, 1000, fp) == NULL) LFATAL("fgets failed"); sscanf(inLine, "%d %s", &nSamples, comment); 00484 00485 // the number of categories 00486 int tNcat; if (fgets(inLine, 1000, fp) == NULL) LFATAL("fgets failed"); sscanf(inLine, "%d %s", &tNcat, comment); 00487 00488 // get the type of ground truth 00489 char gtOpt[100]; if (fgets(inLine, 1000, fp) == NULL) LFATAL("fgets failed"); sscanf(inLine, "%s %s", gtOpt, comment); 00490 00491 // skip column headers 00492 if (fgets(inLine, 1000, fp) == NULL) LFATAL("fgets failed"); 00493 00494 char cName[100]; char ext[100]; int cStart, cNum; int gTruth; 00495 while(fgets(inLine, 1000, fp) != NULL) 00496 { 00497 // get the files in this category and ground truth 00498 sscanf(inLine, "%s %d %d %d %s", cName, &cStart, &cNum, &gTruth, ext); 00499 LINFO(" sName: %s %d %d %d %s",cName, cStart, cNum, gTruth, ext); 00500 00501 tag.push_back(cName); 00502 start.push_back(cStart); 00503 num.push_back(cNum); 00504 } 00505 fclose(fp); 00506 } 00507 00508 // ###################################################################### 00509 // setup display window for visualization purposes 00510 void setupDispWin(int w, int h) 00511 { 00512 salWin.reset(new XWinManaged(Dims(2*w, 2*h), 2*w, 0, "Saliency Related" )); 00513 wList.add(*salWin); 00514 00515 objWin.reset(new XWinManaged(Dims(2*w, 2*h), 0, 0, "Object Match" )); 00516 wList.add(*objWin); 00517 00518 trajWin.reset(new XWinManaged(Dims(5*w, 2*h), 0, 0, "Trajectory" )); 00519 wList.add(*objWin); 00520 } 00521 00522 // ###################################################################### 00523 // get saliency display image for visualization purposes 00524 Image< PixRGB<byte> > getSalDispImg (Image< PixRGB<byte> > img, 00525 Image<float> roiImg, 00526 Image< PixRGB<byte> > objImg, 00527 Image< PixRGB<byte> > objImg2) 00528 { 00529 int w = img.getWidth(), h = img.getHeight(); 00530 Image< PixRGB<byte> > salDispImg(2*w,2*h,ZEROS); 00531 00532 inplacePaste(salDispImg, img, Point2D<int>(0, 0)); 00533 Image< PixRGB<byte> > t = makeRGB(roiImg,roiImg,roiImg); 00534 inplacePaste(salDispImg, t, Point2D<int>(0, h)); 00535 inplacePaste(salDispImg, objImg, Point2D<int>(w, 0)); 00536 inplacePaste(salDispImg, objImg2, Point2D<int>(w, h)); 00537 00538 return salDispImg; 00539 } 00540 00541 // ###################################################################### 00542 // get trajectory image for visualization purposes 00543 Image< PixRGB<byte> > getTrajImg (std::vector<Image <double> > traj, int w, int h) 00544 { 00545 Image< PixRGB<byte> > trajImg(w, h, ZEROS); 00546 00547 int sX = 10; int sY = h/2; 00548 00549 // velocity is relative in the range of -1.0 to 1.0 00550 double scale = 5.0; 00551 double locX = double(sX); 00552 double locY = double(sY); 00553 00554 // draw each trajectory in the history 00555 for(uint i = 0; i < traj.size(); i++) 00556 { 00557 double dX = traj[i].getVal(0,2)*scale; 00558 double dY = -traj[i].getVal(0,0)*scale; 00559 LINFO("%d. %f,%f -> dx: %f, dy: %f ", i, traj[i].getVal(0,2), traj[i].getVal(0,0), 00560 dX,dY); 00561 00562 drawDisk(trajImg, Point2D<int>(int(locX),int(locY)), 2, PixRGB<byte>(255,0,0)); 00563 drawLine (trajImg, 00564 Point2D<int>(int(locX),int(locY)), 00565 Point2D<int>(int(locX + dX),int(locY + dY)), 00566 PixRGB<byte>(255,255,255),1); 00567 locX = locX + dX; 00568 locY = locY + dY; 00569 } 00570 00571 return trajImg; 00572 } 00573 00574 // ###################################################################### 00575 /* So things look consistent in everyone's emacs... */ 00576 /* Local Variables: */ 00577 /* indent-tabs-mode: nil */ 00578 /* End: */