matchBase.h
Go to the documentation of this file.
1 // Copyright 2017 Nicolas Mellado
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // -------------------------------------------------------------------------- //
16 //
17 // Authors: Dror Aiger, Yoni Weill, Nicolas Mellado
18 //
19 // This file is part of the implementation of the 4-points Congruent Sets (4PCS)
20 // algorithm presented in:
21 //
22 // 4-points Congruent Sets for Robust Surface Registration
23 // Dror Aiger, Niloy J. Mitra, Daniel Cohen-Or
24 // ACM SIGGRAPH 2008 and ACM Transaction of Graphics.
25 //
26 // Given two sets of points in 3-space, P and Q, the algorithm applies RANSAC
27 // in roughly O(n^2) time instead of O(n^3) for standard RANSAC, using an
28 // efficient method based on invariants, to find the set of all 4-points in Q
29 // that can be matched by rigid transformation to a given set of 4-points in P
30 // called a base. This avoids the need to examine all sets of 3-points in Q
31 // against any base of 3-points in P as in standard RANSAC.
32 // The algorithm can use colors and normals to speed-up the matching
33 // and to improve the quality. It can be easily extended to affine/similarity
34 // transformation but then the speed-up is smaller because of the large number
35 // of congruent sets. The algorithm can also limit the range of transformations
36 // when the application knows something on the initial pose but this is not
37 // necessary in general (though can speed the runtime significantly).
38 
39 // Home page of the 4PCS project (containing the paper, presentations and a
40 // demo): http://graphics.stanford.edu/~niloy/research/fpcs/fpcs_sig_08.html
41 // Use google search on "4-points congruent sets" to see many related papers
42 // and applications.
43 
44 #ifndef _OPENGR_ALGO_MATCH_BASE_
45 #define _OPENGR_ALGO_MATCH_BASE_
46 
47 #include <vector>
48 
49 #ifdef OpenGR_USE_OPENMP
50 #include <omp.h>
51 #endif
52 
53 #include "gr/utils/shared.h"
54 #include "gr/utils/sampling.h"
55 #include "gr/accelerators/kdtree.h"
56 #include "gr/utils/logger.h"
57 #include "gr/utils/crtp.h"
58 
59 #ifdef TEST_GLOBAL_TIMINGS
60 # include "gr/utils/timer.h"
61 #endif
62 
63 namespace gr{
64 
66  template <typename Derived>
67  inline void operator() (float, float, const Eigen::MatrixBase<Derived>&) const {}
68  constexpr bool needsGlobalTransformation() const { return false; }
69 };
70 
71 /// \brief Abstract class for registration algorithms
72 template <typename PointType, typename _TransformVisitor = DummyTransformVisitor,
73  template < class, class > class ... OptExts>
74 class MatchBase {
75 
76 public:
77  using Scalar = typename PointType::Scalar;
78  using VectorType = typename PointType::VectorType;
79  using MatrixType = Eigen::Matrix<Scalar, 4, 4>;
80  using LogLevel = Utils::LogLevel;
81  using TransformVisitor = _TransformVisitor;
82 
83  template < class Derived, class TBase>
84  class Options : public TBase
85  {
86  public:
87  using Scalar = typename PointType::Scalar;
88 
89  /// Distance threshold used to compute the LCP
90  /// \todo Move to DistanceMeasure
91  Scalar delta = Scalar(5.0);
92  /// The number of points in the sample. We sample this number of points
93  /// uniformly from P and Q.
95  /// Maximum time we allow the computation to take. This makes the algorithm
96  /// an ANY TIME algorithm that can be stopped at any time, producing the best
97  /// solution so far.
98  /// \warning Max. computation time must be handled in child classes
99  int max_time_seconds = 60;
100  /// use a constant default seed by default
101  unsigned int randomSeed = std::mt19937::default_seed;
102 
103  /// Constraints about transformations
104 
105  /// Maximum rotation angle. Set negative to ignore
106  Scalar max_angle = -1;
107  /// Maximum translation distance. Set negative to ignore
109  // \FIXME std::pair <Scalar, Scalar> scale_range;
110  };
111 
112  using OptionsType = gr::Utils::CRTP < OptExts ... , Options >;
113 
114  /// A convenience class used to wrap (any) PointType to allow mutation of position
115  /// of point samples for internal computations.
116  struct PosMutablePoint : public PointType
117  {
118  using VectorType = typename PointType::VectorType;
119 
120  private:
121  VectorType posCopy;
122 
123  public:
124  template<typename ExternalType>
125  PosMutablePoint(const ExternalType& i)
126  : PointType(i), posCopy(PointType(i).pos()) { }
127 
128  inline VectorType & pos() { return posCopy; }
129 
130  inline VectorType pos() const { return posCopy; }
131  };
132 
133  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
134 
135  MatchBase(const OptionsType& options, const Utils::Logger &logger);
136 
137  virtual ~MatchBase();
138 
139  /// Read access to the sampled clouds used for the registration
141  return sampled_P_3D_;
142  }
143 
144  /// Read access to the sampled clouds used for the registration
146  return sampled_Q_3D_;
147  }
148 
149 
150 #ifdef PARSED_BY_DOXYGEN
151  /// Computes an approximation of the best LCP (directional) from Q to P
152  /// and the rigid transformation that realizes it. The input sets may or may
153  /// not contain normal information for any point.
154  /// @param [in] P The first input set.
155  /// @param [in] Q The second input set.
156  /// @param [out] transformation Rigid transformation matrix (4x4) that brings
157  /// Q to the (approximate) optimal LCP. Initial value is considered as a guess
158  /// @return the computed LCP measure as a fraction of the size of P ([0..1]).
159 /* template <typename Sampler>
160  Scalar ComputeTransformation(const std::vector<Point3D>& P,
161  const std::vector<Point3D>& Q,
162  Eigen::Ref<MatrixType> transformation,
163  const Sampler& sampler,
164  TransformVisitor& v) {}
165 */
166  /// Computes an approximation of the best LCP (directional) from Q to P
167  /// and the rigid transformation that realizes it. The input sets may or may
168  /// not contain normal information for any point.
169  /// @param [in] P The first input set.
170  /// @param [in] Q The second input set.
171  /// @param [out] transformation Rigid transformation matrix (4x4) that brings
172  /// Q to the (approximate) optimal LCP. Initial value is considered as a guess
173  /// @return the computed LCP measure as a fraction of the size of P ([0..1]).
174  template <typename InputRange1,
175  typename InputRange2,
176  template<typename> typename Sampler>
178  const InputRange2& Q,
180  const Sampler<PointType>& sampler,
181  TransformVisitor& v) {}
182 
183 
184 #endif
185 
186 protected:
187  /// The diameter of P.
189  /// Mean distance between points and their nearest neighbor in the set P.
190  /// Used to normalize the "delta" which is given in terms of this distance.
192  /// The transformation matrix by wich we transform Q to P
194  /// Sampled P (3D coordinates).
196  /// Sampled Q (3D coordinates).
198  /// The centroid of P.
200  /// The centroid of Q.
204  /// KdTree used to compute the LCP
208 
210 
211  /// \todo Rationnalize use and name of this variable
213 
214 protected :
215  template <Utils::LogLevel level, typename...Args>
216  inline void Log(Args...args) const { logger_.Log<level>(args...); }
217 
218 
219  /// Computes the mean distance between points in Q and their nearest neighbor.
220  /// We need this for normalization of the user delta (See the paper) to the
221  /// "scale" of the set.
222  Scalar MeanDistance() const;
223 
224 
225  /// Selects a random triangle in the set P (then we add another point to keep the
226  /// base as planar as possible). We apply a simple heuristic that works in most
227  /// practical cases. The idea is to accept maximum distance, computed by the
228  /// estimated overlap, multiplied by the diameter of P, and try to have
229  /// a triangle with all three edges close to this distance. Wide triangles helps
230  /// to make the transformation robust while too large triangles makes the
231  /// probability of having all points in the inliers small so we try to trade-off.
232  /// \param max_base_diameter Maximum size allowed between two points of the base
233  bool SelectRandomTriangle(Scalar max_base_diameter, int& base1, int& base2, int& base3);
234 
235  /// Computes the best rigid transformation between three corresponding pairs.
236  /// The transformation is characterized by rotation matrix, translation vector
237  /// and a center about which we rotate. The set of pairs is potentially being
238  /// updated by the best permutation of the second set. Returns the RMS of the
239  /// fit. The method is being called with n points but it applies the fit for
240  /// only 3 after the best permutation is selected in the second set (see
241  /// bellow). This is done because the solution for planar points is much
242  /// simpler.
243  /// The method is the closed-form solution by Horn:
244  /// people.csail.mit.edu/bkph/papers/Absolute_Orientation.pdf
245  /// \tparam Coordinates Struct with operator[](int i) ->i\in[0:2]
246  template <typename Coordinates>
247  bool ComputeRigidTransformation(const Coordinates& ref,
248  const Coordinates& candidate,
249  const Eigen::Matrix<Scalar, 3, 1>& centroid1,
250  Eigen::Matrix<Scalar, 3, 1> centroid2,
251  Eigen::Ref<MatrixType> transform,
252  Scalar& rms_,
253  bool computeScale ) const;
254 
255  /// Initializes the data structures and needed values before the match
256  /// computation.
257  /// This method is called once the internal state of the Base class as been
258  /// set.
259  virtual void Initialize() { }
260 
261  /// Initializes the internal state of the Base class
262  /// @param P The first input set.
263  /// @param Q The second input set.
264  /// @param sampler The sampler used to sample the input sets.
265  template <typename InputRange1, typename InputRange2, template<typename> class Sampler>
266  void init(const InputRange1& P,
267  const InputRange2& Q,
268  const Sampler<PointType>& sampler);
269 
270 private:
271 
272  void initKdTree();
273 
274 }; /// class MatchBase
275 } /// namespace gr
276 #include "matchBase.hpp"
277 
278 #endif // _OPENGR_ALGO_MATCH_BASE_
std::mt19937 randomGenerator_
Definition: matchBase.h:206
Scalar MeanDistance() const
Computes the mean distance between points in Q and their nearest neighbor. We need this for normaliza...
Scalar max_angle
Constraints about transformations.
Definition: matchBase.h:106
bool ComputeRigidTransformation(const Coordinates &ref, const Coordinates &candidate, const Eigen::Matrix< Scalar, 3, 1 > &centroid1, Eigen::Matrix< Scalar, 3, 1 > centroid2, Eigen::Ref< MatrixType > transform, Scalar &rms_, bool computeScale) const
Computes the best rigid transformation between three corresponding pairs. The transformation is chara...
size_t sample_size
The number of points in the sample. We sample this number of points uniformly from P and Q...
Definition: matchBase.h:94
int max_time_seconds
Maximum time we allow the computation to take. This makes the algorithm an ANY TIME algorithm that ca...
Definition: matchBase.h:99
void Log(Args...args) const
Definition: matchBase.h:216
constexpr bool needsGlobalTransformation() const
Definition: matchBase.h:68
Scalar max_translation_distance
Maximum translation distance. Set negative to ignore.
Definition: matchBase.h:108
Definition: matchBase.h:84
virtual void Initialize()
Initializes the data structures and needed values before the match computation. This method is called...
Definition: matchBase.h:259
std::vector< PosMutablePoint > sampled_P_3D_
Sampled P (3D coordinates).
Definition: matchBase.h:195
EIGEN_MAKE_ALIGNED_OPERATOR_NEW MatchBase(const OptionsType &options, const Utils::Logger &logger)
VectorType & pos()
Definition: matchBase.h:128
const Utils::Logger & logger_
Definition: matchBase.h:207
VectorType pos() const
Definition: matchBase.h:130
const std::vector< PosMutablePoint > & getFirstSampled() const
Read access to the sampled clouds used for the registration.
Definition: matchBase.h:140
virtual ~MatchBase()
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
const std::vector< PosMutablePoint > & getSecondSampled() const
Read access to the sampled clouds used for the registration.
Definition: matchBase.h:145
void get(int queryId, int nElPerDim, int, typename NeighborhoodType< 3 >::ptr first, typename NeighborhoodType< 3 >::ptr)
Definition: utils.h:279
LogLevel
Definition: logger.h:55
Definition: logger.h:62
unsigned int randomSeed
use a constant default seed by default
Definition: matchBase.h:101
void init(const InputRange1 &P, const InputRange2 &Q, const Sampler< PointType > &sampler)
Initializes the internal state of the Base class.
bool SelectRandomTriangle(Scalar max_base_diameter, int &base1, int &base2, int &base3)
Selects a random triangle in the set P (then we add another point to keep the base as planar as possi...
Scalar ComputeTransformation(const InputRange1 &P, const InputRange2 &Q, Eigen::Ref< MatrixType > transformation, const Sampler< PointType > &sampler, TransformVisitor &v)
Computes an approximation of the best LCP (directional) from Q to P and the rigid transformation that...
Definition: matchBase.h:177
void operator()(float, float, const Eigen::MatrixBase< Derived > &) const
Definition: matchBase.h:67
KdTree< Scalar > kd_tree_
KdTree used to compute the LCP.
Definition: matchBase.h:205
Scalar delta
Distance threshold used to compute the LCP.
Definition: matchBase.h:91
A convenience class used to wrap (any) PointType to allow mutation of position of point samples for i...
Definition: matchBase.h:116
PosMutablePoint(const ExternalType &i)
Definition: matchBase.h:125
OptionsType options_
Definition: matchBase.h:209
std::vector< PosMutablePoint > sampled_Q_3D_
Sampled Q (3D coordinates).
Definition: matchBase.h:197