ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Normals.h
Go to the documentation of this file.
1 /* License Information
2  *
3  * Copyright (C) ONERA, The French Aerospace Lab
4  * Author: Alexandre BOULCH
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  *
25  * Note that this library relies on external libraries subject to their own
26  * license. To use this software, you are subject to the dependencies license,
27  * these licenses applies to the dependency ONLY and NOT this code. Please
28  * refer below to the web sites for license informations: PCL, BOOST,NANOFLANN,
29  * EIGEN
30  *
31  * When using the software please aknowledge the corresponding publication:
32  * "Deep Learning for Robust Normal Estimation in Unstructured Point Clouds "
33  * by Alexandre Boulch and Renaud Marlet
34  * Symposium of Geometry Processing 2016, Computer Graphics Forum
35  */
36 
37 #ifndef NORMALS_HEADER
38 #define NORMALS_HEADER
39 
40 #include <math.h>
41 
42 #include <Eigen/Dense>
43 #include <ctime>
44 #include <iostream>
45 #include <nanoflann.hpp>
46 #include <sstream>
47 #include <string>
48 #include <vector>
49 
50 #ifdef _OPENMP
51 #include <omp.h>
52 
53 #define USE_OPENMP_FOR_NORMEST
54 #endif
55 
57 private:
58  const Eigen::MatrixX3d& pts;
59  Eigen::MatrixX3d& nls;
60  std::vector<double> densities;
63  int n_planes;
64  int n_phi;
65  int n_rot;
66  size_t neighborhood_size; /*size of the neighborhood*/
67  bool use_density;
68  double tol_angle_rad;
69  size_t k_density;
71  std::function<void(int)> progressCallback;
72 
73 public:
74  // accessor
75  const Eigen::MatrixX3d& get_points() const { return pts; }
76 
77  Eigen::MatrixX3d& get_normals() { return nls; }
78  int& get_T() { return n_planes; }
79  int& get_n_phi() { return n_phi; }
80  int& get_n_rot() { return n_rot; }
81  size_t& get_K() { return neighborhood_size; }
82  bool& density_sensitive() { return use_density; }
83  double& get_tol_angle_rad() { return tol_angle_rad; }
84  size_t& get_K_density() { return k_density; }
85 
86  const Eigen::MatrixX3d& get_normals() const { return nls; }
87  const int& get_T() const { return n_planes; }
88  const int& get_n_phi() const { return n_phi; }
89  const int& get_n_rot() const { return n_rot; }
90  const size_t& get_K() const { return neighborhood_size; }
91  const bool& density_sensitive() const { return use_density; }
92  const double& get_tol_angle_rad() const { return tol_angle_rad; }
93  const size_t& get_K_density() const { return k_density; }
94 
96 
97  typedef nanoflann::KDTreeEigenMatrixAdaptor<Eigen::MatrixX3d>
98  kd_tree; // a row is a point
99 
100  // constructor
101  Eigen_Normal_Estimator(const Eigen::MatrixX3d& points,
102  Eigen::MatrixX3d& normals)
103  : pts(points), nls(normals) {
104  n_planes = 700;
105  n_rot = 5;
106  n_phi = 15;
107  tol_angle_rad = 0.79;
108  neighborhood_size = 200;
109  use_density = false;
110  k_density = 5;
111  }
112 
113  void setProgressCallback(std::function<void(int)> callback) {
114  progressCallback = callback;
115  }
116 
117  int maxProgressCounter() const { return pts.rows() * 2; }
118 
120  /*********************************
121  * INIT
122  ********************************/
123 
124  // initialize the random number generator
125  srand(static_cast<unsigned int>(time(NULL)));
126 
127  // creating vector of random int
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());
131  }
132 
133  // confidence intervals (2 intervals length)
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);
137  }
138 
139  // random permutation of the points (avoid thread difficult block)
140  std::vector<int> permutation(pts.rows());
141  for (int i = 0; i < pts.rows(); i++) {
142  permutation[i] = i;
143  }
144  for (int i = 0; i < pts.rows(); i++) {
145  int j = rand() % pts.rows();
146  std::swap(permutation[i], permutation[j]);
147  }
148 
149  // creation of the rotation matrices and their inverses
150  std::vector<Eigen::Matrix3d> rotMat;
151  std::vector<Eigen::Matrix3d> rotMatInv;
152  generate_rotation_matrix(rotMat, rotMatInv, n_rot * 200);
153 
154  // dimensions of the accumulator
155  int d1 = 2 * n_phi;
156  int d2 = n_phi + 1;
157 
158  // progress
159  int progress = 0;
160 
161  /*******************************
162  * ESTIMATION
163  ******************************/
164 
165  // resizing the normal point cloud
166  nls.resize(pts.rows(), 3);
167 
168  // kd tree creation
169  // build de kd_tree
170  kd_tree tree(3, pts, 10 /* max leaf */);
171  tree.index_->buildIndex();
172 
173  // create the density estimation for each point
174  densities.resize(pts.rows());
175 #if defined(USE_OPENMP_FOR_NORMEST)
176 #pragma omp parallel for schedule(guided)
177 #endif
178  for (int per = 0; per < pts.rows(); per++) {
179  // index of the point
180  int n = permutation[per];
181  // getting the list of neighbors
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);
185  // knn for k_density+1 because the point is itself include in the
186  // search tree
187  tree.index_->knnSearch(&pt_query[0], k_density + 1,
188  &pointIdxSearch[0],
189  &pointSquaredDistance[0]);
190  double d = 0;
191  for (size_t i = 0; i < pointSquaredDistance.size(); i++) {
192  d += std::sqrt(pointSquaredDistance[i]);
193  }
194  d /= pointSquaredDistance.size() - 1;
195  densities[n] = d;
196 
197  if (progressCallback) {
198  progressCallback(++progress);
199  }
200  }
201 
202  int rotations = std::max(n_rot, 1);
203 
204  // create the list of triplets in KNN case
205  Eigen::MatrixX3i trip;
206  if (!use_density) {
207  list_of_triplets(trip, neighborhood_size, rotations * n_planes,
208  vecInt);
209  }
210 
211 #if defined(USE_OPENMP_FOR_NORMEST)
212 #pragma omp parallel for schedule(guided)
213 #endif
214  for (int per = 0; per < pts.rows(); per++) {
215  // index of the point
216  int n = permutation[per];
217 
218  // getting the list of neighbors
219  std::vector<Eigen::MatrixX3d::Index> pointIdxSearch;
220  std::vector<double> pointSquaredDistance;
221 
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,
226  &pointIdxSearch[0],
227  &pointSquaredDistance[0]);
228 
229  if (use_density)
230  list_of_triplets(trip, rotations * n_planes, pointIdxSearch,
231  vecInt);
232 
233  // get the points
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]);
238  }
239 
240  std::vector<Eigen::Vector3d> normals_vec(rotations);
241  std::vector<float> normals_conf(rotations);
242 
243  for (int i = 0; i < rotations; i++) {
244  Eigen::MatrixX3i triplets =
245  trip.block(i * n_planes, 0, n_planes, 3);
246 
247  for (size_t pt = 0; pt < points_size; pt++) {
248  points.row(pt) = rotMat[(n + i) % rotMat.size()] *
249  points.row(pt).transpose();
250  }
251  normals_conf[i] = normal_at_point(d1, d2, points, n, triplets,
252  conf_interv);
253 
254  for (size_t pt = 0; pt < points_size; pt++) {
255  points.row(pt) = pts.row(pointIdxSearch[pt]);
256  }
257  normals_vec[i] = rotMatInv[(n + i) % rotMat.size()] *
258  nls.row(n).transpose();
259  }
260 
261  nls.row(n) = normal_selection(rotations, normals_vec, normals_conf);
262 
263  if (progressCallback) {
264  progressCallback(++progress);
265  }
266  }
267  }
268 
269 private:
270  // PRIVATE METHODS
271 
278  inline void generate_rotation_matrix(
279  std::vector<Eigen::Matrix3d>& rotMat,
280  std::vector<Eigen::Matrix3d>& rotMatInv,
281  int rotations) {
282  rotMat.clear();
283  rotMatInv.clear();
284 
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);
290  } else {
291  for (int i = 0; i < rotations; i++) {
292  double theta =
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;
296  Eigen::Matrix3d Rt;
297  Eigen::Matrix3d Rph;
298  Eigen::Matrix3d Rps;
299  Rt << 1, 0, 0, 0, cos(theta), -sin(theta), 0, sin(theta),
300  cos(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),
307  cos(theta);
308  Rphinv << cos(phi), 0, -sin(phi), 0, 1, 0, sin(phi), 0,
309  cos(phi);
310  Rpsinv << cos(psi), sin(psi), 0, -sin(psi), cos(psi), 0, 0, 0,
311  1;
312 
313  Eigen::Matrix3d rMat = Rt * Rph * Rps;
314  Eigen::Matrix3d rMatInv = Rpsinv * Rphinv * Rtinv;
315  rotMat.push_back(rMat);
316  rotMatInv.push_back(rMatInv);
317  }
318  }
319  }
320 
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++) {
337  do {
338  triplets(i, 0) = static_cast<int>(vecRandInt[pos % S] %
339  number_of_points);
340  triplets(i, 1) = static_cast<int>(
341  vecRandInt[(pos + vecRandInt[(pos + 1) % S]) % S] %
342  number_of_points);
343  triplets(i, 2) = static_cast<int>(
344  vecRandInt[(pos +
345  vecRandInt[(pos + 1 +
346  vecRandInt[(pos + 2) % S]) %
347  S]) %
348  S] %
349  number_of_points);
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));
354  }
355  }
356 
363  // return the index of the nearest element in the vector
364  int dichotomic_search_nearest(const std::vector<double> elems, double d) {
365  size_t i1 = 0;
366  size_t i2 = elems.size() - 1;
367  size_t i3;
368  while (i2 > i1) {
369  i3 = (i1 + i2) / 2;
370  if (elems[i3] == d) {
371  break;
372  }
373  if (d < elems[i3]) {
374  i2 = i3;
375  }
376  if (d > elems[i3]) {
377  i1 = i3;
378  }
379  }
380  return static_cast<int>(i3);
381  }
382 
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;
397  double sum = 0;
398  for (size_t i = 0; i < pointIdxSearch.size(); i++) {
399  sum += densities[pointIdxSearch[i]];
400  dists.push_back(sum);
401  }
402 
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;
407  ;
408  for (size_t i = 0; i < triplet_number; i++) {
409  do {
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.) /
413  RAND_MAX;
414  triplets(i, 1) = dichotomic_search_nearest(dists, d);
415  d = (vecRandInt[(pos + vecRandInt[(pos + 1 +
416  vecRandInt[(pos + 2) % S]) %
417  S]) %
418  S] +
419  0.) /
420  RAND_MAX;
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));
426  }
427  }
428 
438  float normal_at_point(const int d1,
439  const int d2,
440  const Eigen::MatrixX3d& points,
441  int n,
442  Eigen::MatrixX3i& triplets,
443  std::vector<float>& conf_interv) {
444  if (points.size() < 3) {
445  nls.row(n).setZero();
446  return 0;
447  }
448 
449  // creation and initialization accumulators
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);
456  }
457  }
458 
459  float max1 = 0;
460  int i1 = 0, i2 = 0;
461  int j1 = 0, j2 = 0;
462 
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);
467 
468  Eigen::Vector3d v1 =
469  points.row(p1).transpose() - points.row(p0).transpose();
470  Eigen::Vector3d v2 =
471  points.row(p2).transpose() - points.row(p0).transpose();
472 
473  Eigen::Vector3d Pn = v1.cross(v2);
474  Pn.normalize();
475  if (Pn.dot(points.row(p0).transpose()) > 0) {
476  Pn = -Pn;
477  }
478 
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));
482  int post;
483 
484  if (posp == 0 || posp == n_phi) {
485  post = 0;
486  } else {
487  double theta =
488  acos(Pn[0] / sqrt(Pn[0] * Pn[0] + Pn[1] * Pn[1]));
489  if (Pn[1] < 0) {
490  theta *= -1;
491  theta += 2 * M_PI;
492  }
493  double dtheta = M_PI / (n_phi * sin(posp * dphi));
494  post = static_cast<int>(floor((theta + dtheta / 2) / dtheta)) %
495  (2 * n_phi);
496  }
497 
498  post = std::max(0, std::min(2 * n_phi - 1, post));
499  posp = std::max(0, std::min(n_phi, posp));
500 
501  votes[post + posp * d1] += 1.;
502  votesV[post + posp * d1] += Pn;
503 
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);
507 
508  if (votes_val > max1) {
509  max2 = max1;
510  i2 = i1;
511  j2 = j1;
512  max1 = votes_val;
513  i1 = post;
514  j1 = posp;
515  } else if (votes_val > max2 && post != i1 && posp != j1) {
516  max2 = votes_val;
517  i2 = post;
518  j2 = posp;
519  }
520 
521  if (max1 - conf_interv[n_try] > max2) {
522  break;
523  }
524  }
525 
526  votesV[i1 + j1 * d1].normalize();
527  nls.row(n) = votesV[i1 + j1 * d1];
528 
529  return max1;
530  }
531 
539  inline Eigen::Vector3d normal_selection(
540  int rotations,
541  std::vector<Eigen::Vector3d>& normals_vec,
542  const std::vector<float>& normals_conf) {
543  std::vector<bool> normals_use(rotations, true);
544  // alignement of normals
545  for (int i = 1; i < rotations; i++) {
546  if (normals_vec[0].dot(normals_vec[i]) < 0) {
547  normals_vec[i] *= -1;
548  }
549  }
550 
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) {
555  // getting the max
556  float max_conf = 0;
557  int idx = 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];
561  idx = i;
562  }
563  }
564 
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;
568  number_to_test--;
569 
570  for (int i = 0; i < rotations; i++) {
571  if (normals_use[i] &&
572  acos(normals_vec[idx].dot(normals_vec[i])) <
573  tol_angle_rad) {
574  normals_use[i] = false;
575  number_to_test--;
576  normals_fin.back().first +=
577  normals_vec[i] * normals_conf[i];
578  normals_fin.back().second += normals_conf[i];
579  }
580  }
581  }
582 
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;
589  }
590  }
591 
592  normal_final.normalize();
593  return normal_final;
594  }
595 };
596 
597 #endif
constexpr double M_PI
Pi.
Definition: CVConst.h:19
std::function< void(std::shared_ptr< core::Tensor >)> callback
int points
#define NULL
Eigen_Normal_Estimator(const Eigen::MatrixX3d &points, Eigen::MatrixX3d &normals)
Definition: Normals.h:101
bool & density_sensitive()
Definition: Normals.h:82
const Eigen::MatrixX3d & get_points() const
Definition: Normals.h:75
size_t & get_K()
Definition: Normals.h:81
const int & get_T() const
Definition: Normals.h:87
const bool & density_sensitive() const
Definition: Normals.h:91
size_t & get_K_density()
Definition: Normals.h:84
Eigen::MatrixX3d & get_normals()
Definition: Normals.h:77
const size_t & get_K_density() const
Definition: Normals.h:93
void setProgressCallback(std::function< void(int)> callback)
Definition: Normals.h:113
nanoflann::KDTreeEigenMatrixAdaptor< Eigen::MatrixX3d > kd_tree
Definition: Normals.h:98
const double & get_tol_angle_rad() const
Definition: Normals.h:92
double & get_tol_angle_rad()
Definition: Normals.h:83
const int & get_n_rot() const
Definition: Normals.h:89
const int & get_n_phi() const
Definition: Normals.h:88
const size_t & get_K() const
Definition: Normals.h:90
int maxProgressCounter() const
Definition: Normals.h:117
const Eigen::MatrixX3d & get_normals() const
Definition: Normals.h:86
double normals[3]
__host__ __device__ float dot(float2 a, float2 b)
Definition: cutil_math.h:1119
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370