1 // ----------------------------------------------------------------------------
2 // - Open3D: www.open3d.org -
3 // ----------------------------------------------------------------------------
4 // The MIT License (MIT)
5 //
6 // Copyright (c) 2018 www.open3d.org
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 // IN THE SOFTWARE.
25 // ----------------------------------------------------------------------------
26
27 #include "PointCloud.h"
28
29 #include <Eigen/Eigenvalues>
30 #include <Core/Utility/Console.h>
31 #include <Core/Geometry/KDTreeFlann.h>
32
33 namespace three{
34
35 namespace {
36
sqr(double x)37 double sqr(double x) { return x * x; }
38
FastEigen3x3(const Eigen::Matrix3d & A)39 Eigen::Vector3d FastEigen3x3(const Eigen::Matrix3d &A)
40 {
41 // Based on:
42 // https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
43 double p1 = sqr(A(0, 1)) + sqr(A(0, 2)) + sqr(A(1, 2));
44 Eigen::Vector3d eigenvalues;
45 if (p1 == 0.0) {
46 eigenvalues(2) = std::min(A(0, 0), std::min(A(1, 1), A(2, 2)));
47 eigenvalues(0) = std::max(A(0, 0), std::max(A(1, 1), A(2, 2)));
48 eigenvalues(1) = A.trace() - eigenvalues(0) - eigenvalues(2);
49 } else {
50 double q = A.trace() / 3.0;
51 double p2 = sqr((A(0, 0) - q)) + sqr(A(1, 1) - q) + sqr(A(2, 2) - q) +
52 2 * p1;
53 double p = sqrt(p2 / 6.0);
54 Eigen::Matrix3d B = (1.0 / p) * (A - q * Eigen::Matrix3d::Identity());
55 double r = B.determinant() / 2.0;
56 double phi;
57 if (r <= -1) {
58 phi = M_PI / 3.0;
59 } else if (r >= 1) {
60 phi = 0.0;
61 } else {
62 phi = std::acos(r) / 3.0;
63 }
64 eigenvalues(0) = q + 2.0 * p * std::cos(phi);
65 eigenvalues(2) = q + 2.0 * p * std::cos(phi + 2.0 * M_PI / 3.0);
66 eigenvalues(1) = q * 3.0 - eigenvalues(0) - eigenvalues(2);
67 }
68
69 Eigen::Vector3d eigenvector =
70 (A - Eigen::Matrix3d::Identity() * eigenvalues(0)) *
71 (A.col(0) - Eigen::Vector3d(eigenvalues(1), 0.0, 0.0));
72 double len = eigenvector.norm();
73 if (len == 0.0) {
74 return Eigen::Vector3d::Zero();
75 } else {
76 return eigenvector.normalized();
77 }
78 }
79
ComputeNormal(const PointCloud & cloud,const std::vector<int> & indices)80 Eigen::Vector3d ComputeNormal(const PointCloud &cloud,
81 const std::vector<int> &indices)
82 {
83 if (indices.size() == 0) {
84 return Eigen::Vector3d::Zero();
85 }
86 Eigen::Matrix3d covariance;
87 Eigen::Matrix<double, 9, 1> cumulants;
88 cumulants.setZero();
89 for (size_t i = 0; i < indices.size(); i++) {
90 const Eigen::Vector3d &point = cloud.points_[indices[i]];
91 cumulants(0) += point(0);
92 cumulants(1) += point(1);
93 cumulants(2) += point(2);
94 cumulants(3) += point(0) * point(0);
95 cumulants(4) += point(0) * point(1);
96 cumulants(5) += point(0) * point(2);
97 cumulants(6) += point(1) * point(1);
98 cumulants(7) += point(1) * point(2);
99 cumulants(8) += point(2) * point(2);
100 }
101 cumulants /= (double)indices.size();
102 covariance(0, 0) = cumulants(3) - cumulants(0) * cumulants(0);
103 covariance(1, 1) = cumulants(6) - cumulants(1) * cumulants(1);
104 covariance(2, 2) = cumulants(8) - cumulants(2) * cumulants(2);
105 covariance(0, 1) = cumulants(4) - cumulants(0) * cumulants(1);
106 covariance(1, 0) = covariance(0, 1);
107 covariance(0, 2) = cumulants(5) - cumulants(0) * cumulants(2);
108 covariance(2, 0) = covariance(0, 2);
109 covariance(1, 2) = cumulants(7) - cumulants(1) * cumulants(2);
110 covariance(2, 1) = covariance(1, 2);
111
112 return FastEigen3x3(covariance);
113 //Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver;
114 //solver.compute(covariance, Eigen::ComputeEigenvectors);
115 //return solver.eigenvectors().col(0);
116 }
117
118 } // unnamed namespace
119
EstimateNormals(PointCloud & cloud,const KDTreeSearchParam & search_param)120 bool EstimateNormals(PointCloud &cloud,
121 const KDTreeSearchParam &search_param/* = KDTreeSearchParamKNN()*/)
122 {
123 bool has_normal = cloud.HasNormals();
124 if (cloud.HasNormals() == false) {
125 cloud.normals_.resize(cloud.points_.size());
126 }
127 KDTreeFlann kdtree;
128 kdtree.SetGeometry(cloud);
129 #ifdef _OPENMP
130 #pragma omp parallel for schedule(static)
131 #endif
132 for (int i = 0; i < (int)cloud.points_.size(); i++) {
133 std::vector<int> indices;
134 std::vector<double> distance2;
135 Eigen::Vector3d normal;
136 if (kdtree.Search(cloud.points_[i], search_param, indices,
137 distance2) >= 3) {
138 normal = ComputeNormal(cloud, indices);
139 if (normal.norm() == 0.0) {
140 if (has_normal) {
141 normal = cloud.normals_[i];
142 } else {
143 normal = Eigen::Vector3d(0.0, 0.0, 1.0);
144 }
145 }
146 if (has_normal && normal.dot(cloud.normals_[i]) < 0.0) {
147 normal *= -1.0;
148 }
149 cloud.normals_[i] = normal;
150 } else {
151 cloud.normals_[i] = Eigen::Vector3d(0.0, 0.0, 1.0);
152 }
153 }
154
155 return true;
156 }
157
OrientNormalsToAlignWithDirection(PointCloud & cloud,const Eigen::Vector3d & orientation_reference)158 bool OrientNormalsToAlignWithDirection(PointCloud &cloud,
159 const Eigen::Vector3d &orientation_reference
160 /* = Eigen::Vector3d(0.0, 0.0, 1.0)*/)
161 {
162 if (cloud.HasNormals() == false) {
163 PrintDebug("[OrientNormalsToAlignWithDirection] No normals in the PointCloud. Call EstimateNormals() first.\n");
164 }
165 #ifdef _OPENMP
166 #pragma omp parallel for schedule(static)
167 #endif
168 for (int i = 0; i < (int)cloud.points_.size(); i++) {
169 auto &normal = cloud.normals_[i];
170 if (normal.norm() == 0.0) {
171 normal = orientation_reference;
172 } else if (normal.dot(orientation_reference) < 0.0) {
173 normal *= -1.0;
174 }
175 }
176 return true;
177 }
178
OrientNormalsTowardsCameraLocation(PointCloud & cloud,const Eigen::Vector3d & camera_location)179 bool OrientNormalsTowardsCameraLocation(PointCloud &cloud,
180 const Eigen::Vector3d &camera_location/* = Eigen::Vector3d::Zero()*/)
181 {
182 if (cloud.HasNormals() == false) {
183 PrintDebug("[OrientNormalsTowardsCameraLocation] No normals in the PointCloud. Call EstimateNormals() first.\n");
184 }
185 #ifdef _OPENMP
186 #pragma omp parallel for schedule(static)
187 #endif
188 for (int i = 0; i < (int)cloud.points_.size(); i++) {
189 Eigen::Vector3d orientation_reference = camera_location -
190 cloud.points_[i];
191 auto &normal = cloud.normals_[i];
192 if (normal.norm() == 0.0) {
193 normal = orientation_reference;
194 if (normal.norm() == 0.0) {
195 normal = Eigen::Vector3d(0.0, 0.0, 1.0);
196 } else {
197 normal.normalize();
198 }
199 } else if (normal.dot(orientation_reference) < 0.0) {
200 normal *= -1.0;
201 }
202 }
203 return true;
204 }
205
206 } // namespace three
207