8#include <unordered_set>
9#include <gtsam_points/util/easy_profiler.hpp>
10#include <gtsam_points/segmentation/region_growing.hpp>
12namespace gtsam_points {
14template <
typename Po
intCloud>
15RegionGrowingContext region_growing_init_(
16 const PointCloud& points,
17 const NearestNeighborSearch& search,
18 const Eigen::Vector4d& seed_point,
19 const RegionGrowingParams& params) {
24 if (!search.knn_search(seed_point.data(), 1, &index, &sq_distance)) {
25 return RegionGrowingContext();
28 RegionGrowingContext context;
29 context.seed_points.emplace_back(index);
30 context.visited_seeds.resize(frame::size(points), 0);
34template <
typename Po
intCloud>
35bool region_growing_step_(
36 RegionGrowingContext& context,
37 const PointCloud& points,
38 const NearestNeighborSearch& search,
39 const RegionGrowingParams& params) {
41 if (frame::has_normals(points) ==
false) {
42 std::cerr <<
"warning: normals are required for region growing" << std::endl;
46 if (context.visited_seeds.size() != frame::size(points)) {
47 std::cerr <<
"error: context.visited_seeds.size() != frame::size(points)" << std::endl;
51 if (context.seed_points.empty()) {
55 if (context.cluster_indices.size() > params.max_cluster_size) {
56 context.seed_points.clear();
60 const int seed_index = context.seed_points.front();
61 context.seed_points.pop_front();
63 if (context.visited_seeds[seed_index]) {
65 return region_growing_step_(context, points, search, params);
69 context.visited_seeds[seed_index] = 1;
70 context.cluster_indices.emplace_back(seed_index);
73 std::vector<size_t> neighbor_indices;
74 std::vector<double> neighbor_sq_dists;
76 .radius_search(frame::point(points, seed_index).data(), params.distance_threshold, neighbor_indices, neighbor_sq_dists, params.max_cluster_size);
78 const double sq_distance_threshold = params.distance_threshold * params.distance_threshold;
79 const double cosine_threshold = std::cos(params.angle_threshold);
80 for (
size_t i = 0; i < neighbor_indices.size(); i++) {
82 if (context.visited_seeds[neighbor_indices[i]]) {
87 if (neighbor_sq_dists[i] > sq_distance_threshold) {
92 const auto& seed_normal = frame::normal(points, seed_index);
93 const auto& pt_normal = frame::normal(points, neighbor_indices[i]);
94 if (seed_normal.dot(pt_normal) < cosine_threshold) {
99 context.seed_points.emplace_back(neighbor_indices[i]);
105template <
typename Po
intCloud>
106void region_growing_dilation_(
107 RegionGrowingContext& context,
108 const PointCloud& points,
109 const NearestNeighborSearch& search,
110 const RegionGrowingParams& params) {
112 std::vector<std::unordered_set<size_t>> new_indices(params.num_threads);
115#pragma omp parallel for num_threads(params.num_threads) schedule(guided, 4)
116 for (
size_t i = 0; i < context.cluster_indices.size(); i++) {
117 std::vector<size_t> indices;
118 std::vector<double> sq_dists;
119 search.radius_search(frame::point(points, context.cluster_indices[i]).data(), params.dilation_radius, indices, sq_dists);
122 const int thread_num = omp_get_thread_num();
124 const int thread_num = 0;
127 new_indices[thread_num].insert(indices.begin(), indices.end());
131 for (
size_t i = 1; i < new_indices.size(); i++) {
132 new_indices[0].insert(new_indices[i].begin(), new_indices[i].end());
135 context.cluster_indices.assign(new_indices[0].begin(), new_indices[0].end());
136 std::sort(context.cluster_indices.begin(), context.cluster_indices.end());
139template <
typename Po
intCloud>
140bool region_growing_update_(
141 RegionGrowingContext& context,
142 const PointCloud& points,
143 const NearestNeighborSearch& search,
144 const RegionGrowingParams& params) {
146 if (context.seed_points.empty()) {
150 for (
int i = 0; i < params.max_steps; i++) {
151 if (context.seed_points.empty()) {
155 region_growing_step_(context, points, search, params);
158 if (context.seed_points.empty() && params.dilation_radius > 0.0) {
159 region_growing_dilation_(context, points, search, params);
162 return context.seed_points.empty();