// // Created by xiang on 2021/9/16. // #ifndef FASTER_LIO_IVOX3D_H #define FASTER_LIO_IVOX3D_H #include #include #include #include #include "eigen_types.h" #include "ivox3d_node.hpp" namespace faster_lio { enum class IVoxNodeType { DEFAULT, // linear ivox PHC, // phc ivox }; /// traits for NodeType template struct IVoxNodeTypeTraits {}; template struct IVoxNodeTypeTraits { using NodeType = IVoxNode; }; template struct IVoxNodeTypeTraits { using NodeType = IVoxNodePhc; }; template class IVox { public: using KeyType = Eigen::Matrix; using PtType = Eigen::Matrix; using NodeType = typename IVoxNodeTypeTraits::NodeType; using PointVector = std::vector>; using DistPoint = typename NodeType::DistPoint; enum class NearbyType { CENTER, // center only NEARBY6, NEARBY18, NEARBY26, }; struct Options { float resolution_ = 0.2; // ivox resolution float inv_resolution_ = 10.0; // inverse resolution NearbyType nearby_type_ = NearbyType::NEARBY6; // nearby range std::size_t capacity_ = 1000000; // capacity }; /** * constructor * @param options ivox options */ explicit IVox(Options options) : options_(options) { options_.inv_resolution_ = 1.0 / options_.resolution_; GenerateNearbyGrids(); } /** * add points * @param points_to_add */ void AddPoints(const PointVector& points_to_add); void GetPoints(PointVector& points); void DownSample(double leafsize=0.02); /// get nn bool GetClosestPoint(const PointType& pt, PointType& closest_pt); /// get nn with condition bool GetClosestPoint(const PointType& pt, PointVector& closest_pt, int max_num = 5, double max_range = 5.0); /// get nn in cloud bool GetClosestPoint(const PointVector& cloud, PointVector& closest_cloud); /// get number of points size_t NumPoints() const; /// get number of valid grids size_t NumValidGrids() const; /// get statistics of the points std::vector StatGridPoints() const; public: /// generate the nearby grids according to the given options void GenerateNearbyGrids(); /// position to grid KeyType Pos2Grid(const PtType& pt) const; Options options_; std::unordered_map>::iterator, hash_vec> grids_map_; // voxel hash map std::list> grids_cache_; // voxel cache std::vector nearby_grids_; // nearbys }; template bool IVox::GetClosestPoint(const PointType& pt, PointType& closest_pt) { std::vector candidates; auto key = Pos2Grid(ToEigen(pt)); std::for_each(nearby_grids_.begin(), nearby_grids_.end(), [&key, &candidates, &pt, this](const KeyType& delta) { auto dkey = key + delta; auto iter = grids_map_.find(dkey); if (iter != grids_map_.end()) { DistPoint dist_point; bool found = iter->second->second.NNPoint(pt, dist_point); if (found) { candidates.emplace_back(dist_point); } } }); if (candidates.empty()) { return false; } auto iter = std::min_element(candidates.begin(), candidates.end()); closest_pt = iter->Get(); return true; } template bool IVox::GetClosestPoint(const PointType& pt, PointVector& closest_pt, int max_num, double max_range) { std::vector candidates; candidates.reserve(max_num * nearby_grids_.size()); auto key = Pos2Grid(ToEigen(pt)); // #define INNER_TIMER #ifdef INNER_TIMER static std::unordered_map> stats; if (stats.empty()) { stats["knn"] = std::vector(); stats["nth"] = std::vector(); } #endif for (const KeyType& delta : nearby_grids_) { auto dkey = key + delta; auto iter = grids_map_.find(dkey); if (iter != grids_map_.end()) { #ifdef INNER_TIMER auto t1 = std::chrono::high_resolution_clock::now(); #endif auto tmp = iter->second->second.KNNPointByCondition(candidates, pt, max_num, max_range); #ifdef INNER_TIMER auto t2 = std::chrono::high_resolution_clock::now(); auto knn = std::chrono::duration_cast(t2 - t1).count(); stats["knn"].emplace_back(knn); #endif } } if (candidates.empty()) { return false; } #ifdef INNER_TIMER auto t1 = std::chrono::high_resolution_clock::now(); #endif if (candidates.size() <= max_num) { } else { std::nth_element(candidates.begin(), candidates.begin() + max_num - 1, candidates.end()); candidates.resize(max_num); } std::nth_element(candidates.begin(), candidates.begin(), candidates.end()); #ifdef INNER_TIMER auto t2 = std::chrono::high_resolution_clock::now(); auto nth = std::chrono::duration_cast(t2 - t1).count(); stats["nth"].emplace_back(nth); constexpr int STAT_PERIOD = 100000; if (!stats["nth"].empty() && stats["nth"].size() % STAT_PERIOD == 0) { for (auto& it : stats) { const std::string& key = it.first; std::vector& stat = it.second; int64_t sum_ = std::accumulate(stat.begin(), stat.end(), 0); int64_t num_ = stat.size(); stat.clear(); std::cout << "inner_" << key << "(ns): sum=" << sum_ << " num=" << num_ << " ave=" << 1.0 * sum_ / num_ << " ave*n=" << 1.0 * sum_ / STAT_PERIOD << std::endl; } } #endif closest_pt.clear(); for (auto& it : candidates) { closest_pt.emplace_back(it.Get()); } return closest_pt.empty() == false; } template size_t IVox::NumPoints() const { int num=0; for( auto it=grids_map_.begin();it!=grids_map_.end();++it) { num+=it->second->second.Size(); } return num; } template size_t IVox::NumValidGrids() const { return grids_map_.size(); } template void IVox::DownSample(double leafsize) { for( auto it=grids_map_.begin();it!=grids_map_.end();++it) { it->second->second.DownSample(leafsize); } } template void IVox::GenerateNearbyGrids() { if (options_.nearby_type_ == NearbyType::CENTER) { nearby_grids_.emplace_back(KeyType::Zero()); } else if (options_.nearby_type_ == NearbyType::NEARBY6) { nearby_grids_ = {KeyType(0, 0, 0), KeyType(-1, 0, 0), KeyType(1, 0, 0), KeyType(0, 1, 0), KeyType(0, -1, 0), KeyType(0, 0, -1), KeyType(0, 0, 1)}; } else if (options_.nearby_type_ == NearbyType::NEARBY18) { nearby_grids_ = {KeyType(0, 0, 0), KeyType(-1, 0, 0), KeyType(1, 0, 0), KeyType(0, 1, 0), KeyType(0, -1, 0), KeyType(0, 0, -1), KeyType(0, 0, 1), KeyType(1, 1, 0), KeyType(-1, 1, 0), KeyType(1, -1, 0), KeyType(-1, -1, 0), KeyType(1, 0, 1), KeyType(-1, 0, 1), KeyType(1, 0, -1), KeyType(-1, 0, -1), KeyType(0, 1, 1), KeyType(0, -1, 1), KeyType(0, 1, -1), KeyType(0, -1, -1)}; } else if (options_.nearby_type_ == NearbyType::NEARBY26) { nearby_grids_ = {KeyType(0, 0, 0), KeyType(-1, 0, 0), KeyType(1, 0, 0), KeyType(0, 1, 0), KeyType(0, -1, 0), KeyType(0, 0, -1), KeyType(0, 0, 1), KeyType(1, 1, 0), KeyType(-1, 1, 0), KeyType(1, -1, 0), KeyType(-1, -1, 0), KeyType(1, 0, 1), KeyType(-1, 0, 1), KeyType(1, 0, -1), KeyType(-1, 0, -1), KeyType(0, 1, 1), KeyType(0, -1, 1), KeyType(0, 1, -1), KeyType(0, -1, -1), KeyType(1, 1, 1), KeyType(-1, 1, 1), KeyType(1, -1, 1), KeyType(1, 1, -1), KeyType(-1, -1, 1), KeyType(-1, 1, -1), KeyType(1, -1, -1), KeyType(-1, -1, -1)}; } else { LOG(ERROR) << "Unknown nearby_type!"; } } template bool IVox::GetClosestPoint(const PointVector& cloud, PointVector& closest_cloud) { std::vector index(cloud.size()); for (int i = 0; i < cloud.size(); ++i) { index[i] = i; } closest_cloud.resize(cloud.size()); std::for_each(std::execution::par_unseq, index.begin(), index.end(), [&cloud, &closest_cloud, this](size_t idx) { PointType pt; if (GetClosestPoint(cloud[idx], pt)) { closest_cloud[idx] = pt; } else { closest_cloud[idx] = PointType(); } }); return true; } template void IVox::GetPoints(PointVector& points) { points.clear(); for(auto it=grids_cache_.begin();it!=grids_cache_.end();it++) { std::vector node_points=it->second.Points(); points.insert(points.end(),node_points.begin(),node_points.end()); } } template void IVox::AddPoints(const PointVector& points_to_add) { for (int i = 0; i < points_to_add.size(); ++i) { PointType pt = points_to_add[i]; auto key = Pos2Grid(ToEigen(pt)); auto iter = grids_map_.find(key); if (iter == grids_map_.end()) { PointType center; center.getVector3fMap() = key.template cast() * options_.resolution_; grids_cache_.push_front({key, NodeType(center, options_.resolution_)}); grids_map_.insert({key, grids_cache_.begin()}); grids_cache_.front().second.InsertPoint(pt); if (grids_map_.size() >= options_.capacity_) { grids_map_.erase(grids_cache_.back().first); grids_cache_.pop_back(); } } else { iter->second->second.InsertPoint(pt); grids_cache_.splice(grids_cache_.begin(), grids_cache_, iter->second); grids_map_[key] = grids_cache_.begin(); } } } template Eigen::Matrix IVox::Pos2Grid(const IVox::PtType& pt) const { return (pt * options_.inv_resolution_).array().round().template cast(); } template std::vector IVox::StatGridPoints() const { int num = grids_cache_.size(), valid_num = 0, max = 0, min = 100000000; int sum = 0, sum_square = 0; for (auto& it : grids_cache_) { int s = it.second.Size(); valid_num += s > 0; max = s > max ? s : max; min = s < min ? s : min; sum += s; sum_square += s * s; } float ave = float(sum) / num; float stddev = num > 1 ? sqrt((float(sum_square) - num * ave * ave) / (num - 1)) : 0; return std::vector{valid_num, ave, max, min, stddev}; } } // namespace faster_lio #endif