44 #ifndef _SUPER4PCS_ALGO_MATCH_4PCS_BASE_IMPL_ 45 #define _SUPER4PCS_ALGO_MATCH_4PCS_BASE_IMPL_ 47 #ifndef _SUPER4PCS_ALGO_MATCH_4PCS_BASE_ 48 #include "super4pcs/algorithms/match4pcsBase.h" 54 #include <Eigen/Geometry> 61 template <
typename Sampler,
typename Visitor>
64 std::vector<Point3D>* Q,
65 Eigen::Ref<MatrixType> transformation,
66 const Sampler& sampler,
74 if (best_LCP_ !=
Scalar(1.))
75 Perform_N_steps(number_of_trials_, transformation, Q, v);
77 #ifdef TEST_GLOBAL_TIMINGS 78 Log<LogLevel::Verbose>(
"----------- Timings (msec) -------------" );
79 Log<LogLevel::Verbose>(
" Total computation time : ", totalTime );
80 Log<LogLevel::Verbose>(
" Total verify time : ", verifyTime );
81 Log<LogLevel::Verbose>(
" Kdtree query : ", kdTreeTime );
82 Log<LogLevel::Verbose>(
"----------------------------------------" );
90 template <
typename Sampler>
91 void Match4PCSBase::init(
const std::vector<Point3D>& P,
92 const std::vector<Point3D>& Q,
93 const Sampler& sampler){
95 #ifdef TEST_GLOBAL_TIMINGS 101 const Scalar kSmallError = 0.00001;
102 const int kMinNumberOfTrials = 4;
103 const Scalar kDiameterFraction = 0.3;
105 centroid_P_ = VectorType::Zero();
106 centroid_Q_ = VectorType::Zero();
108 sampled_P_3D_.clear();
109 sampled_Q_3D_.clear();
113 sampler(P, options_, sampled_P_3D_);
117 Log<LogLevel::ErrorReport>(
"(P) More samples requested than available: use whole cloud" );
125 std::vector<Point3D> uniform_Q;
126 sampler(Q, options_, uniform_Q);
129 std::shuffle(uniform_Q.begin(), uniform_Q.end(), randomGenerator_);
130 size_t nbSamples = std::min(uniform_Q.size(), options_.
sample_size);
131 auto endit = uniform_Q.begin(); std::advance(endit, nbSamples );
132 std::copy(uniform_Q.begin(), endit, std::back_inserter(sampled_Q_3D_));
136 Log<LogLevel::ErrorReport>(
"(Q) More samples requested than available: use whole cloud" );
142 auto centerPoints = [](std::vector<Point3D>&container,
144 for(
const auto& p : container) centroid += p.pos();
145 centroid /=
Scalar(container.size());
146 for(
auto& p : container) p.pos() -= centroid;
148 centerPoints(sampled_P_3D_, centroid_P_);
149 centerPoints(sampled_Q_3D_, centroid_Q_);
157 int at = randomGenerator_() % sampled_Q_3D_.size();
158 int bt = randomGenerator_() % sampled_Q_3D_.size();
160 Scalar l = (sampled_Q_3D_[bt].pos() - sampled_Q_3D_[at].pos()).norm();
161 if (l > P_diameter_) {
168 P_mean_distance_ = MeanDistance();
172 max_base_diameter_ = P_diameter_;
177 static_cast<Scalar>(kMinNumberOfTrials)));
182 static_cast<int>(first_estimation * (P_diameter_ / kDiameterFraction) /
184 if (number_of_trials_ < kMinNumberOfTrials)
185 number_of_trials_ = kMinNumberOfTrials;
187 Log<LogLevel::Verbose>(
"norm_max_dist: ", options_.
delta );
192 for (
int i = 0; i < 4; ++i) {
194 current_congruent_[i] = 0;
196 transform_ = Eigen::Matrix<Scalar, 4, 4>::Identity();
201 best_LCP_ = Verify(transform_);
202 Log<LogLevel::Verbose>(
"Initial LCP: ", best_LCP_ );
208 template <
typename Visitor>
210 Match4PCSBase::Perform_N_steps(
int n,
211 Eigen::Ref<MatrixType> transformation,
212 std::vector<Point3D>* Q,
214 using std::chrono::system_clock;
215 if (Q ==
nullptr)
return false;
217 #ifdef TEST_GLOBAL_TIMINGS 224 auto getGlobalTransform = [
this](Eigen::Ref<MatrixType> transformation){
225 Eigen::Matrix<Scalar, 3, 3> rot, scale;
226 Eigen::Transform<Scalar, 3, Eigen::Affine> (transform_).computeRotationScaling(&rot, &scale);
227 transformation = transform_;
228 transformation.col(3) = (qcentroid1_ + centroid_P_ - ( rot * scale * (qcentroid2_ + centroid_Q_))).homogeneous();
231 Scalar last_best_LCP = best_LCP_;
232 v(0, best_LCP_, transformation);
235 std::chrono::time_point<system_clock> t0 = system_clock::now(), end;
236 for (
int i = current_trial_; i < current_trial_ + n; ++i) {
241 std::chrono::duration_cast<std::chrono::seconds>
242 (system_clock::now() - t0).count() /
244 Scalar fraction = std::max(fraction_time, fraction_try);
246 if (v.needsGlobalTransformation()) {
247 getGlobalTransform(transformation);
249 transformation = transform_;
252 v(fraction, best_LCP_, transformation);
255 if (ok || i > number_of_trials_ || fraction >= 0.99 || best_LCP_ == 1.0)
break;
259 if (best_LCP_ > last_best_LCP) {
262 getGlobalTransform(transformation);
265 for (
size_t i = 0; i < Q->size(); ++i) {
266 (*Q)[i].pos() = (transformation * (*Q)[i].pos().homogeneous()).head<3>();
269 #ifdef TEST_GLOBAL_TIMINGS 270 totalTime +=
Scalar(t.elapsed().count()) /
Scalar(CLOCKS_PER_SEC);
273 return ok || current_trial_ >= number_of_trials_;
281 template<
typename Visitor>
282 bool Match4PCSBase::TryOneBase(
const Visitor &v) {
283 Scalar invariant1, invariant2;
284 int base_id1, base_id2, base_id3, base_id4;
289 static bool first_time =
true;
297 base_3D_[0] = sampled_P_3D_ [base_id1];
298 base_3D_[1] = sampled_P_3D_ [base_id2];
299 base_3D_[2] = sampled_P_3D_ [base_id3];
300 base_3D_[3] = sampled_P_3D_ [base_id4];
302 TryQuadrilateral(&invariant1, &invariant2, base_id1, base_id2, base_id3, base_id4);
311 if (!SelectQuadrilateral(invariant1, invariant2, base_id1, base_id2,
312 base_id3, base_id4)) {
318 const Scalar distance1 = (base_3D_[0].pos()- base_3D_[1].pos()).norm();
319 const Scalar distance2 = (base_3D_[2].pos()- base_3D_[3].pos()).norm();
321 std::vector<std::pair<int, int>> pairs1, pairs2;
322 std::vector<Quadrilateral> congruent_quads;
325 const Scalar normal_angle1 = (base_3D_[0].normal() - base_3D_[1].normal()).norm();
326 const Scalar normal_angle2 = (base_3D_[2].normal() - base_3D_[3].normal()).norm();
335 if (pairs1.size() == 0 || pairs2.size() == 0) {
340 if (!FindCongruentQuadrilaterals(invariant1, invariant2,
351 bool match = TryCongruentSet(base_id1, base_id2, base_id3, base_id4,
363 template <
typename Visitor>
364 bool Match4PCSBase::TryCongruentSet(
369 const std::vector<Quadrilateral>& congruent_quads,
371 size_t &nbCongruent){
372 static const double pi = std::acos(-1);
375 const Point3D& b1 = sampled_P_3D_[base_id1];
376 const Point3D& b2 = sampled_P_3D_[base_id2];
377 const Point3D& b3 = sampled_P_3D_[base_id3];
378 const Point3D& b4 = sampled_P_3D_[base_id4];
381 const std::array<Point3D, 4> congruent_base {{b1, b2, b3, b4}};
385 Eigen::Matrix<Scalar, 3, 1> centroid1 = (b1.
pos() + b2.
pos() + b3.
pos()) /
Scalar(3);
388 std::atomic<size_t> nbCongruentAto(0);
390 #ifdef SUPER4PCS_USE_OPENMP 391 #pragma omp parallel for num_threads(omp_nthread_congruent_) 393 for (
int i = 0; i < int(congruent_quads.size()); ++i) {
394 std::array<Point3D, 4> congruent_candidate;
396 Eigen::Matrix<Scalar, 4, 4> transform;
399 Eigen::Matrix<Scalar, 3, 1> centroid2;
401 const int a = congruent_quads[i].vertices[0];
402 const int b = congruent_quads[i].vertices[1];
403 const int c = congruent_quads[i].vertices[2];
404 const int d = congruent_quads[i].vertices[3];
405 congruent_candidate[0] = sampled_Q_3D_[a];
406 congruent_candidate[1] = sampled_Q_3D_[b];
407 congruent_candidate[2] = sampled_Q_3D_[c];
408 congruent_candidate[3] = sampled_Q_3D_[d];
411 Log<LogLevel::Verbose>(
"Ids: ", base_id1,
"\t", base_id2,
"\t", base_id3,
"\t", base_id4);
412 Log<LogLevel::Verbose>(
" ", a,
"\t", b,
"\t", c,
"\t", d);
415 centroid2 = (congruent_candidate[0].pos() +
416 congruent_candidate[1].pos() +
417 congruent_candidate[2].pos()) /
Scalar(3.);
422 ComputeRigidTransformation(congruent_base,
436 if (ok && rms >=
Scalar(0.)) {
445 Scalar lcp = Verify(transform);
449 auto getGlobalTransform =
450 [
this, transform, centroid1, centroid2]
451 (Eigen::Ref<MatrixType> transformation){
452 Eigen::Matrix<Scalar, 3, 3> rot, scale;
453 Eigen::Transform<Scalar, 3, Eigen::Affine> (transform).computeRotationScaling(&rot, &scale);
454 transformation = transform;
455 transformation.col(3) = (centroid1 + centroid_P_ - ( rot * scale * (centroid2 + centroid_Q_))).homogeneous();
458 if (v.needsGlobalTransformation())
460 Eigen::Matrix<Scalar, 4, 4> transformation = transform;
461 getGlobalTransform(transformation);
462 v(-1, lcp, transformation);
465 v(-1, lcp, transform);
468 if (lcp > best_LCP_) {
475 current_congruent_[0] = a;
476 current_congruent_[1] = b;
477 current_congruent_[2] = c;
478 current_congruent_[3] = d;
481 transform_ = transform;
482 qcentroid1_ = centroid1;
483 qcentroid2_ = centroid2;
493 nbCongruent = nbCongruentAto;
size_t sample_size
The number of points in the sample. We sample this number of points uniformly from P and Q...
Definition: shared4pcs.h:165
static constexpr int kNumberOfDiameterTrials
Definition: match4pcsBase.h:79
Scalar ComputeTransformation(const std::vector< Point3D > &P, std::vector< Point3D > *Q, Eigen::Ref< MatrixType > transformation, const Sampler &sampler=Sampler(), const Visitor &v=Visitor())
Computes an approximation of the best LCP (directional) from Q to P and the rigid transformation that...
Definition: match4pcsBase.hpp:63
Scalar getOverlapEstimation() const
Definition: shared4pcs.h:180
The basic 3D point structure. A point potentially contains also directional information and color...
Definition: shared4pcs.h:61
int max_time_seconds
Maximum time we allow the computation to take. This makes the algorithm an ANY TIME algorithm that ca...
Definition: shared4pcs.h:169
typename Point3D::VectorType VectorType
Definition: match4pcsBase.h:70
Scalar delta
The delta for the LCP (see the paper).
Definition: shared4pcs.h:153
Scalar getTerminateThreshold() const
Definition: shared4pcs.h:179
Scalar max_angle
Maximum rotation angle. Set negative to ignore.
Definition: shared4pcs.h:160
static constexpr Scalar distance_factor
Definition: match4pcsBase.h:81
VectorType & pos()
Definition: shared4pcs.h:77
typename Point3D::Scalar Scalar
Definition: match4pcsBase.h:69
static constexpr Scalar kLargeNumber
Definition: match4pcsBase.h:80