Program Listing for File SlamSystem.cpp

Return to documentation for file (lib/SlamSystem.cpp)

#include <memory>

#include "SlamSystem.h"

#include <boost/thread/shared_lock_guard.hpp>

#include <g3log/g3log.hpp>

#ifdef ANDROID
#include <android/log.h>
#endif

#include <opencv2/opencv.hpp>

#include "DataStructures/Frame.h"
#include "DataStructures/FrameMemory.h"
#include "GlobalMapping/KeyFrameGraph.h"
#include "GlobalMapping/TrackableKeyFrameSearch.h"
#include "util/globalFuncs.h"

#include "SlamSystem/ConstraintSearchThread.h"
#include "SlamSystem/MappingThread.h"
#include "SlamSystem/OptimizationThread.h"

using namespace lsd_slam;

SlamSystem::SlamSystem( )
  : _perf(),
    _outputWrappers( ),
    _finalized(),
    _initialized( false ),
  //    _keyFrames(),
    _keyFrameGraph( new KeyFrameGraph ),
    _trackableKeyFrameSearch( new TrackableKeyFrameSearch( _keyFrameGraph ) )
{

    // Because some of these rely on Conf(), need to explicitly call after
    // static initialization.  Is this true?
    const bool threaded = Conf().runRealTime;
    _optThread.reset( new OptimizationThread( *this, threaded ) );
    _mapThread.reset( new MappingThread( *this, threaded ) );
    _constraintThread.reset( new ConstraintSearchThread( *this, threaded ) );
    _trackingThread.reset( new TrackingThread( *this, threaded ) );

    timeLastUpdate.start();
}

SlamSystem::~SlamSystem() {
  // make sure no-one is waiting for something.
  LOG(INFO) << "... waiting for all threads to exit";

    _mapThread.reset();
    _constraintThread.reset();
    _optThread.reset();
    _trackingThread.reset();
    LOG(INFO) << "DONE waiting for all threads to exit";

    FrameMemory::getInstance().releaseBuffers();
}


SlamSystem *SlamSystem::fullReset( void )
{
    SlamSystem *newSystem = new SlamSystem( );
    for( auto &wrapper : _outputWrappers ) { newSystem->addOutputWrapper( wrapper ); }
    return newSystem;
}

void SlamSystem::finalize()
{
    LOG(INFO) << "Finalizing Graph... adding final constraints!!";

    // Run this in the foreground
    _constraintThread->doFullReConstraintSearch();
    _constraintThread->fullReConstraintSearchComplete.wait();

    LOG(INFO) << "Finalizing Graph... optimizing!!";

    // This happens in the foreground
    // This will kick off a final map publication with the newly optimized offsets (also in foreground)
    _optThread->doFinalOptimization();

    _optThread->finalOptimizationComplete.wait();
    _mapThread->optimizationUpdateMerged.wait();

    LOG(INFO) << "Done Finalizing Graph.!!";
    _finalized.notify();

  LOG(INFO) << "Done Finalizing Graph.!!";
  _finalized.notify();
}

//std::shared_ptr<KeyFrame> &SlamSystem::currentKeyFrame()


// Thin wrapper which turns a bare cv::Mat image into an ImageSe
void SlamSystem::nextImage( unsigned int id, const cv::Mat &img, const libvideoio::Camera &cam )
{
    nextImageSet( std::make_shared<ImageSet>(id, img, cam) );
}


void SlamSystem::nextImageSet( const std::shared_ptr<ImageSet> &set )
{
     if( !_initialized ) {
        _mapThread->createFirstKeyFrame( set->refFrame() );
        _initialized = true;
        return;
    }

    _trackingThread->doTrackSet( set );

    logPerformanceData();
}

//=== Keyframe maintenance functions ====

// void SlamSystem::addKeyFrame( const KeyFrame::SharedPtr &keyframe )
// {
//  _keyFrames.push_back( keyframe );
//
//
//  keyFrameGraph()->idToKeyFrame.insert(std::make_pair(keyframe->id(), keyframe));
// }

// void SlamSystem::changeKeyframe( const Frame::SharedPtr &candidate, bool noCreate, bool force, float maxScore)
// {
//  Frame::SharedPtr newReferenceKF(nullptr);
//
//  if( Conf().doKFReActivation && Conf().SLAMEnabled )
//  {
//      Timer timer;
//      newReferenceKF = trackableKeyFrameSearch()->findRePositionCandidate( candidate, maxScore );
//      _perf.findReferences.update( timer );
//  }
//
//  if(newReferenceKF != 0) {
//      LOG(INFO) << "Reloading existing key frame " << newReferenceKF->id();
//      loadNewCurrentKeyframe(newReferenceKF);
//  } else {
//      if(force)
//      {
//          if(noCreate)
//          {
//              LOG(INFO) << "mapping is disabled & moved outside of known map. Starting Relocalizer!";
//              trackingThread->setTrackingIsBad();
//              //nextRelocIdx = -1; /// What does this do?
//          }
//          else
//          {
//              createNewCurrentKeyframe( candidate );
//          }
//      }
//  }
//  // createNewKeyFrame = false;
// }
//
// void SlamSystem::loadNewCurrentKeyframe( const Frame::SharedPtr &keyframeToLoad)
// {
//  depthMap()->activateExistingKF(keyframeToLoad);
//
//  LOG_IF(DEBUG, Conf().print.regularizeStatistics ) << "re-activate frame " << keyframeToLoad->id() << "!";
//
//  // Not entirely sure why they're doing this lookup...
//  //_currentKeyFrame = keyFrameGraph()->idToKeyFrame.find(keyframeToLoad->id())->second;
//  //currentKeyFrame()->depthHasBeenUpdatedFlag = false;
// }
//
//
// void SlamSystem::createNewCurrentKeyframe( const Frame::SharedPtr &newKeyframeCandidate)
// {
//  LOG_IF(INFO, Conf().print.threadingInfo) << "CREATE NEW KF " << newKeyframeCandidate->id() << ", replacing " << currentKeyFrame()->id();
//
//  if( Conf().SLAMEnabled)
//  {
//      boost::shared_lock_guard< boost::shared_mutex > lock( keyFrameGraph()->idToKeyFrameMutex );
//      keyFrameGraph()->idToKeyFrame.insert(std::make_pair(newKeyframeCandidate->id(), newKeyframeCandidate));
//  }
//
//  // propagate & make new.
//  depthMap()->createKeyFrame(newKeyframeCandidate);
// }

//===== Debugging output functions =====


void SlamSystem::logPerformanceData()
{
    float sPassed = timeLastUpdate.reset();
    if(sPassed > 1.0f)
    {

        // LOGF(DEBUG, "Mapping: %3.1fms (%.1fHz); Track: %3.1fms (%.1fHz); Create: %3.1fms (%.1fHz); FindRef: %3.1fms (%.1fHz); PermaTrk: %3.1fms (%.1fHz); Opt: %3.1fms (%.1fHz); FindConst: %3.1fms (%.1fHz);\n",
        //          depthMap()->perf().update.ms(), depthMap()->perf().update.rate(),
        //          _trackingThread->perf().track.ms(),  _trackingThread->perf().track.rate(),
        //          depthMap()->perf().create.ms()+depthMap()->perf().finalize.ms(), depthMap()->perf().create.rate(),
        //          _perf.findReferences.ms(), _perf.findReferences.rate(),
        //          0.0, 0.0,
        //          //trackableKeyFrameSearch != 0 ? trackableKeyFrameSearch->trackPermaRef.ms() : 0, trackableKeyFrameSearch != 0 ? trackableKeyFrameSearch->trackPermaRef.rate() : 0,
        //          _optThread->perf.ms(), _optThread->perf.rate(),
        //          _constraintThread->perf().findConstraint.ms(), _constraintThread->perf().findConstraint.rate() );
        //
        // depthMap()->logPerformanceData();

    }

}

void SlamSystem::updateDisplayDepthMap()
{
    if( !Conf().displayDepthMap ) return;

    const double scale = (bool)currentKeyFrame() ? currentKeyFrame()->frame()->getCamToWorld().scale() : 1.0;

    // debug plot depthmap
    char buf1[200] = "";
    char buf2[200] = "";

    if( Conf().onSceenInfoDisplay ){
        // snprintf(buf1,200,"Map: Upd %3.0fms (%2.0fHz); Trk %3.0fms (%2.0fHz); %d / %d",
        //      depthMap()->perf().update.ms(), depthMap()->perf().update.rate(),
        //      _trackingThread->perf().track.ms(), _trackingThread->perf().track.rate(),
        //      currentKeyFrame()->numFramesTrackedOnThis, currentKeyFrame()->numMappedOnThis ); //, (int)unmappedTrackedFrames().size());

    // snprintf(buf2,200,"dens %2.0f%%; good %2.0f%%; scale %2.2f; res %2.1f/; usg %2.0f%%; Map: %d F, %d KF, %d E, %.1fm Pts",
    //      100*currentKeyFrame->numPoints/(float)(Conf().slamImage.area()),
    //      100*tracking_lastGoodPerBad,
    //      scale,
    //      tracking_lastResidual,
    //      100*tracking_lastUsage,
    //      (int)keyFrameGraph()->allFramePoses.size(),
    //      keyFrameGraph()->totalVertices,
    //      (int)keyFrameGraph()->edgesAll.size(),
    //      1e-6 * (float)keyFrameGraph()->totalPoints);

    }

    currentKeyFrame()->depthMap()->plotDepthMap( buf1, buf2 );

    CHECK( currentKeyFrame()->depthMap()->debugImages().depthImage().data != NULL );
    publishDepthImage( currentKeyFrame()->depthMap()->debugImages().depthImage().data );
}


//=== OutputWrapper functions ==

#define OUTPUT_FOR_EACH( func ) \
    for( auto &wrapper  : _outputWrappers ) { wrapper->func; }

void SlamSystem::publishPose(const Sophus::Sim3f &pose ) {
     OUTPUT_FOR_EACH( publishPose( pose ) )
 }

void SlamSystem::publishTrackedFrame( const Frame::SharedPtr &frame ) {
    OUTPUT_FOR_EACH( publishTrackedFrame( frame ) )
}

void SlamSystem::publishKeyframeGraph( void ) {
    OUTPUT_FOR_EACH( publishKeyframeGraph( keyFrameGraph() ) )
}

void SlamSystem::publishDepthImage( unsigned char* data  ) {
    OUTPUT_FOR_EACH( updateDepthImage( data ) )
}

void SlamSystem::publishKeyframe( const Frame::SharedPtr &frame ) {
    OUTPUT_FOR_EACH( publishKeyframe( frame ) )
}

void SlamSystem::publishCurrentKeyframe( )
{
    if( currentKeyFrame() ) {
        OUTPUT_FOR_EACH( publishKeyframe( currentKeyFrame()->frame() ) )
        OUTPUT_FOR_EACH( publishPointCloud( currentKeyFrame()->frame() ) )
    } else {
        LOG(DEBUG) << "No currentKeyframe, unable to publish";
    }
}