00001 /*!@file Beobot/Landmark.C Landmark class for localization */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Christian Siagian <siagian@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Beobot/Landmark.C $ 00035 // $Id: Landmark.C 14309 2010-12-11 02:23:48Z siagian $ 00036 // 00037 // FIXXX ADD: motion recognition 00038 // different VOMA 00039 00040 #include "Beobot/Landmark.H" 00041 #include "Util/Timer.H" 00042 #include "Raster/Raster.H" 00043 #include "Image/CutPaste.H" 00044 #include "Image/DrawOps.H" 00045 #include "Image/MathOps.H" 00046 00047 #include <iostream> 00048 #include <list> 00049 #include <fstream> 00050 00051 #define FWAIT_NUM 5U 00052 #define MIN_LINKS 3U 00053 #define NUM_FRAME_LOOKBACK 10 00054 00055 // ###################################################################### 00056 Landmark::Landmark(const std::string& name): 00057 itsName(name), 00058 itsVoma (new VisualObjectMatchAlgo(VOMA_SIMPLE)), 00059 //VOMA_KDTREE or VOMA_KDTREEBBF 00060 itsVisualObjectDB(new VisualObjectDB()), 00061 itsOffsetCoords(), 00062 itsVisualObjectFNum(), 00063 itsTempVisualObjectDB(new VisualObjectDB()), 00064 itsTempOffsetCoords(), 00065 itsTempVisualObjectFNum(), 00066 itsKPtrackers(), 00067 itsCip() 00068 { 00069 // these numbers are default value (may be incorrect) 00070 // should check if there are objects in the DB first 00071 itsLatestVisualObjectFNum = 0; 00072 itsLatestTempVisualObjectFNum = 0; 00073 } 00074 00075 // ###################################################################### 00076 Landmark::Landmark(rutz::shared_ptr<VisualObject>& obj, 00077 Point2D<int> objOffset, uint fNum, 00078 const std::string& name): 00079 itsName(name), 00080 itsVoma (new VisualObjectMatchAlgo(VOMA_SIMPLE)), 00081 //VOMA_KDTREE or VOMA_KDTREEBBF 00082 itsVisualObjectDB(new VisualObjectDB()), 00083 itsOffsetCoords(), 00084 itsVisualObjectFNum(), 00085 itsTempVisualObjectDB(new VisualObjectDB()), 00086 itsTempOffsetCoords(), 00087 itsTempVisualObjectFNum(), 00088 itsKPtrackers(), 00089 itsCip() 00090 { 00091 itsLatestVisualObjectFNum = fNum; 00092 itsLatestTempVisualObjectFNum = 0; 00093 init(obj, objOffset, fNum); 00094 } 00095 00096 // ###################################################################### 00097 void Landmark::init(rutz::shared_ptr<VisualObject>& obj, 00098 Point2D<int> objOffset, uint fNum) 00099 { 00100 // add the first object to the newly created databases 00101 // landmarks are found because it is salient 00102 // or labelled salient by the input program 00103 addObject(obj, objOffset, fNum); 00104 00105 // register the keypoints locations 00106 for(uint i = 0; i < obj->numKeypoints(); i++) 00107 { 00108 rutz::shared_ptr<KeypointTracker> 00109 newKPtracker(new KeypointTracker(sformat("KPtrk%d_%d", fNum, i))); 00110 newKPtracker->add(obj->getKeypoint(i), objOffset, fNum); 00111 itsKPtrackers.push_back(newKPtracker); 00112 } 00113 LINFO("there are %"ZU" keypoints tracked", itsKPtrackers.size()); 00114 } 00115 00116 // ###################################################################### 00117 bool Landmark::loadFrom(const std::string& fname) 00118 { 00119 // load the actual VisualObjectDatabase 00120 bool succeed = false; 00121 00122 // FILE *fp; 00123 // if((fp = fopen(fname.c_str(),"rb")) == NULL) 00124 // LFATAL("Landmark file %s not found", fname.c_str()); 00125 // LINFO("Loading Environment file %s",envFName.c_str()); 00126 00127 const char *fn = fname.c_str(); 00128 LINFO("Loading Visual Object database: '%s'...", fn); 00129 00130 std::ifstream inf(fn); 00131 if (inf.is_open() == false) 00132 { LERROR("Cannot open '%s' -- USING EMPTY", fn); succeed = false; } 00133 else 00134 { 00135 // inf>>(*this); 00136 std::string name; 00137 std::getline(inf, name); 00138 00139 itsVisualObjectDB->setName(name); 00140 00141 uint size; inf>>size; 00142 itsVisualObjectDB->clearObjects(size); 00143 00144 uint count = 0; 00145 while (count < size) 00146 { 00147 rutz::shared_ptr<VisualObject> newvo(new VisualObject()); 00148 00149 //is>>(*newvo); 00150 newvo->createVisualObject(inf, *newvo, false); 00151 00152 std::string iname = newvo->getImageFname(); 00153 00154 // we add the actual path to the image file name 00155 std::string::size_type spos = fname.find_last_of('/'); 00156 std::string filePath(""); 00157 if(spos != std::string::npos) 00158 filePath = fname.substr(0,spos+1); 00159 std::string fiName = filePath + iname; 00160 newvo->setImageFname(fiName); 00161 newvo->loadImage(); 00162 00163 itsVisualObjectDB->setObject(count, newvo); 00164 count++; 00165 LDEBUG("[%d] %s ", count, fiName.c_str()); 00166 } 00167 00168 inf.close(); 00169 LINFO("Done. Loaded %u VisualObjects.", numObjects()); 00170 succeed = true; 00171 } 00172 //Raster::waitForKey(); 00173 00174 00175 00176 00177 //if(itsVisualObjectDB->loadFrom(fname,false)) 00178 if(succeed) 00179 { 00180 // set the name 00181 itsName = itsVisualObjectDB->getName(); 00182 00183 // extract the other information from the name 00184 // I know. it's clever 00185 for(uint i = 0; i < itsVisualObjectDB->numObjects(); i++) 00186 { 00187 std::string tName = itsVisualObjectDB->getObject(i)->getName(); 00188 int ioff, joff, fNum; 00189 std::string temp; 00190 00191 // ObjName_Ioffset_Ioffset_fNum 00192 // we parse it in reverse 00193 LDEBUG("tName: %s", tName.c_str()); 00194 int lupos = tName.find_last_of('_'); 00195 temp = tName.substr(lupos+1, tName.length()-lupos-1); 00196 tName = tName.substr(0, lupos); 00197 fNum = atoi(temp.c_str()); 00198 00199 lupos = tName.find_last_of('_'); 00200 temp = tName.substr(lupos+1, tName.length()-lupos-1); 00201 tName = tName.substr(0, lupos); 00202 joff = atoi(temp.c_str()); 00203 00204 lupos = tName.find_last_of('_'); 00205 temp = tName.substr(lupos+1, tName.length()-lupos-1); 00206 tName = tName.substr(0, lupos); 00207 ioff = atoi(temp.c_str()); 00208 00209 tName = tName.substr(0, lupos); 00210 00211 LDEBUG("[%3d] %30s: (%3d %3d) fnum: %5d", 00212 i, tName.c_str(), ioff, joff, fNum); 00213 00214 itsOffsetCoords.push_back(Point2D<int>(ioff, joff)); 00215 itsVisualObjectFNum.push_back(fNum); 00216 00217 itsVisualObjectDB->getObject(i)->setName(tName); 00218 00219 } 00220 return true; 00221 } 00222 else return false; 00223 } 00224 00225 // ###################################################################### 00226 void Landmark::setSessionInfo() 00227 { 00228 itsSession.clear(); 00229 itsSessionIndexRange.clear(); 00230 ASSERT(itsVisualObjectDB->numObjects() > 0); 00231 00232 std::string lname; uint sind = 0, eind = 0; 00233 for(uint i = 0; i < itsVisualObjectDB->numObjects(); i++) 00234 { 00235 // get the proper session stem 00236 // take out the suffix object numbers: _SAL_xxxxxxx_xx 00237 std::string sname = itsVisualObjectDB->getObject(i)->getName(); 00238 sname = sname.substr(0, sname.find_last_of('_')); 00239 sname = sname.substr(0, sname.find_last_of('_')); 00240 sname = sname.substr(0, sname.find_last_of('_')); 00241 00242 // if session name changed then add it to the list 00243 if(i != 0 && sname.compare(lname)) 00244 { 00245 itsSessionIndexRange.push_back(std::pair<uint,uint>(sind,eind)); 00246 itsSession.push_back(lname); 00247 sind = i; 00248 } 00249 lname = sname; 00250 eind = i; 00251 } 00252 00253 // add the last session if needed 00254 itsSessionIndexRange.push_back(std::pair<uint,uint>(sind,eind)); 00255 itsSession.push_back(lname); 00256 00257 // print the sessions 00258 uint nSession = itsSession.size(); 00259 for(uint i = 0; i < nSession; i++) 00260 LDEBUG("session[%3d] %30s: [%4d %4d]", i, itsSession[i].c_str(), 00261 itsSessionIndexRange[i].first, itsSessionIndexRange[i].second); 00262 00263 // compute the average salient features of the VO in the landmark 00264 computeSalientFeatures(); 00265 } 00266 00267 // ###################################################################### 00268 void Landmark::computeSalientFeatures() 00269 { 00270 uint nObject = numObjects(); 00271 if (nObject == 0) return; 00272 uint nFeatures = itsVisualObjectDB->getObject(0)->numFeatures(); 00273 itsSalientFeatures.resize(nFeatures); 00274 00275 // get features of each object 00276 for(uint i = 0; i < nFeatures; i++) 00277 { 00278 Image<double> temp(1,nObject, NO_INIT); 00279 Image<double>::iterator aptr = temp.beginw(), stop = temp.endw(); 00280 00281 uint j = 0; 00282 while (aptr != stop) 00283 { 00284 *aptr++ = itsVisualObjectDB->getObject(j)->getFeature(i); j++; 00285 } 00286 double tMean = mean(temp); 00287 double tStdev = stdev(temp); 00288 LDEBUG("[%5d] m: %f s: %f", i, tMean, tStdev); 00289 00290 itsSalientFeatures[i] = std::pair<double,double>(tMean, tStdev); 00291 } 00292 } 00293 00294 // ###################################################################### 00295 float Landmark::matchSalientFeatures(rutz::shared_ptr<VisualObject> object) 00296 { 00297 // uint nObject = numObjects(); 00298 // if (nObject == 0) return -1.0; 00299 00300 std::vector<float> objFeat = object->getFeatures(); 00301 00302 // check with every objects 00303 // float min = 1.0; float max = 0.0; 00304 // for(uint j = 0; j < nObject; j++) 00305 // { 00306 // std::vector<double> feat2 = getObject(j)->getFeatures(); 00307 00308 // // feature similarity [ 0.0 ... 1.0 ]: 00309 // float cval = 0.0; 00310 // for(uint i = 0; i < objFeat.size(); i++) 00311 // { 00312 // float val = pow(objFeat[i] - feat2[i], 2.0); cval += val; 00313 // } 00314 // cval = sqrt(cval/objFeat.size()); 00315 // if(cval < min) min = cval; 00316 // if(cval > max) max = cval; 00317 // LDEBUG("cval[%d]: %f", j, cval); 00318 // } 00319 00320 // feature similarity [ 0.0 ... 1.0 ]: 00321 float cval = 0.0; 00322 00323 std::vector<float>::iterator aptr = objFeat.begin(), stop = objFeat.end(); 00324 std::vector<std::pair<double,double> >::iterator 00325 bptr = itsSalientFeatures.begin(); 00326 while (aptr != stop) 00327 { 00328 float val = pow(*aptr++ - (*bptr).first, 2.0F); bptr++; 00329 cval += val; 00330 } 00331 00332 // float cval2 = 0.0F; 00333 // for(uint i = 0; i < objFeat.size(); i++) 00334 // { 00335 // float val = pow(objFeat[i] - itsSalientFeatures[i].first, 2.0F); 00336 // cval2 += val; 00337 // } 00338 00339 cval = sqrt(cval/objFeat.size()); 00340 // LDEBUG("min: %f: max: %f cval: %f", min, max, cval); 00341 00342 // range: [ 0.0 ... 1.0 ] the lower the better the match 00343 return cval; 00344 } 00345 00346 // ###################################################################### 00347 bool Landmark::saveTo(const std::string& fname) 00348 { 00349 // put the Landmark name as the DB name 00350 itsVisualObjectDB->setName(itsName); 00351 00352 uint nObjects = itsVisualObjectDB->numObjects(); 00353 std::vector<std::string> tNames(nObjects); 00354 00355 // put all the individual object information on the VO name 00356 // ObjName iOffset jOffset fNum 00357 for(uint i = 0; i < nObjects; i ++) 00358 { 00359 int ioff, joff, fNum; 00360 ioff = itsOffsetCoords[i].i; 00361 joff = itsOffsetCoords[i].j; 00362 fNum = itsVisualObjectFNum[i]; 00363 00364 std::string orgName = itsVisualObjectDB->getObject(i)->getName(); 00365 tNames[i] = orgName; 00366 LDEBUG("%s__%d_%d_%d", orgName.c_str(), ioff, joff, fNum); 00367 std::string 00368 appName(sformat("%s_%d_%d_%d", 00369 orgName.c_str(), ioff, joff, fNum)); 00370 00371 itsVisualObjectDB->getObject(i)->setName(std::string(appName)); 00372 } 00373 00374 // save the VisualObject DB 00375 bool retVal = itsVisualObjectDB->saveTo(fname); 00376 00377 // revert back to the original names 00378 for(uint i = 0; i < nObjects; i ++) 00379 { 00380 itsVisualObjectDB->getObject(i)->setName(tNames[i]); 00381 } 00382 return retVal; 00383 } 00384 00385 // ###################################################################### 00386 Landmark::~Landmark() 00387 { } 00388 00389 // ###################################################################### 00390 // build the landmark by adding the new Visual Object 00391 rutz::shared_ptr<VisualObjectMatch> 00392 Landmark::build(rutz::shared_ptr<VisualObject> obj, 00393 Point2D<int> objOffset, uint fNum) 00394 { 00395 // if this is the first item 00396 if(numObjects() == 0) 00397 { 00398 init(obj, objOffset, fNum); 00399 return rutz::shared_ptr<VisualObjectMatch>(); 00400 } 00401 else 00402 return input(obj, objOffset, fNum); 00403 } 00404 00405 // ###################################################################### 00406 // build the landmark by adding visual object with no salient point 00407 // (image of whole frame, for example) 00408 rutz::shared_ptr<VisualObjectMatch> 00409 Landmark::build(rutz::shared_ptr<VisualObject> obj, uint fNum) 00410 { 00411 return build(obj, Point2D<int>( 0, 0), fNum); 00412 } 00413 00414 // ###################################################################### 00415 // just like build except it does not add the object 00416 // only return where it would have been placed (in DB or Temp DB) 00417 // or if it is rejected altogether 00418 rutz::shared_ptr<VisualObjectMatch> 00419 Landmark::buildCheck(rutz::shared_ptr<VisualObject> obj, 00420 Point2D<int> objOffset, uint fNum, 00421 bool &inDB, bool &inTDB, int &tIndex) 00422 { 00423 // if this is the first item 00424 if(numObjects() == 0) 00425 { 00426 // init situation 00427 inDB = false; inTDB = false; 00428 return rutz::shared_ptr<VisualObjectMatch>(); 00429 } 00430 else 00431 return inputCheck(obj, objOffset, fNum, inDB, inTDB, tIndex); 00432 } 00433 00434 // ###################################################################### 00435 // corresponding non salient object input 00436 rutz::shared_ptr<VisualObjectMatch> 00437 Landmark::buildCheck(rutz::shared_ptr<VisualObject> obj, uint fNum, 00438 bool &inDB, bool &inTDB, int &tIndex) 00439 { 00440 return buildCheck(obj, Point2D<int>( 0, 0), fNum, 00441 inDB, inTDB, tIndex); 00442 } 00443 00444 // ###################################################################### 00445 // build the landmark using salient object 00446 // based on information passed by build check 00447 rutz::shared_ptr<VisualObjectMatch> 00448 Landmark::build(rutz::shared_ptr<VisualObject> obj, 00449 Point2D<int> objOffset, uint fNum, 00450 bool inDB, bool inTDB, int tIndex, 00451 rutz::shared_ptr<VisualObjectMatch> cmatch) 00452 { 00453 // if this is the first item 00454 if(numObjects() == 0) 00455 { 00456 init(obj, objOffset, fNum); 00457 return rutz::shared_ptr<VisualObjectMatch>(); 00458 } 00459 else 00460 return input(obj, objOffset, fNum, inDB, inTDB, tIndex, cmatch); 00461 } 00462 00463 // ###################################################################### 00464 // build the landmark using non-salient object 00465 // based on information passed by build check 00466 rutz::shared_ptr<VisualObjectMatch> 00467 Landmark::build(rutz::shared_ptr<VisualObject> obj, uint fNum, 00468 bool inDB, bool inTDB, int tIndex, 00469 rutz::shared_ptr<VisualObjectMatch> cmatch) 00470 { 00471 return build(obj, Point2D<int>(0,0), fNum, inDB, inTDB, tIndex, cmatch); 00472 } 00473 00474 // ###################################################################### 00475 // track the landmark using the new Visual Object 00476 rutz::shared_ptr<VisualObjectMatch> 00477 Landmark::input(rutz::shared_ptr<VisualObject> obj, 00478 Point2D<int> objOffset, uint fNum) 00479 { 00480 Image<PixRGB<byte> > objImg = obj->getImage(); 00481 int objW = objImg.getDims().w(); 00482 int objH = objImg.getDims().h(); 00483 00484 Rectangle objRect(objOffset, objImg.getDims()); 00485 LINFO("%s (sal:[%d %d]) info: at [%d, %d, %d, %d]: %d by %d", 00486 obj->getName().c_str(), obj->getSalPoint().i, obj->getSalPoint().j, 00487 objOffset.i, objOffset.j, 00488 objOffset.i+objW-1, objOffset.j + objH-1, objW, objH); 00489 00490 // find match with the last NUM_FRAME_LOOKBACK 00491 // objects in the DB 00492 rutz::shared_ptr<VisualObjectMatch> cmatch; 00493 int mInd = findDBmatch(obj, cmatch, NUM_FRAME_LOOKBACK); 00494 LINFO("found DB match: %d", mInd); 00495 00496 // if match not found and DB is empty 00497 if(mInd == -1) 00498 { 00499 int mtInd = findTempDBmatch(obj, cmatch, NUM_FRAME_LOOKBACK); 00500 LINFO("found TDB match: %d",mtInd); 00501 00502 // if we find a match with a temp object 00503 if(mtInd != -1) 00504 { 00505 // move the temp object to DB 00506 moveTempVisualObjectToDB(mtInd); 00507 LINFO("now have %d objects", 00508 itsVisualObjectDB->numObjects()); 00509 } 00510 else 00511 { 00512 // also could not find a match in the temp holding 00513 LINFO("not adding %s (%d) to landmark %s", 00514 obj->getName().c_str(), fNum, itsName.c_str()); 00515 return cmatch; 00516 } 00517 } 00518 00519 // special framing for checking with whole scene 00520 if(obj->getSalPoint().i == -1 && obj->getSalPoint().j == -1) 00521 cmatch = cropInputImage(obj, objOffset, mInd, cmatch); 00522 00523 // add object to the temp db: 00524 LINFO("put to temp holding first"); 00525 tAddObject(obj, objOffset, fNum); 00526 00527 // use the matches from the actual inserted object 00528 //trackKeypoints(cmatch, mInd); 00529 00530 return cmatch; 00531 } 00532 00533 // ###################################################################### 00534 // track check the landmark using the new Visual Object 00535 rutz::shared_ptr<VisualObjectMatch> 00536 Landmark::inputCheck(rutz::shared_ptr<VisualObject> obj, 00537 Point2D<int> objOffset, uint fNum, 00538 bool &inDB, bool &inTDB, int &tIndex) 00539 { 00540 Image<PixRGB<byte> > objImg = obj->getImage(); 00541 int objW = objImg.getDims().w(); 00542 int objH = objImg.getDims().h(); 00543 00544 Rectangle objRect(objOffset, objImg.getDims()); 00545 LINFO("%s (sal:[%d %d]) info: at [%d, %d, %d, %d]: %d by %d", 00546 obj->getName().c_str(), obj->getSalPoint().i, obj->getSalPoint().j, 00547 objOffset.i, objOffset.j, 00548 objOffset.i+objW-1, objOffset.j + objH-1, objW, objH); 00549 00550 // find match with the last NUM_FRAME_LOOKBACK 00551 // objects in the DB 00552 rutz::shared_ptr<VisualObjectMatch> cmatch; 00553 int mInd = findDBmatch(obj, cmatch, NUM_FRAME_LOOKBACK); 00554 LINFO("find DB match: %d", mInd); 00555 00556 // if match not found and DB is empty 00557 if(mInd == -1) 00558 { 00559 inDB = false; 00560 int mtInd = findTempDBmatch(obj, cmatch, NUM_FRAME_LOOKBACK); 00561 LINFO("found match at temp: %d",mtInd); 00562 00563 // if we find a match with a temp objects 00564 if(mtInd != -1) { inTDB = true; tIndex = mtInd; } 00565 else 00566 { 00567 // also could not find a match in the temp holding 00568 inTDB = false; tIndex = -1; 00569 LINFO("cannot add %s (%d) to landmark %s", 00570 obj->getName().c_str(), fNum, itsName.c_str()); 00571 return cmatch; 00572 } 00573 } 00574 // it matches with DB, so TDB is irrelevant (default to false) 00575 else { inDB = true; inTDB = false; tIndex = mInd; } 00576 00577 return cmatch; 00578 } 00579 00580 // ###################################################################### 00581 // track the landmark using the new Visual Object 00582 // with the information passed by buildcheck 00583 rutz::shared_ptr<VisualObjectMatch> 00584 Landmark::input(rutz::shared_ptr<VisualObject> obj, 00585 Point2D<int> objOffset, uint fNum, 00586 bool inDB, bool inTDB, int tIndex, 00587 rutz::shared_ptr<VisualObjectMatch> cmatch) 00588 { 00589 Image<PixRGB<byte> > objImg = obj->getImage(); 00590 int objW = objImg.getDims().w(); 00591 int objH = objImg.getDims().h(); 00592 00593 Rectangle objRect(objOffset, objImg.getDims()); 00594 LINFO("%s (sal:[%d %d]) at [%d, %d, %d, %d]: %d by %d: {%d %d %d}", 00595 obj->getName().c_str(), obj->getSalPoint().i, obj->getSalPoint().j, 00596 objOffset.i, objOffset.j, 00597 objOffset.i+objW-1, objOffset.j + objH-1, objW, objH, 00598 inDB, inTDB,tIndex); 00599 00600 // if not match just return without inserting 00601 if(!inDB && !inTDB) 00602 { 00603 // no matching was done, return null pointer 00604 return rutz::shared_ptr<VisualObjectMatch>(); 00605 } 00606 00607 // if match was found at TDB 00608 if(!inDB && inTDB) 00609 { 00610 // move the temp object to DB 00611 moveTempVisualObjectToDB(tIndex); 00612 LINFO("now have %d objects", itsVisualObjectDB->numObjects()); 00613 } 00614 00615 // special framing for checking with whole scene 00616 if(obj->getSalPoint().i == -1 && obj->getSalPoint().j == -1) 00617 cmatch = cropInputImage(obj, objOffset, tIndex, cmatch); 00618 00619 // add object to the temp db: 00620 LINFO("put to temp holding first"); 00621 tAddObject(obj, objOffset, fNum); 00622 00623 // no matching was done, return null pointer 00624 return rutz::shared_ptr<VisualObjectMatch>(); 00625 } 00626 00627 // ###################################################################### 00628 rutz::shared_ptr<VisualObjectMatch> Landmark::cropInputImage 00629 ( rutz::shared_ptr<VisualObject> &obj, Point2D<int> &objOffset, 00630 int mInd, rutz::shared_ptr<VisualObjectMatch> cmatch) 00631 { 00632 const Image<PixRGB<byte> > objImg = obj->getImage(); 00633 00634 // get the bounding box for tracked subregion 00635 Rectangle tempR = cmatch->getOverlapRect(); 00636 LINFO("getOverlapRect [ %d, %d, %d, %d ]", 00637 tempR.top(), tempR.left(), tempR.bottomI(), tempR.rightI()); 00638 tempR = tempR.getOverlap(objImg.getBounds()); 00639 00640 // if we are tracking, 00641 // make sure the image does not increase uncontrollably 00642 Dims prevDims = 00643 itsVisualObjectDB->getObject(mInd)->getImage().getDims(); 00644 if((tempR.width() > 1.05 * prevDims.w()) || 00645 (tempR.height() > 1.05 * prevDims.h()) ) 00646 { 00647 LINFO("%d by %d vs %d by %d resize window manually", 00648 tempR.width(),tempR.height(), prevDims.w(), prevDims.h()); 00649 00650 Point2D<int> nPt(int(-cmatch->getSIFTaffine().tx), 00651 int(-cmatch->getSIFTaffine().ty) ); 00652 00653 tempR = Rectangle(nPt, prevDims).getOverlap(objImg.getBounds()); 00654 LINFO(" at [%d, %d, %d, %d]: %d by %d", 00655 nPt.i , nPt.j, 00656 nPt.i + prevDims.w() - 1, nPt.j + prevDims.h() - 1, 00657 prevDims.w(), prevDims.h()); 00658 } 00659 00660 // crop image and create new Visual Object 00661 Image< PixRGB<byte> > cImg = crop(objImg, tempR); 00662 objOffset.i += tempR.left(); 00663 objOffset.j += tempR.top(); 00664 LINFO("Overlap object: (%d,%d)", objOffset.i, objOffset.j); 00665 00666 std::string wName = obj->getName() + std::string("-SAL"); 00667 std::string wfName = wName + std::string(".png"); 00668 obj.reset(new VisualObject(wName, wfName, cImg)); 00669 00670 // return match between DB and the object 00671 bool isSIFTfit; 00672 rutz::shared_ptr<VisualObjectMatch> 00673 cmatch2 = match(itsVisualObjectDB->getObject(mInd), obj, isSIFTfit); 00674 return cmatch2; 00675 } 00676 00677 // ###################################################################### 00678 void Landmark::transferEvidence 00679 ( rutz::shared_ptr<Landmark> landmark2, 00680 bool indb2, bool intdb2, int tIndex2, 00681 rutz::shared_ptr<VisualObjectMatch> cmatch) 00682 { 00683 rutz::shared_ptr<VisualObject> object2; 00684 Point2D<int> objOffset2; 00685 uint fnum2; 00686 00687 LINFO("{%d, %d, %d}", indb2, intdb2, tIndex2); 00688 00689 // insert straight to the Visual Object DB 00690 if(indb2) 00691 { 00692 object2 = landmark2->getObject(tIndex2); 00693 objOffset2 = landmark2->getOffsetCoords(tIndex2); 00694 fnum2 = landmark2->getVisualObjectFNum(tIndex2); 00695 00696 addObject(object2, objOffset2, fnum2); 00697 } 00698 else if(intdb2) 00699 { 00700 object2 = landmark2->getTempObject(tIndex2); 00701 objOffset2 = landmark2->getTempOffsetCoords(tIndex2); 00702 fnum2 = landmark2->getTempVisualObjectFNum(tIndex2); 00703 00704 addObject(object2, objOffset2, fnum2); 00705 } 00706 00707 // cleanly delete the visual evidence from the other list 00708 if(indb2 || intdb2) 00709 landmark2->cleanDelete(indb2, intdb2, tIndex2); 00710 } 00711 00712 // ###################################################################### 00713 void Landmark::cleanDelete(bool indb, bool intdb, int tIndex) 00714 { 00715 if(indb) 00716 { 00717 LINFO("in db"); 00718 if(numTempObjects() == 0 || 00719 itsLatestTempVisualObjectFNum < itsVisualObjectFNum[tIndex]) 00720 { 00721 LINFO(" none to move in temp"); 00722 eraseObject(tIndex); 00723 eraseOffsetCoords(tIndex); 00724 eraseVisualObjectFNum(tIndex); 00725 } 00726 else 00727 { 00728 LINFO(" something to move in temp"); 00729 // move the next frame on temp to the soon to be 00730 // un-occupied spot in the DB 00731 uint fnum = itsVisualObjectFNum[tIndex]; 00732 uint ltfnum = itsLatestTempVisualObjectFNum; 00733 00734 int rindex = -1; 00735 for(uint fn = fnum+1; fn <= ltfnum; fn++) 00736 { 00737 for(int i = int(itsTempVisualObjectFNum.size()-1); i >= 0; i--) 00738 { 00739 uint cn = itsTempVisualObjectFNum[i]; 00740 if(cn == fn) 00741 { 00742 rindex = i; 00743 LINFO("rind: %d i: %d cn: %d fn: %d", rindex, i, cn, fn); 00744 i = 0; fn = ltfnum+1; 00745 } 00746 } 00747 } 00748 00749 if(rindex != -1) 00750 { 00751 LINFO("before: fnum: %d",itsVisualObjectFNum[tIndex]); 00752 itsVisualObjectDB->setObject 00753 (tIndex, itsTempVisualObjectDB->getObject(rindex)); 00754 itsOffsetCoords[tIndex] = itsTempOffsetCoords[rindex]; 00755 itsVisualObjectFNum[tIndex] = itsTempVisualObjectFNum[rindex]; 00756 LINFO("after : fnum: %d",itsVisualObjectFNum[tIndex]); 00757 00758 LINFO("before: T: %d", numTempObjects()); 00759 eraseTempObject(rindex); 00760 eraseTempOffsetCoords(rindex); 00761 eraseTempVisualObjectFNum(rindex); 00762 LINFO("after : T: %d", numTempObjects()); 00763 } 00764 00765 // update latest number for temp 00766 itsLatestTempVisualObjectFNum = 0; 00767 for(uint i = 0; i < itsTempVisualObjectFNum.size(); i++) 00768 { 00769 if(itsLatestTempVisualObjectFNum < itsTempVisualObjectFNum[i]) 00770 itsLatestTempVisualObjectFNum = itsTempVisualObjectFNum[i]; 00771 } 00772 } 00773 00774 // update latest number for vodb 00775 itsLatestVisualObjectFNum = 0; 00776 for(uint i = 0; i < itsVisualObjectFNum.size(); i++) 00777 { 00778 if(itsLatestVisualObjectFNum < itsVisualObjectFNum[i]) 00779 itsLatestVisualObjectFNum = itsVisualObjectFNum[i]; 00780 } 00781 } 00782 else if(intdb) 00783 { 00784 LINFO("intdb"); 00785 00786 eraseTempObject(tIndex); 00787 eraseTempOffsetCoords(tIndex); 00788 eraseTempVisualObjectFNum(tIndex); 00789 00790 // update latest number for temp 00791 itsLatestTempVisualObjectFNum = 0; 00792 for(uint i = 0; i < itsTempVisualObjectFNum.size(); i++) 00793 { 00794 if(itsLatestTempVisualObjectFNum < itsTempVisualObjectFNum[i]) 00795 itsLatestTempVisualObjectFNum = itsTempVisualObjectFNum[i]; 00796 } 00797 } 00798 } 00799 00800 // ###################################################################### 00801 // get the position of the landmark 00802 Point2D<int> Landmark::getPosition() 00803 { 00804 uint last = numObjects() - 1; 00805 //uint fNum = itsVisualObjectFNum[last]; 00806 00807 // get the active trackers 00808 std::vector<rutz::shared_ptr<Keypoint> > activeKP = getActiveKeypoints(); 00809 LINFO("have %"ZU" active keypoints", activeKP.size()); 00810 00811 // get fittest tracker 00812 rutz::shared_ptr<KeypointTracker> fittestKPtr = getFittestKPtr(); 00813 std::vector<rutz::shared_ptr<Keypoint> > 00814 fKP = fittestKPtr->getKeypoints(); 00815 00816 // get its last absolute loc 00817 Point2D<int> res = fittestKPtr->getAbsLoc(); 00818 LINFO("%s: (%d,%d)", fittestKPtr->getName().c_str(), res. i, res.j); 00819 00820 // draw the changes 00821 if(itsMatchWin.is_valid()) 00822 { 00823 // draw the points 00824 int w = itsMatchWin->getDims().w()/2; 00825 int h = itsMatchWin->getDims().h()/2; 00826 Image< PixRGB<byte> > tImg(w,h,ZEROS); 00827 Image< PixRGB<byte> > oImg = 00828 itsVisualObjectDB->getObject(last)->getImage(); 00829 for(uint i = 0; i < fKP.size(); i++) 00830 { 00831 const float x = fKP[i]->getX(); 00832 const float y = fKP[i]->getY(); 00833 Point2D<int> loc(int(x + 0.5F), int(y + 0.5F)); 00834 drawDisk(oImg, loc, 2, PixRGB<byte>(255,0,0)); 00835 LINFO("Loc: (%d,%d)",loc.i,loc.j); 00836 } 00837 inplacePaste(tImg, oImg, Point2D<int>(0, 0)); 00838 itsMatchWin->drawImage(tImg,w,h); 00839 } 00840 00841 return res; 00842 } 00843 00844 // ###################################################################### 00845 // get the current velocity of the landmark 00846 // ===> USE VELOCITY LATER 00847 Point2D<int> Landmark::getVelocity() 00848 { 00849 // stationary keypoint by default 00850 if(numObjects() == 1) 00851 return Point2D<int>(0,0); 00852 00853 // extrapolate speed from the last 5 points 00854 00855 return Point2D<int>(0,0); 00856 } 00857 00858 // ###################################################################### 00859 // get the keypoints in the current frames that are likely to be 00860 // as determined using the trackers 00861 std::vector<rutz::shared_ptr<Keypoint> > Landmark::getActiveKeypoints() 00862 { 00863 // the number of frames the landmark has been activated 00864 uint last = numObjects() - 1; 00865 uint fNum = itsVisualObjectFNum[last]; 00866 uint sfNum = itsVisualObjectFNum[0]; 00867 uint dfnum = fNum - sfNum; 00868 dfnum+=0; 00869 LINFO("fNum: %d, sfNum: %d, dfnum: %d",fNum, sfNum, dfnum); 00870 00871 std::vector<rutz::shared_ptr<Keypoint> > res; 00872 //uint count = 0; 00873 00874 // go through all the trackers 00875 for(uint i = 0; i < itsKPtrackers.size(); i++) 00876 { 00877 // if it is active and has length at least 3 00878 if((itsKPtrackers[i]->hasKeypointInFrame(fNum)) && 00879 (itsKPtrackers[i]->numKeypoints() > dfnum || 00880 itsKPtrackers[i]->numKeypoints() >= MIN_LINKS)) 00881 { 00882 res.push_back(itsKPtrackers[i]->getKeypointInFrame(fNum)); 00883 //LINFO("from tracker%d: %d, %d", i, 00884 // itsKPtrackers[i]->hasKeypointInFrame(fNum), 00885 // itsKPtrackers[i]->numKeypoints() ); 00886 //count++; 00887 } 00888 //if(count > 4) break; 00889 } 00890 00891 // check it out 00892 if(itsMatchWin.is_valid()) 00893 { 00894 // draw the points 00895 int w = itsMatchWin->getDims().w()/2; 00896 int h = itsMatchWin->getDims().h()/2; 00897 Image< PixRGB<byte> > tImg(w,h,ZEROS); 00898 Image< PixRGB<byte> > 00899 oImg = itsVisualObjectDB->getObject(last)->getImage(); 00900 for(uint i = 0; i < res.size(); i++) 00901 { 00902 const float x = res[i]->getX(); 00903 const float y = res[i]->getY(); 00904 Point2D<int> loc(int(x + 0.5F), int(y + 0.5F)); 00905 drawDisk(oImg, loc, 2, PixRGB<byte>(255,0,0)); 00906 //LINFO("Loc: (%d,%d)",loc.i,loc.j); 00907 } 00908 inplacePaste(tImg, oImg, Point2D<int>(0, 0)); 00909 itsMatchWin->drawImage(tImg,w,h); 00910 } 00911 00912 return res; 00913 } 00914 00915 // ###################################################################### 00916 //get the longest active tracker 00917 rutz::shared_ptr<KeypointTracker> Landmark::getFittestKPtr() 00918 { 00919 // the current frame number 00920 uint last = numObjects() - 1; 00921 uint fNum = itsVisualObjectFNum[last]; 00922 00923 rutz::shared_ptr<KeypointTracker> res; 00924 uint maxLength = 0U; 00925 00926 // go through all the tracker 00927 for(uint i = 0; i < itsKPtrackers.size(); i++) 00928 { 00929 if((itsKPtrackers[i]->hasKeypointInFrame(fNum)) && 00930 (itsKPtrackers[i]->numKeypoints() > maxLength)) 00931 { 00932 res = itsKPtrackers[i]; 00933 maxLength = itsKPtrackers[i]->numKeypoints(); 00934 } 00935 } 00936 00937 LINFO("%s: %d", res->getName().c_str(), res->numKeypoints()); 00938 return res; 00939 } 00940 00941 // ###################################################################### 00942 // Prune the Visual Object keypoints of frame index temporally 00943 void Landmark::temporalPrune(uint index) 00944 { 00945 rutz::shared_ptr<VisualObject> pObj = 00946 itsVisualObjectDB->getObject(index); 00947 //std::vector< rutz::shared_ptr<Keypoint> > fkeypoints = 00948 // pObj->getKeypoints(); 00949 LDEBUG("pObj[%d] BEFORE numFeatures: %d", index, pObj->numKeypoints()); 00950 00951 // create a temporary list of keypoints indexes to keep 00952 std::vector< rutz::shared_ptr<Keypoint> > prunedfKp; 00953 00954 // go through the trackers 00955 for(uint i = 0; i < itsKPtrackers.size(); i++) 00956 { 00957 // for the ones that has the index 00958 if(itsKPtrackers[i]->hasKeypointInFrame(index)) 00959 { 00960 // store it 00961 rutz::shared_ptr<Keypoint> kp = 00962 itsKPtrackers[i]->getKeypointInFrame(index); 00963 prunedfKp.push_back(kp); 00964 } 00965 } 00966 00967 // delete the keypoints that are noted as kept 00968 rutz::shared_ptr<VisualObject> prunedfObj 00969 (new VisualObject(pObj->getName(), 00970 pObj->getImageFname(), pObj->getImage(), 00971 pObj->getSalPoint(), 00972 pObj->getFeatures(), prunedfKp)); 00973 LDEBUG("PRUNED numFeatures: %d",prunedfObj->numKeypoints()); 00974 itsVisualObjectDB->setObject(index, prunedfObj); 00975 LDEBUG("fAFTER numFeatures: %d", pObj->numKeypoints()); 00976 00977 // isSorted = false; DO WE NEED TO DO THIS??? 00978 } 00979 00980 // // ###################################################################### 00981 // // Prune the Visual Object keypoints of the last 00982 // void Landmark::temporalPrune(rutz::shared_ptr<VisualObjectMatch> match) 00983 // { 00984 // // prune the just added object 00985 // uint last = itsVisualObjectDB->numObjects() - 1; 00986 // rutz::shared_ptr<VisualObject> obj = 00987 // itsVisualObjectDB->getObject(last); 00988 // std::vector< rutz::shared_ptr<Keypoint> > keypoints = 00989 // obj->getKeypoints(); 00990 00991 // // create a temporary list of keypoints indexes to keep 00992 // std::vector< rutz::shared_ptr<Keypoint> > prunedKp; 00993 00994 // LDEBUG("match->size: %d, kp->size: %d ", 00995 // match->size(), keypoints.size()); 00996 // // go through all the keypoint matches 00997 // for(uint i = 0; i < match->size(); i++) 00998 // { 00999 // // go through the previous frame keypoints 01000 // for(uint j = 0; j < keypoints.size(); j++) 01001 // { 01002 // // if the keypoint in the match are found 01003 // if((*match)[i].refkp == keypoints[j]) 01004 // { 01005 // // add to the temporary list 01006 // prunedKp.push_back(keypoints[j]); 01007 // } 01008 // } 01009 // } 01010 // // // note: the behavior of vector::erase() 01011 // // guarantees this code works... 01012 01013 // // delete the keypoints that are noted as kept 01014 // LDEBUG("BEFORE numFeatures: %d", 01015 // itsVisualObjectDB->getObject(last)->numKeypoints()); 01016 // rutz::shared_ptr<VisualObject> prunedObj 01017 // (new VisualObject(obj->getName(), 01018 // obj->getImageFname(), obj->getImage(), 01019 // obj->getFeatures(), prunedKp)); 01020 // LDEBUG("PRUNED OBJ numFeatures: %d",prunedObj->numKeypoints()); 01021 // itsVisualObjectDB->setObject(last, prunedObj); 01022 // LINFO("AFTER numFeatures: %d", 01023 // itsVisualObjectDB->getObject(last)->numKeypoints()); 01024 // } 01025 01026 // ###################################################################### 01027 // track the keypoints of the just added object 01028 void Landmark::trackKeypoints(rutz::shared_ptr<VisualObjectMatch> match, 01029 int mInd) 01030 { 01031 uint last = itsVisualObjectDB->numObjects() - 1; 01032 uint fNum = itsVisualObjectFNum[last]; 01033 rutz::shared_ptr<VisualObject> voTst = itsVisualObjectDB->getObject(last); 01034 rutz::shared_ptr<VisualObject> voRef = itsVisualObjectDB->getObject(mInd); 01035 01036 LINFO("we are comparing frames %d and %d",mInd, last); 01037 LINFO("ref->size %d, tst->size: %d match->size: %d, kp->size: %"ZU" ", 01038 voRef->numKeypoints(), voTst->numKeypoints(), 01039 match->size(), itsKPtrackers.size()); 01040 01041 // initialize all tst keypoints as not yet added 01042 std::vector<bool> voTstkpIsAdded; 01043 for(uint i = 0; i < voTst->numKeypoints(); i++) 01044 voTstkpIsAdded.push_back(false); 01045 01046 // // go through all the matches 01047 // for(uint i = 0; i < match->size(); i++) 01048 // { 01049 // printf ("[%4d]",i); 01050 // // the ref keypoint 01051 // for(uint k = 0; k < voRef->numKeypoints(); k++) 01052 // if((*match)[i].refkp == voRef->getKeypoint(k)) printf("%4d ->", k); 01053 01054 // // the tst keypoint 01055 // for(uint k = 0; k < voTst->numKeypoints(); k++) 01056 // if((*match)[i].tstkp == voTst->getKeypoint(k)) printf("%4d\n", k); 01057 // } 01058 // Raster::waitForKey(); 01059 01060 // go through all the matches 01061 for(uint i = 0; i < match->size(); i++) 01062 { 01063 //LINFO("[%3d]",i); 01064 01065 // get the tracker with the ref keypoint 01066 for(uint j = 0; j < itsKPtrackers.size(); j++) 01067 { 01068 // if the keypoint is in one of the matches 01069 if(itsKPtrackers[j]->hasKeypointInFrame(mInd) && 01070 (*match)[i].refkp == itsKPtrackers[j]->getKeypointInFrame(mInd)) 01071 { 01072 // add it to the tracker 01073 itsKPtrackers[j]->add((*match)[i].tstkp, 01074 itsOffsetCoords[last], 01075 itsVisualObjectFNum[last]); 01076 01077 // note that the tst keypoint has been added 01078 for(uint k = 0; k < voTst->numKeypoints(); k++) 01079 { 01080 if((*match)[i].tstkp == voTst->getKeypoint(k)) 01081 { 01082 voTstkpIsAdded[k] = true; 01083 //LINFO("added to %3d -> %d", k, j); 01084 } 01085 } 01086 } 01087 } 01088 } 01089 01090 // create new tracker for the rest of the tst keypoints 01091 uint nAdded = 0U; 01092 for(uint i = 0; i < voTst->numKeypoints(); i++) 01093 { 01094 if(!voTstkpIsAdded[i]) 01095 { 01096 rutz::shared_ptr<KeypointTracker> 01097 newKPtracker(new KeypointTracker 01098 (sformat("KPtrk%d_%d", fNum, nAdded))); 01099 newKPtracker->add(voTst->getKeypoint(i), 01100 itsOffsetCoords[last], 01101 itsVisualObjectFNum[last]); 01102 itsKPtrackers.push_back(newKPtracker); 01103 nAdded++; 01104 //LINFO("Adding tracker: %d for %d", itsKPtrackers.size(), i); 01105 } 01106 } 01107 01108 // prune inactive and dormant trackers from 5 frames ago 01109 // and that frame was in the list 01110 uint ndel = 0U; 01111 if(fNum >= FWAIT_NUM) 01112 { 01113 uint pfNum = fNum - FWAIT_NUM; 01114 01115 // prune the keypoints 01116 std::vector<rutz::shared_ptr<KeypointTracker> >::iterator 01117 itr = itsKPtrackers.begin(); 01118 while (itr < itsKPtrackers.end()) 01119 { 01120 // that are inactive since pfNum and has less than MIN_LINKS chain 01121 if((*itr)->isInactiveSince((pfNum+1)) && 01122 (*itr)->numKeypoints() < MIN_LINKS) 01123 { 01124 itr = itsKPtrackers.erase(itr); ++ ndel; 01125 } 01126 else ++ itr; 01127 // note: the behavior of vector::erase() guarantees this code works.. 01128 } 01129 LINFO("we pruned %d inactive tracker from frame %d", ndel, pfNum); 01130 01131 // pruning the keypoints off the VO 01132 LINFO(":{}: %d",pfNum); 01133 int pfNumInd = getFNumIndex(pfNum); 01134 01135 if(pfNumInd != -1) 01136 { 01137 LINFO("BEFORE(%d) -> %d numFeatures: %d", pfNum, pfNumInd, 01138 itsVisualObjectDB->getObject(pfNumInd)->numKeypoints()); 01139 temporalPrune(pfNumInd); 01140 LINFO("AFTER (%d) -> %d numFeatures: %d", pfNum, pfNumInd, 01141 itsVisualObjectDB->getObject(pfNumInd)->numKeypoints()); 01142 //Raster::waitForKey(); 01143 } 01144 else 01145 { 01146 LINFO("skipping %d -> %d", pfNum, pfNumInd); 01147 Raster::waitForKey(); 01148 } 01149 } 01150 else 01151 LINFO("we pruned 0 inactive trackers"); 01152 01153 LINFO("we added %d tracker for frame %d", nAdded, fNum); 01154 LINFO("there are %"ZU" keypoints being tracked", itsKPtrackers.size()); 01155 // for(uint j = 0; j < itsKPtrackers.size(); j++) 01156 // { 01157 // printf("[%3d]",j); itsKPtrackers[j]->print(); 01158 // } 01159 } 01160 01161 // ###################################################################### 01162 int Landmark::getFNumIndex(uint fNum) 01163 { 01164 uint cInd = itsVisualObjectDB->numObjects() - 1; 01165 uint cfNum = itsVisualObjectFNum[cInd]; 01166 //LINFO("cInd: %d, (cfNum: %d, fNum: %d)",cInd,cfNum,fNum); 01167 01168 // look for the index only when fNum is still bigger than current 01169 while (fNum <= cfNum && cInd >= 0) 01170 { 01171 //LINFO(" cInd: %d, (cfNum: %d, fNum: %d)",cInd,cfNum,fNum); 01172 if(fNum == cfNum) return cInd; 01173 if(cInd == 0) return -1; 01174 cInd --; 01175 cfNum = itsVisualObjectFNum[cInd]; 01176 } 01177 01178 return -1; 01179 } 01180 01181 // ###################################################################### 01182 Rectangle Landmark::getObjectRect(uint index) 01183 { 01184 rutz::shared_ptr<VisualObject> 01185 vo = itsVisualObjectDB->getObject(index); 01186 Dims voDims = vo->getImage().getDims(); 01187 return Rectangle(itsOffsetCoords[index], voDims); 01188 } 01189 01190 // ###################################################################### 01191 rutz::shared_ptr<VisualObjectMatch> 01192 Landmark::match(rutz::shared_ptr<VisualObject> a, 01193 rutz::shared_ptr<VisualObject> b, bool &isFit, 01194 float maxPixDist, float minfsim, 01195 float minscore, uint minmatch, 01196 float maxRotate, float maxScale, float maxShear, 01197 bool showMatch) 01198 { 01199 // compute the matching keypoints: 01200 Timer tim(1000000); tim.reset(); 01201 rutz::shared_ptr<VisualObjectMatch> 01202 matchRes(new VisualObjectMatch(a, b, *itsVoma)); 01203 uint64 t = tim.get(); 01204 01205 // let's prune the matches: 01206 uint orgSize = matchRes->size(); 01207 tim.reset(); 01208 uint np = matchRes->prune(); 01209 uint t2 = tim.get(); 01210 01211 LDEBUG("Found %u matches for (%s & %s) in %.3fms: pruned %u in %.3fms", 01212 orgSize, a->getName().c_str(), 01213 b->getName().c_str(), float(t) * 0.001F, 01214 np, float(t2) * 0.001F); 01215 01216 // matching scores 01217 float kpAvgDist = matchRes->getKeypointAvgDist(); 01218 float afAvgDist = matchRes->getAffineAvgDist(); 01219 float score = matchRes->getScore(); 01220 bool isSIFTaffine = matchRes-> 01221 checkSIFTaffine(maxRotate, maxScale, maxShear); 01222 LDEBUG("maxPixDist: %f minfsim: %f minscore: %f minmatch: %d " 01223 "maxRotate: %f maxScale: %f maxShear: %f", 01224 maxPixDist, minfsim, minscore, minmatch, 01225 maxRotate, maxScale, maxShear); 01226 LDEBUG("kpAvgDist = %.4f|affAvgDist = %.4f|score: %.4f|aff? %d", 01227 kpAvgDist, afAvgDist, score, isSIFTaffine); 01228 01229 if (!isSIFTaffine) LDEBUG("### bad Affine -- BOGUS MATCH"); 01230 else if(matchRes->size() >= minscore) 01231 { 01232 // show our final affine transform: 01233 SIFTaffine s = matchRes->getSIFTaffine(); 01234 LDEBUG("[tstX] [ %- .3f %- .3f ] [refX] [%- .3f]", s.m1, s.m2, s.tx); 01235 LDEBUG("[tstY] = [ %- .3f %- .3f ] [refY] + [%- .3f]", s.m3, s.m4, s.ty); 01236 } 01237 01238 // check SIFT match 01239 bool isSIFTfit = (isSIFTaffine && (score >= minscore) && 01240 (matchRes->size() >= minmatch)); 01241 LDEBUG("isSIFTfit: %d: (%d && (%f >= %f) && (%d >= %d)) ", isSIFTfit, 01242 isSIFTaffine, score, minscore, matchRes->size(), minmatch); 01243 01244 // check sal match 01245 // we skip this step if the salient point is not provided 01246 bool isSalfit = false; 01247 bool salAvailable = 01248 (a->getSalPoint().isValid() && a->getSalPoint().isValid()); 01249 if(isSIFTfit && salAvailable) 01250 { 01251 float sdiff = matchRes->getSalDiff(); 01252 float sdist = matchRes->getSalDist(); 01253 01254 // has to pass distance proximity test (maxPixDist pixels), 01255 // and minfsim feature similarity 01256 if(sdist <= maxPixDist && sdiff >= minfsim) isSalfit = true; 01257 LDEBUG("isSalFit: %d: (%f <= %f && %f >= %f", isSalfit, 01258 sdist, maxPixDist, sdiff, minfsim); 01259 } 01260 else isSalfit = true; 01261 01262 isFit = isSIFTfit && isSalfit; 01263 LDEBUG("isFit? %d", isFit); 01264 01265 // if there are images to be displayed 01266 if(showMatch && itsMatchWin.is_valid() && 01267 a->getImage().initialized() && b->getImage().initialized()) 01268 { 01269 int w = itsMatchWin->getDims().w()/2; 01270 int h = itsMatchWin->getDims().h()/2; 01271 01272 // get an image showing the matches and the fused image 01273 Image< PixRGB<byte> > mImg = matchRes->getMatchImage(1.0F); 01274 Image< PixRGB<byte> > fImg = matchRes->getFusedImage(0.25F); 01275 Image< PixRGB<byte> > tImg(2*w,2*h,ZEROS); 01276 01277 inplacePaste(tImg, mImg, Point2D<int>(0, 0)); 01278 inplacePaste(tImg, fImg, Point2D<int>(w, 0)); 01279 01280 itsMatchWin->drawImage(tImg,0,0); 01281 } 01282 return matchRes; 01283 } 01284 01285 // ###################################################################### 01286 void Landmark::addObject(rutz::shared_ptr<VisualObject> obj, 01287 Point2D<int> objOffset, uint fNum) 01288 { 01289 // add the object to the db: 01290 if (itsVisualObjectDB->addObject(obj)) 01291 { 01292 // note the frame number and coordinates 01293 itsVisualObjectFNum.push_back(fNum); 01294 itsOffsetCoords.push_back(objOffset); 01295 if(itsLatestVisualObjectFNum < fNum) 01296 itsLatestVisualObjectFNum = fNum; 01297 01298 if(obj->getSalPoint().i != -1) 01299 LINFO("Added SAL VisualObject '%s' as part of %s evidence.", 01300 obj->getName().c_str(), itsName.c_str()); 01301 else 01302 LINFO("Added NON_SAL VisualObject '%s' as part of %s evidence.", 01303 obj->getName().c_str(), itsName.c_str()); 01304 } 01305 else 01306 LERROR("FAILED adding VisualObject '%s' to database -- IGNORING", 01307 obj->getName().c_str()); 01308 } 01309 01310 // ###################################################################### 01311 void Landmark::tAddObject(rutz::shared_ptr<VisualObject> obj, 01312 Point2D<int> objOffset, uint fNum) 01313 { 01314 // add to the tempVODB instead 01315 if (itsTempVisualObjectDB->addObject(obj)) 01316 { 01317 // note the frame number and coordinates 01318 itsTempVisualObjectFNum.push_back(fNum); 01319 itsTempOffsetCoords.push_back(objOffset); 01320 if(itsLatestTempVisualObjectFNum < fNum) 01321 itsLatestTempVisualObjectFNum = fNum; 01322 01323 if(obj->getSalPoint().i != -1) 01324 LINFO("Added SAL VisualObject '%s' as part of %s " 01325 "temporary holding", 01326 obj->getName().c_str(), itsName.c_str()); 01327 else 01328 LINFO("Added NON_SAL VisualObject '%s' as part of %s " 01329 "temporary holding", 01330 obj->getName().c_str(), itsName.c_str()); 01331 } 01332 else 01333 LERROR("FAILED adding VisualObject '%s' to temp holding", 01334 obj->getName().c_str()); 01335 } 01336 01337 // ###################################################################### 01338 int Landmark::match 01339 ( rutz::shared_ptr<VisualObject> obj, 01340 rutz::shared_ptr<VisualObjectMatch> &cmatch, int start, int end, 01341 float maxPixDist, float minfsim, float minscore, uint minmatch, 01342 float maxRotate, float maxScale, float maxShear) 01343 { 01344 if(start == -1) start = 0; ASSERT(start >= 0); 01345 if(end == -1) end = numObjects() - 1; ASSERT(end < int(numObjects())); 01346 ASSERT(start <= end); 01347 01348 // return -1 if match not found 01349 int index = findDBmatch(obj, cmatch, end-start+1, true, start, 01350 maxPixDist, minfsim, minscore, minmatch, 01351 maxRotate, maxScale, maxShear); 01352 return index; 01353 } 01354 01355 // ###################################################################### 01356 int Landmark::findDBmatch 01357 (rutz::shared_ptr<VisualObject> obj, 01358 rutz::shared_ptr<VisualObjectMatch> &cmatch, uint nFrames, 01359 bool isForward, int start, 01360 float maxPixDist, float minfsim, float minscore, uint minmatch, 01361 float maxRotate, float maxScale, float maxShear) 01362 { 01363 // if db is empty return -1 right away 01364 if(itsVisualObjectDB->numObjects() == 0) 01365 { LINFO("%s DB is empty", itsName.c_str()); return -1; } 01366 01367 // setup the range for vo checking 01368 int sInd, eInd, inc; // note: end index not included 01369 ASSERT((start >= 0 && start < int(numObjects())) || start == -1); 01370 if(start == -1 &&isForward) sInd = 0; 01371 else if(start == -1 && !isForward) sInd = numObjects() - 1; 01372 else sInd = start; 01373 01374 if(isForward) 01375 { eInd = start+nFrames; inc = 1; } 01376 else 01377 { eInd = sInd - nFrames; if(eInd < -1) eInd = -1; inc = -1; } 01378 LDEBUG("Range %s DB: [%d, %d} by %d", itsName.c_str(), sInd, eInd, inc); 01379 int i = sInd; int matchInd = -1; 01380 while(i != eInd) 01381 { 01382 LDEBUG("check %s DB(%d): %s",itsName.c_str(), i, 01383 itsVisualObjectDB->getObject(i)->getName().c_str()); 01384 01385 // check SIFT and Saliency match 01386 bool isFit; cmatch = match(obj, itsVisualObjectDB->getObject(i), isFit, 01387 maxPixDist, minfsim, minscore, minmatch, 01388 maxRotate, maxScale, maxShear); 01389 01390 // if we didn't have a match, flip the order (assymetric matching) 01391 if(!isFit) 01392 { 01393 cmatch = match(itsVisualObjectDB->getObject(i), obj, isFit, 01394 maxPixDist, minfsim, minscore, minmatch, 01395 maxRotate, maxScale, maxShear); 01396 } 01397 01398 // found first good match: break (maybe can do better) 01399 if(isFit) { matchInd = i; return matchInd; } 01400 i += inc; 01401 } 01402 return matchInd; 01403 } 01404 01405 // ###################################################################### 01406 int Landmark::findTempDBmatch(rutz::shared_ptr<VisualObject> obj, 01407 rutz::shared_ptr<VisualObjectMatch> &cmatch, 01408 uint nFrames, 01409 float maxPixDist, float minfsim, 01410 float minscore, uint minmatch) 01411 { 01412 // if temp db is empty return -1 right away 01413 if(itsTempVisualObjectDB->numObjects() == 0) 01414 { LINFO("TDB of %s is empty", itsName.c_str()); return -1; } 01415 01416 // find match with the last nFrames objects in the tempDB 01417 int mtInd = -1; int mintIndex; 01418 if(itsTempVisualObjectDB->numObjects() < nFrames) 01419 mintIndex = 0; 01420 else 01421 mintIndex = itsTempVisualObjectDB->numObjects() - nFrames; 01422 LINFO("Range %s tempDB: [%d, %d]", itsName.c_str(), mintIndex, 01423 itsTempVisualObjectDB->numObjects() - 1); 01424 01425 for(int i = itsTempVisualObjectDB->numObjects() - 1; i >= mintIndex; i--) 01426 { 01427 LDEBUG("check %s tempDB(%d): %s",itsName.c_str(), i, 01428 itsTempVisualObjectDB->getObject(i)->getName().c_str()); 01429 01430 // check SIFT and Saliency match 01431 bool isFit; 01432 cmatch = match(obj, itsTempVisualObjectDB->getObject(i), isFit, 01433 maxPixDist, minfsim, minscore, minmatch); 01434 01435 // if we didn't have a match, flip the order (asymmetric matching) 01436 if(!isFit) 01437 { 01438 cmatch = match(itsTempVisualObjectDB->getObject(i), obj, isFit, 01439 maxPixDist, minfsim, minscore, minmatch); 01440 } 01441 01442 // found first good match: break (maybe a bad policy) 01443 if(isFit){ mtInd = i; return mtInd; } 01444 } 01445 return mtInd; 01446 } 01447 01448 // ###################################################################### 01449 void Landmark::moveLatestTempVisualObjectToDB() 01450 { 01451 int index = itsTempVisualObjectFNum.size() - 1; 01452 if(index == -1) 01453 { 01454 LINFO("nothing to move in temp of %s", itsName.c_str()); 01455 return; 01456 } 01457 01458 LINFO("moving T(%d): %s", index, 01459 itsTempVisualObjectDB->getObject(index)->getName().c_str()); 01460 moveTempVisualObjectToDB(index); 01461 } 01462 01463 // ###################################################################### 01464 void Landmark::moveTempVisualObjectToDB(int index) 01465 { 01466 ASSERT(index >= 0 && index < int(numTempObjects())); 01467 01468 // push back the temp objects to the DB 01469 addObject(itsTempVisualObjectDB->getObject(index), 01470 itsTempOffsetCoords[index], 01471 itsTempVisualObjectFNum[index]); 01472 01473 // update the latest number for DB 01474 if(itsLatestVisualObjectFNum < itsTempVisualObjectFNum[index]) 01475 itsLatestVisualObjectFNum = itsTempVisualObjectFNum[index]; 01476 01477 // erase the info off the temp list 01478 itsTempVisualObjectDB->eraseObject(index); 01479 itsTempOffsetCoords.erase(itsTempOffsetCoords.begin() + index); 01480 itsTempVisualObjectFNum.erase(itsTempVisualObjectFNum.begin() + index); 01481 01482 // update latest number for temp 01483 itsLatestTempVisualObjectFNum = 0; 01484 for(uint i = 0; i < itsTempVisualObjectFNum.size(); i++) 01485 { 01486 if(itsLatestTempVisualObjectFNum < itsTempVisualObjectFNum[i]) 01487 itsLatestTempVisualObjectFNum = itsTempVisualObjectFNum[i]; 01488 } 01489 } 01490 01491 // ###################################################################### 01492 uint Landmark::numMatch(rutz::shared_ptr<Landmark> lmk, 01493 float maxPixDist, float minfsim, 01494 float minscore, uint minmatch) 01495 { 01496 uint count = 0; 01497 01498 // go through all the objects in the input Landmark 01499 for(uint i = 0; i < lmk->numObjects(); i++) 01500 { 01501 LDEBUG("checking lmk->object(%d)", i); 01502 rutz::shared_ptr<VisualObject> obj = lmk->getObject(i); 01503 01504 // check with the objects in the DBlist 01505 for(uint j = 0; j < numObjects(); j++) 01506 { 01507 LDEBUG(" with object(%d)", j); 01508 01509 // check SIFT and Saliency match 01510 bool isFit; 01511 rutz::shared_ptr<VisualObjectMatch> cmatch = 01512 match(obj, itsVisualObjectDB->getObject(j), isFit, 01513 maxPixDist, minfsim, minscore, minmatch); 01514 //if(itsMatchWin.is_valid()) 01515 // itsMatchWin->setTitle(sformat("M: obj(%d)-db(%d)",i,j).c_str()); 01516 01517 // if necesarry reverse the order 01518 if(!isFit) 01519 { 01520 cmatch = match(itsVisualObjectDB->getObject(j), obj, isFit, 01521 maxPixDist, minfsim, minscore, minmatch); 01522 //if(itsMatchWin.is_valid()) 01523 // itsMatchWin->setTitle(sformat("M: db(%d)&in(%d)",i,j).c_str()); 01524 } 01525 01526 // if we pass both sal and sift tests 01527 if(isFit) 01528 { 01529 count++; 01530 LDEBUG("Match: %s(%d) & %s(%d), count: %d", 01531 lmk->getName().c_str(),i, itsName.c_str(), j, count); 01532 01533 // end the inner loop 01534 j = numObjects(); 01535 } 01536 } 01537 } 01538 01539 return count; 01540 } 01541 01542 // ###################################################################### 01543 void Landmark::combine(rutz::shared_ptr<Landmark> lmk1, 01544 rutz::shared_ptr<Landmark> lmk2) 01545 { 01546 // add all objects in lmk1 01547 for(uint i = 0; i <lmk1-> numObjects(); i++) 01548 { 01549 addObject(lmk1->getObject(i), 01550 lmk1->getOffsetCoords(i), 01551 lmk1->getVisualObjectFNum(i)); 01552 } 01553 01554 // add all objects in lmk2 01555 for(uint i = 0; i <lmk2-> numObjects(); i++) 01556 { 01557 addObject(lmk2->getObject(i), 01558 lmk2->getOffsetCoords(i), 01559 lmk2->getVisualObjectFNum(i)); 01560 } 01561 } 01562 01563 // ###################################################################### 01564 void Landmark::append(rutz::shared_ptr<Landmark> lmk) 01565 { 01566 // add all objects in the input lmk 01567 for(uint i = 0; i <lmk->numObjects(); i++) 01568 { 01569 addObject(lmk->getObject(i), 01570 lmk->getOffsetCoords(i), 01571 lmk->getVisualObjectFNum(i)); 01572 } 01573 } 01574 01575 // ###################################################################### 01576 struct SortObj 01577 { 01578 SortObj() { }; 01579 01580 SortObj(const rutz::shared_ptr<VisualObject> _obj, 01581 const Point2D<int> _objOffset, 01582 const uint _fNum, 01583 const uint _sNum) : 01584 obj(_obj), 01585 objOffset(_objOffset), 01586 fNum(_fNum), 01587 sNum(_sNum) 01588 { } 01589 01590 rutz::shared_ptr<VisualObject> obj; 01591 Point2D<int> objOffset; 01592 uint fNum; 01593 uint sNum; 01594 01595 bool operator < (const SortObj& rhs) 01596 { 01597 if(sNum != rhs.sNum) return sNum < rhs.sNum; 01598 else return fNum < rhs.fNum; 01599 } 01600 01601 }; 01602 01603 void Landmark::sort(std::vector<std::string> sessionNames) 01604 { 01605 std::list<SortObj> tList; 01606 for(uint i = 0; i < numObjects(); i++) 01607 { 01608 // save the objects, offset coordinates, and frame number 01609 // to a temporary place 01610 rutz::shared_ptr<VisualObject> obj = 01611 itsVisualObjectDB->getObject(i); 01612 01613 // get the object name 01614 std::string sname = obj->getName(); 01615 LDEBUG("session name: %s", sname.c_str()); 01616 sname = sname.substr(0, sname.find_last_of('_')); 01617 sname = sname.substr(0, sname.find_last_of('_')); 01618 sname = sname.substr(0, sname.find_last_of('_')); 01619 01620 uint j = 0; 01621 // check it against the names on the list 01622 while((j < sessionNames.size()) && (sname != sessionNames[j])) j++; 01623 if(j == sessionNames.size()) 01624 LFATAL("Session not in list: %s (%s)", 01625 sname.c_str(), obj->getName().c_str()); 01626 01627 Point2D<int> objOffset = itsOffsetCoords[i]; 01628 uint fNum = itsVisualObjectFNum[i]; 01629 01630 LDEBUG("B[%s] [%3d,%3d] [%3d] sNum: %d", obj->getName().c_str(), 01631 objOffset.i, objOffset.j, fNum, j); 01632 01633 tList.push_back(SortObj(obj,objOffset,fNum,j)); 01634 } 01635 01636 tList.sort(); 01637 01638 rutz::shared_ptr<VisualObjectDB> vodb(new VisualObjectDB()); 01639 std::list<SortObj>::iterator itr = tList.begin(); 01640 uint ii = 0; 01641 while (itr != tList.end()) 01642 { 01643 vodb->addObject((*itr).obj); 01644 itsOffsetCoords[ii] = (*itr).objOffset; 01645 itsVisualObjectFNum[ii] = (*itr).fNum; 01646 itr++; ii++; 01647 } 01648 01649 itsVisualObjectDB = vodb; 01650 01651 // for(uint i = 0; i < numObjects(); i++) 01652 // { 01653 // rutz::shared_ptr<VisualObject> obj = 01654 // itsVisualObjectDB->getObject(i); 01655 // Point2D<int> objOffset = itsOffsetCoords[i]; 01656 // uint fNum = itsVisualObjectFNum[i]; 01657 01658 // LINFO("B[%s] [%3d,%3d] [%3d]", obj->getName().c_str(), 01659 // objOffset.i, objOffset.j, fNum); 01660 // } 01661 } 01662 01663 // ###################################################################### 01664 /* So things look consistent in everyone's emacs... */ 01665 /* Local Variables: */ 01666 /* indent-tabs-mode: nil */ 01667 /* End: */