Program Listing for File Frame.cpp

Return to documentation for file (lib/DataStructures/Frame.cpp)

#include "DataStructures/Frame.h"
#include "DataStructures/FrameMemory.h"
#include "DataStructures/KeyFrame.h"

#include "DepthEstimation/DepthMapPixelHypothesis.h"
#include "Tracking/TrackingReference.h"

#include "DataStructures/FramePoseStruct.h"
#include "DepthEstimation/DepthMap.h"

#include <g3log/g3log.hpp>

namespace lsd_slam {

int privateFrameAllocCount = 0;

Frame::Frame(int frameId, const Camera &cam, const ImageSize &sz,
             double timestamp, const unsigned char *image)
    : _trackingParent(nullptr), _id(frameId), _timestamp(timestamp),
      data(cam, sz, image), pose(new FramePoseStruct(*this)), referenceID(-1),
      referenceLevel(-1), numMappablePixels(-1), meanIdepth(1), numPoints(0),
      idxInKeyframes(-1), edgeErrorSum(1), edgesNum(1),
      lastConstraintTrackedCamToWorld(Sim3()), isActive(false) {
  privateFrameAllocCount++;

  LOG_IF(INFO, Conf().print.memoryDebugInfo)
      << "ALLOCATED frame " << id() << ", now there are "
      << privateFrameAllocCount;
}

Frame::Frame(int frameId, const Camera &cam, const ImageSize &sz,
             double timestamp, const float *image)
    : _trackingParent(nullptr), _id(frameId), _timestamp(timestamp),
      data(cam, sz, image), pose(new FramePoseStruct(*this)), referenceID(-1),
      referenceLevel(-1), numMappablePixels(-1), meanIdepth(1), numPoints(0),
      idxInKeyframes(-1), edgeErrorSum(1), edgesNum(1),
      lastConstraintTrackedCamToWorld(Sim3()), isActive(false) {
  privateFrameAllocCount++;

  LOG_IF(INFO, Conf().print.memoryDebugInfo)
      << "ALLOCATED frame " << id() << ", now there are "
      << privateFrameAllocCount;
}

Frame::~Frame() {

  LOGF_IF(INFO, Conf().print.frameBuildDebugInfo, "DELETING frame %d", id());

  FrameMemory::getInstance().deactivateFrame(this);

  // if(!pose->isRegisteredToGraph)
  //    delete pose;
  // // else
  // //     pose->frame = 0;

  // if(permaRef_colorAndVarData != 0)
  //    delete permaRef_colorAndVarData;
  // if(permaRef_posData != 0)
  //    delete permaRef_posData;

  privateFrameAllocCount--;
  LOGF_IF(DEBUG, Conf().print.frameBuildDebugInfo,
          "DELETED frame %d, now there are %d\n", id(), privateFrameAllocCount);
}

bool Frame::isTrackingParent(const std::shared_ptr<Frame> &other) const {
  return isTrackingParent(other->id());
}

bool Frame::isTrackingParent(const std::shared_ptr<KeyFrame> &other) const {
  return isTrackingParent(other->id());
}

bool Frame::isTrackingParent(int id) const {
  return hasTrackingParent() && (id == trackingParent()->id());
}

void Frame::calculateMeanInformation() {
  return;

  if (numMappablePixels < 0)
    maxGradients(0);

  const float *idv = idepthVar(0);
  const float *idv_max = idv + area(0);
  float sum = 0;
  int goodpx = 0;
  for (const float *pt = idv; pt < idv_max; pt++) {
    if (*pt > 0) {
      sum += sqrtf(1.0f / *pt);
      goodpx++;
    }
  }

  meanInformation = sum / goodpx;
}

void Frame::setDepth(
    const DepthMap::SharedPtr &depthMap) // PixelHypothesis* newDepth)
{

  boost::shared_lock<boost::shared_mutex> lock = getActiveLock();
  boost::unique_lock<boost::mutex> lock2(buildMutex);

  if (data.idepth[0] == 0)
    data.idepth[0] = FrameMemory::getInstance().getFloatBuffer(area(0));
  if (data.idepthVar[0] == 0)
    data.idepthVar[0] = FrameMemory::getInstance().getFloatBuffer(area(0));

  float *pyrIDepth = data.idepth[0];
  float *pyrIDepthVar = data.idepthVar[0];
  float *pyrIDepthMax = pyrIDepth + (area(0));

  DepthMapPixelHypothesis *newDepth = depthMap->hypothesisAt(0, 0);

  float sumIdepth = 0;
  int numIdepth = 0;

  for (; pyrIDepth < pyrIDepthMax;
       ++pyrIDepth, ++pyrIDepthVar, ++newDepth) //, ++ pyrRefID)
  {
    if (newDepth->isValid && newDepth->idepth_smoothed >= -0.05) {
      *pyrIDepth = newDepth->idepth_smoothed;
      *pyrIDepthVar = newDepth->idepth_var_smoothed;

      numIdepth++;
      sumIdepth += newDepth->idepth_smoothed;
    } else {
      *pyrIDepth = -1;
      *pyrIDepthVar = -1;
    }
  }

  meanIdepth = sumIdepth / numIdepth;
  numPoints = numIdepth;

  LOG(INFO) << "Imported " << numPoints << " points from DepthMap";

  data.idepthValid[0] = true;
  data.idepthVarValid[0] = true;
  release(IDEPTH | IDEPTH_VAR, true, true);

  data.hasIDepthBeenSet = true;
  // depthHasBeenUpdatedFlag = true;
}

void Frame::setDepthFromGroundTruth(const float *depth, float cov_scale) {
  boost::shared_lock<boost::shared_mutex> lock = getActiveLock();
  const float *pyrMaxGradient = maxGradients(0);

  boost::unique_lock<boost::mutex> lock2(buildMutex);
  if (data.idepth[0] == 0)
    data.idepth[0] = FrameMemory::getInstance().getFloatBuffer(area(0));
  if (data.idepthVar[0] == 0)
    data.idepthVar[0] = FrameMemory::getInstance().getFloatBuffer(area(0));

  float *pyrIDepth = data.idepth[0];
  float *pyrIDepthVar = data.idepthVar[0];

  const int width0 = width(0);
  const int height0 = height(0);

  for (int y = 0; y < height0; y++) {
    for (int x = 0; x < width0; x++) {
      if (x > 0 && x < width0 - 1 && y > 0 &&
          y < height0 - 1 && // pyramidMaxGradient is not valid for the border
          pyrMaxGradient[x + y * width0] >= MIN_ABS_GRAD_CREATE &&
          !isnanf(*depth) && *depth > 0) {
        *pyrIDepth = 1.0f / *depth;
        *pyrIDepthVar = VAR_GT_INIT_INITIAL * cov_scale;
      } else {
        *pyrIDepth = -1;
        *pyrIDepthVar = -1;
      }

      ++depth;
      ++pyrIDepth;
      ++pyrIDepthVar;
    }
  }

  data.idepthValid[0] = true;
  data.idepthVarValid[0] = true;
  //    data.refIDValid[0] = true;
  // Invalidate higher levels, they need to be updated with the new data
  release(IDEPTH | IDEPTH_VAR, true, true);
  data.hasIDepthBeenSet = true;
}

void Frame::prepareForStereoWith(const Frame::SharedPtr &other,
                                 Sim3 thisToOther, const int level) {
  Sim3 otherToThis = thisToOther.inverse();

  // otherToThis = data.worldToCam * other->data.camToWorld;
  K_otherToThis_R = other->camera(level).K *
                    otherToThis.rotationMatrix().cast<float>() *
                    otherToThis.scale();
  otherToThis_t = otherToThis.translation().cast<float>();
  K_otherToThis_t = other->camera(level).K * otherToThis_t;

  thisToOther_t = thisToOther.translation().cast<float>();
  K_thisToOther_t = camera(level).K * thisToOther_t;
  thisToOther_R =
      thisToOther.rotationMatrix().cast<float>() * thisToOther.scale();
  otherToThis_R_row0 = thisToOther_R.col(0);
  otherToThis_R_row1 = thisToOther_R.col(1);
  otherToThis_R_row2 = thisToOther_R.col(2);

  distSquared = otherToThis.translation().dot(otherToThis.translation());

  referenceID = other->id();
  referenceLevel = level;
}

cv::Mat Frame::getCvImage() {
  float *img = image(0);

  const int size = width() * height();
  const float *end = &img[size];
  float imgCv[size];
  int i = 0;
  for (float *pt = img; pt != end; pt++) {
    imgCv[i] = *pt / 255.0;
    i++;
  }

  return cv::Mat(height(), width(), CV_32F, &imgCv);
}

void Frame::require(int dataFlags, int level) {
  if ((dataFlags & IMAGE) && !data.imageValid[level]) {
    buildImage(level);
  }
  if ((dataFlags & GRADIENTS) && !data.gradientsValid[level]) {
    buildGradients(level);
  }
  if ((dataFlags & MAX_GRADIENTS) && !data.maxGradientsValid[level]) {
    buildMaxGradients(level);
  }
  if (((dataFlags & IDEPTH) && !data.idepthValid[level]) ||
      ((dataFlags & IDEPTH_VAR) && !data.idepthVarValid[level])) {
    buildIDepthAndIDepthVar(level);
  }
}

void Frame::release(int dataFlags, bool pyramidsOnly, bool invalidateOnly) {
  for (int level = (pyramidsOnly ? 1 : 0); level < PYRAMID_LEVELS; ++level) {
    if ((dataFlags & IMAGE) && data.imageValid[level]) {
      data.imageValid[level] = false;
      if (!invalidateOnly)
        releaseImage(level);
    }
    if ((dataFlags & GRADIENTS) && data.gradientsValid[level]) {
      data.gradientsValid[level] = false;
      if (!invalidateOnly)
        releaseGradients(level);
    }
    if ((dataFlags & MAX_GRADIENTS) && data.maxGradientsValid[level]) {
      data.maxGradientsValid[level] = false;
      if (!invalidateOnly)
        releaseMaxGradients(level);
    }
    if ((dataFlags & IDEPTH) && data.idepthValid[level]) {
      data.idepthValid[level] = false;
      if (!invalidateOnly)
        releaseIDepth(level);
    }
    if ((dataFlags & IDEPTH_VAR) && data.idepthVarValid[level]) {
      data.idepthVarValid[level] = false;
      if (!invalidateOnly)
        releaseIDepthVar(level);
    }
  }
}

bool Frame::minimizeInMemory() {
  if (activeMutex.timed_lock(boost::posix_time::milliseconds(10))) {
    buildMutex.lock();
    LOGF_IF(DEBUG, Conf().print.frameBuildDebugInfo, "minimizing frame %d\n",
            id());

    release(IMAGE | IDEPTH | IDEPTH_VAR, true, false);
    release(GRADIENTS | MAX_GRADIENTS, false, false);

    clear_refPixelWasGood();

    buildMutex.unlock();
    activeMutex.unlock();
    return true;
  }
  return false;
}

void Frame::setDepth_Allocate() { return; }

void Frame::buildImage(int level) {
  if (level == 0) {
    LOG(WARNING) << "Frame::buildImage(0): Loading image from disk is not "
                    "implemented yet! No-op.";
    return;
  }

  require(IMAGE, level - 1);
  boost::unique_lock<boost::mutex> lock2(buildMutex);

  if (data.imageValid[level])
    return;

  const int width = imgSize(level - 1).width;
  const int height = imgSize(level - 1).height;
  const float *source = data.image[level - 1];

  LOGF_IF(DEBUG, Conf().print.frameBuildDebugInfo,
          "CREATE Image lvl %d for frame %d,  %f x %f", level, id(),
          width / 2.0, height / 2.0);

  if (data.image[level] == 0)
    data.image[level] = FrameMemory::getInstance().getFloatBuffer(area(level));
  float *dest = data.image[level];

#if defined(FOOBAR)
#if defined(ENABLE_SSE)
  // I assume all all subsampled width's are a multiple of 8.
  // if this is not the case, this still works except for the last * pixel,
  // which will produce a segfault. in that case, reduce this loop and calculate
  // the last 0-3 dest pixels by hand....
  if (width % 8 == 0) {
    __m128 p025 = _mm_setr_ps(0.25f, 0.25f, 0.25f, 0.25f);

    const float *maxY = source + width * height;
    for (const float *y = source; y < maxY; y += width * 2) {
      const float *maxX = y + width;
      for (const float *x = y; x < maxX; x += 8) {
        // i am calculating four dest pixels at a time.

        __m128 top_left = _mm_load_ps((float *)x);
        __m128 bot_left = _mm_load_ps((float *)x + width);
        __m128 left = _mm_add_ps(top_left, bot_left);

        __m128 top_right = _mm_load_ps((float *)x + 4);
        __m128 bot_right = _mm_load_ps((float *)x + width + 4);
        __m128 right = _mm_add_ps(top_right, bot_right);

        __m128 sumA = _mm_shuffle_ps(left, right, _MM_SHUFFLE(2, 0, 2, 0));
        __m128 sumB = _mm_shuffle_ps(left, right, _MM_SHUFFLE(3, 1, 3, 1));

        __m128 sum = _mm_add_ps(sumA, sumB);
        sum = _mm_mul_ps(sum, p025);

        _mm_store_ps(dest, sum);
        dest += 4;
      }
    }

    data.imageValid[level] = true;
    return;
  }
#elif defined(ENABLE_NEON)
  // I assume all all subsampled width's are a multiple of 8.
  // if this is not the case, this still works except for the last * pixel,
  // which will produce a segfault. in that case, reduce this loop and calculate
  // the last 0-3 dest pixels by hand....
  if (width % 8 == 0) {
    static const float p025[] = {0.25, 0.25, 0.25, 0.25};
    int width_iteration_count = width / 8;
    int height_iteration_count = height / 2;
    const float *cur_px = source;
    const float *next_row_px = source + width;

    __asm__ __volatile__(
        "vldmia %[p025], {q10}                        \n\t" // p025(q10)

        ".height_loop:                                \n\t"

        "mov r5, %[width_iteration_count]             \n\t" // store
                                                            // width_iteration_count
        ".width_loop:                                 \n\t"

        "vldmia   %[cur_px]!, {q0-q1}             \n\t" // top_left(q0),
                                                        // top_right(q1)
        "vldmia   %[next_row_px]!, {q2-q3}        \n\t" // bottom_left(q2),
                                                        // bottom_right(q3)

        "vadd.f32 q0, q0, q2                      \n\t" // left(q0)
        "vadd.f32 q1, q1, q3                      \n\t" // right(q1)

        "vpadd.f32 d0, d0, d1                     \n\t" // pairwise add into
                                                        // sum(q0)
        "vpadd.f32 d1, d2, d3                     \n\t"
        "vmul.f32 q0, q0, q10                     \n\t" // multiply with 0.25 to
                                                        // get average

        "vstmia %[dest]!, {q0}                    \n\t"

        "subs     %[width_iteration_count], %[width_iteration_count], #1 \n\t"
        "bne      .width_loop                     \n\t"
        "mov      %[width_iteration_count], r5    \n\t" // restore
                                                        // width_iteration_count

        // Advance one more line
        "add      %[cur_px], %[cur_px], %[rowSize]    \n\t"
        "add      %[next_row_px], %[next_row_px], %[rowSize] \n\t"

        "subs     %[height_iteration_count], %[height_iteration_count], #1 \n\t"
        "bne      .height_loop                       \n\t"

        : /* outputs */ [cur_px] "+&r"(cur_px),
          [next_row_px] "+&r"(next_row_px),
          [width_iteration_count] "+&r"(width_iteration_count),
          [height_iteration_count] "+&r"(height_iteration_count),
          [dest] "+&r"(dest)
        : /* inputs  */ [p025] "r"(p025), [rowSize] "r"(width * sizeof(float))
        : /* clobber */ "memory", "cc", "r5", "q0", "q1", "q2", "q3", "q10");

    data.imageValid[level] = true;
    return;
  }
#endif
#endif

  // Width and height are valid for level _above_ this one
  CHECK(width % 2 == 0);
  CHECK(height % 2 == 0);

  int wh = width * height;
  const float *s;
  for (int y = 0; y < height; y += 2) {
    for (int x = 0; x < width; x += 2) {
      s = source + x + y * width;
      *dest = (s[0] + s[1] + s[width] + s[1 + width]) * 0.25f;
      dest++;
    }
  }

  data.imageValid[level] = true;
}

void Frame::releaseImage(int level) {
  if (level == 0) {
    LOG(WARNING) << "Frame::releaseImage(0): Storing image on disk is not "
                    "supported yet! No-op.";
    return;
  }
  FrameMemory::getInstance().returnBuffer(data.image[level]);
  data.image[level] = 0;
}

void Frame::buildGradients(int level) {
  require(IMAGE, level);
  boost::unique_lock<boost::mutex> lock2(buildMutex);

  if (data.gradientsValid[level])
    return;

  LOGF_IF(DEBUG, Conf().print.frameBuildDebugInfo,
          "CREATE Gradients lvl %d for frame %d", level, id());

  const int width = imgSize(level).width;
  const int height = imgSize(level).height;
  if (data.gradients[level] == 0)
    data.gradients[level] =
        (Eigen::Vector4f *)FrameMemory::getInstance().getBuffer(
            sizeof(Eigen::Vector4f) * width * height);
  const float *img_pt = data.image[level] + width;
  const float *img_pt_max = data.image[level] + width * (height - 1);
  Eigen::Vector4f *gradxyii_pt = data.gradients[level] + width;

  // in each iteration i need -1,0,p1,mw,pw
  float val_m1 = *(img_pt - 1);
  float val_00 = *img_pt;
  float val_p1;

  for (; img_pt < img_pt_max; img_pt++, gradxyii_pt++) {
    val_p1 = *(img_pt + 1);

    *((float *)gradxyii_pt) = 0.5f * (val_p1 - val_m1);
    *(((float *)gradxyii_pt) + 1) =
        0.5f * (*(img_pt + width) - *(img_pt - width));
    *(((float *)gradxyii_pt) + 2) = val_00;

    val_m1 = val_00;
    val_00 = val_p1;
  }

  data.gradientsValid[level] = true;
}

void Frame::releaseGradients(int level) {
  FrameMemory::getInstance().returnBuffer(
      reinterpret_cast<float *>(data.gradients[level]));
  data.gradients[level] = 0;
}

void Frame::buildMaxGradients(int level) {
  require(GRADIENTS, level);
  boost::unique_lock<boost::mutex> lock2(buildMutex);

  if (data.maxGradientsValid[level])
    return;

  LOGF_IF(DEBUG, Conf().print.frameBuildDebugInfo,
          "CREATE AbsGrad lvl %d for frame %d", level, id());

  const int width = imgSize(level).width;
  const int height = imgSize(level).height;
  if (data.maxGradients[level] == 0)
    data.maxGradients[level] =
        FrameMemory::getInstance().getFloatBuffer(width * height);

  float *maxGradTemp =
      FrameMemory::getInstance().getFloatBuffer(width * height);

  // 1. write abs gradients in real data.
  Eigen::Vector4f *gradxyii_pt = data.gradients[level] + width;
  float *maxgrad_pt = data.maxGradients[level] + width;
  float *maxgrad_pt_max = data.maxGradients[level] + width * (height - 1);

  for (; maxgrad_pt < maxgrad_pt_max; maxgrad_pt++, gradxyii_pt++) {
    float dx = *((float *)gradxyii_pt);
    float dy = *(1 + (float *)gradxyii_pt);
    *maxgrad_pt = sqrtf(dx * dx + dy * dy);
  }

  // 2. smear up/down direction into temp buffer
  maxgrad_pt = data.maxGradients[level] + width + 1;
  maxgrad_pt_max = data.maxGradients[level] + width * (height - 1) - 1;
  float *maxgrad_t_pt = maxGradTemp + width + 1;
  for (; maxgrad_pt < maxgrad_pt_max; maxgrad_pt++, maxgrad_t_pt++) {
    float g1 = maxgrad_pt[-width];
    float g2 = maxgrad_pt[0];
    if (g1 < g2)
      g1 = g2;
    float g3 = maxgrad_pt[width];
    if (g1 < g3)
      *maxgrad_t_pt = g3;
    else
      *maxgrad_t_pt = g1;
  }

  float numMappablePixels = 0;
  // 2. smear left/right direction into real data
  maxgrad_pt = data.maxGradients[level] + width + 1;
  maxgrad_pt_max = data.maxGradients[level] + width * (height - 1) - 1;
  maxgrad_t_pt = maxGradTemp + width + 1;
  for (; maxgrad_pt < maxgrad_pt_max; maxgrad_pt++, maxgrad_t_pt++) {
    float g1 = maxgrad_t_pt[-1];
    float g2 = maxgrad_t_pt[0];
    if (g1 < g2)
      g1 = g2;
    float g3 = maxgrad_t_pt[1];
    if (g1 < g3) {
      *maxgrad_pt = g3;
      if (g3 >= MIN_ABS_GRAD_CREATE)
        numMappablePixels++;
    } else {
      *maxgrad_pt = g1;
      if (g1 >= MIN_ABS_GRAD_CREATE)
        numMappablePixels++;
    }
  }

  if (level == 0)
    this->numMappablePixels = numMappablePixels;

  FrameMemory::getInstance().returnBuffer(maxGradTemp);

  data.maxGradientsValid[level] = true;
}

void Frame::releaseMaxGradients(int level) {
  FrameMemory::getInstance().returnBuffer(data.maxGradients[level]);
  data.maxGradients[level] = 0;
}

void Frame::buildIDepthAndIDepthVar(int level) {
  if (!hasIDepthBeenSet()) {
    LOG(WARNING)
        << "Frame::buildIDepthAndIDepthVar(): idepth has not been set yet!";
    return;
  }
  if (level == 0) {
    LOG(DEBUG) << "Frame::buildIDepthAndIDepthVar(0): Loading depth from disk "
                  "is not implemented yet! No-op.";
    return;
  }

  require(IDEPTH, level - 1);
  boost::unique_lock<boost::mutex> lock2(buildMutex);

  if (data.idepthValid[level] && data.idepthVarValid[level])
    return;

  LOGF_IF(DEBUG, Conf().print.frameBuildDebugInfo,
          "CREATE IDepth lvl %d for frame %d", level, id());

  const int width = imgSize(level).width;
  const int height = imgSize(level).height;

  if (data.idepth[level] == 0)
    data.idepth[level] =
        FrameMemory::getInstance().getFloatBuffer(width * height);
  if (data.idepthVar[level] == 0)
    data.idepthVar[level] =
        FrameMemory::getInstance().getFloatBuffer(width * height);

  int sw = imgSize(level - 1).width;

  const float *idepthSource = data.idepth[level - 1];
  const float *idepthVarSource = data.idepthVar[level - 1];
  float *idepthDest = data.idepth[level];
  float *idepthVarDest = data.idepthVar[level];

  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      int idx = 2 * (x + y * sw);
      int idxDest = (x + y * width);

      float idepthSumsSum = 0;
      float ivarSumsSum = 0;
      int num = 0;

      // build sums
      float ivar;
      float var = idepthVarSource[idx];
      if (var > 0) {
        ivar = 1.0f / var;
        ivarSumsSum += ivar;
        idepthSumsSum += ivar * idepthSource[idx];
        num++;
      }

      var = idepthVarSource[idx + 1];
      if (var > 0) {
        ivar = 1.0f / var;
        ivarSumsSum += ivar;
        idepthSumsSum += ivar * idepthSource[idx + 1];
        num++;
      }

      var = idepthVarSource[idx + sw];
      if (var > 0) {
        ivar = 1.0f / var;
        ivarSumsSum += ivar;
        idepthSumsSum += ivar * idepthSource[idx + sw];
        num++;
      }

      var = idepthVarSource[idx + sw + 1];
      if (var > 0) {
        ivar = 1.0f / var;
        ivarSumsSum += ivar;
        idepthSumsSum += ivar * idepthSource[idx + sw + 1];
        num++;
      }

      if (num > 0) {
        float depth = ivarSumsSum / idepthSumsSum;
        idepthDest[idxDest] = 1.0f / depth;
        idepthVarDest[idxDest] = num / ivarSumsSum;
      } else {
        idepthDest[idxDest] = -1;
        idepthVarDest[idxDest] = -1;
      }
    }
  }

  data.idepthValid[level] = true;
  data.idepthVarValid[level] = true;
}

void Frame::releaseIDepth(int level) {
  if (level == 0) {
    LOG(WARNING) << "Frame::releaseIDepth(0): Storing depth on disk is not "
                    "supported yet! No-op.";
    return;
  }

  FrameMemory::getInstance().returnBuffer(data.idepth[level]);
  data.idepth[level] = 0;
}

void Frame::releaseIDepthVar(int level) {
  if (level == 0) {
    LOG(WARNING) << "Frame::releaseIDepthVar(0): Storing depth variance on "
                    "disk is not supported yet! No-op.";
    return;
  }
  FrameMemory::getInstance().returnBuffer(data.idepthVar[level]);
  data.idepthVar[level] = 0;
}

//====================

} // namespace lsd_slam