QT_Navigation.C

00001 /*!@file Robots2/Beobot2/Navigation/QT_Navigation/QT_Navigation.C */
00002 // //////////////////////////////////////////////////////////////////// //
00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00004 // University of Southern California (USC) and the iLab at USC.         //
00005 // See http://iLab.usc.edu for information about this project.          //
00006 // //////////////////////////////////////////////////////////////////// //
00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00009 // in Visual Environments, and Applications'' by Christof Koch and      //
00010 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00011 // pending; application number 09/912,225 filed July 23, 2001; see      //
00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00013 // //////////////////////////////////////////////////////////////////// //
00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00015 //                                                                      //
00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00017 // redistribute it and/or modify it under the terms of the GNU General  //
00018 // Public License as published by the Free Software Foundation; either  //
00019 // version 2 of the License, or (at your option) any later version.     //
00020 //                                                                      //
00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00024 // PURPOSE.  See the GNU General Public License for more details.       //
00025 //                                                                      //
00026 // You should have received a copy of the GNU General Public License    //
00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00029 // Boston, MA 02111-1307 USA.                                           //
00030 // //////////////////////////////////////////////////////////////////// //
00031 //
00032 // Primary maintainer for this file: Chin-Kai Chang <chinkaic@usc.edu>
00033 // $HeadURL: svn://ilab.usc.edu/trunk/saliency/src/Robots/Beobot2/QT_Navigation.C
00034 // $ $Id: QT_Navigation.C 13084 2010-03-30 02:42:00Z kai $
00035 //
00036 //////////////////////////////////////////////////////////////////////////
00037 
00038 #include "Robots/Beobot2/Navigation/QT_Navigation/QT_Navigation.H"
00039 #include "Ice/BeobotEvents.ice.H"
00040 
00041 #include "Raster/Raster.H"
00042 #include "Util/sformat.H"
00043 #include "Image/Image.H"
00044 #include "Image/DrawOps.H"
00045 #include "Image/ColorOps.H"   // for luminance()
00046 #include "Image/ShapeOps.H"   // for rescale()
00047 #include "Image/MathOps.H"    // for stdev()
00048 #include "Image/MatrixOps.H"  // for matrixMult()
00049 #include "Image/CutPaste.H"   // for inplacePaste()
00050 
00051 #include "Util/Timer.H"
00052 
00053 #include "SIFT/Keypoint.H"
00054 #include "SIFT/VisualObject.H"
00055 #include "SIFT/VisualObjectMatch.H"
00056 #include "Transport/FrameInfo.H"
00057 
00058 #include "Ice/IceImageUtils.H"
00059 
00060 #include "Component/ModelParam.H"
00061 #include "Component/ModelOptionDef.H"
00062 
00063 
00064 #define  TEACH_FOLDER                "../data/logs/2010_02_26__18_32_21/image_0000000000"
00065 #define  TEACH_START_NUMBER        14363 
00066 #define  TEACH_END_NUMBER          15038 
00067 
00068 
00069 //#define  TEACH_FOLDER                   "../data/logs/2009_11_12__17_30_33/image_0000000000"
00070 //#define  TEACH_START_NUMBER        71449
00071 //#define  TEACH_END_NUMBER          75900
00072 #define  REPLAY_FOLDER                   "../data/logs/2010_02_26__18_33_26/image_0000000000"
00073 #define  REPLAY_START_NUMBER       16298
00074 #define  REPLAY_END_NUMBER         17136
00075 
00076 #define  DFRAME                    (TEACH_START_NUMBER - REPLAY_START_NUMBER)
00077 #define  IMAX                      1.0
00078 #define  IMIN                      0.0
00079 
00080 const ModelOptionCateg MOC_QT_Navigation =
00081   { MOC_SORTPRI_3, "QT Navigation Related Options" };
00082 const ModelOptionDef OPT_RealCamera =
00083   { MODOPT_ARG(bool), "RealCamera", &MOC_QT_Navigation, OPTEXP_CORE,
00084     "Do we want use real camera from BeoCamera? Default will load from image files.",
00085     "camera", '\0', "true | false", "false" };
00086 
00087 // ######################################################################
00088 QT_Navigation::QT_Navigation(OptionManager& mgr,
00089                              const std::string& descrName, const std::string& tagName) :
00090   RobotBrainComponent(mgr, descrName, tagName),
00091   itsFftComputer(new GistEstimatorFFT(mgr)),
00092   itsOfs(new OutputFrameSeries(mgr)),
00093   itsMapImg(320,240,ZEROS),
00094   itsVisualObjectDB(new VisualObjectDB()),
00095   itsVisualObjectDB2(new VisualObjectDB()),
00096   itsFfn(new FeedForwardNetwork()),
00097   itsTimer(1000000),
00098   itsCurrImgID(-1),
00099   itsPrevProcImgID(-1),
00100   itsTeachImgID(TEACH_START_NUMBER +5 +300),//FIXX
00101   itsReplayImgID(REPLAY_START_NUMBER +300),//FIXX
00102   itsTransSpeed(0.0),
00103   itsRotSpeed(0.0),
00104   itsDirVector(0.0),
00105   itsDir(0),
00106   itsMilestoneErr(999.99),
00107   itsFalseErrRate(0.0),
00108   itsDirCount(0),
00109   itsPosition(0.0, 0.0, 0.0),
00110   itsError(0.0, 0.0, 0.0),
00111   itsRealCamera(&OPT_RealCamera, this, 0),
00112   itsEstop(true),
00113   itsReplayDBLoaded(false),
00114   itsIState(0.0),
00115   itsDState(0.0),
00116   itsPGain(1.0),
00117   itsIGain(0.01),
00118   itsDGain(0.0)
00119 {
00120   addSubComponent(itsOfs);
00121 
00122   // load the teach Visual Object Database
00123   loadDB(sformat("%s.sift",TEACH_FOLDER));
00124 }
00125 
00126 // ######################################################################
00127 void QT_Navigation::loadReplayDB()
00128 {
00129   if(!itsReplayDBLoaded)
00130     {
00131       if(!itsRealCamera.getVal())
00132         {
00133 
00134           if(itsVisualObjectDB2->loadFrom
00135              (sformat("%s.sift",REPLAY_FOLDER), false))
00136             LINFO("Load Replay Database");
00137           else
00138             {
00139               LINFO("Can't load database");
00140               saveDB(REPLAY_START_NUMBER,
00141                      REPLAY_END_NUMBER,
00142                      REPLAY_FOLDER);
00143           }
00144         }
00145       itsReplayDBLoaded = true;
00146     }
00147 }
00148 
00149 // ######################################################################
00150 void QT_Navigation::initFFN()
00151 { }
00152 
00153 // ######################################################################
00154 QT_Navigation::~QT_Navigation()
00155 { }
00156 
00157 // ######################################################################
00158 void QT_Navigation::start1()
00159 { }
00160 
00161 // ######################################################################
00162 void QT_Navigation::registerTopics()
00163 {
00164   // subscribe to all sensor data
00165   this->registerSubscription("CameraMessageTopic");
00166   this->registerSubscription("MotorMessageTopic");
00167   this->registerPublisher("MotorRequestTopic");
00168 }
00169 
00170 // ######################################################################
00171 void QT_Navigation::evolve()
00172 {
00173   // if simulation
00174   if(!itsRealCamera.getVal())
00175     {
00176       navigation();
00177     }
00178 
00179   // if real camera
00180   else
00181     {
00182       // check if the current image is updated
00183       its_Curr_Img_mutex.lock();
00184       bool newImageFlag = (itsPrevProcImgID < itsCurrImgID);
00185       its_Curr_Img_mutex.unlock();
00186 
00187       // if so, process
00188       if(newImageFlag)
00189         {
00190           itsTimer.reset();
00191           its_Curr_Img_mutex.lock();
00192           itsProcImg = itsCurrImg;
00193           itsPrevProcImgID = itsCurrImgID;
00194           its_Curr_Img_mutex.unlock();
00195           navigation();
00196         }
00197     }
00198 
00199   drawState();
00200   itsOfs->writeRGB(itsDispImg, "QT_nav", FrameInfo("QT_nav",SRC_POS));
00201   if(itsEstop == false)
00202     {
00203       //Raster::waitForKey();
00204       //itsEstop = true;
00205     }
00206 }
00207 
00208 // ######################################################################
00209 void QT_Navigation::recovery()
00210 {
00211   int trytimes = 0;
00212   do{
00213     loadFrame();
00214     std::vector< rutz::shared_ptr<VisualObjectMatch> > matches;
00215     int nmatches =
00216       itsVisualObjectDB->getObjectMatches
00217       (itsReplayVo,matches,VOMA_SIMPLE,
00218        10U,
00219        0.5F, //keypoint distance score default 0.5F
00220        0.5F, //affine distance score default 0.5F
00221        1.0F, //minscore  default 1.0F
00222        4U, //min # of keypoint match
00223        100U, //keypoint selection thershold
00224        false //sort by preattentive
00225        );
00226     std::string objName;
00227     int closestFrame = 999999999;
00228     int tmpFrameID = itsTeachImgID;
00229     LINFO("Searching For Nearest Key Frame in the Database");
00230     if (nmatches > 0 )
00231       {
00232         for(int i = 0; i < nmatches; i++)
00233           {
00234             LINFO("Found [%d] frames matches Frame[%d]", nmatches, i);
00235 
00236             // so that we will have a ref to the last matches obj
00237             rutz::shared_ptr<VisualObject> obj;
00238             rutz::shared_ptr<VisualObjectMatch> vom;
00239             vom = matches[i];
00240             obj = vom->getVoTest();
00241             //      score = vom->getScore();
00242             //      nkeyp = vom->size();
00243             //      avgScore = vom->getKeypointAvgDist();
00244             //      affineAvgDist = vom->getAffineAvgDist();
00245             objName = obj->getName();
00246             int tmpID = atoi(objName.c_str());
00247             int dx = tmpID - itsTeachImgID;
00248 
00249             LINFO("Dist. from current teach frame[%d]=======new ID[%d]",
00250                   dx,tmpID);
00251             // find closest frame that matches
00252             //FIXX: we don't consider the match rate yet.
00253             if(dx < closestFrame)
00254               {
00255                 closestFrame = dx;
00256                 tmpFrameID = tmpID;
00257               }
00258           }
00259         LINFO("Recovery Found:TID[%d]RID[%d] Match[%d]",
00260               itsTeachImgID,itsReplayImgID,tmpFrameID);
00261         itsTeachImgID = tmpFrameID;
00262         itsEstop = true;
00263         itsTransSpeed = 0.8;
00264         loadFrame();
00265         computeSIFT();
00266       }
00267     else
00268       {
00269         LINFO("No Match Point,Try get new frame and try again");
00270         loadFrame();
00271         trytimes++;
00272       }
00273   }
00274   while(!itsEstop && trytimes < 10);
00275 }
00276 
00277 // ######################################################################
00278 // The main function of navigation
00279 void QT_Navigation::navigation()
00280 {
00281 
00282   // load key frame from database
00283   // compute SIFT keypoint and matching
00284   loadFrame();
00285   //computeSIFT();
00286 
00287   // apply QT algo for direction
00288   double error = computeDirection2(itsTeachImgID);
00289 
00290         //When the mean error and stderr have too much difference
00291         //Then ignore the SIFT bogus match
00292         if(error != 0.0 && itsMatchList->size()!=0 &&
00293                         (itsMatchList->checkSIFTaffine() || itsFalseErrRate > 1.0)
00294                 ){
00295                 updateMotorPID(itsTransSpeed, itsRotSpeed,error);
00296                 updateMotor(itsTransSpeed, itsRotSpeed);
00297                 updateKeyframe(error);
00298                 itsEstop = true;
00299 
00300       // update keyframe when reaching milestone
00301       updateKeyframe(error);
00302       itsEstop = true;
00303     }
00304 
00305   // when the mean error and stderr have too much difference
00306   // then ignore the SIFT bogus match
00307   else
00308     {
00309       itsEstop = false;
00310       LINFO("lost matching; start recover mechanism");
00311       itsTransSpeed = 0.0;
00312       itsRotSpeed = 0.0;
00313       updateMotor(itsTransSpeed, itsRotSpeed);
00314       //Raster::waitForKey();
00315       //recovery();
00316       //itsEstop = true;
00317     }
00318 }
00319 
00320 // ######################################################################
00321 void QT_Navigation::loadDB(const std::string& path)
00322 {
00323   // load the actual VisualObjectDatabase but don't load images
00324   if(itsVisualObjectDB->loadFrom(path,false))
00325     {
00326       LINFO("Load SIFT Database");
00327     }
00328   else
00329     {
00330       LINFO("SIFT Database %s not exist, create new", path.c_str());
00331       saveDB(TEACH_START_NUMBER,TEACH_END_NUMBER,TEACH_FOLDER);
00332     }
00333 }
00334 
00335 // ######################################################################
00336 void QT_Navigation::saveDB(int start,int end,const std::string& path)
00337 {
00338   itsVisualObjectDB.reset(new VisualObjectDB());
00339   for(int i = start; i < end; i++)
00340     {
00341       itsTimer.reset();
00342       std::string teachFileName(sformat("%s%05d.ppm",path.c_str(),i));
00343       Image< PixRGB<byte> > img = Raster::ReadRGB(teachFileName);
00344       rutz::shared_ptr<VisualObject> vo1
00345         (new VisualObject(sformat("%d",i),
00346                           sformat("%s%05d.png",path.c_str(),i),
00347                           rescale(img,img.getWidth()/2, img.getHeight()/2)));
00348       itsVisualObjectDB->addObject(vo1);
00349       LINFO("Time[%f] Compute SIFT for Frame %d/%d",
00350             itsTimer.get()/1000.0,i-start+1,end-start+1);
00351     }
00352   std::string teachDB(sformat("%s.sift",path.c_str()));
00353   //        itsVisualObjectDB->setName();
00354   itsVisualObjectDB->saveTo(teachDB);
00355 }
00356 
00357 // ######################################################################
00358 void QT_Navigation::loadFrame()
00359 {
00360   // load teach image data
00361   itsTimer.reset();
00362   std::string teachFileName
00363     (sformat("%s%05d.ppm", TEACH_FOLDER, itsTeachImgID));
00364   itsTeachImg = Raster::ReadRGB(teachFileName);
00365   itsTeachVo = itsVisualObjectDB->getObject
00366     (itsTeachImgID - TEACH_START_NUMBER);
00367   LINFO("Time for Computing SIFT on TeachImg %f",
00368         itsTimer.get()/1000.0);
00369   itsTimer.reset();
00370 
00371   // Load Current Replay image data
00372 
00373   // running from image files
00374   if(!itsRealCamera.getVal())
00375     {
00376       loadReplayDB();
00377       std::string replayFileName
00378         (sformat("%s%05d.ppm", REPLAY_FOLDER, itsReplayImgID));
00379       itsReplayImg = Raster::ReadRGB(replayFileName);
00380 
00381       itsReplayVo = itsVisualObjectDB2->
00382         getObject(itsReplayImgID - REPLAY_START_NUMBER);
00383       itsReplayImgID++;
00384     }
00385 
00386   // running from real camera
00387   else
00388     {
00389       itsReplayImg   = itsProcImg;
00390       itsReplayImgID = itsPrevProcImgID;
00391                 //      LINFO("TeachVo Width = %d",itsTeachVo->getKeypointImage(1.0).getWidth());
00392                 //      if(itsProcImg.getWidth()==itsTeachVo->getKeypointImage(1.0).getWidth()){
00393                 //              itsReplayImg   = itsProcImg;    
00394                 //      }else
00395                 //      {
00396                 //              itsReplayImg   = rescale(itsProcImg,itsTeachVo->getKeypointImage(1.0).getWidth(),
00397                 //                              itsTeachVo->getKeypointImage(1.0).getHeight()); 
00398                 //      }
00399       rutz::shared_ptr<VisualObject> vo2
00400         (new VisualObject
00401          ("1", "",
00402           rescale(itsReplayImg,itsReplayImg.getWidth()/2,
00403                   itsReplayImg.getHeight()/2)));
00404       itsReplayVo = vo2;
00405     }
00406   LINFO("Time for Computing SIFT on ReplayImg %f",
00407         itsTimer.get()/1000.0);
00408 }
00409 
00410 // ######################################################################
00411 bool QT_Navigation::computeSIFT()
00412 {
00413   itsTimer.reset();
00414   VisualObjectMatchAlgo voma(VOMA_SIMPLE);
00415   itsMatchList.reset(new VisualObjectMatch(itsTeachVo, itsReplayVo, voma));
00416   LINFO("=========================================");
00417   //LINFO("Found %u matches between Teach and Replay", match.size());
00418 
00419   // let's prune the matches:
00420   //uint np =
00421   itsMatchList->prune();
00422   //LINFO("Pruned %u outlier matches.", np);
00423   LINFO("Match size now is %u ", itsMatchList->size());
00424 
00425   // show our final affine transform:
00426   //        std::cerr<<match.getSIFTaffine();
00427 
00428   //LINFO("getKeypointAvgDist = %f", match.getKeypointAvgDist());
00429   //LINFO("getAffineAvgDist = %f", match.getAffineAvgDist());
00430   //LINFO("getScore = %f", match.getScore());
00431   if (itsMatchList->checkSIFTaffine() == false)
00432     LINFO("### Affine is too weird -- BOGUS MATCH");
00433 
00434   // record the time
00435   LINFO("Time for Matching SIFT %f",itsTimer.get()/1000.0);
00436 
00437   // if the sift point is valid, return true
00438   return (itsMatchList->size()!=0 && itsMatchList->checkSIFTaffine());
00439 }
00440 
00441 // ######################################################################
00442 double QT_Navigation::computeDirection2(int teachID)
00443 {
00444   int dirF = 0,dirL = 0,dirR = 0;
00445   rutz::shared_ptr<VisualObjectMatch> bestMatch;
00446   int bestMatchSize = -1;
00447   int bestID = 0;
00448   LINFO("Direction2 Start");
00449 
00450   // boundary condition
00451   if((teachID-5) < TEACH_START_NUMBER) teachID = TEACH_START_NUMBER+2;
00452 
00453   // looking 10 frame ahead to find best match frame
00454   for(int id = teachID-2; id < teachID+10; id++)
00455     {
00456       std::string teachFileName(sformat("%s%05d.ppm",TEACH_FOLDER,id));
00457       rutz::shared_ptr<VisualObject> vo1 =
00458         itsVisualObjectDB->getObject(id - TEACH_START_NUMBER);
00459 
00460       VisualObjectMatchAlgo voma(VOMA_SIMPLE);
00461       rutz::shared_ptr<VisualObjectMatch>
00462         match(new VisualObjectMatch(vo1, itsReplayVo, voma));
00463       match->prune();
00464 
00465       // =========================
00466       // find the best Keyframe
00467       float theta = 2.0, sx = 2.0, sy = 2.0, str = 0;
00468 
00469       // check if the match is valid
00470       if (match->checkSIFTaffine())
00471         {
00472           match->getSIFTaffine().decompose(theta, sx, sy, str);
00473         }
00474       int msize = match->size();
00475       if((msize > bestMatchSize && sx < 1.0 && sy < 1.0) ||
00476          (bestMatchSize == -1 && id==teachID))
00477         {
00478           bestMatchSize = msize;
00479           bestMatch = match;
00480           bestID = id;
00481         }
00482       LINFO("Best Match Size %d,",bestMatchSize);
00483       itsMatchList = bestMatch;
00484 
00485       int dirGain = 1; if(id == bestID) dirGain = 2;
00486 
00487       // compute the direction and find mean of error
00488       for (int i = 0; i < msize; i ++)
00489         {
00490           rutz::shared_ptr<Keypoint> refk = match->getKeypointMatch(i).refkp;
00491           rutz::shared_ptr<Keypoint> tstk = match->getKeypointMatch(i).tstkp;
00492 
00493           double ut = tstk->getX() - itsTeachImg.getWidth()/4;
00494           double ud = refk->getX() - itsTeachImg.getWidth()/4;
00495 
00496           if(ut > 0.0 && ud < 0.0)
00497             {
00498               dirR += dirGain;
00499             }
00500           else if(ut < 0.0 && ud > 0.0)
00501             {
00502               dirL += dirGain;
00503             }
00504           else if(ut > 0.0 && ut > ud)
00505             {
00506               dirR += dirGain;
00507             }
00508           else if(ut < 0.0 && ut < ud)
00509             {
00510               dirL += dirGain;
00511             }
00512           else
00513             {
00514               dirF +=dirGain;
00515             }
00516         }
00517     }//end for
00518 
00519   //=========================
00520   double stdx = 0.0,stdy = 0.0;
00521   double avgdx = 0.0,avgdy = 0.0;
00522 
00523   // compute the std error
00524   for (int i = 0; i < bestMatchSize; i ++)
00525     {
00526       // LINFO("Loading best match point %d",i);
00527       rutz::shared_ptr<Keypoint> refk = bestMatch->getKeypointMatch(i).refkp;
00528       rutz::shared_ptr<Keypoint> tstk = bestMatch->getKeypointMatch(i).tstkp;
00529       double dx = fabs(tstk->getX() - refk->getX())+ 0.000001;
00530       stdx += dx * dx;
00531       avgdx+= dx;
00532 
00533       double dy = fabs(tstk->getY() - refk->getY())+ 0.000001;
00534       stdy += dy * dy;
00535       avgdy+= dy;
00536       LINFO("Error! i=[%d] BestMatchSize[%d] "
00537             "avgdx[%f] avgdy[%f],dx[%f], dy[%f]",
00538             i,bestMatchSize,avgdx,avgdy,dx,dy);
00539     }
00540   if(bestMatchSize != 0)
00541     {
00542       avgdx/= bestMatchSize;
00543       avgdy/= bestMatchSize;
00544       stdx -= (avgdx*avgdx);
00545       stdy -= (avgdy*avgdy);
00546       stdx /= bestMatchSize;
00547       stdx  = sqrt(stdx);
00548       stdy /= bestMatchSize;
00549       stdy  = sqrt(stdy);
00550       LINFO("Error! BestMatchSize[%d]  avgdx[%f] avgdy[%f]",
00551             bestMatchSize,avgdx,avgdy);
00552     }
00553   else
00554     {
00555       avgdx = 0;
00556       avgdy = 0;
00557       stdx  = 0;
00558       stdy  = 0;
00559     }
00560 
00561   LINFO("stdx %f stdy %f",stdx,stdy);
00562   uint stdcount = 0;
00563   double newAvgX = 0.0,newAvgY = 0.0;
00564 
00565   // Pick one stddev error as the new error data
00566   for (int i = 0; i < bestMatchSize; i ++)
00567     {
00568       rutz::shared_ptr<Keypoint> refk = bestMatch->getKeypointMatch(i).refkp;
00569       rutz::shared_ptr<Keypoint> tstk = bestMatch->getKeypointMatch(i).tstkp;
00570       double dx = fabs(tstk->getX() - refk->getX())+ 0.000001;
00571       double dy = fabs(tstk->getY() - refk->getY())+ 0.000001 ;
00572       double ddx = fabs(dx - avgdx);
00573       double ddy = fabs(dy - avgdy);
00574 
00575       if(ddx < stdx && ddy < stdy)
00576         {
00577           newAvgX += dx;
00578           newAvgY += dy;
00579           stdcount++;
00580         }
00581     }
00582 
00583   if(stdcount != 0)
00584     {
00585       newAvgX/= stdcount;
00586       newAvgY/= stdcount;
00587     }
00588   double newAvgerr = sqrt(newAvgX*newAvgX + newAvgY*newAvgY);
00589   double avgerr = sqrt(avgdx*avgdx + avgdy*avgdy);
00590   double angle = (newAvgX*(16.0/33.0));
00591   itsError.x = newAvgX;
00592   itsError.y = newAvgY;
00593   itsError.z = newAvgerr;
00594   //FIXXX
00595   if(newAvgerr == 0.0)
00596     {
00597       //Raster::waitForKey();
00598       newAvgerr = avgerr;
00599       itsFalseErrRate = 0;
00600     }
00601   else
00602     {
00603       itsFalseErrRate = avgerr/newAvgerr;
00604     }
00605   LINFO("False Error Rate %1.3f",itsFalseErrRate);
00606   if(itsFalseErrRate == 0.0)
00607     {
00608       LINFO("Error!!!! stdcount[%d]  avgerr[%f]",
00609             stdcount,avgerr);
00610       LINFO("Error!!!! bestMatchSize[%d]  stdx[%f] stdy[%f]",
00611             bestMatchSize,stdx,stdy);
00612       //Raster::waitForKey();
00613     }
00614 
00615   // reset target keyframe if we found better match
00616   if(bestID > itsTeachImgID && newAvgerr < 10.0)
00617     itsTeachImgID = bestID;
00618 
00619   // compute the forward speed based on scale
00620   if (bestMatch->checkSIFTaffine())//Check if the match is valid
00621     {
00622       float theta = 0.0 , sx = 0.0, sy = 0.0, str = 0.0 ;
00623       bestMatch->getSIFTaffine().decompose(theta, sx, sy, str);
00624       //if the size difference is large(sx/sy << 1.0),we can speed up
00625       double rms = 1.0 -(sqrt(sx*sx+sy*sy)/sqrt(2));
00626 
00627       double gain = 1.0;
00628       if(rms > 0.0)
00629         itsTransSpeed = 0.8 + rms * gain;
00630       if(itsTransSpeed > 1.0)itsTransSpeed = 1.0;
00631       if(itsTransSpeed < 0.0)itsTransSpeed = 0.0;
00632     }
00633   else
00634     {
00635       itsTransSpeed = 0.8;
00636     }
00637 
00638   // compute turning speed
00639   if(dirF >= abs(dirR + dirL))
00640     {
00641       updatePosition(0);
00642       itsRotSpeed = 0.4;
00643       if(itsDir == 0)//same heading since last time
00644         {
00645           itsDirCount++;
00646         }
00647       else
00648         {
00649           itsDirCount = 0;//reset direction count
00650           itsDir = 0;//reset direction
00651         }
00652     }
00653   else if(dirR > dirL)
00654     {
00655       updatePosition(angle);
00656       //Average the speed
00657       itsRotSpeed -= newAvgX/100.0;
00658       itsRotSpeed /= 2.0;
00659       if(itsDir == 1)//same heading since last time
00660         {
00661           itsDirCount++;
00662         }
00663       else
00664         {
00665           itsDirCount = 0;//reset direction count
00666           itsDir = 1;//reset direction
00667         }
00668     }
00669   else
00670     {
00671       updatePosition(-1*angle);
00672       //Average the speed
00673       itsRotSpeed += newAvgX/100.0+1.0;
00674       itsRotSpeed /= 2.0;
00675       if(itsDir == -1)//same heading since last time
00676         {
00677           itsDirCount++;
00678         }else{
00679         itsDirCount = 0;//reset direction count
00680         itsDir = -1;//reset direction
00681       }
00682     }
00683   return newAvgerr;
00684 }
00685 
00686 // ######################################################################
00687 double QT_Navigation::computeDirection()
00688 {
00689   itsTimer.reset();
00690   int dirF = 0,dirL = 0,dirR = 0;
00691   double avgdx = 0.0,avgdy = 0.0;
00692   uint msize = itsMatchList->size();
00693 
00694   // compute the direction and find mean of error
00695   for (uint i = 0; i < msize; i ++)
00696     {
00697       rutz::shared_ptr<Keypoint> refk =
00698         itsMatchList->getKeypointMatch(i).refkp;
00699       rutz::shared_ptr<Keypoint> tstk =
00700         itsMatchList->getKeypointMatch(i).tstkp;
00701 
00702       double ut = tstk->getX() - itsTeachImg.getWidth()/4;
00703       double ud = refk->getX() - itsTeachImg.getWidth()/4;
00704       double dx = fabs(ut - ud);
00705       double dy = fabs(tstk->getY() - refk->getY());
00706       avgdx += dx;
00707       avgdy += dy;
00708       std::string dir;
00709 
00710       if(ut > 0.0 && ud < 0.0)
00711         {
00712           dir = std::string("RIGHT");
00713           dirR ++;
00714         }
00715       else if(ut < 0.0 && ud > 0.0)
00716         {
00717           dir = std::string("LEFT");
00718           dirL ++;
00719         }
00720       else if(ut > 0.0 && ut > ud  )
00721         {
00722           dir = std::string("RIGHT");
00723           dirR ++;
00724         }
00725       else if(ut < 0.0 && ut < ud)
00726         {
00727           dir = std::string("LEFT");
00728           dirL ++;
00729         }
00730       else
00731         {
00732           dir = std::string("Forward");
00733           dirF ++;
00734         }
00735       LDEBUG("MatchPoint[%d] Teach(%f,%f) Replay(%f,%f), "
00736              "ud:[%f],ut:[%f],dx:[%f],dy:[%f] Turn %s",
00737              i,
00738              (refk->getX()),
00739              (refk->getY()),
00740              (tstk->getX()),
00741              (tstk->getY()),
00742              ud,ut,dx,dy,dir.c_str()
00743              );
00744     }
00745   avgdx/= msize;
00746   avgdy/= msize;
00747   double stdx = 0.0,stdy = 0.0;
00748 
00749   // compute the std error
00750   for (uint i = 0; i < msize; i ++)
00751     {
00752       rutz::shared_ptr<Keypoint> refk =
00753         itsMatchList->getKeypointMatch(i).refkp;
00754       rutz::shared_ptr<Keypoint> tstk =
00755         itsMatchList->getKeypointMatch(i).tstkp;
00756       double ddx = fabs(tstk->getX() - refk->getX()) - avgdx;
00757       stdx += ddx * ddx;
00758       double ddy = fabs(tstk->getY() - refk->getY()) - avgdy;
00759       stdy += ddy * ddy;
00760     }
00761   stdx /= msize;
00762   //stdx = sqrt(stdx);
00763   stdy /= msize;
00764   //stdy = sqrt(stdy);
00765   uint stdcount = 0;
00766   double newAvgX = 0.0,newAvgY = 0.0;
00767 
00768   // pick one std error as the new error data
00769   for (uint i = 0; i < msize; i ++)
00770     {
00771       rutz::shared_ptr<Keypoint> refk =
00772         itsMatchList->getKeypointMatch(i).refkp;
00773       rutz::shared_ptr<Keypoint> tstk =
00774         itsMatchList->getKeypointMatch(i).tstkp;
00775       double dx = fabs(tstk->getX() - refk->getX()) ;
00776       double dy = fabs(tstk->getY() - refk->getY()) ;
00777       double ddx = fabs(dx - avgdx);
00778       double ddy = fabs(dy - avgdy);
00779 
00780       if(ddx < sqrt(stdx) && ddy < sqrt(stdy))
00781         {
00782           newAvgX += dx;
00783           newAvgY += dy;
00784           stdcount++;
00785         }
00786     }
00787   if(stdcount != 0)
00788     {
00789       newAvgX/=stdcount;
00790       newAvgY/=stdcount;
00791     }
00792 
00793   double stdeverr = sqrt(stdx+stdy);
00794   double avgerr = sqrt(avgdx*avgdx + avgdy*avgdy);
00795   double newAvgerr = sqrt(newAvgX*newAvgX + newAvgY*newAvgY);
00796   double df = DFRAME - (itsTeachImgID - itsReplayImgID );
00797   double angle = (newAvgX*(56.0/33.0))/df;
00798   //double angle = (newAvgX*(16.0/33.0));
00799   LINFO("StdError[%f],StdErrX[%f],StdErrY[%f],Frame[%1.0f]",
00800         stdeverr,sqrt(stdx),sqrt(stdy),df);
00801   LINFO("AvgErr[%f],AvgX[%f],AvgY[%f]",avgerr,avgdx,avgdy);
00802   LINFO("StdAvgErr[%f],StdAvgX[%f],StdAvgY[%f]",newAvgerr,newAvgX,newAvgY);
00803   LINFO("Forward[%d], Right[%d], Left[%d] Angle[%f]",dirF,dirR,dirL,angle);
00804   itsError.x = newAvgX;
00805   itsError.y = newAvgY;
00806   itsError.z = newAvgerr;
00807   //FIXXX
00808   if(newAvgerr == 0.0)
00809     {
00810       //Raster::waitForKey();
00811       newAvgerr = avgerr;
00812     }
00813 
00814   itsFalseErrRate = avgerr/newAvgerr;
00815 
00816   LINFO("Time for QT Navigation %f",itsTimer.get()/1000.0);
00817   return newAvgerr;
00818 }
00819 
00820 // ######################################################################
00821 void QT_Navigation::updateKeyframe(double error)
00822 {
00823   LINFO("itsMerror %f,new error %f", itsMilestoneErr, error);
00824 
00825   // check if the match is valid
00826   if (itsMatchList->checkSIFTaffine())
00827     {
00828       float theta, sx, sy, str;
00829       itsMatchList->getSIFTaffine().decompose(theta, sx, sy, str);
00830       LINFO("SIFT SCALE SX[%f] SY[%f] Theta[%f] Str[%f]",sx,sy,theta,str);
00831 
00832       //Check the frame reach the milestone image or not
00833       //if(itsMilestoneErr > error && (sx <1.0&&sy <1.0))
00834       //        itsMilestoneErr = error;
00835       //else
00836       //{
00837       //FIXXX: Hack,Single threshold will not work in all environment
00838       if(((sx >=0.9 && sy >=0.9) && error <50.0 ) && itsMatchList->size()>2){
00839         itsMilestoneErr = 999.99;
00840         itsTeachImgID += 20;//FIXXX when turning sharp,slow down the forward rate
00841         LINFO("*********=========Switch Keyframe=========**********");
00842       }
00843       //}
00844     }
00845 }
00846 
00847 //Given Current image frame,it will try to find proper keyframe for navation
00848 //
00849 // ######################################################################
00850 int QT_Navigation::computeEntropy()
00851 {
00852   return 1;
00853 }
00854 
00855 // ######################################################################
00856 void QT_Navigation::drawState()
00857 {
00858   uint w = 320, h = 240;
00859 
00860   itsDispImg.resize(w*2, 3*h, NO_INIT);
00861   // original image
00862   inplacePaste(itsDispImg, itsProcImg, Point2D<int>(0, 0));
00863 
00864   its_Curr_Img_mutex.lock();
00865   bool newImageFlag = (itsPrevProcImgID < itsCurrImgID);
00866   its_Curr_Img_mutex.unlock();
00867   if(!itsRealCamera.getVal() || newImageFlag){
00868     char buffer[128];
00869     sprintf(buffer,"img%5d",itsTeachImgID);
00870     Image< PixRGB<byte> > tmpImg = itsTeachImg;
00871     writeText(tmpImg, Point2D<int>(5,5),
00872               buffer, PixRGB<byte>(255,255,255),
00873               PixRGB<byte>(0,0,0), SimpleFont::FIXED(8));
00874     inplacePaste(itsDispImg, tmpImg, Point2D<int>(0, h));
00875 
00876                 //Check the both input image have same size
00877 //              if(itsReplayImg.getWidth() == w)
00878 //              {
00879                         tmpImg = itsReplayImg;  
00880 //              }else{
00881 //                      tmpImg = rescale(itsReplayImg,w,h);
00882 //              }
00883     sprintf(buffer,"img%5d",itsReplayImgID);
00884     writeText(itsReplayImg, Point2D<int>(5,5),
00885               buffer, PixRGB<byte>(255,255,255),
00886               PixRGB<byte>(0,0,0),SimpleFont::FIXED(8));
00887 
00888     inplacePaste(itsDispImg, tmpImg, Point2D<int>(0, 2*h));
00889     inplacePaste(itsDispImg, itsTeachVo->getKeypointImage(1.0) ,
00890                  Point2D<int>(w, h));
00891     inplacePaste(itsDispImg, itsReplayVo->getKeypointImage(1.0),
00892                  Point2D<int>(w, h*1.5));
00893     if(itsEstop)
00894       {
00895         Image< PixRGB<byte> > mimg = itsMatchList->getMatchImage(1.0F);
00896         inplacePaste(itsDispImg, mimg, Point2D<int>(w*1.5, h));
00897       }
00898     double mapScale = 0.20;
00899     Point2D<int> drawPos
00900       (int(itsPosition.x*mapScale + itsMapImg.getWidth()/2),
00901        int(itsPosition.y*mapScale + itsMapImg.getHeight()/2));
00902     if(itsMapImg.coordsOk(drawPos))
00903       itsMapImg.setVal(drawPos, PixRGB<byte>(0,255,0));
00904     inplacePaste(itsDispImg, itsMapImg, Point2D<int>(w, h*2));
00905     inplacePaste(itsDispImg, drawInfoImg(), Point2D<int>(w, 0));
00906   }
00907 }
00908 
00909 // ######################################################################
00910 // Only for display current heading
00911 Image<PixRGB<byte> > QT_Navigation::drawInfoImg()
00912 {
00913   char buffer[128];
00914   Image<PixRGB<byte> > dirImg(320,240,ZEROS);
00915 
00916   if(itsDir == 0)
00917     {
00918       sprintf(buffer, "|||");
00919     }
00920   else if(itsDir > 0)
00921     {
00922       sprintf(buffer, "-->  ");
00923     }
00924   else
00925     {
00926       sprintf(buffer, "<--  ");
00927     }
00928   writeText(dirImg, Point2D<int>(dirImg.getWidth()/2-20,
00929                                  dirImg.getHeight()/2-20),
00930             buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),
00931             SimpleFont::FIXED(20));
00932 
00933   sprintf(buffer, "[%d]",itsDirCount);
00934   writeText(dirImg, Point2D<int>(dirImg.getWidth()/2-10,
00935                                  dirImg.getHeight()/2+40),
00936             buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),
00937             SimpleFont::FIXED(20));
00938 
00939   sprintf(buffer, "False Error Rate:[%1.3f]",itsFalseErrRate);
00940   writeText(dirImg, Point2D<int>(10,10),
00941             buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),
00942             SimpleFont::FIXED(8));
00943   if(itsEstop)
00944     {
00945       sprintf(buffer, "Match:[%d]",itsMatchList->size());
00946       writeText(dirImg, Point2D<int>(210,10),
00947                 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),
00948                 SimpleFont::FIXED(8));
00949     }
00950   sprintf(buffer, "Error:[%1.3f] X:[%1.3f] Y:[%1.3f]",
00951           itsError.z,itsError.x,itsError.y);
00952   writeText(dirImg, Point2D<int>(10,25),
00953             buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),
00954             SimpleFont::FIXED(8));
00955 
00956   // check if the match is valid
00957   if (itsEstop && itsMatchList->checkSIFTaffine())
00958     {
00959       float theta, sx, sy, str;
00960       itsMatchList->getSIFTaffine().decompose(theta, sx, sy, str);
00961       sprintf(buffer, "Affine T:[%1.3f] X:[%1.3f] Y:[%1.3f]",theta,sx,sy);
00962       writeText(dirImg, Point2D<int>(10,40),
00963                 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),
00964                 SimpleFont::FIXED(8));
00965     }
00966   sprintf(buffer, "Motor Speed :[%1.3f] Motor Rot:[%1.3f]",
00967           itsTransSpeed, itsRotSpeed);
00968   writeText(dirImg, Point2D<int>(10,55),
00969             buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),
00970             SimpleFont::FIXED(8));
00971   return dirImg;
00972 }
00973 
00974 // ######################################################################
00975 void QT_Navigation::updatePosition(double turn)
00976 {
00977   itsDirVector += turn;
00978   if(itsDirVector > 360.0) itsDirVector = 0.0;
00979   if(itsDirVector < 0.0) itsDirVector = 360.0;
00980 
00981   itsPosition.x -= sin(itsDirVector*(M_PI/180.0));
00982   itsPosition.y -= cos(itsDirVector*(M_PI/180.0));
00983 }
00984 
00985 // ######################################################################
00986 void QT_Navigation::updateMessage
00987 (const RobotSimEvents::EventMessagePtr& eMsg, const Ice::Current&)
00988 {
00989   // camera message
00990   if(eMsg->ice_isA("::BeobotEvents::CameraMessage"))
00991     {
00992       // store the image
00993       BeobotEvents::CameraMessagePtr cameraMsg =
00994         BeobotEvents::CameraMessagePtr::dynamicCast(eMsg);
00995 
00996       int currRequestID = cameraMsg->RequestID;
00997       Image<PixRGB<byte> > img = Ice2Image<PixRGB<byte> >(cameraMsg->image);
00998 
00999       LDEBUG("Got a CameraMessage with Request ID = %d", currRequestID);
01000 
01001       its_Curr_Img_mutex.lock();
01002       itsCurrImg = img;
01003       itsCurrImgID = cameraMsg->RequestID;
01004       its_Curr_Img_mutex.unlock();
01005     }
01006 
01007   // motor message
01008   else if(eMsg->ice_isA("::BeobotEvents::MotorMessage"))
01009     {
01010       BeobotEvents::MotorMessagePtr mtrMsg =
01011         BeobotEvents::MotorMessagePtr::dynamicCast(eMsg);
01012       LDEBUG("Got a MotorMessage with Request ID = %d: RC Trans %f, Rot %f",
01013              mtrMsg->RequestID, itsRcTransSpeed, itsRcRotSpeed);
01014       its_Curr_Mtr_mutex.lock();
01015       itsRemoteMode = mtrMsg->rcMode;
01016       itsRcTransSpeed = mtrMsg->rcTransVel;
01017       itsRcRotSpeed = mtrMsg->rcRotVel;
01018       its_Curr_Mtr_mutex.unlock();
01019     }
01020   //        LINFO("updateMessage");
01021 }
01022 
01023 // ######################################################################
01024 void QT_Navigation::updateMotorPID(double tran, double rot,double error)
01025 {
01026   error/=100.0;
01027   if(itsDir == 1)
01028     error *= -1.0;
01029   double pTerm,iTerm,dTerm;
01030   pTerm = itsPGain * error;
01031   itsIState += error;
01032   if(itsIState > IMAX)
01033     itsIState = IMAX;
01034   else if(itsIState < IMIN)
01035     itsIState = IMIN;
01036   iTerm = itsIGain * itsIState;
01037   dTerm = itsDGain * (rot - itsDState);
01038   itsDState = rot;
01039   double pid = pTerm + iTerm - dTerm;
01040 
01041   LINFO("P[%1.2f] I[%1.2f] D[%1.2f], Istate[%1.2f] DState[%1.2f]",
01042         pTerm,iTerm,dTerm,itsIState,itsDState);
01043   LINFO("pid[%1.2f],rot[%1.2f]",pid,rot);
01044   updateMotor(tran,pid);
01045 }
01046 
01047 // ######################################################################
01048 void QT_Navigation::updateMotor(double tran, double rot)
01049 {
01050   BeobotEvents::MotorRequestPtr msg = new BeobotEvents::MotorRequest;
01051   msg->transVel = tran;
01052   msg->rotVel   = rot;
01053   this->publish("MotorRequestTopic", msg);
01054   LINFO("[%d] Publish motor request Trans %f Rotation %f",
01055         itsPrevProcImgID,tran,rot);
01056 }
01057 
01058 // ######################################################################
01059 /* So things look consistent in everyone's emacs... */
01060 /* Local Variables: */
01061 /* indent-tabs-mode: nil */
01062 /* End: */
Generated on Sun May 8 08:41:19 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3