📄 tracker.cc
字号:
// Copyright 2008 Isis Innovation Limited#include "OpenGL.h"#include "Tracker.h"#include "MEstimator.h"#include "ShiTomasi.h"#include "SmallMatrixOpts.h"#include "PatchFinder.h"#include "TrackerData.h"#include <cvd/utility.h>#include <cvd/gl_helpers.h>#include <cvd/fast_corner.h>#include <cvd/vision.h>#include <TooN/wls_cholesky.h>#include <gvars3/instances.h>#include <gvars3/GStringUtil.h>#include <fstream>#include <fcntl.h>using namespace CVD;using namespace std;using namespace GVars3;// The constructor mostly sets up interal reference variables// to the other classes..Tracker::Tracker(ImageRef irVideoSize, const ATANCamera &c, Map &m, MapMaker &mm) : mMap(m), mMapMaker(mm), mCamera(c), mRelocaliser(mMap, mCamera), mirSize(irVideoSize){ mCurrentKF.bFixed = false; GUI.RegisterCommand("Reset", GUICommandCallBack, this); GUI.RegisterCommand("KeyPress", GUICommandCallBack, this); GUI.RegisterCommand("PokeTracker", GUICommandCallBack, this); TrackerData::irImageSize = mirSize; // Most of the initialisation is done in Reset() Reset();}// Resets the tracker, wipes the map.// This is the main Reset-handler-entry-point of the program! Other classes' resets propagate from here.// It's always called in the Tracker's thread, often as a GUI command.void Tracker::Reset(){ mbDidCoarse = false; mbUserPressedSpacebar = false; mTrackingQuality = GOOD; mnLostFrames = 0; mdMSDScaledVelocityMagnitude = 0; mCurrentKF.dSceneDepthMean = 1.0; mCurrentKF.dSceneDepthSigma = 1.0; mnInitialStage = TRAIL_TRACKING_NOT_STARTED; mlTrails.clear(); mCamera.SetImageSize(mirSize); mCurrentKF.mMeasurements.clear(); mnLastKeyFrameDropped = -20; mnFrame=0; Zero(mv6CameraVelocity); mbJustRecoveredSoUseCoarse = false; // Tell the MapMaker to reset itself.. // this may take some time, since the mapmaker thread may have to wait // for an abort-check during calculation, so sleep while waiting. // MapMaker will also clear the map. mMapMaker.RequestReset(); while(!mMapMaker.ResetDone())#ifndef WIN32 usleep(10);#else Sleep(1);#endif}// TrackFrame is called by System.cc with each incoming video frame.// It figures out what state the tracker is in, and calls appropriate internal tracking// functions. bDraw tells the tracker wether it should output any GL graphics// or not (it should not draw, for example, when AR stuff is being shown.)void Tracker::TrackFrame(Image<byte> &imFrame, bool bDraw){ mbDraw = bDraw; mMessageForUser.str(""); // Wipe the user message clean // Take the input video image, and convert it into the tracker's keyframe struct mCurrentKF.mMeasurements.clear(); mCurrentKF.MakeKeyFrame_Lite(imFrame); // From now on we only use the keyframe struct! mnFrame++; if(mbDraw) { glDrawPixels(mCurrentKF.aLevels[0].im); if(GV2.GetInt("Tracker.DrawFASTCorners",0, SILENT)) { glColor3f(1,0,1); glPointSize(1); glBegin(GL_POINTS); for(unsigned int i=0; i<mCurrentKF.aLevels[0].vCorners.size(); i++) glVertex(mCurrentKF.aLevels[0].vCorners[i]); glEnd(); } } // Decide what to do - if there is a map, try to track the map ... if(mMap.IsGood()) { if(mnLostFrames < 3) // .. but only if we're not lost! { ApplyMotionModel(); // TrackMap(); // These three lines do the main tracking work. UpdateMotionModel(); // AssessTrackingQuality(); // Check if we're lost or if tracking is poor. { // Provide some feedback for the user: mMessageForUser << "Tracking Map, quality "; if(mTrackingQuality == GOOD) mMessageForUser << "good."; if(mTrackingQuality == DODGY) mMessageForUser << "poor."; if(mTrackingQuality == BAD) mMessageForUser << "bad."; mMessageForUser << " Found:"; for(int i=0; i<LEVELS; i++) mMessageForUser << " " << manMeasFound[i] << "/" << manMeasAttempted[i]; // mMessageForUser << " Found " << mnMeasFound << " of " << mnMeasAttempted <<". ("; mMessageForUser << " Map: " << mMap.vpPoints.size() << "P, " << mMap.vpKeyFrames.size() << "KF"; } // Heuristics to check if a key-frame should be added to the map: if(mTrackingQuality == GOOD && mMapMaker.NeedNewKeyFrame(mCurrentKF) && mnFrame - mnLastKeyFrameDropped > 20 && mMapMaker.QueueSize() < 3) { mMessageForUser << " Adding key-frame."; AddNewKeyFrame(); }; } else // what if there is a map, but tracking has been lost? { mMessageForUser << "** Attempting recovery **."; if(AttemptRecovery()) { TrackMap(); AssessTrackingQuality(); } } if(mbDraw) RenderGrid(); } else // If there is no map, try to make one. TrackForInitialMap(); // GUI interface while(!mvQueuedCommands.empty()) { GUICommandHandler(mvQueuedCommands.begin()->sCommand, mvQueuedCommands.begin()->sParams); mvQueuedCommands.erase(mvQueuedCommands.begin()); }};// Try to relocalise in case tracking was lost.// Returns success or failure as a bool.// Actually, the SBI relocaliser will almost always return true, even if// it has no idea where it is, so graphics will go a bit // crazy when lost. Could use a tighter SSD threshold and return more false,// but the way it is now gives a snappier response and I prefer it.bool Tracker::AttemptRecovery(){ bool bRelocGood = mRelocaliser.AttemptRecovery(mCurrentKF); if(!bRelocGood) return false; SE3 se3Best = mRelocaliser.BestPose(); mse3CamFromWorld = mse3StartPos = se3Best; Zero(mv6CameraVelocity); mbJustRecoveredSoUseCoarse = true; return true;}// Draw the reference grid to give the user an idea of wether tracking is OK or not.void Tracker::RenderGrid(){ // The colour of the ref grid shows if the coarse stage of tracking was used // (it's turned off when the camera is sitting still to reduce jitter.) if(mbDidCoarse) glColor4f(.0, 0.5, .0, 0.6); else glColor4f(0,0,0,0.6); // The grid is projected manually, i.e. GL receives projected 2D coords to draw. int nHalfCells = 8; int nTot = nHalfCells * 2 + 1; Image<Vector<2> > imVertices(ImageRef(nTot,nTot)); for(int i=0; i<nTot; i++) for(int j=0; j<nTot; j++) { Vector<3> v3; v3[0] = (i - nHalfCells) * 0.1; v3[1] = (j - nHalfCells) * 0.1; v3[2] = 0.0; Vector<3> v3Cam = mse3CamFromWorld * v3; if(v3Cam[2] < 0.001) v3Cam[2] = 0.001; imVertices[i][j] = mCamera.Project(project(v3Cam)); } glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLineWidth(2); for(int i=0; i<nTot; i++) { glBegin(GL_LINE_STRIP); for(int j=0; j<nTot; j++) glVertex(imVertices[i][j]); glEnd(); glBegin(GL_LINE_STRIP); for(int j=0; j<nTot; j++) glVertex(imVertices[j][i]); glEnd(); }; glLineWidth(1); glColor3f(1,0,0);}// GUI interface. Stuff commands onto the back of a queue so the tracker handles// them in its own thread at the end of each frame. Note the charming lack of// any thread safety (no lock on mvQueuedCommands).void Tracker::GUICommandCallBack(void* ptr, string sCommand, string sParams){ Command c; c.sCommand = sCommand; c.sParams = sParams; ((Tracker*) ptr)->mvQueuedCommands.push_back(c);}// This is called in the tracker's own thread.void Tracker::GUICommandHandler(string sCommand, string sParams) // Called by the callback func..{ if(sCommand=="Reset") { Reset(); return; } // KeyPress commands are issued by GLWindow if(sCommand=="KeyPress") { if(sParams == "Space") { mbUserPressedSpacebar = true; } else if(sParams == "r") { Reset(); } else if(sParams == "q" || sParams == "Escape") { GUI.ParseLine("quit"); } return; } if((sCommand=="PokeTracker")) { mbUserPressedSpacebar = true; return; } cout << "! Tracker::GUICommandHandler: unhandled command "<< sCommand << endl; exit(1);}; // Routine for establishing the initial map. This requires two spacebar presses from the user// to define the first two key-frames. Salient points are tracked between the two keyframes// using cheap frame-to-frame tracking (which is very brittle - quick camera motion will// break it.) The salient points are stored in a list of `Trail' data structures.// What action TrackForInitialMap() takes depends on the mnInitialStage enum variable..void Tracker::TrackForInitialMap(){ // MiniPatch tracking threshhold. static gvar3<int> gvnMaxSSD("Tracker.MiniPatchMaxSSD", 100000, SILENT); MiniPatch::mnMaxSSD = *gvnMaxSSD; // What stage of initial tracking are we at? if(mnInitialStage == TRAIL_TRACKING_NOT_STARTED) { if(mbUserPressedSpacebar) // First spacebar = this is the first keyframe { mbUserPressedSpacebar = false; TrailTracking_Start(); mnInitialStage = TRAIL_TRACKING_STARTED; } else mMessageForUser << "Point camera at planar scene and press spacebar to start tracking for initial map." << endl; return; }; if(mnInitialStage == TRAIL_TRACKING_STARTED) { int nGoodTrails = TrailTracking_Advance(); // This call actually tracks the trails if(nGoodTrails < 10) // if most trails have been wiped out, no point continuing. { Reset(); return; } // If the user pressed spacebar here, use trails to run stereo and make the intial map.. if(mbUserPressedSpacebar) { mbUserPressedSpacebar = false; vector<pair<ImageRef, ImageRef> > vMatches; // This is the format the mapmaker wants for the stereo pairs for(list<Trail>::iterator i = mlTrails.begin(); i!=mlTrails.end(); i++) vMatches.push_back(pair<ImageRef, ImageRef>(i->irInitialPos, i->irCurrentPos)); mMapMaker.InitFromStereo(mFirstKF, mCurrentKF, vMatches, mse3CamFromWorld); // This will take some time! mnInitialStage = TRAIL_TRACKING_COMPLETE; } else mMessageForUser << "Translate the camera slowly sideways, and press spacebar again to perform stereo init." << endl; }}// The current frame is to be the first keyframe!void Tracker::TrailTracking_Start()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -