Tracker.cc
Go to the documentation of this file.
00001 // Copyright 2008 Isis Innovation Limited
00002 #include "OpenGL.h"
00003 #include "Tracker.h"
00004 #include "MEstimator.h"
00005 #include "ShiTomasi.h"
00006 #include "SmallMatrixOpts.h"
00007 #include "PatchFinder.h"
00008 #include "TrackerData.h"
00009 
00010 #include <cvd/utility.h>
00011 #include <cvd/gl_helpers.h>
00012 #include <cvd/fast_corner.h>
00013 #include <cvd/vision.h>
00014 #include <TooN/wls.h>
00015 #include <gvars3/instances.h>
00016 #include <gvars3/GStringUtil.h>
00017 
00018 #include <fstream>
00019 #include <fcntl.h>
00020 
00021 #include "settingsCustom.h"
00022 
00023 using namespace CVD;
00024 using namespace std;
00025 using namespace GVars3;
00026 
00027 // The constructor mostly sets up interal reference variables
00028 // to the other classes..
00029 Tracker::Tracker(ImageRef irVideoSize, const ATANCamera &c, Map &m, MapMaker &mm) :
00030   mMap(m),
00031   mMapMaker(mm),
00032   mCamera(c),
00033   mRelocaliser(mMap, mCamera),
00034   mirSize(irVideoSize)
00035 {
00036   mCurrentKF.bFixed = false;
00037   //GUI.RegisterCommand("Reset", GUICommandCallBack, this);
00038   //GUI.RegisterCommand("KeyPress", GUICommandCallBack, this);
00039   //GUI.RegisterCommand("PokeTracker", GUICommandCallBack, this);
00040   TrackerData::irImageSize = mirSize;
00041 
00042   mpSBILastFrame = NULL;
00043   mpSBIThisFrame = NULL;
00044   mnLastKeyFrameDroppedClock = 0;
00045 
00046 
00047   // Most of the initialisation is done in Reset()
00048   Reset();
00049 }
00050 
00051 // Resets the tracker, wipes the map.
00052 // This is the main Reset-handler-entry-point of the program! Other classes' resets propagate from here.
00053 // It's always called in the Tracker's thread, often as a GUI command.
00054 void Tracker::Reset()
00055 {
00056   mbDidCoarse = false;
00057   mbUserPressedSpacebar = false;
00058   mTrackingQuality = GOOD;
00059   mnLostFrames = 0;
00060   mdMSDScaledVelocityMagnitude = 0;
00061   mCurrentKF.dSceneDepthMean = 1.0;
00062   mCurrentKF.dSceneDepthSigma = 1.0;
00063   mnInitialStage = TRAIL_TRACKING_NOT_STARTED;
00064   mlTrails.clear();
00065   mCamera.SetImageSize(mirSize);
00066   mCurrentKF.mMeasurements.clear();
00067   mnLastKeyFrameDropped = -20;
00068   mnLastKeyFrameDroppedClock = 0;
00069   mnFrame=0;
00070   mv6CameraVelocity = Zeros;
00071   mbJustRecoveredSoUseCoarse = false;
00072   predictedCFromW = SE3<>();
00073 
00074   // Tell the MapMaker to reset itself..
00075   // this may take some time, since the mapmaker thread may have to wait
00076   // for an abort-check during calculation, so sleep while waiting.
00077   // MapMaker will also clear the map.
00078   mMapMaker.RequestReset();
00079   while(!mMapMaker.ResetDone())
00080 #ifndef WIN32
00081           usleep(10);
00082 #else
00083           Sleep(1);
00084 #endif
00085 }
00086 
00087 // TrackFrame is called by System.cc with each incoming video frame.
00088 // It figures out what state the tracker is in, and calls appropriate internal tracking
00089 // functions. bDraw tells the tracker wether it should output any GL graphics
00090 // or not (it should not draw, for example, when AR stuff is being shown.)
00091 void Tracker::TrackFrame(Image<byte> &imFrame, bool bDraw)
00092 {
00093   mbDraw = bDraw;
00094   mMessageForUser.str("");   // Wipe the user message clean
00095 
00096   // Take the input video image, and convert it into the tracker's keyframe struct
00097   // This does things like generate the image pyramid and find FAST corners
00098   mCurrentKF.mMeasurements.clear();
00099   mCurrentKF.MakeKeyFrame_Lite(imFrame);
00100 
00101   // Update the small images for the rotation estimator
00102   static gvar3<double> gvdSBIBlur("Tracker.RotationEstimatorBlur", TRACKER_ROTATION_ESTIMATOR_BLUR, SILENT);
00103   static gvar3<int> gvnUseSBI("Tracker.UseRotationEstimator", 1, SILENT);
00104   mbUseSBIInit = *gvnUseSBI;
00105   if(!mpSBIThisFrame)
00106     {
00107       mpSBIThisFrame = new SmallBlurryImage(mCurrentKF, *gvdSBIBlur);
00108       mpSBILastFrame = new SmallBlurryImage(mCurrentKF, *gvdSBIBlur);
00109     }
00110   else
00111     {
00112       delete  mpSBILastFrame;
00113       mpSBILastFrame = mpSBIThisFrame;
00114       mpSBIThisFrame = new SmallBlurryImage(mCurrentKF, *gvdSBIBlur);
00115     }
00116 
00117   // From now on we only use the keyframe struct!
00118   mnFrame++;
00119 
00120   if(mbDraw)
00121     {
00122       glDrawPixels(mCurrentKF.aLevels[0].im);
00123       if(GV2.GetInt("Tracker.DrawFASTCorners",TRACKER_DRAW_FAST_CORNERS_DEFAULT, SILENT))
00124         {
00125           glColor3f(1,0,1);  glPointSize(1); glBegin(GL_POINTS);
00126           for(unsigned int i=0; i<mCurrentKF.aLevels[0].vCorners.size(); i++)
00127             glVertex(mCurrentKF.aLevels[0].vCorners[i]);
00128           glEnd();
00129         }
00130     }
00131 
00132   // Decide what to do - if there is a map, try to track the map ...
00133   if(mMap.IsGood())
00134   {
00135         if(mnLostFrames < 3)  // .. but only if we're not lost!
00136         {
00137           if(mbUseSBIInit)
00138             CalcSBIRotation();
00139 
00140           ApplyMotionModel();
00141           TrackMap();
00142           UpdateMotionModel();
00143 
00144           AssessTrackingQuality();
00145         if(mTrackingQuality == GOOD)
00146                 lastStepResult = T_GOOD;
00147         if(mTrackingQuality == DODGY)
00148                 lastStepResult = T_DODGY;
00149         if(mTrackingQuality == BAD)
00150                 lastStepResult = T_LOST;
00151 
00152 
00153           { // Provide some feedback for the user:
00154             mMessageForUser << "Tracking Map, quality ";
00155             if(mTrackingQuality == GOOD)  mMessageForUser << "good.";
00156             if(mTrackingQuality == DODGY) mMessageForUser << "poor.";
00157             if(mTrackingQuality == BAD)   mMessageForUser << "bad.";
00158             mMessageForUser << " Found:";
00159             for(int i=0; i<LEVELS; i++) mMessageForUser << " " << manMeasFound[i] << "/" << manMeasAttempted[i];
00160             //      mMessageForUser << " Found " << mnMeasFound << " of " << mnMeasAttempted <<". (";
00161             mMessageForUser << " Map: " << mMap.vpPoints.size() << "P, " << mMap.vpKeyFrames.size() << "KF";
00162           }
00163 
00164           /*/ Heuristics to check if a key-frame should be added to the map:
00165           if(mTrackingQuality == GOOD && (forceKF || (
00166              mMapMaker.NeedNewKeyFrame(mCurrentKF) &&
00167              mnFrame - mnLastKeyFrameDropped > FRAMES_BETWEEN_KEYFRAMES  &&
00168              mMapMaker.QueueSize() < 3)))
00169             {
00170               mMessageForUser << " Adding key-frame.";
00171               AddNewKeyFrame();
00172                   forceKF = false;
00173             };*/
00174         }
00175       else  // what if there is a map, but tracking has been lost?
00176         {
00177           tryToRecover();
00178         }
00179       //if(mbDraw)
00180                 //RenderGrid();
00181     }
00182   else // If there is no map, try to make one.
00183   {
00184         //ApplyMotionModel();
00185         //UpdateMotionModel();
00186 
00187     TrackForInitialMap();
00188   }
00189 
00190   // GUI interface
00191   while(!mvQueuedCommands.empty())
00192     {
00193       GUICommandHandler(mvQueuedCommands.begin()->sCommand, mvQueuedCommands.begin()->sParams);
00194       mvQueuedCommands.erase(mvQueuedCommands.begin());
00195     }
00196 };
00197 
00198 
00199 void Tracker::tryToRecover()
00200 {
00201         if(mMap.IsGood())
00202         {
00203                 mMessageForUser.str("");
00204                 mMessageForUser << "** Attempting recovery **.";
00205                 if(AttemptRecovery())
00206                 {
00207                         TrackMap();
00208                         AssessTrackingQuality();
00209 
00210                         if(mTrackingQuality == GOOD)
00211                                 lastStepResult = T_RECOVERED_GOOD;
00212                         if(mTrackingQuality == DODGY)
00213                                 lastStepResult = T_RECOVERED_DODGY;
00214                         if(mTrackingQuality == BAD)
00215                                 lastStepResult = T_LOST;
00216                 }
00217                 else
00218                         lastStepResult = T_LOST;
00219         }
00220 }
00221 
00222 void Tracker::TakeKF(bool force)
00223 {
00224         if(mMap.IsGood())
00225         {
00226           // Heuristics to check if a key-frame should be added to the map:
00227           if(mTrackingQuality == GOOD && (force || (
00228              mMapMaker.NeedNewKeyFrame(mCurrentKF) &&
00229              ((double)(clock() - mnLastKeyFrameDroppedClock))/CLOCKS_PER_SEC > minKFTimeDist &&
00230              mMapMaker.QueueSize() < 3)))
00231             {
00232               mMessageForUser << " Adding key-frame.";
00233               AddNewKeyFrame();
00234                   lastStepResult = T_TOOK_KF;
00235             };
00236         }
00237 }
00238 
00239 
00240 // Try to relocalise in case tracking was lost.
00241 // Returns success or failure as a bool.
00242 // Actually, the SBI relocaliser will almost always return true, even if
00243 // it has no idea where it is, so graphics will go a bit
00244 // crazy when lost. Could use a tighter SSD threshold and return more false,
00245 // but the way it is now gives a snappier response and I prefer it.
00246 bool Tracker::AttemptRecovery()
00247 {
00248         if(true)
00249         {
00250                   bool bRelocGood = mRelocaliser.AttemptRecovery(mCurrentKF);
00251                   if(!bRelocGood)
00252                         return false;
00253 
00254                   SE3<> se3Best = mRelocaliser.BestPose();
00255                   mse3CamFromWorld = mse3StartPos = se3Best;
00256                   mv6CameraVelocity = Zeros;
00257                   mbJustRecoveredSoUseCoarse = true;
00258                   return true;
00259         }
00260         else
00261         {
00262                   SE3<> se3Best = predictedCFromW;
00263                   mse3CamFromWorld = mse3StartPos = se3Best;
00264                   mv6CameraVelocity = Zeros;
00265                   mbJustRecoveredSoUseCoarse = true;
00266                   return true;
00267         }
00268 }
00269 
00270 // Draw the reference grid to give the user an idea of wether tracking is OK or not.
00271 void Tracker::RenderGrid()
00272 {
00273   // The colour of the ref grid shows if the coarse stage of tracking was used
00274   // (it's turned off when the camera is sitting still to reduce jitter.)
00275   if(mbDidCoarse)
00276     glColor4f(.0, 0.5, .0, 0.6);
00277   else
00278     glColor4f(0,0,0,0.6);
00279 
00280   // The grid is projected manually, i.e. GL receives projected 2D coords to draw.
00281   int nHalfCells = 8;
00282   int nTot = nHalfCells * 2 + 1;
00283   Image<Vector<2> >  imVertices(ImageRef(nTot,nTot));
00284   for(int i=0; i<nTot; i++)
00285     for(int j=0; j<nTot; j++)
00286       {
00287         Vector<3> v3;
00288         v3[0] = (i - nHalfCells) * 0.1;
00289         v3[1] = (j - nHalfCells) * 0.1;
00290         v3[2] = 0.0;
00291         Vector<3> v3Cam = mse3CamFromWorld * v3;
00292         if(v3Cam[2] < 0.001)
00293           v3Cam[2] = 0.001;
00294         imVertices[i][j] = mCamera.Project(project(v3Cam));
00295       }
00296   glEnable(GL_LINE_SMOOTH);
00297   glEnable(GL_BLEND);
00298   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00299   glLineWidth(2);
00300   for(int i=0; i<nTot; i++)
00301     {
00302       glBegin(GL_LINE_STRIP);
00303       for(int j=0; j<nTot; j++)
00304         glVertex(imVertices[i][j]);
00305       glEnd();
00306 
00307       glBegin(GL_LINE_STRIP);
00308       for(int j=0; j<nTot; j++)
00309         glVertex(imVertices[j][i]);
00310       glEnd();
00311     };
00312 
00313   glLineWidth(1);
00314   glColor3f(1,0,0);
00315 }
00316 
00317 // GUI interface. Stuff commands onto the back of a queue so the tracker handles
00318 // them in its own thread at the end of each frame. Note the charming lack of
00319 // any thread safety (no lock on mvQueuedCommands).
00320 void Tracker::GUICommandCallBack(void* ptr, string sCommand, string sParams)
00321 {
00322   Command c;
00323   c.sCommand = sCommand;
00324   c.sParams = sParams;
00325   ((Tracker*) ptr)->mvQueuedCommands.push_back(c);
00326 }
00327 
00328 // This is called in the tracker's own thread.
00329 void Tracker::GUICommandHandler(string sCommand, string sParams)  // Called by the callback func..
00330 {
00331   if(sCommand=="Reset")
00332     {
00333       Reset();
00334       return;
00335     }
00336 
00337   // KeyPress commands are issued by GLWindow
00338   if(sCommand=="KeyPress")
00339     {
00340       if(sParams == "Space")
00341         {
00342           mbUserPressedSpacebar = true;
00343         }
00344       else if(sParams == "r")
00345         {
00346           Reset();
00347         }
00348       else if(sParams == "q" || sParams == "Escape")
00349         {
00350           GUI.ParseLine("quit");
00351         }
00352       return;
00353     }
00354   if((sCommand=="PokeTracker"))
00355     {
00356       mbUserPressedSpacebar = true;
00357       return;
00358     }
00359 
00360 
00361   cout << "! Tracker::GUICommandHandler: unhandled command "<< sCommand << endl;
00362   exit(1);
00363 };
00364 
00365 // Routine for establishing the initial map. This requires two spacebar presses from the user
00366 // to define the first two key-frames. Salient points are tracked between the two keyframes
00367 // using cheap frame-to-frame tracking (which is very brittle - quick camera motion will
00368 // break it.) The salient points are stored in a list of `Trail' data structures.
00369 // What action TrackForInitialMap() takes depends on the mnInitialStage enum variable..
00370 void Tracker::TrackForInitialMap()
00371 {
00372 
00373   // MiniPatch tracking threshhold.
00374         static gvar3<int> gvnMaxSSD("Tracker.MiniPatchMaxSSD", TRACKER_MINIPATCH_MAX_SSD_DEFAULT, SILENT);
00375         MiniPatch::mnMaxSSD = *gvnMaxSSD;
00376 
00377   // What stage of initial tracking are we at?
00378         if(mnInitialStage == TRAIL_TRACKING_NOT_STARTED)
00379     {
00380                 if(mbUserPressedSpacebar)  // First spacebar = this is the first keyframe
00381                 {
00382                         mbUserPressedSpacebar = false;
00383                         TrailTracking_Start();
00384                         mnInitialStage = TRAIL_TRACKING_STARTED;
00385                         lastStepResult = I_FIRST;
00386                         KFZeroDesiredCamFromWorld = predictedCFromW;
00387                 }
00388                 else
00389                 {
00390                         lastStepResult = NOT_TRACKING;
00391                         mMessageForUser << "Point camera at planar scene and press spacebar to start tracking for initial map." << endl;
00392                 }
00393 
00394       return;
00395     };
00396 
00397         if(mnInitialStage == TRAIL_TRACKING_STARTED)
00398         {
00399                 int nGoodTrails = TrailTracking_Advance();  // This call actually tracks the trails
00400                 if(nGoodTrails < 10) // if most trails have been wiped out, no point continuing.
00401                 {
00402                         Reset();
00403                         lastStepResult = NOT_TRACKING;
00404                         return;
00405                 }
00406 
00407       // If the user pressed spacebar here, use trails to run stereo and make the intial map..
00408                 if(mbUserPressedSpacebar)
00409                 {
00410                         mbUserPressedSpacebar = false;
00411                         vector<pair<ImageRef, ImageRef> > vMatches;   // This is the format the mapmaker wants for the stereo pairs
00412                         for(list<Trail>::iterator i = mlTrails.begin(); i!=mlTrails.end(); i++)
00413                                 vMatches.push_back(pair<ImageRef, ImageRef>(i->irInitialPos,
00414                                                         i->irCurrentPos));
00415                         bool succ = mMapMaker.InitFromStereo(mFirstKF, mCurrentKF, vMatches, mse3CamFromWorld, KFZeroDesiredCamFromWorld, predictedCFromW);  // This will take some time!
00416                         if(succ)
00417                                 lastStepResult = I_SECOND;
00418                         else
00419                                 lastStepResult = I_FAILED;
00420                         mnInitialStage = TRAIL_TRACKING_COMPLETE;
00421                 }
00422                 else
00423                 {
00424                         lastStepResult = INITIALIZING;
00425                         mMessageForUser << "Translate the camera slowly sideways, and press spacebar again to perform stereo init." << endl;
00426                 }
00427         }
00428 }
00429 
00430 // The current frame is to be the first keyframe!
00431 void Tracker::TrailTracking_Start()
00432 {
00433   mCurrentKF.MakeKeyFrame_Rest();  // This populates the Candidates list, which is Shi-Tomasi thresholded.
00434   mFirstKF = mCurrentKF;
00435   vector<pair<double,ImageRef> > vCornersAndSTScores;
00436   for(unsigned int i=0; i<mCurrentKF.aLevels[0].vCandidates.size(); i++)  // Copy candidates into a trivially sortable vector
00437     {                                                                     // so that we can choose the image corners with max ST score
00438       Candidate &c = mCurrentKF.aLevels[0].vCandidates[i];
00439       if(!mCurrentKF.aLevels[0].im.in_image_with_border(c.irLevelPos, MiniPatch::mnHalfPatchSize))
00440         continue;
00441       vCornersAndSTScores.push_back(pair<double,ImageRef>(-1.0 * c.dSTScore, c.irLevelPos)); // negative so highest score first in sorted list
00442     };
00443   sort(vCornersAndSTScores.begin(), vCornersAndSTScores.end());  // Sort according to Shi-Tomasi score
00444   int nToAdd = GV2.GetInt("MaxInitialTrails", 1000, SILENT);
00445   for(unsigned int i = 0; i<vCornersAndSTScores.size() && nToAdd > 0; i++)
00446     {
00447       if(!mCurrentKF.aLevels[0].im.in_image_with_border(vCornersAndSTScores[i].second, MiniPatch::mnHalfPatchSize))
00448         continue;
00449       Trail t;
00450       t.mPatch.SampleFromImage(vCornersAndSTScores[i].second, mCurrentKF.aLevels[0].im);
00451       t.irInitialPos = vCornersAndSTScores[i].second;
00452       t.irCurrentPos = t.irInitialPos;
00453       mlTrails.push_back(t);
00454       nToAdd--;
00455     }
00456   mPreviousFrameKF = mFirstKF;  // Always store the previous frame so married-matching can work.
00457 }
00458 
00459 // Steady-state trail tracking: Advance from the previous frame, remove duds.
00460 int Tracker::TrailTracking_Advance()
00461 {
00462   int nGoodTrails = 0;
00463   if(mbDraw)
00464     {
00465       glPointSize(5);
00466       glLineWidth(2);
00467       glEnable(GL_POINT_SMOOTH);
00468       glEnable(GL_LINE_SMOOTH);
00469       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00470       glEnable(GL_BLEND);
00471       glBegin(GL_LINES);
00472     }
00473 
00474   MiniPatch BackwardsPatch;
00475   Level &lCurrentFrame = mCurrentKF.aLevels[0];
00476   Level &lPreviousFrame = mPreviousFrameKF.aLevels[0];
00477 
00478   for(list<Trail>::iterator i = mlTrails.begin(); i!=mlTrails.end();)
00479     {
00480           list<Trail>::iterator next = i; next++;
00481 
00482       Trail &trail = *i;
00483       ImageRef irStart = trail.irCurrentPos;
00484       ImageRef irEnd = irStart;
00485       bool bFound = trail.mPatch.FindPatch(irEnd, lCurrentFrame.im, 10, lCurrentFrame.vCorners);
00486       if(bFound)
00487         {
00488           // Also find backwards in a married-matches check
00489           BackwardsPatch.SampleFromImage(irEnd, lCurrentFrame.im);
00490           ImageRef irBackWardsFound = irEnd;
00491           bFound = BackwardsPatch.FindPatch(irBackWardsFound, lPreviousFrame.im, 10, lPreviousFrame.vCorners);
00492           if((irBackWardsFound - irStart).mag_squared() > 2)
00493             bFound = false;
00494 
00495           trail.irCurrentPos = irEnd;
00496           nGoodTrails++;
00497         }
00498       if(mbDraw)
00499         {
00500           if(!bFound)
00501             glColor3f(0,1,1); // Failed trails flash purple before dying.
00502           else
00503             glColor3f(1,1,0);
00504           glVertex(trail.irInitialPos);
00505           if(bFound) glColor3f(1,0,0);
00506           glVertex(trail.irCurrentPos);
00507         }
00508       if(!bFound) // Erase from list of trails if not found this frame.
00509         {
00510           mlTrails.erase(i);
00511         }
00512           i = next;
00513     }
00514   if(mbDraw)
00515     glEnd();
00516 
00517   mPreviousFrameKF = mCurrentKF;
00518   return nGoodTrails;
00519 }
00520 
00521 // TrackMap is the main purpose of the Tracker.
00522 // It first projects all map points into the image to find a potentially-visible-set (PVS);
00523 // Then it tries to find some points of the PVS in the image;
00524 // Then it updates camera pose according to any points found.
00525 // Above may happen twice if a coarse tracking stage is performed.
00526 // Finally it updates the tracker's current-frame-KeyFrame struct with any
00527 // measurements made.
00528 // A lot of low-level functionality is split into helper classes:
00529 // class TrackerData handles the projection of a MapPoint and stores intermediate results;
00530 // class PatchFinder finds a projected MapPoint in the current-frame-KeyFrame.
00531 void Tracker::TrackMap()
00532 {
00533   // Some accounting which will be used for tracking quality assessment:
00534   for(int i=0; i<LEVELS; i++)
00535     manMeasAttempted[i] = manMeasFound[i] = 0;
00536 
00537   // The Potentially-Visible-Set (PVS) is split into pyramid levels.
00538   vector<TrackerData*> avPVS[LEVELS];
00539   for(int i=0; i<LEVELS; i++)
00540     avPVS[i].reserve(500);
00541 
00542   // For all points in the map..
00543   for(unsigned int i=0; i<mMap.vpPoints.size(); i++)
00544     {
00545       MapPoint &p= *(mMap.vpPoints[i]);
00546       // Ensure that this map point has an associated TrackerData struct.
00547       if(!p.pTData) p.pTData = new TrackerData(&p);
00548       TrackerData &TData = *p.pTData;
00549 
00550       // Project according to current view, and if it's not in the image, skip.
00551       TData.Project(mse3CamFromWorld, mCamera);
00552       if(!TData.bInImage)
00553         continue;
00554 
00555       // Calculate camera projection derivatives of this point.
00556       TData.GetDerivsUnsafe(mCamera);
00557 
00558       // And check what the PatchFinder (included in TrackerData) makes of the mappoint in this view..
00559       TData.nSearchLevel = TData.Finder.CalcSearchLevelAndWarpMatrix(TData.Point, mse3CamFromWorld, TData.m2CamDerivs);
00560       if(TData.nSearchLevel == -1)
00561         continue;   // a negative search pyramid level indicates an inappropriate warp for this view, so skip.
00562 
00563       // Otherwise, this point is suitable to be searched in the current image! Add to the PVS.
00564       TData.bSearched = false;
00565       TData.bFound = false;
00566       avPVS[TData.nSearchLevel].push_back(&TData);
00567     };
00568 
00569   // Next: A large degree of faffing about and deciding which points are going to be measured!
00570   // First, randomly shuffle the individual levels of the PVS.
00571   for(int i=0; i<LEVELS; i++)
00572     random_shuffle(avPVS[i].begin(), avPVS[i].end());
00573 
00574   // The next two data structs contain the list of points which will next
00575   // be searched for in the image, and then used in pose update.
00576   vector<TrackerData*> vNextToSearch;
00577   vector<TrackerData*> vIterationSet;
00578 
00579   // Tunable parameters to do with the coarse tracking stage:
00580   static gvar3<unsigned int> gvnCoarseMin("Tracker.CoarseMin", TRACKER_COARSE_MIN_DEFAULT, SILENT);   // Min number of large-scale features for coarse stage
00581   static gvar3<unsigned int> gvnCoarseMax("Tracker.CoarseMax", TRACKER_COARSE_MAX_DEFAULT, SILENT);   // Max number of large-scale features for coarse stage
00582   static gvar3<unsigned int> gvnCoarseRange("Tracker.CoarseRange", TRACKER_COARSE_RANGE_DEFAULT, SILENT);       // Pixel search radius for coarse features
00583   static gvar3<int> gvnCoarseSubPixIts("Tracker.CoarseSubPixIts", TRACKER_COARSE_SUBPIXELITS_DEFAULT, SILENT); // Max sub-pixel iterations for coarse features
00584   static gvar3<int> gvnCoarseDisabled("Tracker.DisableCoarse", TRACKER_COARSE_DISABLE_DEFAULT, SILENT);    // Set this to 1 to disable coarse stage (except after recovery)
00585   static gvar3<double> gvdCoarseMinVel("Tracker.CoarseMinVelocity", TRACKER_COARSE_MIN_VELOCITY_DEFAULT, SILENT);  // Speed above which coarse stage is used.
00586 
00587   unsigned int nCoarseMax = *gvnCoarseMax;
00588   unsigned int nCoarseRange = *gvnCoarseRange;
00589 
00590   mbDidCoarse = false;
00591 
00592   // Set of heuristics to check if we should do a coarse tracking stage.
00593   bool bTryCoarse = true;
00594   if(*gvnCoarseDisabled ||
00595      mdMSDScaledVelocityMagnitude < *gvdCoarseMinVel  ||
00596      nCoarseMax == 0)
00597     bTryCoarse = false;
00598   if(mbJustRecoveredSoUseCoarse)
00599     {
00600       bTryCoarse = true;
00601       nCoarseMax *=2;
00602       nCoarseRange *=2;
00603       mbJustRecoveredSoUseCoarse = false;
00604     };
00605 
00606   // If we do want to do a coarse stage, also check that there's enough high-level
00607   // PV map points. We use the lowest-res two pyramid levels (LEVELS-1 and LEVELS-2),
00608   // with preference to LEVELS-1.
00609   if(bTryCoarse && avPVS[LEVELS-1].size() + avPVS[LEVELS-2].size() > *gvnCoarseMin )
00610     {
00611       // Now, fill the vNextToSearch struct with an appropriate number of
00612       // TrackerDatas corresponding to coarse map points! This depends on how many
00613       // there are in different pyramid levels compared to CoarseMin and CoarseMax.
00614 
00615       if(avPVS[LEVELS-1].size() <= nCoarseMax)
00616         { // Fewer than CoarseMax in LEVELS-1? then take all of them, and remove them from the PVS list.
00617           vNextToSearch = avPVS[LEVELS-1];
00618           avPVS[LEVELS-1].clear();
00619         }
00620       else
00621         { // ..otherwise choose nCoarseMax at random, again removing from the PVS list.
00622           for(unsigned int i=0; i<nCoarseMax; i++)
00623             vNextToSearch.push_back(avPVS[LEVELS-1][i]);
00624           avPVS[LEVELS-1].erase(avPVS[LEVELS-1].begin(), avPVS[LEVELS-1].begin() + nCoarseMax);
00625         }
00626 
00627       // If didn't source enough from LEVELS-1, get some from LEVELS-2... same as above.
00628       if(vNextToSearch.size() < nCoarseMax)
00629         {
00630           unsigned int nMoreCoarseNeeded = nCoarseMax - vNextToSearch.size();
00631           if(avPVS[LEVELS-2].size() <= nMoreCoarseNeeded)
00632             {
00633               vNextToSearch = avPVS[LEVELS-2];
00634               avPVS[LEVELS-2].clear();
00635             }
00636           else
00637             {
00638               for(unsigned int i=0; i<nMoreCoarseNeeded; i++)
00639                 vNextToSearch.push_back(avPVS[LEVELS-2][i]);
00640               avPVS[LEVELS-2].erase(avPVS[LEVELS-2].begin(), avPVS[LEVELS-2].begin() + nMoreCoarseNeeded);
00641             }
00642         }
00643       // Now go and attempt to find these points in the image!
00644       unsigned int nFound = SearchForPoints(vNextToSearch, nCoarseRange, *gvnCoarseSubPixIts);
00645       vIterationSet = vNextToSearch;  // Copy over into the to-be-optimised list.
00646       if(nFound >= *gvnCoarseMin)  // Were enough found to do any meaningful optimisation?
00647         {
00648           mbDidCoarse = true;
00649           for(int iter = 0; iter<10; iter++) // If so: do ten Gauss-Newton pose updates iterations.
00650             {
00651               if(iter != 0)
00652                 { // Re-project the points on all but the first iteration.
00653                   for(unsigned int i=0; i<vIterationSet.size(); i++)
00654                     if(vIterationSet[i]->bFound)
00655                       vIterationSet[i]->ProjectAndDerivs(mse3CamFromWorld, mCamera);
00656                 }
00657               for(unsigned int i=0; i<vIterationSet.size(); i++)
00658                 if(vIterationSet[i]->bFound)
00659                   vIterationSet[i]->CalcJacobian();
00660               double dOverrideSigma = 0.0;
00661               // Hack: force the MEstimator to be pretty brutal
00662               // with outliers beyond the fifth iteration.
00663               if(iter > 5)
00664                 dOverrideSigma = 1.0;
00665 
00666               // Calculate and apply the pose update...
00667               Vector<6> v6Update =
00668                 CalcPoseUpdate(vIterationSet, dOverrideSigma);
00669               mse3CamFromWorld = SE3<>::exp(v6Update) * mse3CamFromWorld;
00670             };
00671         }
00672     };
00673 
00674   // So, at this stage, we may or may not have done a coarse tracking stage.
00675   // Now do the fine tracking stage. This needs many more points!
00676 
00677   int nFineRange = 10;  // Pixel search range for the fine stage.
00678   if(mbDidCoarse)       // Can use a tighter search if the coarse stage was already done.
00679     nFineRange = 5;
00680 
00681   // What patches shall we use this time? The high-level ones are quite important,
00682   // so do all of these, with sub-pixel refinement.
00683   {
00684     int l = LEVELS - 1;
00685     for(unsigned int i=0; i<avPVS[l].size(); i++)
00686       avPVS[l][i]->ProjectAndDerivs(mse3CamFromWorld, mCamera);
00687     SearchForPoints(avPVS[l], nFineRange, 8);
00688     for(unsigned int i=0; i<avPVS[l].size(); i++)
00689       vIterationSet.push_back(avPVS[l][i]);  // Again, plonk all searched points onto the (maybe already populate) vIterationSet.
00690   };
00691 
00692   // All the others levels: Initially, put all remaining potentially visible patches onto vNextToSearch.
00693   vNextToSearch.clear();
00694   for(int l=LEVELS - 2; l>=0; l--)
00695     for(unsigned int i=0; i<avPVS[l].size(); i++)
00696       vNextToSearch.push_back(avPVS[l][i]);
00697 
00698   // But we haven't got CPU to track _all_ patches in the map - arbitrarily limit
00699   // ourselves to 1000, and choose these randomly.
00700   static gvar3<int> gvnMaxPatchesPerFrame("Tracker.MaxPatchesPerFrame", TRACKER_MAX_PATCHES_PER_FRAME_DEFAULT, SILENT);
00701   int nFinePatchesToUse = *gvnMaxPatchesPerFrame - vIterationSet.size();
00702   if(nFinePatchesToUse < 0)
00703     nFinePatchesToUse = 0;
00704   if((int) vNextToSearch.size() > nFinePatchesToUse)
00705     {
00706       random_shuffle(vNextToSearch.begin(), vNextToSearch.end());
00707       vNextToSearch.resize(nFinePatchesToUse); // Chop!
00708     };
00709 
00710   // If we did a coarse tracking stage: re-project and find derivs of fine points
00711   if(mbDidCoarse)
00712     for(unsigned int i=0; i<vNextToSearch.size(); i++)
00713       vNextToSearch[i]->ProjectAndDerivs(mse3CamFromWorld, mCamera);
00714 
00715   // Find fine points in image:
00716   SearchForPoints(vNextToSearch, nFineRange, 0);
00717   // And attach them all to the end of the optimisation-set.
00718   for(unsigned int i=0; i<vNextToSearch.size(); i++)
00719     vIterationSet.push_back(vNextToSearch[i]);
00720 
00721   // Again, ten gauss-newton pose update iterations.
00722   Vector<6> v6LastUpdate;
00723   v6LastUpdate = Zeros;
00724   for(int iter = 0; iter<10; iter++)
00725     {
00726       bool bNonLinearIteration; // For a bit of time-saving: don't do full nonlinear
00727                                 // reprojection at every iteration - it really isn't necessary!
00728       if(iter == 0 || iter == 4 || iter == 9)
00729         bNonLinearIteration = true;   // Even this is probably overkill, the reason we do many
00730       else                            // iterations is for M-Estimator convergence rather than
00731         bNonLinearIteration = false;  // linearisation effects.
00732 
00733       if(iter != 0)   // Either way: first iteration doesn't need projection update.
00734         {
00735           if(bNonLinearIteration)
00736             {
00737               for(unsigned int i=0; i<vIterationSet.size(); i++)
00738                 if(vIterationSet[i]->bFound)
00739                   vIterationSet[i]->ProjectAndDerivs(mse3CamFromWorld, mCamera);
00740             }
00741           else
00742             {
00743               for(unsigned int i=0; i<vIterationSet.size(); i++)
00744                 if(vIterationSet[i]->bFound)
00745                   vIterationSet[i]->LinearUpdate(v6LastUpdate);
00746             };
00747         }
00748 
00749       if(bNonLinearIteration)
00750         for(unsigned int i=0; i<vIterationSet.size(); i++)
00751           if(vIterationSet[i]->bFound)
00752             vIterationSet[i]->CalcJacobian();
00753 
00754       // Again, an M-Estimator hack beyond the fifth iteration.
00755       double dOverrideSigma = 0.0;
00756       if(iter > 5)
00757         dOverrideSigma = 16.0;
00758 
00759       // Calculate and update pose; also store update vector for linear iteration updates.
00760       Vector<6> v6Update =
00761         CalcPoseUpdate(vIterationSet, dOverrideSigma, iter==9);
00762       mse3CamFromWorld = SE3<>::exp(v6Update) * mse3CamFromWorld;
00763       v6LastUpdate = v6Update;
00764     };
00765 
00766 
00767   if(mbDraw)
00768     {
00769       glPointSize(6);
00770       glEnable(GL_BLEND);
00771       glEnable(GL_POINT_SMOOTH);
00772       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00773       glBegin(GL_POINTS);
00774       for(vector<TrackerData*>::reverse_iterator it = vIterationSet.rbegin();
00775           it!= vIterationSet.rend();
00776           it++)
00777         {
00778           if(! (*it)->bFound)
00779             continue;
00780           glColor(gavLevelColors[(*it)->nSearchLevel]);
00781           glVertex((*it)->v2Image);
00782         }
00783       glEnd();
00784       glDisable(GL_BLEND);
00785     }
00786   /*
00787   glEnable(GL_BLEND);
00788   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00789         glPixelZoom(1,1);
00790         glViewport(0, 0, 640, 480);
00791           glMatrixMode(GL_PROJECTION);
00792           glLoadIdentity();
00793           glOrtho(0,(double)640, (double) 480, 0, -1.0, 1.0);
00794 
00795         glRasterPos2d(0,0);
00796         glPixelZoom(1,-1);
00797 
00798         unsigned int* mask = new unsigned int[640*480];
00799 
00800         // set to black
00801         for(int i=0;i<640*480;i++)
00802                 mask[i] = 0x000000FF;
00803 
00804         // set features and region to invisible
00805     for(vector<TrackerData*>::reverse_iterator it = vIterationSet.rbegin();
00806           it!= vIterationSet.rend();
00807           it++)
00808         {
00809           if(! (*it)->bFound)
00810             continue;
00811 
00812           int xPos = (*it)->v2Image[0]*2;
00813           int yPos = (*it)->v2Image[1]*2;
00814           int radius = 6*pow(2.0f,(float)(*it)->nSearchLevel);
00815 
00816           for(int x=xPos-radius;x<=xPos+radius;x++)
00817                 for(int y=yPos-radius;y<=yPos+radius;y++)
00818                         if(x+640*y < 640*480 && x+640*y >= 0)
00819                                 mask[x+640*y] = 0x00000000;
00820         }
00821 
00822 
00823         glDrawPixels(640,480,GL_RGBA,GL_UNSIGNED_INT_8_8_8_8,mask);
00824         delete mask;
00825         */
00826 
00827 
00828   // Update the current keyframe with info on what was found in the frame.
00829   // Strictly speaking this is unnecessary to do every frame, it'll only be
00830   // needed if the KF gets added to MapMaker. Do it anyway.
00831   // Export pose to current keyframe:
00832   mCurrentKF.se3CfromW = mse3CamFromWorld;
00833 
00834   // Record successful measurements. Use the KeyFrame-Measurement struct for this.
00835   mCurrentKF.mMeasurements.clear();
00836   for(vector<TrackerData*>::iterator it = vIterationSet.begin();
00837       it!= vIterationSet.end();
00838       it++)
00839     {
00840       if(! (*it)->bFound)
00841         continue;
00842       Measurement m;
00843       m.v2RootPos = (*it)->v2Found;
00844       m.nLevel = (*it)->nSearchLevel;
00845       m.bSubPix = (*it)->bDidSubPix;
00846       m.Source = Measurement::SRC_TRACKER;
00847       mCurrentKF.mMeasurements[& ((*it)->Point)] = m;
00848     }
00849 
00850   // Finally, find the mean scene depth from tracked features
00851   {
00852     double dSum = 0;
00853     double dSumSq = 0;
00854     int nNum = 0;
00855     for(vector<TrackerData*>::iterator it = vIterationSet.begin();
00856         it!= vIterationSet.end();
00857         it++)
00858       if((*it)->bFound)
00859         {
00860           double z = (*it)->v3Cam[2];
00861           dSum+= z;
00862           dSumSq+= z*z;
00863           nNum++;
00864         };
00865     if(nNum > 20)
00866       {
00867         mCurrentKF.dSceneDepthMean = dSum/nNum;
00868         mCurrentKF.dSceneDepthSigma = sqrt((dSumSq / nNum) - (mCurrentKF.dSceneDepthMean) * (mCurrentKF.dSceneDepthMean));
00869       }
00870   }
00871 }
00872 
00873 // Find points in the image. Uses the PatchFiner struct stored in TrackerData
00874 int Tracker::SearchForPoints(vector<TrackerData*> &vTD, int nRange, int nSubPixIts)
00875 {
00876   int nFound = 0;
00877   for(unsigned int i=0; i<vTD.size(); i++)   // for each point..
00878     {
00879       // First, attempt a search at pixel locations which are FAST corners.
00880       // (PatchFinder::FindPatchCoarse)
00881       TrackerData &TD = *vTD[i];
00882       PatchFinder &Finder = TD.Finder;
00883       Finder.MakeTemplateCoarseCont(TD.Point);
00884       if(Finder.TemplateBad())
00885         {
00886           TD.bInImage = TD.bPotentiallyVisible = TD.bFound = false;
00887           continue;
00888         }
00889       manMeasAttempted[Finder.GetLevel()]++;  // Stats for tracking quality assessmenta
00890 
00891       bool bFound =
00892         Finder.FindPatchCoarse(ir(TD.v2Image), mCurrentKF, nRange);
00893       TD.bSearched = true;
00894       if(!bFound)
00895         {
00896           TD.bFound = false;
00897           continue;
00898         }
00899 
00900       TD.bFound = true;
00901       TD.dSqrtInvNoise = (1.0 / Finder.GetLevelScale());
00902 
00903       nFound++;
00904       manMeasFound[Finder.GetLevel()]++;
00905 
00906       // Found the patch in coarse search - are Sub-pixel iterations wanted too?
00907       if(nSubPixIts > 0)
00908         {
00909           TD.bDidSubPix = true;
00910           Finder.MakeSubPixTemplate();
00911           bool bSubPixConverges=Finder.IterateSubPixToConvergence(mCurrentKF, nSubPixIts);
00912           if(!bSubPixConverges)
00913             { // If subpix doesn't converge, the patch location is probably very dubious!
00914               TD.bFound = false;
00915               nFound--;
00916               manMeasFound[Finder.GetLevel()]--;
00917               continue;
00918             }
00919           TD.v2Found = Finder.GetSubPixPos();
00920         }
00921       else
00922         {
00923           TD.v2Found = Finder.GetCoarsePosAsVector();
00924           TD.bDidSubPix = false;
00925         }
00926     }
00927   return nFound;
00928 };
00929 
00930 //Calculate a pose update 6-vector from a bunch of image measurements.
00931 //User-selectable M-Estimator.
00932 //Normally this robustly estimates a sigma-squared for all the measurements
00933 //to reduce outlier influence, but this can be overridden if
00934 //dOverrideSigma is positive. Also, bMarkOutliers set to true
00935 //records any instances of a point being marked an outlier measurement
00936 //by the Tukey MEstimator.
00937 Vector<6> Tracker::CalcPoseUpdate(vector<TrackerData*> vTD, double dOverrideSigma, bool bMarkOutliers)
00938 {
00939   // Which M-estimator are we using?
00940   int nEstimator = 0;
00941   static gvar3<string> gvsEstimator("TrackerMEstimator", TRACKER_M_ESTIMATOR_DEFAULT, SILENT);
00942   if(*gvsEstimator == "Tukey")
00943     nEstimator = 0;
00944   else if(*gvsEstimator == "Cauchy")
00945     nEstimator = 1;
00946   else if(*gvsEstimator == "Huber")
00947     nEstimator = 2;
00948   else
00949     {
00950       cout << "Invalid TrackerMEstimator, choices are Tukey, Cauchy, Huber" << endl;
00951       nEstimator = 0;
00952       *gvsEstimator = "Tukey";
00953     };
00954 
00955   // Find the covariance-scaled reprojection error for each measurement.
00956   // Also, store the square of these quantities for M-Estimator sigma squared estimation.
00957   vector<double> vdErrorSquared;
00958   for(unsigned int f=0; f<vTD.size(); f++)
00959     {
00960       TrackerData &TD = *vTD[f];
00961       if(!TD.bFound)
00962         continue;
00963       TD.v2Error_CovScaled = TD.dSqrtInvNoise* (TD.v2Found - TD.v2Image);
00964       vdErrorSquared.push_back((double)(TD.v2Error_CovScaled * TD.v2Error_CovScaled));
00965     };
00966 
00967   // No valid measurements? Return null update.
00968   if(vdErrorSquared.size() == 0)
00969     return makeVector( 0,0,0,0,0,0);
00970 
00971   // What is the distribution of errors?
00972   double dSigmaSquared;
00973   if(dOverrideSigma > 0)
00974     dSigmaSquared = dOverrideSigma; // Bit of a waste having stored the vector of square errors in this case!
00975   else
00976     {
00977       if (nEstimator == 0)
00978         dSigmaSquared = Tukey::FindSigmaSquared(vdErrorSquared);
00979       else if(nEstimator == 1)
00980         dSigmaSquared = Cauchy::FindSigmaSquared(vdErrorSquared);
00981       else
00982         dSigmaSquared = Huber::FindSigmaSquared(vdErrorSquared);
00983     }
00984 
00985   // The TooN WLSCholesky class handles reweighted least squares.
00986   // It just needs errors and jacobians.
00987   WLS<6> wls;
00988   wls.add_prior(100.0); // Stabilising prior
00989   for(unsigned int f=0; f<vTD.size(); f++)
00990     {
00991       TrackerData &TD = *vTD[f];
00992       if(!TD.bFound)
00993         continue;
00994       Vector<2> &v2 = TD.v2Error_CovScaled;
00995       double dErrorSq = v2 * v2;
00996       double dWeight;
00997 
00998       if(nEstimator == 0)
00999         dWeight= Tukey::Weight(dErrorSq, dSigmaSquared);
01000       else if(nEstimator == 1)
01001         dWeight= Cauchy::Weight(dErrorSq, dSigmaSquared);
01002       else
01003         dWeight= Huber::Weight(dErrorSq, dSigmaSquared);
01004 
01005       // Inlier/outlier accounting, only really works for cut-off estimators such as Tukey.
01006       if(dWeight == 0.0)
01007         {
01008           if(bMarkOutliers)
01009             TD.Point.nMEstimatorOutlierCount++;
01010           continue;
01011         }
01012       else
01013         if(bMarkOutliers)
01014           TD.Point.nMEstimatorInlierCount++;
01015 
01016       Matrix<2,6> &m26Jac = TD.m26Jacobian;
01017       wls.add_mJ(v2[0], (Vector<6>)(TD.dSqrtInvNoise * m26Jac[0]), dWeight); // These two lines are currently
01018       wls.add_mJ(v2[1], (Vector<6>)(TD.dSqrtInvNoise * m26Jac[1]), dWeight); // the slowest bit of poseits
01019     }
01020 
01021   wls.compute();
01022   return wls.get_mu();
01023 }
01024 
01025 
01026 // Just add the current velocity to the current pose.
01027 // N.b. this doesn't actually use time in any way, i.e. it assumes
01028 // a one-frame-per-second camera. Skipped frames etc
01029 // are not handled properly here.
01030 void Tracker::ApplyMotionModel()
01031 {
01032 
01033   mse3StartPos = mse3CamFromWorld;
01034 
01035 
01036   Vector<6> v6Velocity = mv6CameraVelocity;
01037   if(mbUseSBIInit)
01038     {
01039       v6Velocity.slice<3,3>() = mv6SBIRot.slice<3,3>();
01040       v6Velocity[0] = 0.0;
01041       v6Velocity[1] = 0.0;
01042     }
01043   mse3CamFromWorld = SE3<>::exp(v6Velocity) * mse3StartPos;
01044 
01045 
01046   //mse3CamFromWorld = mPredictor.globalToFront;
01047 };
01048 
01049 
01050 // The motion model is entirely the tracker's, and is kept as a decaying
01051 // constant velocity model.
01052 void Tracker::UpdateMotionModel()
01053 {
01054 
01055   SE3<> se3NewFromOld = mse3CamFromWorld * mse3StartPos.inverse();
01056   Vector<6> v6Motion = SE3<>::ln(se3NewFromOld);
01057   Vector<6> v6OldVel = mv6CameraVelocity;
01058 
01059   mv6CameraVelocity = 0.9 * (0.5 * v6Motion + 0.5 * v6OldVel);
01060   mdVelocityMagnitude = sqrt((double)(mv6CameraVelocity * mv6CameraVelocity));
01061 
01062   // Also make an estimate of this which has been scaled by the mean scene depth.
01063   // This is used to decide if we should use a coarse tracking stage.
01064   // We can tolerate more translational vel when far away from scene!
01065   Vector<6> v6 = mv6CameraVelocity;
01066   v6.slice<0,3>() *= 1.0 / mCurrentKF.dSceneDepthMean;
01067   mdMSDScaledVelocityMagnitude = sqrt((double)(v6*v6));
01068 }
01069 
01070 // Time to add a new keyframe? The MapMaker handles most of this.
01071 void Tracker::AddNewKeyFrame()
01072 {
01073   mMapMaker.AddKeyFrame(mCurrentKF);
01074   mnLastKeyFrameDropped = mnFrame;
01075   mnLastKeyFrameDroppedClock = clock();
01076 }
01077 
01078 // Some heuristics to decide if tracking is any good, for this frame.
01079 // This influences decisions to add key-frames, and eventually
01080 // causes the tracker to attempt relocalisation.
01081 void Tracker::AssessTrackingQuality()
01082 {
01083   int nTotalAttempted = 0;
01084   int nTotalFound = 0;
01085   int nLargeAttempted = 0;
01086   int nLargeFound = 0;
01087 
01088   for(int i=0; i<LEVELS; i++)
01089     {
01090       nTotalAttempted += manMeasAttempted[i];
01091       nTotalFound += manMeasFound[i];
01092       if(i>=2) nLargeAttempted += manMeasAttempted[i];
01093       if(i>=2) nLargeFound += manMeasFound[i];
01094     }
01095 
01096   if(nTotalFound == 0 || nTotalAttempted == 0)
01097     mTrackingQuality = BAD;
01098   else
01099     {
01100       double dTotalFracFound = (double) nTotalFound / nTotalAttempted;
01101       double dLargeFracFound;
01102       if(nLargeAttempted > 10)
01103         dLargeFracFound = (double) nLargeFound / nLargeAttempted;
01104       else
01105         dLargeFracFound = dTotalFracFound;
01106 
01107       static gvar3<double> gvdQualityGood("Tracker.TrackingQualityGood", TRACKER_QUALITY_GOOD_DEFAULT, SILENT);
01108       static gvar3<double> gvdQualityLost("Tracker.TrackingQualityLost", TRACKER_QUALITY_LOST_DEFAULT, SILENT);
01109 
01110 
01111       if(dTotalFracFound > *gvdQualityGood)
01112         mTrackingQuality = GOOD;
01113       else if(dLargeFracFound < *gvdQualityLost)
01114         mTrackingQuality = BAD;
01115       else
01116         mTrackingQuality = DODGY;
01117     }
01118 
01119   if(mTrackingQuality == DODGY)
01120     {
01121       // Further heuristics to see if it's actually bad, not just dodgy...
01122       // If the camera pose estimate has run miles away, it's probably bad.
01123       if(mMapMaker.IsDistanceToNearestKeyFrameExcessive(mCurrentKF))
01124         mTrackingQuality = BAD;
01125     }
01126 
01127   if(mTrackingQuality==BAD)
01128     mnLostFrames++;
01129   else
01130     mnLostFrames = 0;
01131 
01132   numPointsFound = nTotalFound;
01133   numPointsAttempted = nTotalAttempted;
01134 }
01135 
01136 string Tracker::GetMessageForUser()
01137 {
01138   return mMessageForUser.str();
01139 }
01140 
01141 void Tracker::CalcSBIRotation()
01142 {
01143   mpSBILastFrame->MakeJacs();
01144   pair<SE2<>, double> result_pair;
01145   result_pair = mpSBIThisFrame->IteratePosRelToTarget(*mpSBILastFrame, 6);
01146   SE3<> se3Adjust = SmallBlurryImage::SE3fromSE2(result_pair.first, mCamera);
01147   mv6SBIRot = se3Adjust.ln();
01148 }
01149 
01150 ImageRef TrackerData::irImageSize;  // Static member of TrackerData lives here
01151 
01152 
01153 
01154 
01155 
01156 
01157 
01158 


uga_tum_ardrone
Author(s):
autogenerated on Sat Jun 8 2019 20:30:11