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       mCurrentKF.mMeasurements[& ((*it)->Point)] = m;
00847     }
00848   
00849   // Finally, find the mean scene depth from tracked features
00850   {
00851     double dSum = 0;
00852     double dSumSq = 0;
00853     int nNum = 0;
00854     for(vector<TrackerData*>::iterator it = vIterationSet.begin();
00855         it!= vIterationSet.end(); 
00856         it++)
00857       if((*it)->bFound)
00858         {
00859           double z = (*it)->v3Cam[2];
00860           dSum+= z;
00861           dSumSq+= z*z;
00862           nNum++;
00863         };
00864     if(nNum > 20)
00865       {
00866         mCurrentKF.dSceneDepthMean = dSum/nNum;
00867         mCurrentKF.dSceneDepthSigma = sqrt((dSumSq / nNum) - (mCurrentKF.dSceneDepthMean) * (mCurrentKF.dSceneDepthMean));
00868       }
00869   }
00870 }
00871 
00872 // Find points in the image. Uses the PatchFiner struct stored in TrackerData
00873 int Tracker::SearchForPoints(vector<TrackerData*> &vTD, int nRange, int nSubPixIts)
00874 {
00875   int nFound = 0;
00876   for(unsigned int i=0; i<vTD.size(); i++)   // for each point..
00877     {
00878       // First, attempt a search at pixel locations which are FAST corners.
00879       // (PatchFinder::FindPatchCoarse)
00880       TrackerData &TD = *vTD[i];
00881       PatchFinder &Finder = TD.Finder;
00882       Finder.MakeTemplateCoarseCont(TD.Point);
00883       if(Finder.TemplateBad())
00884         {
00885           TD.bInImage = TD.bPotentiallyVisible = TD.bFound = false;
00886           continue;
00887         }
00888       manMeasAttempted[Finder.GetLevel()]++;  // Stats for tracking quality assessmenta
00889       
00890       bool bFound = 
00891         Finder.FindPatchCoarse(ir(TD.v2Image), mCurrentKF, nRange);
00892       TD.bSearched = true;
00893       if(!bFound) 
00894         {
00895           TD.bFound = false;
00896           continue;
00897         }
00898       
00899       TD.bFound = true;
00900       TD.dSqrtInvNoise = (1.0 / Finder.GetLevelScale());
00901       
00902       nFound++;
00903       manMeasFound[Finder.GetLevel()]++;
00904       
00905       // Found the patch in coarse search - are Sub-pixel iterations wanted too?
00906       if(nSubPixIts > 0)
00907         {
00908           TD.bDidSubPix = true;
00909           Finder.MakeSubPixTemplate();
00910           bool bSubPixConverges=Finder.IterateSubPixToConvergence(mCurrentKF, nSubPixIts);
00911           if(!bSubPixConverges)
00912             { // If subpix doesn't converge, the patch location is probably very dubious!
00913               TD.bFound = false;
00914               nFound--;
00915               manMeasFound[Finder.GetLevel()]--;
00916               continue;
00917             }
00918           TD.v2Found = Finder.GetSubPixPos();
00919         }
00920       else
00921         {
00922           TD.v2Found = Finder.GetCoarsePosAsVector();
00923           TD.bDidSubPix = false;
00924         }
00925     }
00926   return nFound;
00927 };
00928 
00929 //Calculate a pose update 6-vector from a bunch of image measurements.
00930 //User-selectable M-Estimator.
00931 //Normally this robustly estimates a sigma-squared for all the measurements
00932 //to reduce outlier influence, but this can be overridden if
00933 //dOverrideSigma is positive. Also, bMarkOutliers set to true
00934 //records any instances of a point being marked an outlier measurement
00935 //by the Tukey MEstimator.
00936 Vector<6> Tracker::CalcPoseUpdate(vector<TrackerData*> vTD, double dOverrideSigma, bool bMarkOutliers)
00937 {
00938   // Which M-estimator are we using?
00939   int nEstimator = 0;
00940   static gvar3<string> gvsEstimator("TrackerMEstimator", TRACKER_M_ESTIMATOR_DEFAULT, SILENT);
00941   if(*gvsEstimator == "Tukey")
00942     nEstimator = 0;
00943   else if(*gvsEstimator == "Cauchy")
00944     nEstimator = 1;
00945   else if(*gvsEstimator == "Huber")
00946     nEstimator = 2;
00947   else 
00948     {
00949       cout << "Invalid TrackerMEstimator, choices are Tukey, Cauchy, Huber" << endl;
00950       nEstimator = 0;
00951       *gvsEstimator = "Tukey";
00952     };
00953   
00954   // Find the covariance-scaled reprojection error for each measurement.
00955   // Also, store the square of these quantities for M-Estimator sigma squared estimation.
00956   vector<double> vdErrorSquared;
00957   for(unsigned int f=0; f<vTD.size(); f++)
00958     {
00959       TrackerData &TD = *vTD[f];
00960       if(!TD.bFound)
00961         continue;
00962       TD.v2Error_CovScaled = TD.dSqrtInvNoise* (TD.v2Found - TD.v2Image);
00963       vdErrorSquared.push_back((double)(TD.v2Error_CovScaled * TD.v2Error_CovScaled));
00964     };
00965   
00966   // No valid measurements? Return null update.
00967   if(vdErrorSquared.size() == 0)
00968     return makeVector( 0,0,0,0,0,0);
00969   
00970   // What is the distribution of errors?
00971   double dSigmaSquared;
00972   if(dOverrideSigma > 0)
00973     dSigmaSquared = dOverrideSigma; // Bit of a waste having stored the vector of square errors in this case!
00974   else
00975     {
00976       if (nEstimator == 0)
00977         dSigmaSquared = Tukey::FindSigmaSquared(vdErrorSquared);
00978       else if(nEstimator == 1)
00979         dSigmaSquared = Cauchy::FindSigmaSquared(vdErrorSquared);
00980       else 
00981         dSigmaSquared = Huber::FindSigmaSquared(vdErrorSquared);
00982     }
00983   
00984   // The TooN WLSCholesky class handles reweighted least squares.
00985   // It just needs errors and jacobians.
00986   WLS<6> wls;
00987   wls.add_prior(100.0); // Stabilising prior
00988   for(unsigned int f=0; f<vTD.size(); f++)
00989     {
00990       TrackerData &TD = *vTD[f];
00991       if(!TD.bFound)
00992         continue;
00993       Vector<2> &v2 = TD.v2Error_CovScaled;
00994       double dErrorSq = v2 * v2;
00995       double dWeight;
00996       
00997       if(nEstimator == 0)
00998         dWeight= Tukey::Weight(dErrorSq, dSigmaSquared);
00999       else if(nEstimator == 1)
01000         dWeight= Cauchy::Weight(dErrorSq, dSigmaSquared);
01001       else 
01002         dWeight= Huber::Weight(dErrorSq, dSigmaSquared);
01003       
01004       // Inlier/outlier accounting, only really works for cut-off estimators such as Tukey.
01005       if(dWeight == 0.0)
01006         {
01007           if(bMarkOutliers)
01008             TD.Point.nMEstimatorOutlierCount++;
01009           continue;
01010         }
01011       else
01012         if(bMarkOutliers)
01013           TD.Point.nMEstimatorInlierCount++;
01014       
01015       Matrix<2,6> &m26Jac = TD.m26Jacobian;
01016       wls.add_mJ(v2[0], (Vector<6>)(TD.dSqrtInvNoise * m26Jac[0]), dWeight); // These two lines are currently
01017       wls.add_mJ(v2[1], (Vector<6>)(TD.dSqrtInvNoise * m26Jac[1]), dWeight); // the slowest bit of poseits
01018     }
01019   
01020   wls.compute();
01021   return wls.get_mu();
01022 }
01023 
01024 
01025 // Just add the current velocity to the current pose.
01026 // N.b. this doesn't actually use time in any way, i.e. it assumes
01027 // a one-frame-per-second camera. Skipped frames etc
01028 // are not handled properly here.
01029 void Tracker::ApplyMotionModel()
01030 {
01031 
01032   mse3StartPos = mse3CamFromWorld;
01033         
01034   
01035   Vector<6> v6Velocity = mv6CameraVelocity;
01036   if(mbUseSBIInit)
01037     {
01038       v6Velocity.slice<3,3>() = mv6SBIRot.slice<3,3>();
01039       v6Velocity[0] = 0.0;
01040       v6Velocity[1] = 0.0;
01041     }
01042   mse3CamFromWorld = SE3<>::exp(v6Velocity) * mse3StartPos;
01043   
01044 
01045   //mse3CamFromWorld = mPredictor.globalToFront;
01046 };
01047 
01048 
01049 // The motion model is entirely the tracker's, and is kept as a decaying
01050 // constant velocity model.
01051 void Tracker::UpdateMotionModel()
01052 {
01053 
01054   SE3<> se3NewFromOld = mse3CamFromWorld * mse3StartPos.inverse();
01055   Vector<6> v6Motion = SE3<>::ln(se3NewFromOld);
01056   Vector<6> v6OldVel = mv6CameraVelocity;
01057   
01058   mv6CameraVelocity = 0.9 * (0.5 * v6Motion + 0.5 * v6OldVel);
01059   mdVelocityMagnitude = sqrt((double)(mv6CameraVelocity * mv6CameraVelocity));
01060   
01061   // Also make an estimate of this which has been scaled by the mean scene depth.
01062   // This is used to decide if we should use a coarse tracking stage.
01063   // We can tolerate more translational vel when far away from scene!
01064   Vector<6> v6 = mv6CameraVelocity;
01065   v6.slice<0,3>() *= 1.0 / mCurrentKF.dSceneDepthMean;
01066   mdMSDScaledVelocityMagnitude = sqrt((double)(v6*v6));
01067 }
01068 
01069 // Time to add a new keyframe? The MapMaker handles most of this.
01070 void Tracker::AddNewKeyFrame()
01071 {
01072   mMapMaker.AddKeyFrame(mCurrentKF);
01073   mnLastKeyFrameDropped = mnFrame;
01074   mnLastKeyFrameDroppedClock = clock();
01075 }
01076 
01077 // Some heuristics to decide if tracking is any good, for this frame.
01078 // This influences decisions to add key-frames, and eventually
01079 // causes the tracker to attempt relocalisation.
01080 void Tracker::AssessTrackingQuality()
01081 {
01082   int nTotalAttempted = 0;
01083   int nTotalFound = 0;
01084   int nLargeAttempted = 0;
01085   int nLargeFound = 0;
01086   
01087   for(int i=0; i<LEVELS; i++)
01088     {
01089       nTotalAttempted += manMeasAttempted[i];
01090       nTotalFound += manMeasFound[i];
01091       if(i>=2) nLargeAttempted += manMeasAttempted[i];
01092       if(i>=2) nLargeFound += manMeasFound[i];
01093     }
01094   
01095   if(nTotalFound == 0 || nTotalAttempted == 0)
01096     mTrackingQuality = BAD;
01097   else
01098     {
01099       double dTotalFracFound = (double) nTotalFound / nTotalAttempted;
01100       double dLargeFracFound;
01101       if(nLargeAttempted > 10)
01102         dLargeFracFound = (double) nLargeFound / nLargeAttempted;
01103       else
01104         dLargeFracFound = dTotalFracFound;
01105 
01106       static gvar3<double> gvdQualityGood("Tracker.TrackingQualityGood", TRACKER_QUALITY_GOOD_DEFAULT, SILENT);
01107       static gvar3<double> gvdQualityLost("Tracker.TrackingQualityLost", TRACKER_QUALITY_LOST_DEFAULT, SILENT);
01108       
01109       
01110       if(dTotalFracFound > *gvdQualityGood)
01111         mTrackingQuality = GOOD;
01112       else if(dLargeFracFound < *gvdQualityLost)
01113         mTrackingQuality = BAD;
01114       else
01115         mTrackingQuality = DODGY;
01116     }
01117   
01118   if(mTrackingQuality == DODGY)
01119     {
01120       // Further heuristics to see if it's actually bad, not just dodgy...
01121       // If the camera pose estimate has run miles away, it's probably bad.
01122       if(mMapMaker.IsDistanceToNearestKeyFrameExcessive(mCurrentKF))
01123         mTrackingQuality = BAD;
01124     }
01125   
01126   if(mTrackingQuality==BAD)
01127     mnLostFrames++;
01128   else
01129     mnLostFrames = 0;
01130 
01131   numPointsFound = nTotalFound;
01132   numPointsAttempted = nTotalAttempted;
01133 }
01134 
01135 string Tracker::GetMessageForUser()
01136 {
01137   return mMessageForUser.str();
01138 }
01139 
01140 void Tracker::CalcSBIRotation()
01141 {
01142   mpSBILastFrame->MakeJacs();
01143   pair<SE2<>, double> result_pair;
01144   result_pair = mpSBIThisFrame->IteratePosRelToTarget(*mpSBILastFrame, 6);
01145   SE3<> se3Adjust = SmallBlurryImage::SE3fromSE2(result_pair.first, mCamera);
01146   mv6SBIRot = se3Adjust.ln();
01147 }
01148 
01149 ImageRef TrackerData::irImageSize;  // Static member of TrackerData lives here
01150 
01151 
01152 
01153 
01154 
01155 
01156 
01157 


tum_ardrone
Author(s):
autogenerated on Sat Jun 8 2019 20:27:23