#include #include #include #include #include #include #include #include #include "hilbert.hpp" namespace faster_lio { // squared distance of two pcl points template inline double distance2(const PointT& pt1, const PointT& pt2) { Eigen::Vector3f d = pt1.getVector3fMap() - pt2.getVector3fMap(); return d.squaredNorm(); } // convert from pcl point to eigen template inline Eigen::Matrix ToEigen(const PointType& pt) { return Eigen::Matrix(pt.x, pt.y, pt.z); } template <> inline Eigen::Matrix ToEigen(const pcl::PointXYZ& pt) { return pt.getVector3fMap(); } template <> inline Eigen::Matrix ToEigen(const pcl::PointXYZI& pt) { return pt.getVector3fMap(); } template <> inline Eigen::Matrix ToEigen(const pcl::PointXYZINormal& pt) { return pt.getVector3fMap(); } template class IVoxNode { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW; struct DistPoint; IVoxNode() = default; IVoxNode(const PointT& center, const float& side_length) {} /// same with phc void InsertPoint(const PointT& pt); std::vector Points(){return points_;} inline bool Empty() const; inline std::size_t Size() const; void DownSample(double leafSize=0.02); inline PointT GetPoint(const std::size_t idx) const; int KNNPointByCondition(std::vector& dis_points, const PointT& point, const int& K, const double& max_range); int UpdateRecord(); private: std::vector points_; }; template class IVoxNodePhc { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW; struct DistPoint; struct PhcCube; IVoxNodePhc() = default; IVoxNodePhc(const PointT& center, const float& side_length, const int& phc_order = 6); void InsertPoint(const PointT& pt); void ErasePoint(const PointT& pt, const double erase_distance_th_); inline bool Empty() const; inline std::size_t Size() const; PointT GetPoint(const std::size_t idx) const; bool NNPoint(const PointT& cur_pt, DistPoint& dist_point) const; int KNNPointByCondition(std::vector& dis_points, const PointT& cur_pt, const int& K = 5, const double& max_range = 5.0); private: uint32_t CalculatePhcIndex(const PointT& pt) const; private: std::vector phc_cubes_; PointT center_; float side_length_ = 0; int phc_order_ = 6; float phc_side_length_ = 0; float phc_side_length_inv_ = 0; Eigen::Matrix min_cube_; }; template struct IVoxNode::DistPoint { double dist = 0; IVoxNode* node = nullptr; int idx = 0; DistPoint() = default; DistPoint(const double d, IVoxNode* n, const int i) : dist(d), node(n), idx(i) {} PointT Get() { return node->GetPoint(idx); } inline bool operator()(const DistPoint& p1, const DistPoint& p2) { return p1.dist < p2.dist; } inline bool operator<(const DistPoint& rhs) { return dist < rhs.dist; } }; template void IVoxNode::InsertPoint(const PointT& pt) { points_.emplace_back(pt); } template void IVoxNode::DownSample(double leafSize) { /*pcl::PointCloud result; pcl::VoxelGrid grid; grid.setLeafSize(leafSize,leafSize,leafSize); grid.setInputCloud(points_.makeShared()); grid.filter(result); points_=result;*/ } template bool IVoxNode::Empty() const { return points_.empty(); } template std::size_t IVoxNode::Size() const { return points_.size(); } template PointT IVoxNode::GetPoint(const std::size_t idx) const { return points_[idx]; } template int IVoxNode::KNNPointByCondition(std::vector& dis_points, const PointT& point, const int& K, const double& max_range) { std::size_t old_size = dis_points.size(); // #define INNER_TIMER #ifdef INNER_TIMER static std::unordered_map> stats; if (stats.empty()) { stats["dis"] = std::vector(); stats["put"] = std::vector(); stats["nth"] = std::vector(); } #endif for (const auto& pt : points_) { #ifdef INNER_TIMER auto t0 = std::chrono::high_resolution_clock::now(); #endif double d = distance2(pt, point); #ifdef INNER_TIMER auto t1 = std::chrono::high_resolution_clock::now(); #endif if (d < max_range * max_range) { dis_points.template emplace_back(DistPoint(d, this, &pt - points_.data())); } #ifdef INNER_TIMER auto t2 = std::chrono::high_resolution_clock::now(); auto dis = std::chrono::duration_cast(t1 - t0).count(); stats["dis"].emplace_back(dis); auto put = std::chrono::duration_cast(t2 - t1).count(); stats["put"].emplace_back(put); #endif } #ifdef INNER_TIMER auto t1 = std::chrono::high_resolution_clock::now(); #endif // sort by distance if (old_size + K >= dis_points.size()) { } else { std::nth_element(dis_points.begin() + old_size, dis_points.begin() + old_size + K - 1, dis_points.end()); dis_points.resize(old_size + K); } #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 return dis_points.size(); } template struct IVoxNodePhc::DistPoint { double dist = 0; IVoxNodePhc* node = nullptr; int idx = 0; DistPoint() {} DistPoint(const double d, IVoxNodePhc* n, const int i) : dist(d), node(n), idx(i) {} PointT Get() { return node->GetPoint(idx); } inline bool operator()(const DistPoint& p1, const DistPoint& p2) { return p1.dist < p2.dist; } inline bool operator<(const DistPoint& rhs) { return dist < rhs.dist; } }; template struct IVoxNodePhc::PhcCube { uint32_t idx = 0; pcl::CentroidPoint mean; PhcCube(uint32_t index, const PointT& pt) { mean.add(pt); } void AddPoint(const PointT& pt) { mean.add(pt); } PointT GetPoint() const { PointT pt; mean.get(pt); return std::move(pt); } }; template IVoxNodePhc::IVoxNodePhc(const PointT& center, const float& side_length, const int& phc_order) : center_(center), side_length_(side_length), phc_order_(phc_order) { assert(phc_order <= 8); phc_side_length_ = side_length_ / (std::pow(2, phc_order_)); phc_side_length_inv_ = (std::pow(2, phc_order_)) / side_length_; min_cube_ = center_.getArray3fMap() - side_length / 2.0; phc_cubes_.reserve(64); } template void IVoxNodePhc::InsertPoint(const PointT& pt) { uint32_t idx = CalculatePhcIndex(pt); PhcCube cube{idx, pt}; auto it = std::lower_bound(phc_cubes_.begin(), phc_cubes_.end(), cube, [](const PhcCube& a, const PhcCube& b) { return a.idx < b.idx; }); if (it == phc_cubes_.end()) { phc_cubes_.emplace_back(cube); } else { if (it->idx == idx) { it->AddPoint(pt); } else { phc_cubes_.insert(it, cube); } } } template void IVoxNodePhc::ErasePoint(const PointT& pt, const double erase_distance_th_) { uint32_t idx = CalculatePhcIndex(pt); PhcCube cube{idx, pt}; auto it = std::lower_bound(phc_cubes_.begin(), phc_cubes_.end(), cube, [](const PhcCube& a, const PhcCube& b) { return a.idx < b.idx; }); if (erase_distance_th_ > 0) { } if (it != phc_cubes_.end() && it->idx == idx) { phc_cubes_.erase(it); } } template bool IVoxNodePhc::Empty() const { return phc_cubes_.empty(); } template std::size_t IVoxNodePhc::Size() const { return phc_cubes_.size(); } template PointT IVoxNodePhc::GetPoint(const std::size_t idx) const { return phc_cubes_[idx].GetPoint(); } template bool IVoxNodePhc::NNPoint(const PointT& cur_pt, DistPoint& dist_point) const { if (phc_cubes_.empty()) { return false; } uint32_t cur_idx = CalculatePhcIndex(cur_pt); PhcCube cube{cur_idx, cur_pt}; auto it = std::lower_bound(phc_cubes_.begin(), phc_cubes_.end(), cube, [](const PhcCube& a, const PhcCube& b) { return a.idx < b.idx; }); if (it == phc_cubes_.end()) { it--; dist_point = DistPoint(distance2(cur_pt, it->GetPoint()), this, it - phc_cubes_.begin()); } else if (it == phc_cubes_.begin()) { dist_point = DistPoint(distance2(cur_pt, it->GetPoint()), this, it - phc_cubes_.begin()); } else { auto last_it = it; last_it--; double d1 = distance2(cur_pt, it->GetPoint()); double d2 = distance2(cur_pt, last_it->GetPoint()); if (d1 > d2) { dist_point = DistPoint(d2, this, it - phc_cubes_.begin()); } else { dist_point = DistPoint(d1, this, it - phc_cubes_.begin()); } } return true; } template int IVoxNodePhc::KNNPointByCondition(std::vector& dis_points, const PointT& cur_pt, const int& K, const double& max_range) { uint32_t cur_idx = CalculatePhcIndex(cur_pt); PhcCube cube{cur_idx, cur_pt}; auto it = std::lower_bound(phc_cubes_.begin(), phc_cubes_.end(), cube, [](const PhcCube& a, const PhcCube& b) { return a.idx < b.idx; }); const int max_search_cube_side_length = std::pow(2, std::ceil(std::log2(max_range * phc_side_length_inv_))); const int max_search_idx_th = 8 * max_search_cube_side_length * max_search_cube_side_length * max_search_cube_side_length; auto create_dist_point = [&cur_pt, this](typename std::vector::const_iterator forward_it) { double d = distance2(forward_it->GetPoint(), cur_pt); return DistPoint(d, this, forward_it - phc_cubes_.begin()); }; typename std::vector::const_iterator forward_it(it); typename std::vector::const_reverse_iterator backward_it(it); if (it != phc_cubes_.end()) { dis_points.emplace_back(create_dist_point(it)); forward_it++; } if (backward_it != phc_cubes_.rend()) { backward_it++; } auto forward_reach_boundary = [&]() { return forward_it == phc_cubes_.end() || forward_it->idx - cur_idx > max_search_idx_th; }; auto backward_reach_boundary = [&]() { return backward_it == phc_cubes_.rend() || cur_idx - backward_it->idx > max_search_idx_th; }; while (!forward_reach_boundary() && !backward_reach_boundary()) { if (forward_it->idx - cur_idx > cur_idx - backward_it->idx) { dis_points.emplace_back(create_dist_point(forward_it)); forward_it++; } else { dis_points.emplace_back(create_dist_point(backward_it.base())); backward_it++; } if (dis_points.size() > K) { break; } } if (forward_reach_boundary()) { while (!backward_reach_boundary() && dis_points.size() < K) { dis_points.emplace_back(create_dist_point(backward_it.base())); backward_it++; } } if (backward_reach_boundary()) { while (!forward_reach_boundary() && dis_points.size() < K) { dis_points.emplace_back(create_dist_point(forward_it)); forward_it++; } } return dis_points.size(); } template uint32_t IVoxNodePhc::CalculatePhcIndex(const PointT& pt) const { Eigen::Matrix eposf = (pt.getVector3fMap() - min_cube_) * phc_side_length_inv_; Eigen::Matrix eposi = eposf.template cast(); for (int i = 0; i < dim; ++i) { if (eposi(i, 0) < 0) { eposi(i, 0) = 0; } if (eposi(i, 0) > std::pow(2, phc_order_)) { eposi(i, 0) = std::pow(2, phc_order_) - 1; } } std::array apos{eposi(0), eposi(1), eposi(2)}; std::array tmp = hilbert::v2::PositionToIndex(apos); uint32_t idx = (uint32_t(tmp[0]) << 16) + (uint32_t(tmp[1]) << 8) + (uint32_t(tmp[2])); return idx; } } // namespace faster_lio