PointCloud::DetectPlanarPatches(
double normal_similarity_deg,
double coplanarity_deg,
double outlier_ratio,
double min_plane_edge_length,
size_t min_num_points,
const geometry::KDTreeSearchParam& search_param) const {
if (!HasNormals()) {
utility::LogError(
"DetectPlanarPatches requires pre-computed normal vectors.");
return {};
}
const Eigen::Vector3d min_bound = GetMinBound();
const Eigen::Vector3d max_bound = GetMaxBound();
if (min_plane_edge_length <= 0) {
min_plane_edge_length = 0.01 * (max_bound - min_bound).maxCoeff();
}
if (min_num_points == 0) {
min_num_points = std::max(static_cast<size_t>(10),
static_cast<size_t>(points_.size() * 0.001));
}
// identify the neighbors of each point in point cloud
geometry::KDTreeFlann kdtree;
kdtree.SetGeometry(*this);
std::vector<std::vector<int>> neighbors;
neighbors.resize(points_.size());
#pragma omp parallel for schedule(static)
for (int i = 0; i < static_cast<int>(points_.size()); i++) {
std::vector<int> indices;
std::vector<double> distance2;
kdtree.Search(points_[i], search_param, neighbors[i], distance2);
}
const double normal_similarity_rad = normal_similarity_deg * M_PI / 180.0;
const double coplanarity_rad = coplanarity_deg * M_PI / 180.0;
// partition the point cloud and search for planar regions
BoundaryVolumeHierarchyPtr root = std::make_shared<BoundaryVolumeHierarchy>(
this, min_bound, max_bound);
std::vector<PlaneDetectorPtr> planes;
std::vector<PlaneDetectorPtr> plane_points(points_.size(), nullptr);
SplitAndDetectPlanesRecursive(root, min_num_points,
std::cos(normal_similarity_rad),
std::cos(coplanarity_rad), outlier_ratio,
min_plane_edge_length, planes, plane_points);
// iteratively grow and merge planes until each is stable
bool changed;
do {
Grow(planes, plane_points, neighbors);
Merge(planes, plane_points, neighbors, *this);
changed = Update(planes);
} while (changed);
// extract planar patches by calculating the bounds of each detected plane
std::vector<std::shared_ptr<OrientedBoundingBox>> patches;
ExtractPatchesFromPlanes(planes, patches);
return patches;
}
min_plane_edge_length ,最小平面的长度
min_num_points ,最小平面的点数
通过kdtree 查找每个点云的邻居点 参数是通过设置,半径和最大数
static constexpr double radius = 1;
static constexpr int max_nn = 100;
const geometry::KDTreeSearchParam &search_param =
geometry::KDTreeSearchParamHybrid(radius, max_nn);
const double normal_similarity_rad = normal_similarity_deg * M_PI / 180.0;
const double coplanarity_rad = coplanarity_deg * M_PI / 180.0;
把输入的角度转为弧度
normal_similarity_deg 是以度表示的法线相似性阈值的角度。
normal_similarity_rad 存储了相同角度的弧度表示。
coplanarity_deg 是以度表示的共面性阈值的角度。
coplanarity_rad 存储了相同角度的弧度表示。
```cpp
SplitAndDetectPlanesRecursive(root, min_num_points,
std::cos(normal_similarity_rad),
std::cos(coplanarity_rad), outlier_ratio,
min_plane_edge_length, planes, plane_points);
这是关键,通过partition把点云数据分成8分,然后迭代在分,检测点云
bool SplitAndDetectPlanesRecursive(
const BoundaryVolumeHierarchyPtr& node,
size_t min_num_points,
double normal_similarity,
double coplanarity,
double outlier_ratio,
double plane_edge_length,
std::vector<PlaneDetectorPtr>& planes,
std::vector<PlaneDetectorPtr>& plane_points) {
// if there aren't enough points to find a good plane, don't even try
if (node->indices_.size() < min_num_points) return false;
bool node_has_plane = false;
bool child_has_plane = false;
// partition into eight children and check each recursively for a plane
node->Partition();
for (const auto& child : node->children_) {
if (child != nullptr &&
SplitAndDetectPlanesRecursive(
child, min_num_points, normal_similarity, coplanarity,
outlier_ratio, plane_edge_length, planes, plane_points)) {
child_has_plane = true;
}
}
if (!child_has_plane && node->level_ > 2) {
auto plane = std::make_shared<PlaneDetector>(normal_similarity,
coplanarity, outlier_ratio,
plane_edge_length);
if (plane->DetectFromPointCloud(node->point_cloud_, node->indices_)) {
node_has_plane = true;
planes.push_back(plane);
// assume ownership of these indices
for (const size_t& idx : plane->indices_) {
plane_points[idx] = plane;
}
}
}
return node_has_plane || child_has_plane;
}
通过partition把点云数据分成8分
void Partition() {
// Nothing to do if already partitioned
if (!leaf_) return;
// size of each child
const double child_size = size_ / 2.;
// Does this node have enough data to be able to partition further
if (indices_.size() <= min_points_ || child_size < min_size_ ||
indices_.size() < 2)
return;
// split points and create children
for (const size_t& pidx : indices_) {
// calculate child index comparing position to child center
const size_t cidx =
CalculateChildIndex(point_cloud_->points_[pidx]);
// if child does not yet exist, create and initialize
if (children_[cidx] == nullptr) {
const Eigen::Vector3d child_center =
CalculateChildCenter(cidx, child_size);
children_[cidx].reset(new BoundaryVolumeHierarchy(
point_cloud_, level_ + 1, child_center, min_points_,
min_size_, child_size, cidx));
children_[cidx]->indices_.reserve(indices_.size());
}
children_[cidx]->indices_.push_back(pidx);
}
// now that I have children, I am no longer a leaf node
leaf_ = false;
}