37 #ifndef NORMALS_HEADER
38 #define NORMALS_HEADER
42 #include <Eigen/Dense>
45 #include <nanoflann.hpp>
53 #define USE_OPENMP_FOR_NORMEST
58 const Eigen::MatrixX3d& pts;
59 Eigen::MatrixX3d& nls;
60 std::vector<double> densities;
66 size_t neighborhood_size;
71 std::function<void(
int)> progressCallback;
75 const Eigen::MatrixX3d&
get_points()
const {
return pts; }
78 int&
get_T() {
return n_planes; }
81 size_t&
get_K() {
return neighborhood_size; }
87 const int&
get_T()
const {
return n_planes; }
90 const size_t&
get_K()
const {
return neighborhood_size; }
97 typedef nanoflann::KDTreeEigenMatrixAdaptor<Eigen::MatrixX3d>
107 tol_angle_rad = 0.79;
108 neighborhood_size = 200;
125 srand(
static_cast<unsigned int>(time(
NULL)));
128 std::vector<size_t> vecInt(1000000);
129 for (
size_t i = 0; i < vecInt.size(); i++) {
130 vecInt[i] =
static_cast<size_t>(rand());
134 std::vector<float> conf_interv(n_planes);
135 for (
int i = 0; i < n_planes; i++) {
136 conf_interv[i] = 2.f / std::sqrt(i + 1.f);
140 std::vector<int> permutation(pts.rows());
141 for (
int i = 0; i < pts.rows(); i++) {
144 for (
int i = 0; i < pts.rows(); i++) {
145 int j = rand() % pts.rows();
146 std::swap(permutation[i], permutation[j]);
150 std::vector<Eigen::Matrix3d> rotMat;
151 std::vector<Eigen::Matrix3d> rotMatInv;
152 generate_rotation_matrix(rotMat, rotMatInv, n_rot * 200);
166 nls.resize(pts.rows(), 3);
171 tree.index_->buildIndex();
174 densities.resize(pts.rows());
175 #if defined(USE_OPENMP_FOR_NORMEST)
176 #pragma omp parallel for schedule(guided)
178 for (
int per = 0; per < pts.rows(); per++) {
180 int n = permutation[per];
182 const Eigen::Vector3d& pt_query = pts.row(n);
183 std::vector<Eigen::MatrixX3d::Index> pointIdxSearch(k_density + 1);
184 std::vector<double> pointSquaredDistance(k_density + 1);
187 tree.index_->knnSearch(&pt_query[0], k_density + 1,
189 &pointSquaredDistance[0]);
191 for (
size_t i = 0; i < pointSquaredDistance.size(); i++) {
192 d += std::sqrt(pointSquaredDistance[i]);
194 d /= pointSquaredDistance.size() - 1;
197 if (progressCallback) {
198 progressCallback(++progress);
205 Eigen::MatrixX3i trip;
207 list_of_triplets(trip, neighborhood_size, rotations * n_planes,
211 #if defined(USE_OPENMP_FOR_NORMEST)
212 #pragma omp parallel for schedule(guided)
214 for (
int per = 0; per < pts.rows(); per++) {
216 int n = permutation[per];
219 std::vector<Eigen::MatrixX3d::Index> pointIdxSearch;
220 std::vector<double> pointSquaredDistance;
222 const Eigen::Vector3d& pt_query = pts.row(n);
223 pointIdxSearch.resize(neighborhood_size);
224 pointSquaredDistance.resize(neighborhood_size);
225 tree.index_->knnSearch(&pt_query[0], neighborhood_size,
227 &pointSquaredDistance[0]);
230 list_of_triplets(trip, rotations * n_planes, pointIdxSearch,
234 size_t points_size = pointIdxSearch.size();
235 Eigen::MatrixX3d
points(points_size, 3);
236 for (
size_t pt = 0; pt < pointIdxSearch.size(); pt++) {
237 points.row(pt) = pts.row(pointIdxSearch[pt]);
240 std::vector<Eigen::Vector3d> normals_vec(rotations);
241 std::vector<float> normals_conf(rotations);
243 for (
int i = 0; i < rotations; i++) {
244 Eigen::MatrixX3i triplets =
245 trip.block(i * n_planes, 0, n_planes, 3);
247 for (
size_t pt = 0; pt < points_size; pt++) {
248 points.row(pt) = rotMat[(n + i) % rotMat.size()] *
249 points.row(pt).transpose();
251 normals_conf[i] = normal_at_point(d1, d2,
points, n, triplets,
254 for (
size_t pt = 0; pt < points_size; pt++) {
255 points.row(pt) = pts.row(pointIdxSearch[pt]);
257 normals_vec[i] = rotMatInv[(n + i) % rotMat.size()] *
258 nls.row(n).transpose();
261 nls.row(n) = normal_selection(rotations, normals_vec, normals_conf);
263 if (progressCallback) {
264 progressCallback(++progress);
278 inline void generate_rotation_matrix(
279 std::vector<Eigen::Matrix3d>& rotMat,
280 std::vector<Eigen::Matrix3d>& rotMatInv,
285 if (rotations == 0) {
286 Eigen::Matrix3d rMat;
287 rMat << 1, 0, 0, 0, 1, 0, 0, 0, 1;
288 rotMat.push_back(rMat);
289 rotMatInv.push_back(rMat);
291 for (
int i = 0; i < rotations; i++) {
293 static_cast<double>(rand()) / RAND_MAX * 2 *
M_PI;
294 double phi =
static_cast<double>(rand()) / RAND_MAX * 2 *
M_PI;
295 double psi =
static_cast<double>(rand()) / RAND_MAX * 2 *
M_PI;
299 Rt << 1, 0, 0, 0, cos(theta), -sin(theta), 0, sin(theta),
301 Rph << cos(phi), 0, sin(phi), 0, 1, 0, -sin(phi), 0, cos(phi);
302 Rps << cos(psi), -sin(psi), 0, sin(psi), cos(psi), 0, 0, 0, 1;
303 Eigen::Matrix3d Rtinv;
304 Eigen::Matrix3d Rphinv;
305 Eigen::Matrix3d Rpsinv;
306 Rtinv << 1, 0, 0, 0, cos(theta), sin(theta), 0, -sin(theta),
308 Rphinv << cos(phi), 0, -sin(phi), 0, 1, 0, sin(phi), 0,
310 Rpsinv << cos(psi), sin(psi), 0, -sin(psi), cos(psi), 0, 0, 0,
313 Eigen::Matrix3d rMat = Rt * Rph * Rps;
314 Eigen::Matrix3d rMatInv = Rpsinv * Rphinv * Rtinv;
315 rotMat.push_back(rMat);
316 rotMatInv.push_back(rMatInv);
329 inline void list_of_triplets(Eigen::MatrixX3i& triplets,
330 size_t number_of_points,
331 size_t triplet_number,
332 const std::vector<size_t>& vecRandInt) {
333 size_t S = vecRandInt.size();
334 triplets.resize(triplet_number, 3);
335 size_t pos = vecRandInt[0] % S;
336 for (
size_t i = 0; i < triplet_number; i++) {
338 triplets(i, 0) =
static_cast<int>(vecRandInt[pos % S] %
340 triplets(i, 1) =
static_cast<int>(
341 vecRandInt[(pos + vecRandInt[(pos + 1) % S]) % S] %
343 triplets(i, 2) =
static_cast<int>(
345 vecRandInt[(pos + 1 +
346 vecRandInt[(pos + 2) % S]) %
350 pos += vecRandInt[(pos + 3) % S] % S;
351 }
while (triplets(i, 0) == triplets(i, 1) ||
352 triplets(i, 1) == triplets(i, 2) ||
353 triplets(i, 2) == triplets(i, 0));
364 int dichotomic_search_nearest(
const std::vector<double> elems,
double d) {
366 size_t i2 = elems.size() - 1;
370 if (elems[i3] == d) {
380 return static_cast<int>(i3);
391 inline void list_of_triplets(
392 Eigen::MatrixX3i& triplets,
393 size_t triplet_number,
394 const std::vector<Eigen::MatrixX3d::Index>& pointIdxSearch,
395 const std::vector<size_t>& vecRandInt) {
396 std::vector<double> dists;
398 for (
size_t i = 0; i < pointIdxSearch.size(); i++) {
399 sum += densities[pointIdxSearch[i]];
400 dists.push_back(sum);
403 size_t S = vecRandInt.size();
404 size_t number_of_points = pointIdxSearch.size();
405 triplets.resize(triplet_number, 3);
406 size_t pos = vecRandInt[0] % S;
408 for (
size_t i = 0; i < triplet_number; i++) {
410 double d = (vecRandInt[pos % S] + 0.) / RAND_MAX * sum;
411 triplets(i, 0) = dichotomic_search_nearest(dists, d);
412 d = (vecRandInt[(pos + vecRandInt[(pos + 1) % S]) % S] + 0.) /
414 triplets(i, 1) = dichotomic_search_nearest(dists, d);
415 d = (vecRandInt[(pos + vecRandInt[(pos + 1 +
416 vecRandInt[(pos + 2) % S]) %
421 triplets(i, 2) = dichotomic_search_nearest(dists, d);
422 pos += vecRandInt[(pos + 3) % S] % S;
423 }
while (triplets(i, 0) == triplets(i, 1) ||
424 triplets(i, 1) == triplets(i, 2) ||
425 triplets(i, 2) == triplets(i, 0));
438 float normal_at_point(
const int d1,
440 const Eigen::MatrixX3d&
points,
442 Eigen::MatrixX3i& triplets,
443 std::vector<float>& conf_interv) {
445 nls.row(n).setZero();
450 std::vector<double> votes(d1 * d2);
451 std::vector<Eigen::Vector3d> votesV(d1 * d2);
452 for (
int i = 0; i < d1; i++) {
453 for (
int j = 0; j < d2; j++) {
454 votes[i + j * d1] = 0;
455 votesV[i + j * d1] = Eigen::Vector3d(0, 0, 0);
463 for (
int n_try = 0; n_try < n_planes; n_try++) {
464 int p0 = triplets(n_try, 0);
465 int p1 = triplets(n_try, 1);
466 int p2 = triplets(n_try, 2);
469 points.row(p1).transpose() -
points.row(p0).transpose();
471 points.row(p2).transpose() -
points.row(p0).transpose();
473 Eigen::Vector3d Pn = v1.cross(v2);
475 if (Pn.dot(
points.row(p0).transpose()) > 0) {
479 double phi = acos(Pn[2]);
480 double dphi =
M_PI / n_phi;
481 int posp =
static_cast<int>(
floor((phi + dphi / 2) * n_phi /
M_PI));
484 if (posp == 0 || posp == n_phi) {
488 acos(Pn[0] / sqrt(Pn[0] * Pn[0] + Pn[1] * Pn[1]));
493 double dtheta =
M_PI / (n_phi * sin(posp * dphi));
494 post =
static_cast<int>(
floor((theta + dtheta / 2) / dtheta)) %
501 votes[post + posp * d1] += 1.;
502 votesV[post + posp * d1] += Pn;
504 max1 = votes[i1 + j1 * d1] / (n_try + 1);
505 float max2 = votes[i2 + j2 * d1] / (n_try + 1);
506 float votes_val = votes[post + posp * d1] / (n_try + 1);
508 if (votes_val > max1) {
515 }
else if (votes_val > max2 && post != i1 && posp != j1) {
521 if (max1 - conf_interv[n_try] > max2) {
526 votesV[i1 + j1 * d1].normalize();
527 nls.row(n) = votesV[i1 + j1 * d1];
539 inline Eigen::Vector3d normal_selection(
541 std::vector<Eigen::Vector3d>& normals_vec,
542 const std::vector<float>& normals_conf) {
543 std::vector<bool> normals_use(rotations,
true);
545 for (
int i = 1; i < rotations; i++) {
546 if (normals_vec[0].
dot(normals_vec[i]) < 0) {
547 normals_vec[i] *= -1;
551 Eigen::Vector3d normal_final;
552 std::vector<std::pair<Eigen::Vector3d, float>> normals_fin;
553 int number_to_test = rotations;
554 while (number_to_test > 0) {
558 for (
int i = 0; i < rotations; i++) {
559 if (normals_use[i] && normals_conf[i] > max_conf) {
560 max_conf = normals_conf[i];
565 normals_fin.push_back(std::pair<Eigen::Vector3d, float>(
566 normals_vec[idx] * normals_conf[idx], normals_conf[idx]));
567 normals_use[idx] =
false;
570 for (
int i = 0; i < rotations; i++) {
571 if (normals_use[i] &&
572 acos(normals_vec[idx].
dot(normals_vec[i])) <
574 normals_use[i] =
false;
576 normals_fin.back().first +=
577 normals_vec[i] * normals_conf[i];
578 normals_fin.back().second += normals_conf[i];
583 normal_final = normals_fin[0].first;
584 float conf_fin = normals_fin[0].second;
585 for (
size_t i = 1; i < normals_fin.size(); i++) {
586 if (normals_fin[i].second > conf_fin) {
587 conf_fin = normals_fin[i].second;
588 normal_final = normals_fin[i].first;
592 normal_final.normalize();
std::function< void(std::shared_ptr< core::Tensor >)> callback
Eigen_Normal_Estimator(const Eigen::MatrixX3d &points, Eigen::MatrixX3d &normals)
bool & density_sensitive()
const Eigen::MatrixX3d & get_points() const
const int & get_T() const
const bool & density_sensitive() const
Eigen::MatrixX3d & get_normals()
const size_t & get_K_density() const
void setProgressCallback(std::function< void(int)> callback)
nanoflann::KDTreeEigenMatrixAdaptor< Eigen::MatrixX3d > kd_tree
const double & get_tol_angle_rad() const
double & get_tol_angle_rad()
const int & get_n_rot() const
const int & get_n_phi() const
const size_t & get_K() const
int maxProgressCounter() const
const Eigen::MatrixX3d & get_normals() const
__host__ __device__ float dot(float2 a, float2 b)
MiniVec< float, N > floor(const MiniVec< float, N > &a)
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.