matchBase.hpp
Go to the documentation of this file.
1 //
2 // Created by Sandra Alfaro on 24/05/18.
3 //
4 
5 #include <vector>
6 #include <atomic>
7 #include <chrono>
8 #include <numeric> // std::iota
9 
10 #ifdef OpenGR_USE_OPENMP
11 #include <omp.h>
12 #endif
13 
14 #include "gr/algorithms/matchBase.h"
15 
16 #ifdef TEST_GLOBAL_TIMINGS
17 # include "gr/utils/timer.h"
18 #endif
19 
20 
21 #define MATCH_BASE_TYPE MatchBase<PointType, TransformVisitor, OptExts ... >
22 
23 
24 namespace gr {
25 
26 template <typename PointType, typename TransformVisitor, template < class, class > class ... OptExts>
28  const Utils::Logger& logger
29  )
31  , logger_(logger)
32  , options_(options)
33 {}
34 
35 template <typename PointType, typename TransformVisitor, template < class, class > class ... OptExts>
37 
38 
39 template <typename PointType, typename TransformVisitor, template < class, class > class ... OptExts>
40 typename MATCH_BASE_TYPE::Scalar
42  const Scalar kDiameterFraction = 0.2;
43  using RangeQuery = typename gr::KdTree<Scalar>::template RangeQuery<>;
44 
45  int number_of_samples = 0;
46  Scalar distance = 0.0;
47 
48  for (size_t i = 0; i < sampled_P_3D_.size(); ++i) {
49 
52  query.queryPoint = sampled_P_3D_[i].pos().template cast<Scalar>();
53 
55 
56  if (resId != gr::KdTree<Scalar>::invalidIndex()) {
59  }
60  }
61 
62  return distance / number_of_samples;
63 }
64 
65 template <typename PointType, typename TransformVisitor, template < class, class > class ... OptExts>
66 bool
69  base1 = base2 = base3 = -1;
70 
71  // Pick the first point at random.
73 
75  std::cout << "sq_max_base_diameter_: " << sq_max_base_diameter_ << std::endl;
76 
77  // Try fixed number of times retaining the best other two.
78  Scalar best_wide = 0.0;
79  for (int i = 0; i < kNumberOfDiameterTrials; ++i) {
80  // Pick and compute
83  const VectorType u =
86  const VectorType w =
89  // We try to have wide triangles but still not too large.
90  Scalar how_wide = (u.cross(w)).norm();
91  if (how_wide > best_wide &&
98  }
99  }
100  return base1 != -1 && base2 != -1 && base3 != -1;
101 }
102 
103 template <typename PointType, typename TransformVisitor, template < class, class > class ... OptExts>
104 void
107 
108  // Build the kdtree.
110 
111  for (size_t i = 0; i < number_of_points; ++i) {
113  }
114  kd_tree_.finalize();
115 }
116 
117 
118 template <typename PointType, typename TransformVisitor, template < class, class > class ... OptExts>
119 template <typename Coordinates>
120 bool
122  const Coordinates& candidate,
123  const Eigen::Matrix<Scalar, 3, 1>& centroid1,
124  Eigen::Matrix<Scalar, 3, 1> centroid2,
126  Scalar& rms_,
127  bool computeScale ) const {
128  static const Scalar pi = std::acos(-1);
129 
131 
132  Scalar kSmallNumber = 1e-6;
133 
134  // We only use the first 3 pairs. This simplifies the process considerably
135  // because it is the planar case.
136 
137  const VectorType& p0 = ref[0]->pos();
138  const VectorType& p1 = ref[1]->pos();
139  const VectorType& p2 = ref[2]->pos();
140  VectorType q0 = candidate[0]->pos();
141  VectorType q1 = candidate[1]->pos();
142  VectorType q2 = candidate[2]->pos();
143 
144  Scalar scaleEst (1.);
145 
146  // Compute scale factor if needed
147  if (computeScale){
148  const VectorType& p3 = ref[3]->pos();
149  const VectorType& q3 = candidate[3]->pos();
150 
151  const Scalar ratio1 = (p1 - p0).norm() / (q1 - q0).norm();
152  const Scalar ratio2 = (p3 - p2).norm() / (q3 - q2).norm();
153 
154  const Scalar ratioDev = std::abs(ratio1/ratio2 - Scalar(1.)); // deviation between the two
155  const Scalar ratioMean = (ratio1+ratio2)/Scalar(2.); // mean of the two
156 
157  if ( ratioDev > Scalar(0.1) )
158  return std::numeric_limits<Scalar>::max();
159 
160 
161  //Log<LogLevel::Verbose>( ratio1, " ", ratio2, " ", ratioDev, " ", ratioMean);
163 
164  // apply scale factor to q
165  q0 = q0*scaleEst;
166  q1 = q1*scaleEst;
167  q2 = q2*scaleEst;
168  centroid2 *= scaleEst;
169  }
170 
172  if (vector_p1.squaredNorm() == 0) return std::numeric_limits<Scalar>::max();
174  VectorType vector_p2 = (p2 - p0) - ((p2 - p0).dot(vector_p1)) * vector_p1;
175  if (vector_p2.squaredNorm() == 0) return std::numeric_limits<Scalar>::max();
178  if (vector_p3.squaredNorm() == 0) return std::numeric_limits<Scalar>::max();
180 
182  if (vector_q1.squaredNorm() == 0) return std::numeric_limits<Scalar>::max();
184  VectorType vector_q2 = (q2 - q0) - ((q2 - q0).dot(vector_q1)) * vector_q1;
185  if (vector_q2.squaredNorm() == 0) return std::numeric_limits<Scalar>::max();
188  if (vector_q3.squaredNorm() == 0) return std::numeric_limits<Scalar>::max();
190 
191  //cv::Mat rotation = cv::Mat::eye(3, 3, CV_64F);
192  Eigen::Matrix<Scalar, 3, 3> rotation = Eigen::Matrix<Scalar, 3, 3>::Identity();
193 
194  Eigen::Matrix<Scalar, 3, 3> rotate_p;
195  rotate_p.row(0) = vector_p1;
196  rotate_p.row(1) = vector_p2;
197  rotate_p.row(2) = vector_p3;
198 
199  Eigen::Matrix<Scalar, 3, 3> rotate_q;
200  rotate_q.row(0) = vector_q1;
201  rotate_q.row(1) = vector_q2;
202  rotate_q.row(2) = vector_q3;
203 
205 
206 
207  // Discard singular solutions. The rotation should be orthogonal.
208  if (((rotation * rotation).diagonal().array() - Scalar(1) > kSmallNumber).any())
209  return false;
210 
211  //Filter transformations.
212  // \fixme Need to consider max_translation_distance and max_scale too
213  if (options_.max_angle >= 0) {
214  Scalar mangle = options_.max_angle * pi / 180.0;
215  // Discard too large solutions (todo: lazy evaluation during boolean computation
216  if (! (
217  std::abs(std::atan2(rotation(2, 1), rotation(2, 2)))
218  <= mangle &&
219 
220  std::abs(std::atan2(-rotation(2, 0),
221  std::sqrt(std::pow(rotation(2, 1),2) +
222  std::pow(rotation(2, 2),2))))
223  <= mangle &&
224 
225  std::abs(atan2(rotation(1, 0), rotation(0, 0)))
226  <= mangle
227  ))
228  return false;
229  }
230 
231 
232  //FIXME
233  // Compute rms and return it.
234  rms_ = Scalar(0.0);
235  {
237 
238  //cv::Mat first(3, 1, CV_64F), transformed;
239  for (int i = 0; i < 3; ++i) {
242  rms_ += (transformed - ref[i]->pos() + centroid1).norm();
243  }
244  }
245 
246  rms_ /= Scalar(ref.size());
247 
249  transform = etrans
250  .scale(scaleEst)
252  .rotate(rotation)
254  .matrix();
255 
256  return true;
257 }
258 
259 template <typename PointType, typename TransformVisitor, template < class, class > class ... OptExts>
260 template <typename InputRange1, typename InputRange2, template<typename> class Sampler>
261 void MATCH_BASE_TYPE::init(const InputRange1& P,
262  const InputRange2& Q,
263  const Sampler<PointType>& sampler) {
264 
267 
270 
271  // prepare P
272  if (P.size() > options_.sample_size){
274 
276  }
277  else
278  {
279  Log<LogLevel::ErrorReport>( "(P) More samples requested than available: use whole cloud" );
280 
281  // copy all the points
283  }
284 
285  // prepare Q
286  if (Q.size() > options_.sample_size){
288 
290 
291  std::vector<int> indices(uniform_Q.size());
292  std::iota( std::begin(indices), std::end(indices), 0 );
296 
297  // using the indices, copy elements from uniform_Q to sampled_P_3D_
298  for(int i : indices)
300 
301  uniform_Q.clear();
302  }
303  else
304  {
305  Log<LogLevel::ErrorReport>( "(Q) More samples requested than available: use whole cloud" );
306 
307  // copy all the points
309  }
310 
311 
312  // center points around centroids
315  for(auto& p : container) centroid += p.pos();
317  for(auto& p : container) p.pos() -= centroid;
318  };
321 
322 
323  initKdTree();
324 
325  // Compute the diameter of P approximately (randomly). This is far from being
326  // Guaranteed close to the diameter but gives good results for most common
327  // objects if they are densely sampled.
328  P_diameter_ = 0.0;
329  for (int i = 0; i < kNumberOfDiameterTrials; ++i) {
332 
334  if (l > P_diameter_) {
335  P_diameter_ = l;
336  }
337  }
338 
339  // Mean distance and a bit more... We increase the estimation to allow for
340  // noise, wrong estimation and non-uniform sampling.
342 
343  transform_ = Eigen::Matrix<Scalar, 4, 4>::Identity();
344 
345  // call Virtual handler
346  Initialize();
347 }
348 
349 } // namespace gr
350 
351 #undef MATCH_BASE_TYPE
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
#define MATCH_BASE_TYPE
Definition: matchBase.hpp:21