normalHealSet.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_INDEXED_NORMAL_HEAL_SET_H_
49 #define _OPENGR_ACCELERATORS_INDEXED_NORMAL_HEAL_SET_H_
50 
51 #include "gr/utils/disablewarnings.h"
52 #include "gr/accelerators/utils.h"
53 #include "chealpix.h"
54 #include <Eigen/Core>
55 
56 #include <vector>
57 #include <set>
58 #include <cmath> //std::floor
59 
60 namespace gr{
61 
62 /*!
63  Work only in 3D, based on healpix.
64  Scalar are constrained to be double
65  Points are constrained to be Eigen vec3d
66  */
68 public:
69  using Scalar = double;
70  typedef Eigen::Vector3d Point;
71  typedef Eigen::Vector3i Index3D;
72  typedef std::vector<std::vector<unsigned int>> ChealMap;
73 
74 #ifdef DEBUG
75 #define VALIDATE_INDICES true
76 #pragma message "Index validation is enabled in debug mode"
77 #else
78 #define VALIDATE_INDICES false
79 #endif
80 
81  //! \brief State of the index validation, disabled when compiled in release mode
83 
84 #undef VALIDATE_INDICES
85 
86 private:
87  double _epsilon;
88  int _resolution;
89  int _egSize; //! <\brief Size of the euclidean grid for each dimension
90  long _ngLength; //! ,\brief Length of the normal map from healpix
91  std::vector<ChealMap*> _grid;
92 
93  //! \brief Return the coordinates corresponding to index coord
94  inline int indexCoordinates( const Index3D& coord) const
95  { return Utils::UnrollIndexLoop<INDEX_VALIDATION_ENABLED>( coord, 2, _egSize ); }
96 
97  //! \brief Return the index corresponding to position p
98  inline int indexPos ( const Point& p) const{
99  return indexCoordinates( coordinatesPos(p) );
100  }
101 
102  //! \brief Return the index corresponding to normal n \warning Bounds are not tested
103  inline int indexNormal( const Point& n) const {
104  long id;
105  vec2pix_ring(_resolution, n.data(), &id);
106  return id;
107  }
108 
109  //! \brief Return the coordinates corresponding to position p
110  inline Index3D coordinatesPos ( const Point& p) const
111  {
112  return (p/_epsilon)
113  .unaryExpr([](typename Point::Scalar in) { return std::floor(in);} )
114  .cast<typename Index3D::Scalar>();
115  }
116 
117 
118 public:
119  inline IndexedNormalHealSet(double epsilon, int resolution = 4)
120  : _epsilon(epsilon), _resolution(resolution) {
121  // We need to check if epsilon is a power of two and correct it if needed
122  const int gridDepth = -std::log2(epsilon);
123  _egSize = std::pow(2,gridDepth);
124 
125  if (gridDepth <= 0 || !isValid())
126  throw std::invalid_argument(
127  std::string("[IndexedNormalHealSet] Invalid configuration (depth=") +
128  std::to_string(gridDepth) +
129  std::string(", size=") +
130  std::to_string(_egSize) +
131  std::string(")"));
132  _grid = std::vector<ChealMap*> (std::pow(_egSize, 3), NULL);
133 
134  _ngLength = nside2npix(resolution);
135 
136  _epsilon = 1.f/_egSize;
137  }
138 
140  for(unsigned int i = 0; i != _grid.size(); i++)
141  delete(_grid[i]);
142  }
143 
144  //! \brief Add a new couple pos/normal, and its associated id
145  template <typename PointT>
146  bool addElement(const PointT& pos,
147  const PointT& normal,
148  unsigned int id);
149 
150  //! \return NULL if the grid does not exist or p is out of bound
151  inline ChealMap* getMap(const Point& p) {
152  const int pId = indexPos(p);
153  if (pId == -1) return NULL;
154  return _grid[pId];
155  }
156 
157  //! \return a vector of maps containing points that can be close to p (according to input epsilon)
159  const Index3D pId3 = coordinatesPos(p);
160  const int pId = indexCoordinates(pId3);
161  std::vector<ChealMap*> result;
162 
163  if (pId == -1) return result;
164 
165 
166  // Here we extract the 8-neighorhood of the map in pId
167  // We use three umbricated for loops, need to check how loop unrolling
168  // is performed.
169  // The following is really ugly, metaprog would be better
170  // \FIXME
171  const int lastId = _egSize-1;
172  int imin = pId3[0] == 0 ? 0 : -1;
173  int jmin = pId3[1] == 0 ? 0 : -1;
174  int kmin = pId3[2] == 0 ? 0 : -1;
175  int imax = pId3[0] == lastId ? 1 : 2;
176  int jmax = pId3[1] == lastId ? 1 : 2;
177  int kmax = pId3[2] == lastId ? 1 : 2;
178 
179  for (int i = imin; i < imax; ++i) // x axis
180  for (int j = jmin; j < jmax; ++j) // y axis
181  for (int k = kmin; k < kmax; ++k) // z axis
182  {
183  const int id = indexCoordinates(pId3 + Index3D(i,j,k));
184  ChealMap* g = _grid[id];
185  if (g != nullptr)
186  result.push_back(g);
187  }
188  return result;
189  }
190 
191 
192  //! Get closest points in euclidean space
193  template <typename PointT>
194  void getNeighbors(const PointT &p,
195  std::vector<unsigned int>&nei);
196  //! Get closest points in euclidean an normal space
197  template <typename PointT>
198  void getNeighbors( const PointT& p,
199  const PointT& n,
200  std::vector<unsigned int>&nei);
201  template <typename PointT>
202  //! Get closest poitns in euclidean an normal space with angular deviation
203  void getNeighbors( const PointT& p,
204  const PointT& n,
205  double alpha,
206  std::vector<unsigned int>&nei);
207 
208  inline bool isValid() const {
209  return _egSize > 0;
210  }
211 
212 }; // class IndexedNormalHealSet
213 
214 
215 ////////////////////////////////////////////////////////////////////////////////
216 /// Template functions
217 ///
218 
219 
220 template <typename PointT>
221 bool
223  const PointT& p,
224  const PointT& n,
225  unsigned int id)
226 {
227  const int pId = indexPos(p.template cast<Scalar>());
228  if (pId == -1) return false;
229 
230  const int nId = indexNormal(n.template cast<Scalar>());
231  if (nId == -1) return false;
232 
233  if (_grid[pId] == NULL) _grid[pId] = new ChealMap(_ngLength);
234  (_grid[pId])->at(nId).push_back(id);
235 
236  return true;
237 }
238 
239 template <typename PointT>
240 void
242  const PointT& p,
243  std::vector<unsigned int>&nei)
244 {
246  ChealMap* grid = getMap(p.template cast<Scalar>());
247  if ( grid == NULL ) return;
248 
249  for(ChealMapIterator it = grid->cbegin();
250  it != grid->cend(); it++){
251  const std::vector<unsigned int>& lnei = *it;
252  nei.insert( nei.end(), lnei.begin(), lnei.end() );
253  }
254 }
255 
256 template <typename PointT>
257 void
259  const PointT& p,
260  const PointT& n,
261  std::vector<unsigned int>&nei)
262 {
263  ChealMap* grid = getMap(p.template cast<Scalar>());
264  if ( grid == NULL ) return;
265 
266  const std::vector<unsigned int>& lnei = grid->at(indexNormal(n.template cast<Scalar>()));
267  nei.insert( nei.end(), lnei.begin(), lnei.end() );
268 }
269 
270 
271 template <typename PointT>
272 void
274  const PointT& p,
275  const PointT& n,
276  double cosAlpha,
277  std::vector<unsigned int>&nei)
278 {
279  //ChealMap* grid = getMap(p);
280  std::vector<ChealMap*> grids = getEpsilonMaps(p.template cast<Scalar>());
281  if ( grids.empty() ) return;
282 
283  const double alpha = std::acos(cosAlpha);
284  //const double perimeter = double(2) * M_PI * std::atan(alpha);
285  const unsigned int nbSample = std::pow(2,_resolution+1);
286  const double angleStep = double(2) * M_PI / double(nbSample);
287 
288 
289  const double sinAlpha = std::sin(alpha);
290 
291  Eigen::Quaternion<double> q;
292  q.setFromTwoVectors(Point(0.,0.,1.), n.template cast<Scalar>());
293 
294  // store a pair with
295  // first = grid id in grids
296  // second = normal id in grids[first]
297  typedef std::pair<unsigned int,unsigned int> PairId;
298  std::set< PairId > colored;
299  const int nbgrid = grids.size();
300 
301  // Do the rendering independently of the content
302  for(unsigned int a = 0; a != nbSample; a++){
303  double theta = double(a) * angleStep;
304  const Point dir = ( q * Point(sinAlpha*std::cos(theta),
305  sinAlpha*std::sin(theta),
306  cosAlpha ) ).normalized();
307  int id = indexNormal( dir );
308 
309  for (int i = 0; i != nbgrid; ++i){
310  if(grids[i]->at(id).size() != 0){
311  colored.emplace(i,id);
312  }
313  }
314 
315  }
316 
318  it != colored.cend(); it++){
319  const std::vector<unsigned int>& lnei = grids[it->first]->at(it->second);
320  nei.insert( nei.end(), lnei.begin(), lnei.end() );
321  }
322 }
323 
324 
325 
326 } // namespace gr
327 
328 #endif // _INDEXED_NORMAL_SET_H_
void getNeighbors(const PointT &p, const PointT &n, double alpha, std::vector< unsigned int > &nei)
Get closest poitns in euclidean an normal space with angular deviation.
Definition: normalHealSet.h:273
ChealMap * getMap(const Point &p)
Definition: normalHealSet.h:151
Eigen::Vector3i Index3D
Definition: normalHealSet.h:71
bool isValid() const
Definition: normalHealSet.h:208
#define M_PI
Definition: utils.h:57
std::vector< ChealMap * > getEpsilonMaps(const Point &p)
Definition: normalHealSet.h:158
bool addElement(const PointT &pos, const PointT &normal, unsigned int id)
Add a new couple pos/normal, and its associated id.
Definition: normalHealSet.h:222
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
IndexedNormalHealSet(double epsilon, int resolution=4)
Definition: normalHealSet.h:119
virtual ~IndexedNormalHealSet()
Definition: normalHealSet.h:139
#define VALIDATE_INDICES
Definition: normalHealSet.h:78
std::vector< std::vector< unsigned int > > ChealMap
Definition: normalHealSet.h:72
void getNeighbors(const PointT &p, std::vector< unsigned int > &nei)
Get closest points in euclidean space.
Definition: normalHealSet.h:241
Eigen::Vector3d Point
Definition: normalHealSet.h:70