RSMesh 1.0.0
一个曲面重构的系统,输入为点云,输出为obj,stl等主流格式的网格文件,使用的方法为径向基函数插值,采取了并行优化、Intel-MKL等优化措施,支持百万级别的点云
载入中...
搜索中...
未找到
normal_estimator.cpp
1//
2// Created by RainSure on 2023/11/3.
3//
4
5#include "point_cloud/normal_estimator.h"
6
7#include <stdexcept>
8#include <tuple>
9#include <numeric>
10#include <queue>
11#include <iostream>
12
13#include "common/eigen_utility.h"
14#include "point_cloud/plane_estimator.h"
15#include "geometry/bbox3d.h"
16
17namespace rsmesh {
18 namespace point_cloud {
19 normal_estimator::normal_estimator(const geometry::points3d &points) :
20 n_points_(static_cast<index_t>(points.rows()))
21 , points_(points)
22 , tree_(points, true){}
23
24 normal_estimator &normal_estimator::estimate_with_knn(rsmesh::index_t k, double plane_factor_threshold) {
25 return estimate_with_knn(std::vector<index_t>{k}, plane_factor_threshold);
26 }
27
28 normal_estimator &normal_estimator::estimate_with_radius(double radius, double plane_factor_threshold) {
29 normals_ = geometry::vectors3d::Zero(n_points_, 3);
30
31 {
32 std::vector<index_t> nn_indices;
33 std::vector<double> nn_distances;
34
35 for(index_t i = 0; i < n_points_; i ++) {
36 auto p = points_.row(i);
37 std::tie(nn_indices, nn_distances) = tree_.radius_search(p, radius);
38 if(nn_indices.size() < 3) {
39 continue;
40 }
41
42 plane_estimator est(points_(nn_indices, Eigen::all));
43
44 if(est.plane_factor() >= plane_factor_threshold) {
45 normals_.row(i) = est.plane_normal();
46 }
47 }
48 }
49
50 return *this;
51 }
52
53 normal_estimator& normal_estimator::estimate_with_knn(const std::vector<index_t> &ks,
54 double plane_factor_threshold) {
55 normals_ = geometry::vectors3d::Zero(n_points_, 3);
56
57 std::vector<index_t> nn_indices;
58 std::vector<double> nn_distances;
59
60 std::vector<index_t> ks_sorted(ks);
61 std::sort(ks_sorted.rbegin(), ks_sorted.rend());
62 auto k_max = ks_sorted.front();
63
64 std::vector<double> plane_factors;
65 std::vector<geometry::vector3d> plane_normals;
66
67 for(index_t i = 0; i < n_points_; i ++) {
68 auto p = points_.row(i);
69
70 std::tie(nn_indices, nn_distances) = tree_.knn_search(p, k_max);
71
72 plane_factors.clear();
73 plane_normals.clear();
74
75 for(auto k : ks_sorted) {
76 nn_indices.resize(k);
77 auto tmp = points_(nn_indices, Eigen::all);
78 plane_estimator est(points_(nn_indices, Eigen::all));
79 plane_factors.push_back(est.plane_factor());
80 plane_normals.push_back(est.plane_normal());
81 }
82
83 auto best = std::distance(plane_factors.begin(),
84 std::max_element(plane_factors.begin(), plane_factors.end()));
85
86 if (plane_factors.at(best) >= plane_factor_threshold) {
87 normals_.row(i) = plane_normals.at(best);
88 }
89 }
90 return *this;
91 }
92
93 geometry::vectors3d normal_estimator::orient_by_outward_vector(const geometry::vector3d &v) {
94 if(n_points_ > 0 and normals_.rows() == 0) {
95 throw std::runtime_error("Normals have not been estimated yet.");
96 }
97
98 for(index_t i = 0; i < n_points_; i ++) {
99 auto n = normals_.row(i);
100 if(n.dot(v) < 0.0) {
101 n = -n;
102 }
103 }
104
105 return normals_;
106 }
107
109 public:
110 weighted_pair(index_t first, index_t second, double weight)
111 : first_(first), second_(second), weight_(weight) {}
112
113 bool operator<(const weighted_pair& rhs) const {
114 return weight_ < rhs.weight_;
115 }
116
117 [[nodiscard]] index_t first() const {return first_;}
118
119 [[nodiscard]] index_t second() const {return second_;}
120
121 [[nodiscard]] double weight() const {return weight_;}
122
123
124 private:
125 index_t first_;
126 index_t second_;
127 double weight_;
128 };
129
130
131 geometry::vectors3d normal_estimator::orient_closed_surface(rsmesh::index_t k) {
132 if(n_points_ > 0 and normals_.rows() == 0) {
133 throw std::runtime_error("Normals have not been estimated yet.");
134 }
135
136 auto bbox = geometry::bbox3d::from_points(points_);
137 auto center = bbox.center();
138 geometry::point3d p_outer(center(0), bbox.min()(1) - 1.0, center(2));
139
140 std::vector<bool> oriented(n_points_, false);
141
142 for(index_t i = 0; i < n_points_; i ++) {
143 if(normals_.row(i).isZero()) {
144 oriented.at(i) = true;
145 }
146 }
147
148 std::vector<index_t> indices(n_points_);
149 std::iota(begin(indices), end(indices), 0);
150 {
151 std::vector<double> distances(n_points_);
152 for(index_t i = 0; i < n_points_; i ++) {
153 auto p = points_.row(i);
154 distances.at(i) = (p_outer - p).norm();
155 }
156
157 std::sort(begin(indices), end(indices), [&](int i, int j) {
158 return distances.at(i) < distances.at(j);
159 });
160 }
161
162 auto indices_it = begin(indices);
163
164 std::priority_queue<weighted_pair> queue;
165 std::vector<index_t> nn_indices;
166 std::vector<double> nn_distances;
167
168 index_t n_connected_components = 0;
169 while(std::find(begin(oriented), end(oriented), false) != end(oriented)) {
170 while(oriented.at(*indices_it)) {
171 indices_it ++;
172 }
173
174 auto i_closest = *indices_it;
175 auto p_closest = points_.row(i_closest);
176 if(normals_.row(i_closest).dot(p_outer - p_closest) < 0.0) {
177 normals_.row(i_closest) *= -1.0;
178 }
179 oriented.at(i_closest) = true;
180
181 std::tie(nn_indices, nn_distances) = tree_.knn_search(p_closest, k);
182 for(auto j : nn_indices) {
183 if(oriented.at(j)) {
184 continue;
185 }
186
187 auto weight = std::abs(normals_.row(i_closest).dot(normals_.row(j)))
188 / (p_closest - points_.row(j)).norm();
189 queue.emplace(i_closest, j, weight);
190 }
191
192 while(!queue.empty()) {
193 auto pair = queue.top();
194 queue.pop();
195
196 auto i = pair.first();
197 auto j = pair.second();
198 if(oriented.at(j)) {
199 continue;
200 }
201
202 if(normals_.row(i).dot(normals_.row(j)) < 0.0) {
203 normals_.row(j) *= -1.0;
204 }
205 oriented.at(j) = true;
206
207 auto p = points_.row(j);
208 std::tie(nn_indices, nn_distances) = tree_.knn_search(p, k);
209 for(auto kk : nn_indices) {
210 if(oriented.at(kk)) {
211 continue;
212 }
213
214 auto weight = std::abs(normals_.row(j).dot(normals_.row(kk)))
215 / (p - points_.row(kk)).norm();
216
217 queue.emplace(j, kk, weight);
218 }
219 }
220
221 n_connected_components ++;
222 }
223
224 std::cout << "Number of connected components: " << n_connected_components << std::endl;
225
226 return normals_;
227 }
228
229 } // rsmesh
230} // point_cloud
Eigen::Matrix< double, Eigen::Dynamic, 3, Eigen::RowMajor > vectors3d
3维向量的集合
Definition point3d.h:44
vectors3d points3d
3维点的集合
Definition point3d.h:48
vector3d point3d
3维点
Definition point3d.h:39
本系统的主命名空间,包含了common, examples, fmm, geometry, numeric, point_cloud等子命名空间