📄 mapmaker.cc
字号:
// Copyright 2008 Isis Innovation Limited#include "MapMaker.h"#include "MapPoint.h"#include "Bundle.h"#include "PatchFinder.h"#include "SmallMatrixOpts.h"#include "HomographyInit.h"#include <cvd/vector_image_ref.h>#include <cvd/vision.h>#include <cvd/image_interpolate.h>#include <TooN/SVD.h>#include <TooN/SymEigen.h>#include <gvars3/instances.h>#include <fstream>#ifdef WIN32#define WIN32_LEAN_AND_MEAN#include <windows.h>#endifusing namespace CVD;using namespace std;using namespace GVars3;// Constructor sets up internal reference variable to Map.// Most of the intialisation is done by Reset()..MapMaker::MapMaker(Map& m, const ATANCamera &cam) : mMap(m), mCamera(cam){ mbResetRequested = false; Reset(); start(); // This CVD::thread func starts the map-maker thread with function run() GUI.RegisterCommand("SaveMap", GUICommandCallBack, this);};void MapMaker::Reset(){ // This is only called from within the mapmaker thread... mMap.Reset(); mvFailureQueue.clear(); while(!mqNewQueue.empty()) mqNewQueue.pop(); mMap.vpKeyFrames.clear(); // TODO: actually erase old keyframes mvpKeyFrameQueue.clear(); // TODO: actually erase old keyframes mbBundleRunning = false; mbBundleConverged_Full = true; mbBundleConverged_Recent = true; mbResetDone = true; mbResetRequested = false; mbBundleAbortRequested = false; mdWiggleScale = GV2.GetDouble("MapMaker.WiggleScale", 0.1, SILENT); // Default to 10cm between first two key-frames}// CHECK_RESET is a handy macro which makes the mapmaker thread stop// what it's doing and reset, if required.#define CHECK_RESET if(mbResetRequested) {Reset(); continue;};void MapMaker::run(){
#ifdef WIN32
// For some reason, I get tracker thread starvation on Win32 when
// adding key-frames. Perhaps this will help:
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
#endif
while(!shouldStop()) // ShouldStop is a CVD::Thread func which return true if the thread is told to exit. { CHECK_RESET; sleep(5); // Sleep not really necessary, especially if mapmaker is busy CHECK_RESET; // Handle any GUI commands encountered.. while(!mvQueuedCommands.empty()) { GUICommandHandler(mvQueuedCommands.begin()->sCommand, mvQueuedCommands.begin()->sParams); mvQueuedCommands.erase(mvQueuedCommands.begin()); } if(!mMap.IsGood()) // Nothing to do if there is no map yet! continue; // From here on, mapmaker does various map-maintenance jobs in a certain priority // Hierarchy. For example, if there's a new key-frame to be added (QueueSize() is >0) // then that takes high priority. CHECK_RESET; // Should we run local bundle adjustment? if(!mbBundleConverged_Recent && QueueSize() == 0) BundleAdjustRecent(); CHECK_RESET; // Are there any newly-made map points which need more measurements from older key-frames? if(mbBundleConverged_Recent && QueueSize() == 0) ReFindNewlyMade(); CHECK_RESET; // Run global bundle adjustment? if(mbBundleConverged_Recent && !mbBundleConverged_Full && QueueSize() == 0) BundleAdjustAll(); CHECK_RESET; // Very low priorty: re-find measurements marked as outliers if(mbBundleConverged_Recent && mbBundleConverged_Full && rand()%20 == 0 && QueueSize() == 0) ReFindFromFailureQueue(); CHECK_RESET; HandleBadPoints(); CHECK_RESET; // Any new key-frames to be added? if(QueueSize() > 0) AddKeyFrameFromTopOfQueue(); // Integrate into map data struct, and process }}// Tracker calls this to demand a resetvoid MapMaker::RequestReset(){ mbResetDone = false; mbResetRequested = true;}bool MapMaker::ResetDone(){ return mbResetDone;}// HandleBadPoints() Does some heuristic checks on all points in the map to see if // they should be flagged as bad, based on tracker feedback.void MapMaker::HandleBadPoints(){ // Did the tracker see this point as an outlier more often than as an inlier? for(unsigned int i=0; i<mMap.vpPoints.size(); i++) { MapPoint &p = *mMap.vpPoints[i]; if(p.nMEstimatorOutlierCount > 20 && p.nMEstimatorOutlierCount > p.nMEstimatorInlierCount) p.bBad = true; } // All points marked as bad will be erased - erase all records of them // from keyframes in which they might have been measured. for(unsigned int i=0; i<mMap.vpPoints.size(); i++) if(mMap.vpPoints[i]->bBad) { MapPoint *p = mMap.vpPoints[i]; for(unsigned int j=0; j<mMap.vpKeyFrames.size(); j++) { KeyFrame &k = *mMap.vpKeyFrames[j]; if(k.mMeasurements.count(p)) k.mMeasurements.erase(p); } } // Move bad points to the trash list. mMap.MoveBadPointsToTrash();}MapMaker::~MapMaker(){ mbBundleAbortRequested = true; stop(); // makes shouldStop() return true cout << "Waiting for mapmaker to die.." << endl; join(); cout << " .. mapmaker has died." << endl;}// Finds 3d coords of point in reference frame B from two z=1 plane projectionsVector<3> MapMaker::ReprojectPoint(SE3 se3AfromB, const Vector<2> &v2A, const Vector<2> &v2B){ Matrix<3,4> PDash; PDash.slice<0,0,3,3>() = se3AfromB.get_rotation().get_matrix(); PDash.slice<0,3,3,1>() = se3AfromB.get_translation().as_col(); Matrix<4> A; A[0][0] = -1.0; A[0][1] = 0.0; A[0][2] = v2B[0]; A[0][3] = 0.0; A[1][0] = 0.0; A[1][1] = -1.0; A[1][2] = v2B[1]; A[1][3] = 0.0; A[2] = v2A[0] * PDash[2] - PDash[0]; A[3] = v2A[1] * PDash[2] - PDash[1]; HV_SVD<4,4,Vertical> svd(A); Vector<4> v4Smallest = svd.get_VT()[3]; if(v4Smallest[3] == 0.0) v4Smallest[3] = 0.00001; return project(v4Smallest);}// InitFromStereo() generates the initial match from two keyframes// and a vector of image correspondences. Uses the bool MapMaker::InitFromStereo(KeyFrame &kF, KeyFrame &kS, vector<pair<ImageRef, ImageRef> > &vTrailMatches, SE3 &se3TrackerPose){ mCamera.SetImageSize(kF.aLevels[0].im.size()); vector<HomographyMatch> vMatches; for(unsigned int i=0; i<vTrailMatches.size(); i++) { HomographyMatch m; m.v2CamPlaneFirst = mCamera.UnProject(vTrailMatches[i].first); m.v2CamPlaneSecond = mCamera.UnProject(vTrailMatches[i].second); m.m2PixelProjectionJac = mCamera.GetProjectionDerivs(); vMatches.push_back(m); } SE3 se3; bool bGood; HomographyInit HomographyInit; bGood = HomographyInit.Compute(vMatches, 5.0, se3); if(!bGood) { cout << " Could not init from stereo pair, try again." << endl; return false; } KeyFrame *pkFirst = new KeyFrame(); KeyFrame *pkSecond = new KeyFrame(); *pkFirst = kF; *pkSecond = kS; pkFirst->bFixed = true; pkFirst->se3CfromW = SE3(); pkSecond->bFixed = false; pkSecond->se3CfromW = se3; double dTransMagn = pkSecond->se3CfromW.get_translation() * pkSecond->se3CfromW.get_translation(); if(dTransMagn == 0) { cout << " Estimated zero baseline from stereo pair, try again." << endl; return false; } else { // change the scale of the map so the second camera is wiggleScale away from the first double dInv = mdWiggleScale / dTransMagn; ApplyGlobalScaleToMap(dInv); } // Construct map from the stereo matches. PatchFinder finder; for(unsigned int i=0; i<vMatches.size(); i++) { MapPoint *p = new MapPoint(); // Patch source stuff: p->pPatchSourceKF = pkFirst; p->nSourceLevel = 0; p->v3Normal_NC = (make_Vector, 0,0,-1); p->irCenter = vTrailMatches[i].first; p->v3Center_NC = unproject(mCamera.UnProject(p->irCenter)); p->v3OneDownFromCenter_NC = unproject(mCamera.UnProject(p->irCenter + ImageRef(0,1))); p->v3OneRightFromCenter_NC = unproject(mCamera.UnProject(p->irCenter + ImageRef(1,0))); normalize(p->v3Center_NC); normalize(p->v3OneDownFromCenter_NC); normalize(p->v3OneRightFromCenter_NC); p->RefreshPixelVectors(); // Do sub-pixel alignment on the second image finder.MakeTemplateCoarseNoWarp(*p); finder.MakeSubPixTemplate(); finder.SetSubPixPos(vec(vTrailMatches[i].second)); bool bGood = finder.IterateSubPixToConvergence(*pkSecond,10); if(!bGood) { delete p; continue; } // Triangulate point: Vector<2> v2SecondPos = finder.GetSubPixPos(); p->v3WorldPos = ReprojectPoint(se3, mCamera.UnProject(v2SecondPos), vMatches[i].v2CamPlaneFirst); if(p->v3WorldPos[2] < 0.0) { delete p; continue; } // Not behind map? Good, then add to map. p->pMMData = new MapMakerData(); mMap.vpPoints.push_back(p); // Construct first two measurements and insert into relevant DBs: Measurement mFirst; mFirst.nLevel = 0; mFirst.Source = Measurement::SRC_ROOT; mFirst.v2RootPos = vec(vTrailMatches[i].first); mFirst.bSubPix = true; pkFirst->mMeasurements[p] = mFirst; p->pMMData->sMeasurementKFs.insert(pkFirst); Measurement mSecond; mSecond.nLevel = 0; mSecond.Source = Measurement::SRC_TRAIL; mSecond.v2RootPos = finder.GetSubPixPos(); mSecond.bSubPix = true; pkSecond->mMeasurements[p] = mSecond; p->pMMData->sMeasurementKFs.insert(pkSecond); } pkFirst->dSceneDepthMean = 1.0; // Hacks.. pkSecond->dSceneDepthMean = 1.0; pkFirst->dSceneDepthSigma = 1.0; pkSecond->dSceneDepthSigma = 1.0; mMap.vpKeyFrames.push_back(pkFirst); mMap.vpKeyFrames.push_back(pkSecond); pkFirst->MakeKeyFrame_Rest(); pkSecond->MakeKeyFrame_Rest(); for(int i=0; i<5; i++) BundleAdjustAll(); AddSomeMapPoints(0); AddSomeMapPoints(3); AddSomeMapPoints(1); AddSomeMapPoints(2); mbBundleConverged_Full = false; mbBundleConverged_Recent = false; while(!mbBundleConverged_Full) { BundleAdjustAll(); if(mbResetRequested) return false; } // Rotate and translate the map so the dominant plane is at z=0: ApplyGlobalTransformationToMap(CalcPlaneAligner()); mMap.bGood = true; se3TrackerPose = pkSecond->se3CfromW; cout << " MapMaker: made initial map with " << mMap.vpPoints.size() << " points." << endl; return true; }// ThinCandidates() Thins out a key-frame's candidate list.// Candidates are those salient corners where the mapmaker will attempt // to make a new map point by epipolar search. We don't want to make new points// where there are already existing map points, this routine erases such candidates.// Operates on a single level of a keyframe.void MapMaker::ThinCandidates(KeyFrame &k, int nLevel){ vector<Candidate> &vCSrc = k.aLevels[nLevel].vCandidates; vector<Candidate> vCGood; vector<ImageRef> irBusyLevelPos; // Make a list of `busy' image locations, which already have features at the same level // or at one level higher. for(meas_it it = k.mMeasurements.begin(); it!=k.mMeasurements.end(); it++) { if(!(it->second.nLevel == nLevel || it->second.nLevel == nLevel + 1)) continue; irBusyLevelPos.push_back(ir_rounded(it->second.v2RootPos / LevelScale(nLevel))); } // Only keep those candidates further than 10 pixels away from busy positions. unsigned int nMinMagSquared = 10*10; for(unsigned int i=0; i<vCSrc.size(); i++) { ImageRef irC = vCSrc[i].irLevelPos; bool bGood = true; for(unsigned int j=0; j<irBusyLevelPos.size(); j++) { ImageRef irB = irBusyLevelPos[j]; if((irB - irC).mag_squared() < nMinMagSquared) { bGood = false; break; } } if(bGood) vCGood.push_back(vCSrc[i]); } vCSrc = vCGood;}// Adds map points by epipolar search to the last-added key-frame, at a single// specified pyramid level. Does epipolar search in the target keyframe as closest by// the ClosestKeyFrame function.void MapMaker::AddSomeMapPoints(int nLevel){ KeyFrame &kSrc = *(mMap.vpKeyFrames[mMap.vpKeyFrames.size() - 1]); // The new keyframe KeyFrame &kTarget = *(ClosestKeyFrame(kSrc)); Level &l = kSrc.aLevels[nLevel]; ThinCandidates(kSrc, nLevel); for(unsigned int i = 0; i<l.vCandidates.size(); i++) AddPointEpipolar(kSrc, kTarget, nLevel, i);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -