Program Listing for File TrackableKeyFrameSearch.cpp

Return to documentation for file (lib/GlobalMapping/TrackableKeyFrameSearch.cpp)

#include "GlobalMapping/TrackableKeyFrameSearch.h"


#include "GlobalMapping/KeyFrameGraph.h"
#include "DataStructures/Frame.h"
#include "Tracking/SE3Tracker.h"

namespace lsd_slam
{


TrackableKeyFrameSearch::TrackableKeyFrameSearch( const std::shared_ptr<KeyFrameGraph> &graph )
: graph(graph),
    tracker( new SE3Tracker( Conf().slamImageSize ) )
{

}

TrackableKeyFrameSearch::~TrackableKeyFrameSearch()
{
    ;
}




std::vector<TrackableKFStruct> TrackableKeyFrameSearch::findEuclideanOverlapFrames(const KeyFrame::SharedPtr &keyframe, float distanceTH, float angleTH, bool checkBothScales)
{
    float fowX = 2 * atanf(float(Conf().slamImageSize.width) * keyframe->frame()->fxi() / 2.0f );
    float fowY = 2 * atanf(float(Conf().slamImageSize.height) * keyframe->frame()->fyi() / 2.0f );

    LOGF_IF(INFO, Conf().print.relocalizationInfo,
                    "Relocalization Values: fowX %f, fowY %f\n", fowX, fowY);

    // basically the maximal angle-difference in viewing direction is angleTH*(average FoV).
    // e.g. if the FoV is 130°, then it is angleTH*130°.
    float cosAngleTH = cosf(angleTH*0.5f*(fowX + fowY));


    Eigen::Vector3d pos = keyframe->frame()->getCamToWorld().translation();
    Eigen::Vector3d viewingDir = keyframe->frame()->getCamToWorld().rotationMatrix().rightCols<1>();

    std::vector<TrackableKFStruct> potentialReferenceFrames;

    float distFacReciprocal = 1;
    if(checkBothScales)
        distFacReciprocal = keyframe->frame()->meanIdepth / keyframe->frame()->getCamToWorld().scale();

    // for each frame, calculate the rough score, consisting of pose, scale and angle overlap.
    graph->keyframesAllMutex.lock_shared();
    for(unsigned int i=0;i<graph->keyframesAll.size();i++)
    {
        Eigen::Vector3d otherPos = graph->keyframesAll[i]->frame()->getCamToWorld().translation();

        // get distance between the frames, scaled to fit the potential reference frame.
        float distFac = graph->keyframesAll[i]->frame()->meanIdepth / graph->keyframesAll[i]->frame()->getCamToWorld().scale();
        if(checkBothScales && distFacReciprocal < distFac) distFac = distFacReciprocal;
        Eigen::Vector3d dist = (pos - otherPos) * distFac;
        float dNorm2 = dist.dot(dist);
        if(dNorm2 > distanceTH) continue;

        Eigen::Vector3d otherViewingDir = graph->keyframesAll[i]->frame()->getCamToWorld().rotationMatrix().rightCols<1>();
        float dirDotProd = otherViewingDir.dot(viewingDir);
        if(dirDotProd < cosAngleTH) continue;

        potentialReferenceFrames.push_back(TrackableKFStruct());
        potentialReferenceFrames.back().keyframe = graph->keyframesAll[i];
        potentialReferenceFrames.back().refToFrame = se3FromSim3(graph->keyframesAll[i]->frame()->getCamToWorld().inverse() * keyframe->frame()->getCamToWorld()).inverse();
        potentialReferenceFrames.back().dist = dNorm2;
        potentialReferenceFrames.back().angle = dirDotProd;
    }
    graph->keyframesAllMutex.unlock_shared();

    return potentialReferenceFrames;
}




KeyFrame::SharedPtr TrackableKeyFrameSearch::findRePositionCandidate( const KeyFrame::SharedPtr &keyframe, float maxScore)
{
    std::vector<TrackableKFStruct> potentialReferenceFrames = findEuclideanOverlapFrames(keyframe, maxScore / (KFDistWeight*KFDistWeight), 0.75);

    float bestScore = maxScore;
    float bestDist, bestUsage;
    float bestPoseDiscrepancy = 0;
    KeyFrame::SharedPtr bestFrame(nullptr);
    SE3 bestRefToFrame = SE3();
    SE3 bestRefToFrame_tracked = SE3();

    int checkedSecondary = 0;
    for(unsigned int i=0;i<potentialReferenceFrames.size();i++)
    {
        if(keyframe->frame()->isTrackingParent( potentialReferenceFrames[i].keyframe->frame() ) )
            continue;

        if(potentialReferenceFrames[i].keyframe->frame()->idxInKeyframes < INITIALIZATION_PHASE_COUNT)
            continue;

        {
            Timer time;
            tracker->checkPermaRefOverlap(potentialReferenceFrames[i].keyframe, potentialReferenceFrames[i].refToFrame);
            trackPermaRef.update( time );
        }

        float score = getRefFrameScore(potentialReferenceFrames[i].dist, tracker->pointUsage);

        if(score < maxScore)
        {
            SE3 RefToFrame_tracked = tracker->trackFrameOnPermaref(potentialReferenceFrames[i].keyframe, keyframe->frame(), potentialReferenceFrames[i].refToFrame);
            Sophus::Vector3d dist = RefToFrame_tracked.translation() * potentialReferenceFrames[i].keyframe->frame()->meanIdepth;

            float newScore = getRefFrameScore(dist.dot(dist), tracker->pointUsage);
            float poseDiscrepancy = (potentialReferenceFrames[i].refToFrame * RefToFrame_tracked.inverse()).log().norm();
            float goodVal = tracker->pointUsage * tracker->lastGoodCount() / (tracker->lastGoodCount()+tracker->lastBadCount());
            checkedSecondary++;

            if(tracker->trackingWasGood && goodVal > relocalizationTH && newScore < bestScore && poseDiscrepancy < 0.2)
            {
                bestPoseDiscrepancy = poseDiscrepancy;
                bestScore = score;
                bestFrame = potentialReferenceFrames[i].keyframe;
                bestRefToFrame = potentialReferenceFrames[i].refToFrame;
                bestRefToFrame_tracked = RefToFrame_tracked;
                bestDist = dist.dot(dist);
                bestUsage = tracker->pointUsage;
            }
        }
    }

    if(bestFrame != 0)
    {
        if(Conf().print.relocalizationInfo)
            printf("FindReferences for %d: Checked %d (%d). dist %.3f + usage %.3f = %.3f. pose discrepancy %.2f. TAKE %d!\n",
                    (int)keyframe->id(), (int)potentialReferenceFrames.size(), checkedSecondary,
                    bestDist, bestUsage, bestScore,
                    bestPoseDiscrepancy, bestFrame->id());
        return bestFrame;
    }
    else
    {
        if(Conf().print.relocalizationInfo)
            printf("FindReferences for %d: Checked %d (%d), bestScore %.2f. MAKE NEW\n",
                    (int)keyframe->id(), (int)potentialReferenceFrames.size(), checkedSecondary, bestScore);
        return 0;
    }
}

std::unordered_set<KeyFrame::SharedPtr> TrackableKeyFrameSearch::findCandidates(const KeyFrame::SharedPtr &keyframe, KeyFrame::SharedPtr &fabMapResult_out, bool includeFABMAP, bool closenessTH)
{
    std::unordered_set<KeyFrame::SharedPtr> results;

    // Add all candidates that are similar in an euclidean sense.
    std::vector<TrackableKFStruct> potentialReferenceFrames = findEuclideanOverlapFrames(keyframe, closenessTH * 15 / (KFDistWeight*KFDistWeight), 1.0 - 0.25 * closenessTH, true);
    for(unsigned int i=0;i<potentialReferenceFrames.size();i++)
        results.insert(potentialReferenceFrames[i].keyframe);

    int appearanceBased = 0;
    fabMapResult_out = 0;
    if(includeFABMAP)
    {
        // Add Appearance-based Candidate, and all it's neighbours.
        fabMapResult_out = findAppearanceBasedCandidate(keyframe);
        if(fabMapResult_out != nullptr)
        {
            results.insert(fabMapResult_out);
            results.insert(fabMapResult_out->neighbors.begin(), fabMapResult_out->neighbors.end());
            appearanceBased = 1 + fabMapResult_out->neighbors.size();
        }
    }

    LOGF_IF(DEBUG, Conf().print.constraintSearchInfo, "Early LoopClosure-Candidates for %d: %d euclidean, %d appearance-based, %d total\n",
                (int)keyframe->id(), (int)potentialReferenceFrames.size(), appearanceBased, (int)results.size());

    return results;
}

KeyFrame::SharedPtr TrackableKeyFrameSearch::findAppearanceBasedCandidate( const KeyFrame::SharedPtr &keyframe)
{
#ifdef HAVE_FABMAP
    if(!useFabMap) return nullptr;


    if (! fabMap.isValid())
    {
        printf("Error: called findAppearanceBasedCandidate(), but FabMap instance is not valid!\n");
        return nullptr;
    }


    int newID, loopID;
    fabMap.compareAndAdd(keyframe, &newID, &loopID);
    if (newID < 0)
        return nullptr;

    fabmapIDToKeyframe.insert(std::make_pair(newID, keyframe));
    if (loopID >= 0)
        return fabmapIDToKeyframe.at(loopID);
    else
        return nullptr;
#else
    LOG_IF(WARNING, useFabMap) << "Warning: Compiled without FabMap, but useFabMap is enabled... ignoring.";
    return nullptr;
#endif
}


}