Program Listing for File FabMap.cpp

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

#ifdef HAVE_FABMAP
#include "GlobalMapping/FabMap.h"

#include <fstream>
#include <opencv2/core/core.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include "openfabmap.hpp"

#include "util/settings.h"
#include "DataStructures/KeyFrame.h"

namespace lsd_slam
{


FabMap::FabMap()
{
    valid = false;

    std::string fabmapTrainDataPath = packagePath + "thirdparty/openFabMap/trainingdata/StLuciaShortTraindata.yml";
    std::string vocabPath = packagePath + "thirdparty/openFabMap/trainingdata/StLuciaShortVocabulary.yml";
    std::string chowliutreePath = packagePath + "thirdparty/openFabMap/trainingdata/StLuciaShortTree.yml";

    // Load training data
    cv::FileStorage fsTraining;
    fsTraining.open(fabmapTrainDataPath, cv::FileStorage::READ);
    cv::Mat fabmapTrainData;
    fsTraining["BOWImageDescs"] >> fabmapTrainData;
    if (fabmapTrainData.empty()) {
        std::cerr << fabmapTrainDataPath << ": FabMap Training Data not found" << std::endl;
        return;
    }
    fsTraining.release();

    // Load vocabulary
    cv::FileStorage fsVocabulary;
    fsVocabulary.open(vocabPath, cv::FileStorage::READ);
    cv::Mat vocabulary;
    fsVocabulary["Vocabulary"] >> vocabulary;
    if (vocabulary.empty()) {
        std::cerr << vocabPath << ": Vocabulary not found" << std::endl;
        return;
    }
    fsVocabulary.release();

    //load a chow-liu tree
    cv::FileStorage fsTree;
    fsTree.open(chowliutreePath, cv::FileStorage::READ);
    cv::Mat clTree;
    fsTree["ChowLiuTree"] >> clTree;
    if (clTree.empty()) {
        std::cerr << chowliutreePath << ": Chow-Liu tree not found" << std::endl;
        return;
    }
    fsTree.release();

    // Generate openFabMap object (FabMap2 - needs training bow data!)
    int options = 0;
    options |= of2::FabMap::SAMPLED;
    options |= of2::FabMap::CHOW_LIU;
    fabMap = new of2::FabMap2(clTree, 0.39, 0, options);
    //add the training data for use with the sampling method
    fabMap->addTraining(fabmapTrainData);

//  // Generate openFabMap object (FabMap1 with look up table)
//  int options = 0;
//  options |= of2::FabMap::MEAN_FIELD;
//  options |= of2::FabMap::CHOW_LIU;
//  //fabMap = new of2::FabMap(clTree, 0.39, 0, options);
//  fabMap = new of2::FabMapLUT(clTree, 0.39, 0, options, 3000, 6);
//  //fabMap = new of2::FabMapFBO(clTree, 0.39, 0, options, 3000, 1e-6, 1e-6, 512, 9);

    // Create detector & extractor
    detector = new cv::StarFeatureDetector(32, 10, 18, 18, 20);
    cv::Ptr<cv::DescriptorExtractor> extractor = new cv::SURF(1000, 4, 2, false, true); // new cv::SIFT();

    //use a FLANN matcher to generate bag-of-words representations
    cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("FlannBased"); // alternative: "BruteForce"
    bide = new cv::BOWImgDescriptorExtractor(extractor, matcher);
    bide->setVocabulary(vocabulary);

    printConfusionMatrix = false;
    confusionMat = cv::Mat(0, 0, CV_32F);

    nextImageID = 0;
    valid = true;
}

FabMap::~FabMap()
{
    if (printConfusionMatrix)
    {
        std::ofstream writer((packagePath + "fabMapResult.txt").c_str());
        for(int i = 0; i < confusionMat.rows; i++) {
            for(int j = 0; j < confusionMat.cols; j++) {
                writer << confusionMat.at<float>(i, j) << " ";
            }
            writer << std::endl;
        }
        writer.close();
    }
}



void FabMap::compareAndAdd( const KeyFrame::SharedPtr &keyframe, int* out_newID, int* out_loopID)
{
    // Convert keyframe image data to 3-channel OpenCV Mat (theoretically unneccessary)
    cv::Mat frame;
    cv::Mat keyFrameImage(keyframe->frame()->height(), keyframe->frame()->width(), CV_32F, const_cast<float*>(keyframe->frame()->image()));
    keyFrameImage.convertTo(frame, CV_8UC1);
    //cv::cvtColor(frame, frame, CV_GRAY2RGB);

    // Generate FabMap bag-of-words data (image descriptor)
    cv::Mat bow;
    std::vector<cv::KeyPoint> kpts;
    detector->detect(frame, kpts);
    if (kpts.empty())
    {
        *out_newID = -1;
        *out_loopID = -1;
        return;
    }
    bide->compute(frame, kpts, bow);

    // Run FabMap
    std::vector<of2::IMatch> matches;
    if (nextImageID > 0)
        fabMap->compare(bow, matches);
    fabMap->add(bow);
    *out_newID = nextImageID;
    ++nextImageID;

    if (printConfusionMatrix)
    {
        cv::Mat resizedMat(nextImageID, nextImageID, confusionMat.type(), cv::Scalar(0));
        if (confusionMat.rows > 0)
            confusionMat.copyTo(resizedMat(cv::Rect(cv::Point(0, 0), confusionMat.size())));
        confusionMat = resizedMat;

        for(auto l = matches.begin(); l != matches.end(); ++ l)
        {
            int col = (l->imgIdx < 0) ? (nextImageID-1) : l->imgIdx;
            confusionMat.at<float>(nextImageID-1, col) = l->match;
        }
    }

    const float minLoopProbability = 0.8f;
    float accumulatedProbability = 0;
    const bool debugProbabilites = false;
    if (debugProbabilites)
        printf("FabMap probabilities:");
    for(auto l = matches.begin(); l != matches.end(); ++ l)
    {
        if (debugProbabilites)
            printf(" (%i: %f)", l->imgIdx, l->match);

        if(l->imgIdx < 0)
        {
            // Probability for new place
            accumulatedProbability += l->match;
        }
        else
        {
            // Probability for existing place
            if (l->match >= minLoopProbability)
            {
                *out_loopID = l->imgIdx;
                if (debugProbabilites)
                    printf("\n");
                return;
            }
            accumulatedProbability += l->match;
        }

        if (! debugProbabilites && accumulatedProbability > 1 - minLoopProbability)
            break; // not possible anymore to find a frame with high enough probability
    }
    if (debugProbabilites)
        printf("\n");

    *out_loopID = -1;
    return;
}

bool FabMap::isValid() const
{
    return valid;
}

}

#endif