Program Listing for File occupancy_integrator.h

Return to documentation for file (voxblox/include/voxblox/integrator/occupancy_integrator.h)

#ifndef VOXBLOX_INTEGRATOR_OCCUPANCY_INTEGRATOR_H_
#define VOXBLOX_INTEGRATOR_OCCUPANCY_INTEGRATOR_H_

#include <algorithm>
#include <vector>

#include <glog/logging.h>
#include <Eigen/Core>

#include "voxblox/core/block_hash.h"
#include "voxblox/core/layer.h"
#include "voxblox/core/voxel.h"
#include "voxblox/integrator/integrator_utils.h"
#include "voxblox/utils/timing.h"

namespace voxblox {
class OccupancyIntegrator {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  struct Config {
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    float probability_hit = 0.65f;
    float probability_miss = 0.4f;
    float threshold_min = 0.12f;
    float threshold_max = 0.97f;
    float threshold_occupancy = 0.7f;
    FloatingPoint min_ray_length_m = 0.1;
    FloatingPoint max_ray_length_m = 5.0;
  };

  OccupancyIntegrator(const Config& config, Layer<OccupancyVoxel>* layer)
      : config_(config), layer_(layer) {
    DCHECK(layer_ != NULL);
    DCHECK_GT(layer_->voxel_size(), 0.0);
    DCHECK_GT(layer_->block_size(), 0.0);
    DCHECK_GT(layer_->voxels_per_side(), 0u);

    voxel_size_ = layer_->voxel_size();
    block_size_ = layer_->block_size();
    voxels_per_side_ = layer_->voxels_per_side();

    voxel_size_inv_ = 1.0 / voxel_size_;
    block_size_inv_ = 1.0 / block_size_;
    voxels_per_side_inv_ = 1.0 / voxels_per_side_;

    // Cache rest of the probability settings.
    prob_hit_log_ = logOddsFromProbability(config_.probability_hit);
    prob_miss_log_ = logOddsFromProbability(config_.probability_miss);
    clamp_min_log_ = logOddsFromProbability(config_.threshold_min);
    clamp_max_log_ = logOddsFromProbability(config_.threshold_max);
    min_occupancy_log_ = logOddsFromProbability(config_.threshold_occupancy);
  }

  inline void updateOccupancyVoxel(bool occupied, OccupancyVoxel* occ_voxel) {
    DCHECK(occ_voxel != NULL);
    // Set voxel to observed.
    occ_voxel->observed = true;
    // Skip update if necessary.
    float log_odds_update = occupied ? prob_hit_log_ : prob_miss_log_;
    if ((log_odds_update >= 0 &&
         occ_voxel->probability_log >= clamp_max_log_) ||
        (log_odds_update <= 0 &&
         occ_voxel->probability_log <= clamp_min_log_)) {
      return;
    }
    // Otherwise update and reclamp.
    occ_voxel->probability_log = std::min(
        std::max(occ_voxel->probability_log + log_odds_update, clamp_min_log_),
        clamp_max_log_);
  }

  void integratePointCloud(const Transformation& T_G_C,
                           const Pointcloud& points_C) {
    timing::Timer integrate_timer("integrate_occ");

    const Point& origin = T_G_C.getPosition();

    LongIndexSet free_cells;
    LongIndexSet occupied_cells;

    const Point start_scaled = origin * voxel_size_inv_;
    Point end_scaled = Point::Zero();

    for (size_t pt_idx = 0; pt_idx < points_C.size(); ++pt_idx) {
      const Point& point_C = points_C[pt_idx];
      const Point point_G = T_G_C * point_C;
      const Ray unit_ray = (point_G - origin).normalized();

      timing::Timer cast_ray_timer("integrate_occ/cast_ray");

      AlignedVector<GlobalIndex> global_voxel_indices;
      FloatingPoint ray_distance = (point_G - origin).norm();
      if (ray_distance < config_.min_ray_length_m) {
        continue;
      } else if (ray_distance > config_.max_ray_length_m) {
        // Simply clear up until the max ray distance in this case.
        end_scaled =
            (origin + config_.max_ray_length_m * unit_ray) * voxel_size_inv_;

        if (free_cells.find(getGridIndexFromPoint<GlobalIndex>(end_scaled)) ==
            free_cells.end()) {
          castRay(start_scaled, end_scaled, &global_voxel_indices);
          free_cells.insert(global_voxel_indices.begin(),
                            global_voxel_indices.end());
        }
      } else {
        end_scaled = point_G * voxel_size_inv_;
        if (occupied_cells.find(getGridIndexFromPoint<GlobalIndex>(
                end_scaled)) == occupied_cells.end()) {
          castRay(start_scaled, end_scaled, &global_voxel_indices);

          if (global_voxel_indices.size() > 2) {
            free_cells.insert(global_voxel_indices.begin(),
                              global_voxel_indices.end() - 1);
            occupied_cells.insert(global_voxel_indices.back());
          }
        }
      }
      cast_ray_timer.Stop();
    }

    timing::Timer update_voxels_timer("integrate_occ/update_occupancy");

    // Clean up the lists: remove any occupied cells from free cells.
    for (const GlobalIndex& global_index : occupied_cells) {
      LongIndexSet::iterator cell_it = free_cells.find(global_index);
      if (cell_it != free_cells.end()) {
        free_cells.erase(cell_it);
      }
    }
    // Then actually update the occupancy voxels.
    BlockIndex last_block_idx = BlockIndex::Zero();
    Block<OccupancyVoxel>::Ptr block;

    bool occupied = false;
    for (const GlobalIndex& global_voxel_idx : free_cells) {
      BlockIndex block_idx = getBlockIndexFromGlobalVoxelIndex(
          global_voxel_idx, voxels_per_side_inv_);
      VoxelIndex local_voxel_idx =
          getLocalFromGlobalVoxelIndex(global_voxel_idx, voxels_per_side_);

      if (!block || block_idx != last_block_idx) {
        block = layer_->allocateBlockPtrByIndex(block_idx);
        block->updated() = true;
        last_block_idx = block_idx;
      }

      OccupancyVoxel& occ_voxel = block->getVoxelByVoxelIndex(local_voxel_idx);
      updateOccupancyVoxel(occupied, &occ_voxel);
    }
    block.reset();

    occupied = true;
    for (const GlobalIndex& global_voxel_idx : occupied_cells) {
      BlockIndex block_idx = getBlockIndexFromGlobalVoxelIndex(
          global_voxel_idx, voxels_per_side_inv_);
      VoxelIndex local_voxel_idx =
          getLocalFromGlobalVoxelIndex(global_voxel_idx, voxels_per_side_);

      if (!block || block_idx != last_block_idx) {
        block = layer_->allocateBlockPtrByIndex(block_idx);
        block->updated() = true;
        last_block_idx = block_idx;
      }

      OccupancyVoxel& occ_voxel = block->getVoxelByVoxelIndex(local_voxel_idx);
      updateOccupancyVoxel(occupied, &occ_voxel);
    }

    update_voxels_timer.Stop();
    integrate_timer.Stop();
  }

 protected:
  Config config_;

  Layer<OccupancyVoxel>* layer_;

  // Cached map config.
  FloatingPoint voxel_size_;
  size_t voxels_per_side_;
  FloatingPoint block_size_;

  float prob_hit_log_;
  float prob_miss_log_;
  float clamp_min_log_;
  float clamp_max_log_;
  float min_occupancy_log_;

  // Derived types.
  FloatingPoint voxel_size_inv_;
  FloatingPoint voxels_per_side_inv_;
  FloatingPoint block_size_inv_;
};

}  // namespace voxblox

#endif  // VOXBLOX_INTEGRATOR_OCCUPANCY_INTEGRATOR_H_