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