sparsetv.hpp
Go to the documentation of this file.
1 // kate: replace-tabs off; indent-width 4; indent-mode normal
2 // vim: ts=4:sw=4:noexpandtab
3 /*
4 
5 Copyright (c) 2010--2018
6 
7 All rights reserved.
8 
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11  * Redistributions of source code must retain the above copyright
12  notice, this list of conditions and the following disclaimer.
13  * Redistributions in binary form must reproduce the above copyright
14  notice, this list of conditions and the following disclaimer in the
15  documentation and/or other materials provided with the distribution.
16  * Neither the name of the <organization> nor the
17  names of its contributors may be used to endorse or promote products
18  derived from this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL ETH-ASL BE LIABLE FOR ANY
24 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 */
32 
33 //Import "and", "or", "not" macros
34 #include <iso646.h>
35 
36 //--ctor
37 template <typename T>
38 TensorVoting<T>::TensorVoting(T sigma_, std::size_t k_) : sigma{sigma_}, k{k_}
39 {
40 }
41 //--dtor
42 template <typename T>
44 
45 //--methods
46 
48 template <typename T>
49 void TensorVoting<T>::vote(const DP& pts)
50 {
51  refine(pts);
52 }
53 
54 template <typename T>
56 {
57  const std::size_t nbPts = pts.getNbPoints();
58  tensors.resize(nbPts, 1);
59 
60  switch(encoding)
61  {
62  case Encoding::ZERO:
63  {
64 #pragma omp parallel for
65  for(std::size_t i = 0; i < nbPts; ++i)
66  tensors(i) = Tensor::Zero();
67  break;
68  }
69 //------
70  case Encoding::UBALL:
71  case Encoding::BALL:
72  {
73 #pragma omp parallel for
74  for(std::size_t i = 0; i < nbPts; ++i)
75  tensors(i) = Tensor::Identity();
76  break;
77  }
78  case Encoding::SBALL:
79  {
80  //Check if there is plates info
81  if (not pts.descriptorExists("balls"))
82  throw InvalidField("TensorVoting<T>::encode: Error, cannot find balls in descriptors.");
83 
84  const auto& balls_ = pts.getDescriptorViewByName("balls");
85 #pragma omp parallel for
86  for(std::size_t i = 0; i < nbPts; ++i)
87  tensors(i) = Tensor::Identity() * balls_(0,i);
88 
89  break;
90  }
91 //------
92  case Encoding::UPLATE:
93  {
94 #pragma omp parallel for
95  for(std::size_t i = 0; i < nbPts; ++i)
96  tensors(i) <<
97  1., 0., 0.,
98  0., 1., 0.,
99  0., 0., 0.;
100  break;
101  }
102  case Encoding::PLATE:
103  case Encoding::SPLATE:
104  {
105  //Check if there is plates info
106  if (not pts.descriptorExists("plates"))
107  throw InvalidField("TensorVoting<T>::encode: Error, cannot find plates in descriptors.");
108 
109  const auto& plates_ = pts.getDescriptorViewByName("plates");
110 #pragma omp parallel for
111  for(std::size_t i = 0; i < nbPts; ++i)
112  {
113  const Vector3 n1 = plates_.col(i).segment(1,3);
114  const Vector3 n2 = plates_.col(i).tail(3);
115 
116  tensors(i) = (encoding == Encoding::SPLATE ? plates_(0,i) : 1.) * (n1 * n1.transpose() + n2 * n2.transpose());
117  }
118  break;
119  }
120 //------
121  case Encoding::USTICK:
122  {
123 #pragma omp parallel for
124  for(std::size_t i = 0; i < nbPts; ++i)
125  tensors(i) <<
126  1., 0., 0.,
127  0., 0., 0.,
128  0., 0., 0.;
129  break;
130  }
131  case Encoding::STICK:
132  case Encoding::SSTICK:
133  {
134  //Check if there is normals info
135  if (not pts.descriptorExists("sticks"))
136  throw InvalidField("TensorVoting<T>::encode: Error, cannot find sticks in descriptors.");
137 
138  const auto& sticks_ = pts.getDescriptorViewByName("sticks");
139 #pragma omp parallel for
140  for(std::size_t i = 0; i < nbPts; ++i)
141  {
142  const Vector3 n = sticks_.col(i).tail(3);
143  tensors(i) = (encoding == Encoding::SSTICK ? sticks_(0,i) : 1.) * (n * n.transpose());
144  }
145  break;
146  }
147 //------
148  case Encoding::AWARE_TENSOR:
149  {
150  //Check if there is normals info
151  if (not pts.descriptorExists("sticks"))
152  throw InvalidField("TensorVoting<T>::encode: Error, cannot find sticks in descriptors.");
153  //Check if there is normals info
154  if (not pts.descriptorExists("plates"))
155  throw InvalidField("TensorVoting<T>::encode: Error, cannot find plates in descriptors.");
156 
157  const auto& sticks_ = pts.getDescriptorViewByName("sticks");
158  const auto& plates_ = pts.getDescriptorViewByName("plates");
159 #pragma omp parallel for
160  for(std::size_t i = 0; i < nbPts; ++i)
161  {
162  const Tensor S = sticks_.col(i).tail(3) * sticks_.col(i).tail(3).transpose();
163  const Tensor P = plates_.col(i).segment(1,3) * plates_.col(i).segment(1,3).transpose() + plates_.col(i).tail(3) * plates_.col(i).tail(3).transpose();
164 
165  tensors(i) = (sticks_(0,i) / k) * S + (plates_(0,i) / k) * P;
166  }
167  break;
168  }
169  }
170 }
171 
172 template <typename T>
174 {
175  const std::size_t nbPts = tensors.rows();
176 #pragma omp parallel for
177  for(std::size_t i = 0; i < nbPts; ++i)
178  {
179  const Tensor S = sticks.col(i).tail(3) * sticks.col(i).tail(3).transpose();
180  const Tensor P = plates.col(i).segment(1,3) * plates.col(i).segment(1,3).transpose() + plates.col(i).tail(3) * plates.col(i).tail(3).transpose();
181 
182  tensors(i) = (sticks(0,i) / k) * S + (plates(0,i) / k) * P;
183  }
184 }
185 
186 template <typename T>
187 void TensorVoting<T>::refine(const DP& pts)
188 {
189  //pts = input token (x,y,z)
190  // |
191  encode(pts);
192  // |
193  //tensors = unit ball tensors token (sparse)
194  // |
195  ballVote(pts, true);
196  // |
197  //tensors = generic 2nd order tensors refined tokens (sparse)
198  // |
199  decompose();
200  // |
201  //sparseStick = tensors (saliency, nx, ny, nz)
202  //sparsePlate = tensors (saliency, tx, ty, tz)
203  //sparseBall = tensors (saliency, 0, 0, 0)
204  // |
205  toDescriptors();
206 }
207 
208 /************ Ball Vote ********************************************************
209  * The ball vote can be constructed by subtracting the direct product of
210  * the tangent vector from a full rank tensor with equal eigenvalues
211  * (i.e. the identity matrix). The resulting tensor is attenuated by the same
212  * Gaussian weight according to the distance between the voter and the receiver
213  ******************************************************************************/
214 template <typename T>
215 void TensorVoting<T>::ballVote(const DP& pts, bool doKnn)
216 {
217  const std::size_t nbPts = pts.getNbPoints();
218 
219  const Tensor I = Tensor::Identity();
220 
221  if(doKnn) computeKnn(pts);
222 
223  for(std::size_t voter = 0; voter < nbPts; ++voter)
224  {
225  for(std::size_t j = 0; j < k ; j++)
226  {
227  Index votee = indices(j,voter);
228 
229  if(votee == NNS::InvalidIndex) break;
230  if(votee == Index(voter)) continue;
231 
232  const Vector3 v = pts.features.col(votee).head(3) - pts.features.col(voter).head(3);
233  const T normDist = v.norm()/sigma;
234 
235  if(normDist > 0. and normDist < 3.) //if not too far
236  {
237  const Tensor vv = v * v.transpose(); //outer product for projection in direction of voter
238  const T normVv = vv.norm(); //frobenius norm
239  if(normVv > 0.)
240  {
241  //accumulate vote a votee location with decay function weighting voter vote
242  const Tensor acc = tensors(votee) + DecayFunction::sradial(normDist) * (I - vv / normVv);
243  tensors(votee) = acc;
244  }
245  }
246  }
247  }
248 }
249 
250 
251 template <typename T>
252 void TensorVoting<T>::stickVote(const DP& pts, bool doKnn)
253 {
254  //Check if there is normals info
255  if (not pts.descriptorExists("sticks"))
256  throw InvalidField("TensorVoting<T>::stickVote: Error, cannot find sticks in descriptors.");
257 
258  const auto& sticks_ = pts.getDescriptorViewByName("sticks");
259 
260  const std::size_t nbPts = pts.getNbPoints();
261 
262  if(doKnn) computeKnn(pts);
263 
264  for(std::size_t voter = 0; voter < nbPts; ++voter)
265  {
266  Vector3 vn = sticks_.col(voter).tail(3).normalized(); //normal
267  const Vector3 O = pts.features.col(voter).head(3); //voter coord
268 
269  for(std::size_t j = 0; j < k ; j++)
270  {
271  Index votee = indices(j,voter);
272 
273  if(votee == NNS::InvalidIndex) break;
274  if(votee == Index(voter)) continue;
275 
276  Vector3 v = pts.features.col(votee).head(3) - O; // v = P - O
277 
278  const T normDist = v.norm()/sigma;
279 
280  if(normDist > 0. and normDist < 3.) //if not too far
281  {
282  v.normalize();
283 
284  const T vvn = v.dot(vn); // size of point
285  if(vvn < 0.) vn *= -1.; //reorient normal
286 
287  const T theta = std::asin(vvn); //sepatation angle votee--voter along tangent in [- PI/2; PI/2]
288 
289  //cast vote only if smaller than 45deg
290  if (std::fabs(theta) <= M_PI / 4. or std::fabs(theta) >= 3. * M_PI / 4.)
291  {
292  //get tangent vector to osculating circle
293  Vector3 vt = vn.cross(v.cross(vn)).normalized();
294 
295  const T vvt = v.dot(vt); // size of point
296  if(vvt < 0.) vt *= -1.; //reorient tangent
297 
298  //most likely normal at P
299  const Vector3 vc = vn * std::cos(2. * theta) - vt * std::sin(2. * theta); //vote cast
300 
301  //accumulate vote a votee location with decay function weighting voter vote
302  const Tensor acc = tensors(votee) + sticks_(0, voter) * DecayFunction::eta(normDist * sigma, sigma, vvn) * (vc * vc.transpose());
303  tensors(votee) = acc;
304  }
305  }
306  }
307  }
308 }
309 
310 //FIXME: not sure of the implementation...
311 template <typename T>
312 void TensorVoting<T>::plateVote(const DP& pts, bool doKnn)
313 {
314  //Check if there is normals info
315  if (not pts.descriptorExists("plates"))
316  throw InvalidField("TensorVoting<T>::stickVote: Error, cannot find plates in descriptors.");
317 
318  const auto& plates_ = pts.getDescriptorViewByName("plates");
319 
320  const std::size_t nbPts = pts.getNbPoints();
321 
322  if(doKnn) computeKnn(pts);
323 
324  for(std::size_t voter = 0; voter < nbPts; ++voter)
325  {
326  Matrix U(3,2); U << plates_.col(voter).segment(1,3), plates_.col(voter).tail(3);
327  const Matrix Ns = U*U.transpose(); //normalspace
328 
329  for(std::size_t d = 0; d <= 1 ; ++d)
330  {
331  Vector3 vn = Ns.col(d).normalized(); //vector basis in normal space
332  const Vector3 O = pts.features.col(voter).head(3); //voter coord
333 
334  for(std::size_t j = 0; j < k ; j++)
335  {
336  Index votee = indices(j,voter);
337 
338  if(votee == NNS::InvalidIndex) break;
339  if(votee == Index(voter)) continue;
340 
341  Vector3 v = pts.features.col(votee).head(3) - O; // v = P - O
342 
343  const T normDist = v.norm()/sigma;
344 
345  if(normDist > 0. and normDist < 3.) //if not too far
346  {
347  v.normalize();
348 
349  const T vvn = v.dot(vn); // size of point
350  if(vvn < 0.) vn *= -1.; //reorient normal
351 
352  const T theta = std::asin(vvn); //sepatation angle voter--votee
353 
354  //cast vote only if smaller than 45deg
355  if (std::fabs(theta) <= M_PI / 4. or std::fabs(theta) >= 3. * M_PI / 4.)
356  {
357  //get tangent vector to osculating circle
358  Vector3 vt = vn.cross(v.cross(vn)).normalized();
359 
360  const T vvt = v.dot(vt); // size of point
361  if(vvt < 0.) vt *= -1.; //reorient tangent
362 
363  const Vector3 vc = vn * std::cos(2. * theta) - vt * std::sin(2. * theta); //vote cast
364 
365  //accumulate vote a votee location with decay function weighting voter vote
366  const Tensor acc = tensors(votee) + plates_(0, voter) * DecayFunction::eta(normDist*sigma, sigma, vvn) * (vc * vc.transpose());
367  tensors(votee) = acc;
368  }
369  }
370  }
371  }
372  }
373 }
374 
375 /*******************************************************************************
376  * See Eq (11) in:
377  * T.-P. Wu, S.-K. Yeung, J. Jia, C.-K. Tang, and G. Medioni,
378  * “A Closed-Form Solution to Tensor Voting: Theory and Applications,” 2016.
379  ******************************************************************************/
380 template <typename T>
381 void TensorVoting<T>::cfvote(const DP& pts, bool doKnn)
382 {
383  const std::size_t nbPts = pts.getNbPoints();
384 
385  if(doKnn) computeKnn(pts);
386 
387  const Tensors K = tensors; //save old tensors values
388  encode(pts, Encoding::ZERO); //all tensors are zero
389 
390 #pragma omp parallel for
391  for(std::size_t votee = 0; votee < nbPts; ++votee) //vote sites
392  {
393  const Vector3 x_i = pts.features.col(votee).head(3);
394 
395  for(std::size_t j = 0; j < k ; j++) //voters
396  {
397  const Index voter = indices(j,votee); //get voter at site Xi
398 
399  if(voter == NNS::InvalidIndex) continue;
400  if(voter == Index(votee)) continue;
401 
402  const Vector3 x_j = pts.features.col(voter).head(3);
403  Vector3 r_ij = x_i - x_j;
404 
405  const T normDist = r_ij.norm() / sigma;
406 
407  if(normDist > 0. and normDist < 3.) //if not too far
408  {
409  r_ij.normalize();
410 
411  const Tensor rrt = r_ij * r_ij.transpose();
412  const Tensor R_ij = (Tensor::Identity() - 2. * rrt);
413  const Tensor Rp_ij = (Tensor::Identity() - .5 * rrt) * R_ij;
414  const T c_ij = DecayFunction::cij((x_i - x_j).norm(), sigma);
415 
416  //accumulate vote a voter location with decay function weighting voter vote
417  const Tensor S_ij = c_ij * R_ij * K(voter) * Rp_ij;
418 
419  const Tensor acc = tensors(votee) + S_ij;
420  tensors(votee) = acc;
421  }
422  }
423  }
424 }
425 
426 template <typename T>
428 {
429  const std::size_t nbPts = tensors.rows();
430 
431  sparseStick.resize(nbPts);
432  sparsePlate.resize(nbPts);
433  sparseBall.resize(nbPts);
434 
435 #pragma omp parallel for
436  for(std::size_t i = 0; i < nbPts; ++i)
437  {
438  Eigen::SelfAdjointEigenSolver<Tensor> solver(tensors(i));
439 
440  const Matrix33 eigenVe = solver.eigenvectors();
441  const Vector3 eigenVa = solver.eigenvalues().array().abs();
442 
443  // lambda1 > lambda2 > lambda3 > 0
444  int lambda1_idx; const T lambda1 = eigenVa.maxCoeff(&lambda1_idx);
445  int lambda3_idx; const T lambda3 = eigenVa.minCoeff(&lambda3_idx);
446  const int lambda2_idx = (0+1+2) - (lambda1_idx + lambda3_idx);
447  const T lambda2 = eigenVa(lambda2_idx);
448 
449  const T norm = 1;
450 
451  if(not (lambda1 >= lambda2 and lambda2 >= lambda3) or lambda2_idx > 2. or lambda2_idx < 0.)
452  {
453  sparseStick(i) << 0.0001,0.,0.,0.;
454  sparsePlate(i) << 0.0001,0.,0.,0.;
455  sparseBall(i) << 0.0001,0.,0.,0.;
456 
457  //std::cerr << "Warning: eigen values not ordered ("<<eigenVa(0)<<", "<<eigenVa(1)<<", "<<eigenVa(2)<<")" << std::endl;
458  continue;
459  }
460 
461  // store relevant stick, plate, ball information:
462  sparseStick(i)(0) = (lambda1 - lambda2) / norm; //
463  sparseStick(i).tail(3) = eigenVe.col(lambda1_idx); //normal information
464 
465  sparsePlate(i)(0) = (lambda2 - lambda3) / norm; //
466  sparsePlate(i).tail(3) = eigenVe.col(lambda3_idx); //tangent information
467 
468  sparseBall(i)(0) = lambda3 / norm; //
469  sparseBall(i).tail(3) = eigenVe.col(lambda2_idx); //<< 0.,0.,0.; //no principal direction, but store lambda2 for convinience
470  }
471 }
472 
473 template <typename T>
475 {
476  const std::size_t nbPts = tensors.rows();
477 
478  pointness = PM::Matrix::Zero(1, nbPts);
479  curveness = PM::Matrix::Zero(1, nbPts);
480  surfaceness = PM::Matrix::Zero(1, nbPts);
481 
482  normals = PM::Matrix::Zero(3, nbPts);
483  tangents = PM::Matrix::Zero(3, nbPts);
484 
485  sticks = PM::Matrix::Zero(4, nbPts);
486  plates = PM::Matrix::Zero(7, nbPts);
487  balls = PM::Matrix::Zero(1, nbPts);
488 
489 #pragma omp parallel for
490  for(std::size_t i = 0; i < nbPts; i++)
491  {
492  surfaceness(i) = sparseStick(i)(0) / k;
493  curveness(i) = sparsePlate(i)(0) / k;
494  pointness(i) = sparseBall(i)(0) / k;
495 
496  normals.col(i) = sparseStick(i).tail(3);
497  tangents.col(i) = sparsePlate(i).tail(3);
498 
499  sticks.col(i) = sparseStick(i); //s + e1
500 
501  plates(0,i) = sparsePlate(i)(0); //s
502  plates.col(i).segment(1,3) = sparseStick(i).tail(3); //e1
503  plates.col(i).tail(3) = sparseBall(i).tail(3); //e2
504 
505  balls(i) = sparseBall(i)(0); //s
506  }
507 }
508 
509 template <typename T>
511 {
512  const std::size_t nbPts = pts.getNbPoints();
513 
514  if(k >= nbPts) k = nbPts - 1;
515 
516  std::shared_ptr<NNS> knn(
517  NNS::create(pts.features, pts.features.rows() - 1,
518  (k<30? NNS::SearchType::KDTREE_LINEAR_HEAP : NNS::SearchType::KDTREE_TREE_HEAP)
519  )
520  );
521 
522  indices = IndexMatrix::Zero(k, nbPts);
523  dist = Matrix::Zero(k, nbPts);
524 
525  knn->knn(pts.features, indices, dist, Index(k));
526 }
Tensors tensors
Definition: sparsetv.h:84
void toDescriptors()
Definition: sparsetv.hpp:474
IndexMatrix indices
Definition: sparsetv.h:104
Matrix surfaceness
Definition: sparsetv.h:92
static NearestNeighbourSearch * create(const CloudType &cloud, const Index dim=std::numeric_limits< Index >::max(), const SearchType preferedType=KDTREE_LINEAR_HEAP, const unsigned creationOptionFlags=0, const Parameters &additionalParameters=Parameters())
static const char encoding[]
Definition: binary.cpp:6
void stickVote(const DP &pts, bool doKnn=true)
Definition: sparsetv.hpp:252
Matrix pointness
Definition: sparsetv.h:94
void encode(const DP &pts, Encoding encoding=Encoding::UBALL)
Definition: sparsetv.hpp:55
TensorVoting(T sigma_=T(0.2), std::size_t k_=50)
Definition: sparsetv.hpp:38
Eigen::Matrix< T, 3, 3 > Matrix33
Definition: sparsetv.h:68
void decompose()
Definition: sparsetv.hpp:427
void plateVote(const DP &pts, bool doKnn=true)
Definition: sparsetv.hpp:312
SparseTensors sparsePlate
Definition: sparsetv.h:87
const T sigma
Definition: sparsetv.h:81
typename PM::Matrix Matrix
Definition: sparsetv.h:70
static T eta(const T vv, const T sigma, const T vvn)
Definition: sparsetv.h:169
std::size_t k
Definition: sparsetv.h:82
void refine(const DP &pts)
Definition: sparsetv.hpp:187
void disableBallComponent()
Definition: sparsetv.hpp:173
typename DP::InvalidField InvalidField
Definition: sparsetv.h:58
SparseTensors sparseStick
Definition: sparsetv.h:86
Eigen::Matrix< T, 3, 3 > Tensor
Definition: sparsetv.h:61
Eigen::Matrix< Tensor, Eigen::Dynamic, 1 > Tensors
Definition: sparsetv.h:64
Matrix curveness
Definition: sparsetv.h:93
static T sradial(const T z)
Definition: sparsetv.h:136
static T cij(const T vv, const T sigma)
Definition: sparsetv.h:174
Matrix normals
Definition: sparsetv.h:96
Matrix tangents
Definition: sparsetv.h:97
Matrix plates
Definition: sparsetv.h:100
static constexpr Index InvalidIndex
SparseTensors sparseBall
Definition: sparsetv.h:88
Eigen::Matrix< T, 3, 1 > Vector3
Definition: sparsetv.h:67
The superclass of classes that are constructed using generic parameters. This class provides the para...
void cfvote(const DP &pts, bool doKnn=true)
Definition: sparsetv.hpp:381
typename PM::DataPoints DP
Definition: sparsetv.h:56
Matrix dist
Definition: sparsetv.h:105
Matrix sticks
Definition: sparsetv.h:99
Matrix balls
Definition: sparsetv.h:101
typename NNS::Index Index
Definition: sparsetv.h:74
void computeKnn(const DP &pts)
Definition: sparsetv.hpp:510
void vote(const DP &pts)
Voting methods.
Definition: sparsetv.hpp:49
void ballVote(const DP &pts, bool doKnn=true)
Definition: sparsetv.hpp:215


libpointmatcher
Author(s):
autogenerated on Sat May 27 2023 02:38:03