congruentSetExplorationBase.hpp
Go to the documentation of this file.
1 //
2 // Created by Sandra Alfaro on 24/05/18.
3 //
4 
5 #include <iterator>
6 #include <vector>
7 #include <atomic>
8 #include <chrono>
9 
10 #ifdef OpenGR_USE_OPENMP
11 #include <omp.h>
12 #endif
13 
14 #include "gr/utils/shared.h"
15 #include "gr/utils/sampling.h"
16 #include "gr/accelerators/kdtree.h"
17 #include "gr/utils/logger.h"
18 
19 #include "gr/algorithms/congruentSetExplorationBase.h"
20 
21 #ifdef TEST_GLOBAL_TIMINGS
22 # include "gr/utils/timer.h"
23 #endif
24 
25 
26 namespace gr {
27 
28 namespace internal {
29  template<typename Range>
30  inline bool is_range_empty(const Range& r) { return std::begin(r) == std::end(r); }
31 }
32 
33 template <typename Traits, typename PointType, typename TransformVisitor,
34  typename PairFilteringFunctor,
35  template < class, class > typename ... OptExts >
38  , const Utils::Logger& logger )
41  , best_LCP_(0.0)
42  #ifdef OpenGR_USE_OPENMP
44  #endif
45 // , options_(options)
46 {}
47 
48 template <typename Traits, typename PointType, typename TransformVisitor,
49  typename PairFilteringFunctor,
50  template < class, class > class ... OptExts >
52 
53 
54 // The main 4PCS function. Computes the best rigid transformation and transfoms
55 // Q toward P by this transformation
56 template <typename Traits, typename PointType, typename TransformVisitor,
57  typename PairFilteringFunctor,
58  template < class, class > class ... OptExts >
59 template <typename InputRange1, typename InputRange2, template<typename> class Sampler>
62  const InputRange1& P,
63  const InputRange2& Q,
65  const Sampler<PointType>& sampler,
67  const Scalar kSmallError = 0.00001;
68  const int kMinNumberOfTrials = 4;
69  const Scalar kDiameterFraction = 0.3;
70 
71 #ifdef TEST_GLOBAL_TIMINGS
72  totalTime = 0;
73  verifyTime = 0;
74 #endif
75 
77 
78  // RANSAC probability and number of needed trials.
81  static_cast<Scalar>(kMinNumberOfTrials)));
82  // We use a simple heuristic to elevate the probability to a reasonable value
83  // given that we don't simply sample from P, but instead, we bound the
84  // distance between the points in the base as a fraction of the diameter.
90 
91  MatchBaseType::template Log<LogLevel::Verbose>( "norm_max_dist: ", MatchBaseType::options_.delta );
92  current_trial_ = 0;
93  best_LCP_ = 0.0;
94 
95  for (int i = 0; i < Traits::size(); ++i) {
96  base_[i] = 0;
97  current_congruent_[i] = 0;
98  }
99 
101 
102  // Normalize the delta (See the paper) and the maximum base distance.
103  // delta = P_mean_distance_ * delta;
105 
107  MatchBaseType::template Log<LogLevel::Verbose>( "Initial LCP: ", best_LCP_ );
108 
109  if (best_LCP_ != Scalar(1.))
111 
112 #ifdef TEST_GLOBAL_TIMINGS
113  MatchBaseType::template Log<LogLevel::Verbose>( "----------- Timings (msec) -------------" );
114  MatchBaseType::template Log<LogLevel::Verbose>( " Total computation time : ", totalTime );
115  MatchBaseType::template Log<LogLevel::Verbose>( " Total verify time : ", verifyTime );
116  MatchBaseType::template Log<LogLevel::Verbose>( "----------------------------------------" );
117 #endif
118 
119  return best_LCP_;
120 }
121 
122 
123 
124 // Performs N RANSAC iterations and compute the best transformation. Also,
125 // transforms the set Q by this optimal transformation.
126 template <typename Traits, typename PointType, typename TransformVisitor,
127  typename PairFilteringFunctor,
128  template < class, class > class ... OptExts >
129 bool
131  int n,
133  TransformVisitor &v) {
134  using std::chrono::system_clock;
135 
136 #ifdef TEST_GLOBAL_TIMINGS
137  Timer t (true);
138 #endif
139 
140 
141  // The transformation has been computed between the two point clouds centered
142  // at the origin, we need to recompute the translation to apply it to the original clouds
144  Eigen::Matrix<Scalar, 3, 3> rot, scale;
149  };
150 
152 
153  bool ok = false;
155  for (int i = current_trial_; i < current_trial_ + n; ++i) {
156  ok = TryOneBase(v);
157 
161  (system_clock::now() - t0).count() /
164 
167  } else {
169  }
170 
172 
173  // ok means that we already have the desired LCP.
174  if (ok || i > number_of_trials_ || fraction >= 0.99 || best_LCP_ == 1.0) break;
175  }
176 
177  // Need to force global transformation update at the end of the process
178  // if not already performed for the visitor during the process
181 
182  current_trial_ += n;
183 #ifdef TEST_GLOBAL_TIMINGS
185 #endif
186 
187  return ok || current_trial_ >= number_of_trials_;
188 }
189 
190 
191 
192 template <typename Traits, typename PointType, typename TransformVisitor,
193  typename PairFilteringFunctor,
194  template < class, class > class ... OptExts >
196  TransformVisitor &v) {
200  return false;
201 
202  size_t nb = 0;
203 
205 
206  //if (nb != 0)
207  // MatchBaseType::Log<LogLevel::Verbose>( "Congruent quads: (", nb, ") " );
208 
209  return match;
210 }
211 
212 template <typename Traits, typename PointType, typename TransformVisitor,
213  typename PairFilteringFunctor,
214  template < class, class > class ... OptExts >
219  size_t &nbCongruent) {
220 // static const Scalar pi = std::acos(-1);
221 
222  // get references to the basis coordinate
224 // std::cout << "Process congruent set for base: \n";
225  for (int i = 0; i!= Traits::size(); ++i) {
227 // std::cout << "[" << base[i] << "]: " << references[i].pos().transpose() << "\n";
228  }
229  const Coordinates& ref = references;
230  // Scalar targetAngle = (ref[1]->pos() - ref[0]->pos()).normalized().dot(
231  // (ref[3]->pos() - ref[2]->pos()).normalized());
232 // std::cout << "Target Angle : " << std::acos(targetAngle)*Scalar(180)/pi << std::endl;
233 
234  // Centroid of the basis, computed once and using only the three first points
235  Eigen::Matrix<Scalar, 3, 1> centroid1 = (ref[0]->pos() + ref[1]->pos() + ref[2]->pos()) / Scalar(3);
236 
237 // std::cout << "Congruent set size: " << set.size() << std::endl;
238 
240 
242 #ifdef OpenGR_USE_OPENMP
243 #pragma omp parallel for num_threads(omp_nthread_congruent_)
244 #endif
245  for (int i = 0; i < int(set.size()); ++i) {
246  const auto& congruent_ids = set[i];
247  for (int j = 0; j!= Traits::size(); ++j)
249 
250  Eigen::Matrix<Scalar, 4, 4> transform;
251 
252  // Centroid of the sets, computed in the loop using only the three first points
253  Eigen::Matrix<Scalar, 3, 1> centroid2;
254 
255 
256 #ifdef STATIC_BASE
257  MatchBaseType::Log<LogLevel::Verbose>( "Ids: ");
258  for (int j = 0; j!= Traits::size(); ++j)
259  MatchBaseType::Log<LogLevel::Verbose>( base[j], "\t");
261  for (int j = 0; j!= Traits::size(); ++j)
263 #endif
264 
266  congruent_candidate[1]->pos() +
267  congruent_candidate[2]->pos()) / Scalar(3.);
268 
269  Scalar rms = -1;
270 
271  const bool ok =
272  this->ComputeRigidTransformation(ref, // input congruent quad
273  congruent_candidate,// tested congruent quad
274  centroid1, // input: basis centroid
275  centroid2, // input: candidate quad centroid
276  transform, // output: transformation
277  rms, // output: rms error of the transformation between the basis and the congruent quad
278  #ifdef MULTISCALE
279  true
280  #else
281  false
282  #endif
283  ); // state: compute scale ratio ?
284 
285  if (ok && rms >= Scalar(0.)) {
286 
287  // We give more tolerant in computing the best rigid transformation.
289 
290 // std::cout << "congruent candidate: [";
291 // for (int j = 0; j!= Traits::size(); ++j)
292 // std::cout << congruent_ids[j] << " ";
293 // std::cout << "]";
294 
295  nbCongruentAto++;
296  // The transformation is computed from the point-clouds centered inn [0,0,0]
297 
298  // Verify the rest of the points in Q against P.
300 // std::cout << " " << lcp << " - Angle: ";
301 
302  // compute angle between pairs of points: for debug only
303 // Scalar angle = (congruent_candidate[1].pos() - congruent_candidate[0].pos()).normalized().dot(
304 // (congruent_candidate[3].pos() - congruent_candidate[2].pos()).normalized());
305 // std::cout << std::acos(angle)*Scalar(180)/pi << " (error: "
306 // << (std::acos(targetAngle) - std::acos(angle))*Scalar(180)/pi << ")\n";
307 
308  // transformation has been computed between the two point clouds centered
309  // at the origin, we need to recompute the translation to apply it to the original clouds
310 #ifdef OpenGR_USE_OPENMP
311 #pragma omp critical
312 #endif
313  {
314  auto getGlobalTransform =
315  [this, transform, centroid1, centroid2]
317  Eigen::Matrix<Scalar, 3, 3> rot, scale;
322  };
323 
325  {
328  v(-1, lcp, transformation);
329  }
330  else
331  v(-1, lcp, transform);
332 
333  if (lcp > best_LCP_) {
334  // Retain the best LCP and transformation.
335  for (int j = 0; j!= Traits::size(); ++j)
336  base_[j] = base[j];
337 
338 
339  for (int j = 0; j!= Traits::size(); ++j)
341 
342  best_LCP_ = lcp;
346  }
347 
348  }
349  // Terminate if we have the desired LCP already.
351  continue;
352  }
353  }
354  } else {
355 
356 // std::cout << "skipped candidate: [";
357 // for (int j = 0; j!= Traits::size(); ++j)
358 // std::cout << congruent_ids[j] << " ";
359 // std::cout << "]\n";
360  }
361  }
362 
364 
365  // If we reached here we do not have yet the desired LCP.
367 }
368 
369 
370 // Verify a given transformation by computing the number of points in P at
371 // distance at most (normalized) delta from some point in Q. In the paper
372 // we describe randomized verification. We apply deterministic one here with
373 // early termination. It was found to be fast in practice.
374 template <typename Traits, typename PointType, typename TransformVisitor,
375  typename PairFilteringFunctor,
376  template < class, class > class ... OptExts >
380 
381 #ifdef TEST_GLOBAL_TIMINGS
382  Timer t_verify (true);
383 #endif
384 
388 
389 #ifdef TEST_GLOBAL_TIMINGS
391 #endif
392 
393  return score;
394 }
395 
396 }
Definition: congruentSetExplorationBase.hpp:28
bool is_range_empty(const Range &r)
Definition: congruentSetExplorationBase.hpp:30
CongruentSetExplorationBase< Traits, PointType, TransformVisitor, PairFilteringFunctor, OptExts... >::Scalar ComputeTransformation(const InputRange1 &P, const InputRange2 &Q, Eigen::Ref< typename CongruentSetExplorationBase< Traits, PointType, TransformVisitor, PairFilteringFunctor, OptExts... >::MatrixType > transformation, const Sampler< PointType > &sampler, TransformVisitor &v)
Definition: congruentSetExplorationBase.hpp:61