intersectionFunctor.h
Go to the documentation of this file.
1 // Copyright 2014 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: Nicolas Mellado
18 //
19 // An implementation of the Super 4-points Congruent Sets (Super 4PCS)
20 // algorithm presented in:
21 //
22 // Super 4PCS: Fast Global Pointcloud Registration via Smart Indexing
23 // Nicolas Mellado, Dror Aiger, Niloy J. Mitra
24 // Symposium on Geometry Processing 2014.
25 //
26 // Data acquisition in large-scale scenes regularly involves accumulating
27 // information across multiple scans. A common approach is to locally align scan
28 // pairs using Iterative Closest Point (ICP) algorithm (or its variants), but
29 // requires static scenes and small motion between scan pairs. This prevents
30 // accumulating data across multiple scan sessions and/or different acquisition
31 // modalities (e.g., stereo, depth scans). Alternatively, one can use a global
32 // registration algorithm allowing scans to be in arbitrary initial poses. The
33 // state-of-the-art global registration algorithm, 4PCS, however has a quadratic
34 // time complexity in the number of data points. This vastly limits its
35 // applicability to acquisition of large environments. We present Super 4PCS for
36 // global pointcloud registration that is optimal, i.e., runs in linear time (in
37 // the number of data points) and is also output sensitive in the complexity of
38 // the alignment problem based on the (unknown) overlap across scan pairs.
39 // Technically, we map the algorithm as an ‘instance problem’ and solve it
40 // efficiently using a smart indexing data organization. The algorithm is
41 // simple, memory-efficient, and fast. We demonstrate that Super 4PCS results in
42 // significant speedup over alternative approaches and allows unstructured
43 // efficient acquisition of scenes at scales previously not possible. Complete
44 // source code and datasets are available for research use at
45 // http://geometry.cs.ucl.ac.uk/projects/2014/super4PCS/.
46 
47 
48 #ifndef _OPENGR_ACCELERATORS_INTERSECTION_FUNCTOR_H_
49 #define _OPENGR_ACCELERATORS_INTERSECTION_FUNCTOR_H_
50 
51 #include "gr/accelerators/pairExtraction/intersectionNode.h"
52 #include <list>
53 #include <iostream>
54 
55 namespace gr{
56 
57 template <typename Scalar>
58 static Scalar GetRoundedEpsilonValue(Scalar epsilon, int* lvl = nullptr) {
59  const int lvlMax = -std::log2(epsilon); //!< Maximum level
60 
61  if (lvl != nullptr) *lvl = lvlMax;
62 
63  // Refine epsilon by the closest conservative values
64  return 1.f/pow(2,lvlMax);
65 }
66 
67 //! \brief Extract pairs of points by rasterizing primitives and collect points
68 /*!
69  * Acceleration technique used in Super4PCS
70  * \todo Use Traits to allow custom parameters but similar API between variants
71  * \see BruteForceFunctor
72  */
73 template <class _Primitive, class _Point, int _dim, typename _Scalar>
75  typedef _Point Point;
76  typedef _Primitive Primitive;
77  typedef _Scalar Scalar;
78  enum { dim = _dim };
79 
80  template <class PrimitiveContainer,
81  class PointContainer,
82  class ProcessingFunctor> //!< Process the extracted pairs
83  void
84  process(
85  const PrimitiveContainer& M, //!< Input primitives to intersect with Q
86  const PointContainer & Q, //!< Normalized innput point set \in [0:1]^d
87  Scalar &epsilon, //!< Intersection accuracy, refined
88  unsigned int minNodeSize, //!< Min number of points in nodes
89  ProcessingFunctor& functor
90  );
91 
92 };
93 
94 
95 /*!
96  \return Pairs< PointId, PrimitiveId>
97  */
98 template <class Primitive, class Point, int dim, typename Scalar>
99 template <class PrimitiveContainer,
100  class PointContainer,
101  class ProcessingFunctor>
102 void
103 IntersectionFunctor<Primitive, Point, dim, Scalar>::process(
104  const PrimitiveContainer& M, //!< Input primitives to intersect with Q
105  const PointContainer & Q, //!< Normalized innput point set \in [0:1]^d
106  Scalar &epsilon, //!< Intersection accuracy in [0:1]
107  unsigned int minNodeSize, //!< Min number of points in nodes
108  ProcessingFunctor& functor
109  )
110 {
111  using std::pow;
112 
113  // types definitions
114  typedef NdNode<Point, dim, Scalar, PointContainer> Node;
115  typedef typename std::vector<Node> NodeContainer;
116 
117  typedef typename std::pair<unsigned int, unsigned int> ResPair;
118  typedef typename std::vector<ResPair> ResContainer;
119 
120  // Global variables
121  const unsigned int nbPoint = Q.size(); //!< Number of points
122  int lvlMax = 0;
123  epsilon = GetRoundedEpsilonValue(epsilon, &lvlMax);
124 
125  int clvl = 0; //!< Current level
126 
127  // Use local array and manipulate references to avoid array copies
128  NodeContainer ping, pong;
129  NodeContainer* nodes = &ping; //!< Nodes of the current level
130  NodeContainer* childNodes = &pong; //!< Child nodes for the next level
131 
132  //! Nodes too small for split
133  std::vector< std::pair<Node, Scalar> > earlyNodes;
134 //
135 // // Fill the idContainer with identity values
136  if (functor.ids.size() != nbPoint){
137  std::cout << "[IntersectionFunctor] Init id array" << std::endl;
138  functor.ids.clear();
139  for(unsigned int i = 0; i < nbPoint; i++)
140  functor.ids.push_back(i);
141  }
142 
143 
144  // Buid root node in the child node, will be copied to the current nodes
145  childNodes->push_back(Node::buildUnitRootNode(Q, functor.ids));
146 
147  Scalar edgeLength = 0.f;
148  Scalar edgeHalfLength = 0.f;
149 
150  // First Loop
151  while (clvl != lvlMax-1){
152  // Stop if we not have any nodes to checks
153  if (childNodes->empty())
154  break;
155 
156  edgeLength = Scalar(1.f)/pow(2, clvl);
157  edgeHalfLength = edgeLength/Scalar(2.f);
158 
159  // swap pointers
160  std::swap(nodes, childNodes);
161  childNodes->clear();
162 
163 //#pragma omp parallel
164  for(typename NodeContainer::iterator nit = nodes->begin();
165  nit != nodes->end(); nit++){
166  Node &n = *nit;
167 
168  // Check if the current node intersect one of the primitives
169  // In this case, subdivide, store new nodes and stop the loop
170  for(typename PrimitiveContainer::const_iterator pit = M.begin();
171  pit != M.end(); pit++){
172 
173  if ((*pit).intersect(n.center(), edgeHalfLength+epsilon)){
174  // There is two options now: either there is already few points in the
175  // current node, in that case we stop splitting it, or we split.
176  if (n.rangeLength() > int(minNodeSize)){
177 //#pragma omp critical
178  n.split(*childNodes, edgeHalfLength);
179  }else{
180 //#pragma omp critical
181  earlyNodes.emplace_back(n, edgeHalfLength+epsilon);
182  }
183  break;
184  }
185  }
186  }
187  clvl++;
188  }
189 
190  // Second Loop
191  ResContainer results;
192  results.reserve(childNodes->size());
193 
194  unsigned int pId = 0;
195  for(typename PrimitiveContainer::const_iterator itP = M.begin();
196  itP != M.end(); itP++, pId++){
197  // add childs
198  for(typename NodeContainer::const_iterator itN = childNodes->begin();
199  itN != childNodes->end(); itN++){
200  if ((*itP).intersect((*itN).center(), epsilon*2.f)){
201 
202  functor.beginPrimitiveCollect(pId);
203  for(unsigned int j = 0; j!= (unsigned int)((*itN).rangeLength()); j++){
204  if(pId>(*itN).idInRange(j))
205  if((*itP).intersectPoint((*itN).pointInRange(j),epsilon))
206  functor.process(pId, (*itN).idInRange(j));
207  }
208  functor.endPrimitiveCollect(pId);
209  }
210  }
211 
212  // add other leafs
213  for(typename std::vector< std::pair<Node, Scalar> >::const_iterator itPairs =
214  earlyNodes.begin();
215  itPairs != earlyNodes.end();
216  itPairs++){
217  if((*itP).intersect((*itPairs).first.center(), (*itPairs).second)){
218 
219  // Notice the functor we are collecting points for the current primitive
220  functor.beginPrimitiveCollect(pId);
221  for(unsigned int j = 0; j!= (unsigned int)((*itPairs).first.rangeLength()); j++){
222  if(pId>(*itPairs).first.idInRange(j))
223  if((*itP).intersectPoint((*itPairs).first.pointInRange(j),epsilon))
224  functor.process(pId, (*itPairs).first.idInRange(j));
225 
226  }
227  functor.endPrimitiveCollect(pId);
228  }
229  }
230  }
231 }
232 
233 } // namespace gr
234 
235 #endif
_Primitive Primitive
Definition: intersectionFunctor.h:76
static Scalar GetRoundedEpsilonValue(Scalar epsilon, int *lvl=nullptr)
Definition: intersectionFunctor.h:58
Extract pairs of points by rasterizing primitives and collect points.
Definition: intersectionFunctor.h:74
_Point Point
Definition: intersectionFunctor.h:75
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
_Scalar Scalar
Definition: intersectionFunctor.h:77
Definition: intersectionFunctor.h:78
void process(const PrimitiveContainer &M, const PointContainer &Q, Scalar &epsilon, unsigned int minNodeSize, ProcessingFunctor &functor)
< Process the extracted pairs
Definition: intersectionFunctor.h:103