ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
VoxelPooling.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #pragma once
9 
10 #include <Helper.h>
11 #include <tbb/task_group.h>
12 
13 #include <Eigen/Core>
14 #include <unordered_map>
15 
16 namespace cloudViewer {
17 namespace ml {
18 namespace impl {
19 
21 
22 template <class TReal,
23  class TFeat,
24  AccumulationFn POS_FN,
25  AccumulationFn FEAT_FN>
26 class Accumulator {
27 public:
29  : count_(0),
30  min_sqr_dist_to_center_(std::numeric_limits<TReal>::max()),
31  position_(0, 0, 0) {
32  static_assert(POS_FN != MAX, "MAX is not allowed for point positions");
33  static_assert(FEAT_FN != CENTER,
34  "CENTER is not allowed for feature vectors");
35  }
36 
37  template <class Derived, class Derived2, class Derived3>
38  inline void AddPoint(const Eigen::MatrixBase<Derived>& pos,
39  const Eigen::MatrixBase<Derived2>& voxel_center,
40  const Eigen::ArrayBase<Derived3>& feat) {
41  bool new_nearest_neighbor = false;
42  TReal sqr_d = 0;
43  if (POS_FN == NEAREST_NEIGHBOR || FEAT_FN == NEAREST_NEIGHBOR) {
44  sqr_d = (voxel_center - pos).squaredNorm();
45  if (sqr_d < min_sqr_dist_to_center_) {
46  new_nearest_neighbor = true;
47  min_sqr_dist_to_center_ = sqr_d;
48  }
49  }
50  if (POS_FN == AVERAGE) {
51  position_ += pos.array();
52  } else if (POS_FN == NEAREST_NEIGHBOR && new_nearest_neighbor) {
53  position_ = pos;
54  } else if (POS_FN == CENTER) {
55  if (count_ == 0) position_ = voxel_center;
56  }
57 
58  if (count_ == 0) {
59  features_.resizeLike(feat);
60  features_.setZero();
61  }
62  if (FEAT_FN == AVERAGE) {
63  features_ += feat;
64  } else if (FEAT_FN == NEAREST_NEIGHBOR && new_nearest_neighbor) {
65  features_ = feat;
66  } else if (FEAT_FN == MAX) {
67  features_ = features_.max(feat);
68  }
69  ++count_;
70  }
71 
72  inline Eigen::Array<TReal, 3, 1> Position() const {
73  if (POS_FN == AVERAGE) {
74  return position_ / count_;
75  } else // if( POS_FN == NEAREST_NEIGHBOR || POS_FN == CENTER )
76  {
77  return position_;
78  }
79  }
80 
81  inline Eigen::Array<TFeat, Eigen::Dynamic, 1> Features() const {
82  if (FEAT_FN == AVERAGE) {
83  return features_ / count_;
84  } else // if( FEAT_FN == NEAREST_NEIGHBOR || FEAT_FN == MAX )
85  {
86  return features_;
87  }
88  }
89 
90  inline int Count() const { return count_; }
91 
92 private:
93  int count_;
94  TReal min_sqr_dist_to_center_;
95  Eigen::Array<TReal, 3, 1> position_;
96  Eigen::Array<TFeat, Eigen::Dynamic, 1> features_;
97 }; // namespace
98 
99 template <class TReal,
100  class TFeat,
101  AccumulationFn POS_FN,
102  AccumulationFn FEAT_FN>
104 public:
106  : count_(0),
107  min_sqr_dist_to_center_(std::numeric_limits<TReal>::max()),
108  position_(0, 0, 0) {
109  static_assert(POS_FN != MAX, "MAX is not allowed for point positions");
110  static_assert(FEAT_FN != CENTER,
111  "CENTER is not allowed for feature vectors");
112  }
113 
114  template <class Derived, class Derived2, class Derived3>
115  inline void AddPoint(const Eigen::MatrixBase<Derived>& pos,
116  const Eigen::MatrixBase<Derived2>& voxel_center,
117  const Eigen::ArrayBase<Derived3>& feat,
118  const size_t idx) {
119  bool new_nearest_neighbor = false;
120  TReal sqr_d = 0;
121  if (POS_FN == NEAREST_NEIGHBOR || FEAT_FN == NEAREST_NEIGHBOR) {
122  sqr_d = (voxel_center - pos).squaredNorm();
123  if (sqr_d < min_sqr_dist_to_center_) {
124  new_nearest_neighbor = true;
125  min_sqr_dist_to_center_ = sqr_d;
126  }
127  }
128 
129  if (POS_FN == AVERAGE) {
130  position_ += pos.array();
131  } else if (POS_FN == NEAREST_NEIGHBOR && new_nearest_neighbor) {
132  position_ = pos;
133  } else if (POS_FN == CENTER) {
134  if (count_ == 0) position_ = voxel_center;
135  }
136 
137  if (count_ == 0) {
138  features_.resizeLike(feat);
139  features_.setZero();
140  if (FEAT_FN == NEAREST_NEIGHBOR) {
141  features_ = feat;
142  index_.resize(1);
143  index_(0) = idx;
144  ++count_;
145  return;
146  } else if (FEAT_FN == MAX) {
147  features_ = feat;
148  index_.resizeLike(feat);
149  index_ = idx;
150  ++count_;
151  return;
152  }
153  }
154  if (FEAT_FN == AVERAGE) {
155  features_ += feat;
156  } else if (FEAT_FN == NEAREST_NEIGHBOR && new_nearest_neighbor) {
157  features_ = feat;
158  index_(0) = idx;
159  } else if (FEAT_FN == MAX) {
160  for (int i = 0; i < features_.rows(); ++i) {
161  if (feat(i) > features_(i)) {
162  features_(i) = feat(i);
163  index_(i) = idx;
164  }
165  }
166  }
167  ++count_;
168  }
169 
170  inline Eigen::Array<TReal, 3, 1> Position() const {
171  if (POS_FN == AVERAGE) {
172  return position_ / count_;
173  } else // if( POS_FN == NEAREST_NEIGHBOR || POS_FN == CENTER )
174  {
175  return position_;
176  }
177  }
178 
179  inline Eigen::Array<TFeat, Eigen::Dynamic, 1> Features() const {
180  if (FEAT_FN == AVERAGE) {
181  return features_ / count_;
182  } else // if( FEAT_FN == NEAREST_NEIGHBOR || FEAT_FN == MAX )
183  {
184  return features_;
185  }
186  }
187 
188  inline int Count() const { return count_; }
189 
190  inline Eigen::Array<size_t, Eigen::Dynamic, 1> Index() const {
191  return index_;
192  }
193 
194 private:
195  int count_;
196  TReal min_sqr_dist_to_center_;
197  Eigen::Array<TReal, 3, 1> position_;
198  Eigen::Array<TFeat, Eigen::Dynamic, 1> features_;
199  Eigen::Array<size_t, Eigen::Dynamic, 1> index_;
200 };
201 
203 template <class T>
204 bool CheckVoxelSize(std::string& err,
205  const size_t num_positions,
206  const T* const positions,
207  const T voxel_size) {
208  typedef Eigen::Array<double, 3, 1> Vec3_t;
209  if (num_positions == 0) {
210  return true;
211  }
212 
213  Vec3_t bb_min, bb_max;
214  bb_min << positions[0], positions[1], positions[2];
215  bb_max = bb_min;
216 
217  Vec3_t voxel_size3(voxel_size, voxel_size, voxel_size);
218 
219  for (size_t i = 1; i < num_positions; ++i) {
220  Vec3_t pos(positions[i * 3 + 0], positions[i * 3 + 1],
221  positions[i * 3 + 2]);
222  bb_min = bb_min.min(pos);
223  bb_max = bb_max.max(pos);
224  }
225 
226  // the min and max bounding box shall be a multiple of the voxel size
227  bb_min /= voxel_size3;
228  bb_min = bb_min.floor() * voxel_size3;
229  bb_max /= voxel_size3;
230  bb_max = bb_max.ceil() * voxel_size3;
231 
232  if (voxel_size * double(std::numeric_limits<int>::max()) <
233  bb_max.maxCoeff() ||
234  voxel_size * double(std::numeric_limits<int>::min()) >
235  bb_min.maxCoeff()) {
236  err = "voxel_size is too small\n";
237  return false;
238  }
239  return true;
240 }
241 
247 template <class TDerived>
249  const Eigen::ArrayBase<TDerived>& pos,
250  const typename TDerived::Scalar& inv_voxel_size) {
251  typedef typename TDerived::Scalar Scalar_t;
252  Eigen::Array<Scalar_t, 3, 1> ref_coord = pos * inv_voxel_size;
253 
254  Eigen::Vector3i voxel_index;
255  voxel_index = ref_coord.floor().template cast<int>();
256  return voxel_index;
257 }
258 
259 // implementation for VoxelPooling with template parameter for the accumulator.
260 template <class TReal, class TFeat, class ACCUMULATOR, class OUTPUT_ALLOCATOR>
261 void _VoxelPooling(size_t num_inp,
262  const TReal* const inp_positions,
263  int in_channels,
264  const TFeat* inp_features,
265  TReal voxel_size,
266  OUTPUT_ALLOCATOR& output_allocator) {
267  if (num_inp == 0) {
268  TReal* out_pos_ptr;
269  TFeat* out_feat_ptr;
270  output_allocator.AllocPooledPositions(&out_pos_ptr, 0);
271  output_allocator.AllocPooledFeatures(&out_feat_ptr, 0, in_channels);
272  return;
273  }
274 
275  typedef Eigen::Array<TReal, 3, 1> Vec3_t;
276  typedef Eigen::Array<TFeat, Eigen::Dynamic, 1> FeatureVec_t;
277 
278  std::unordered_map<Eigen::Vector3i, ACCUMULATOR,
280  voxelindex_to_accpoint;
281 
282  Vec3_t voxel_center;
283  Eigen::Vector3i voxel_index;
284  TReal inv_voxel_size = 1 / voxel_size;
285  TReal half_voxel_size = 0.5 * voxel_size;
286  for (size_t i = 0; i < num_inp; ++i) {
287  Eigen::Map<const Vec3_t> pos(inp_positions + i * 3);
288 
289  voxel_index = ComputeVoxelIndex(pos, inv_voxel_size);
290 
291  voxel_center << voxel_index(0) * voxel_size + half_voxel_size,
292  voxel_index(1) * voxel_size + half_voxel_size,
293  voxel_index(2) * voxel_size + half_voxel_size;
294 
295  Eigen::Map<const FeatureVec_t> feat(inp_features + in_channels * i,
296  in_channels);
297  voxelindex_to_accpoint[voxel_index].AddPoint(
298  pos.matrix(), voxel_center.matrix(), feat);
299  }
300 
301  const size_t num_out = voxelindex_to_accpoint.size();
302 
303  TReal* out_pos_ptr;
304  TFeat* out_feat_ptr;
305  output_allocator.AllocPooledPositions(&out_pos_ptr, num_out);
306  output_allocator.AllocPooledFeatures(&out_feat_ptr, num_out, in_channels);
307 
308  size_t i = 0;
309  for (const auto point : voxelindex_to_accpoint) {
310  Vec3_t pos = point.second.Position();
311  Eigen::Map<Vec3_t> out_pos(out_pos_ptr + i * 3);
312  out_pos = pos;
313 
314  Eigen::Map<FeatureVec_t> out_feat(out_feat_ptr + i * in_channels,
315  in_channels);
316  out_feat = point.second.Features();
317  ++i;
318  }
319 }
320 
321 // implementation for VoxelPoolingBackprop with template parameter for the
322 // accumulator.
323 template <class TReal, class TFeat, class ACCUMULATOR, AccumulationFn FEAT_FN>
324 void _VoxelPoolingBackprop(TFeat* features_backprop,
325  size_t num_inp,
326  const TReal* const inp_positions,
327  int in_channels,
328  const TFeat* const inp_features,
329  size_t num_pooled,
330  const TReal* const pooled_positions,
331  const TFeat* const pooled_features_gradient,
332  TReal voxel_size) {
333  if (num_inp == 0) {
334  return;
335  }
336  memset(features_backprop, 0, sizeof(TFeat) * num_inp * in_channels);
337 
338  typedef Eigen::Array<TReal, 3, 1> Vec3_t;
339  typedef Eigen::Array<TFeat, Eigen::Dynamic, 1> FeatureVec_t;
340 
341  Vec3_t voxel_size3(voxel_size, voxel_size, voxel_size);
342 
343  // create the two hash maps in parallel
344  tbb::task_group task_group;
345 
346  std::unordered_map<Eigen::Vector3i, ACCUMULATOR,
348  voxelindex_to_accpoint;
349 
350  task_group.run([&] {
351  Vec3_t voxel_center;
352  Eigen::Vector3i voxel_index;
353  TReal inv_voxel_size = 1 / voxel_size;
354  TReal half_voxel_size = 0.5 * voxel_size;
355  for (size_t i = 0; i < num_inp; ++i) {
356  Eigen::Map<const Vec3_t> pos(inp_positions + i * 3);
357 
358  voxel_index = ComputeVoxelIndex(pos, inv_voxel_size);
359 
360  voxel_center << voxel_index(0) * voxel_size + half_voxel_size,
361  voxel_index(1) * voxel_size + half_voxel_size,
362  voxel_index(2) * voxel_size + half_voxel_size;
363 
364  Eigen::Map<const FeatureVec_t> feat(inp_features + in_channels * i,
365  in_channels);
366  voxelindex_to_accpoint[voxel_index].AddPoint(
367  pos.matrix(), voxel_center.matrix(), feat, i);
368  }
369  });
370 
371  std::unordered_map<Eigen::Vector3i, size_t,
373  voxelindex_to_gradindex;
374 
375  task_group.run([&] {
376  Eigen::Vector3i voxel_index;
377  TReal inv_voxel_size = 1 / voxel_size;
378  for (size_t i = 0; i < num_pooled; ++i) {
379  Eigen::Map<const Vec3_t> pos(pooled_positions + i * 3);
380 
381  voxel_index = ComputeVoxelIndex(pos, inv_voxel_size);
382 
383  voxelindex_to_gradindex[voxel_index] = i;
384  }
385  });
386 
387  task_group.wait();
388 
389  if (FEAT_FN == AVERAGE) {
390  Eigen::Vector3i voxel_index;
391  TReal inv_voxel_size = 1 / voxel_size;
392  for (size_t i = 0; i < num_inp; ++i) {
393  Eigen::Map<const Vec3_t> pos(inp_positions + i * 3);
394 
395  voxel_index = ComputeVoxelIndex(pos, inv_voxel_size);
396 
397  Eigen::Map<FeatureVec_t> feat_bp(
398  features_backprop + in_channels * i, in_channels);
399 
400  size_t grad_idx = voxelindex_to_gradindex[voxel_index];
401  int count = voxelindex_to_accpoint[voxel_index].Count();
402  Eigen::Map<const FeatureVec_t> grad(
403  pooled_features_gradient + in_channels * grad_idx,
404  in_channels);
405  feat_bp = grad / count;
406  }
407  }
408 
409  if (FEAT_FN == NEAREST_NEIGHBOR) {
410  for (const auto point : voxelindex_to_accpoint) {
411  size_t idx = point.second.Index()(0);
412  Eigen::Map<FeatureVec_t> feat_bp(
413  features_backprop + in_channels * idx, in_channels);
414 
415  size_t grad_idx = voxelindex_to_gradindex[point.first];
416  Eigen::Map<const FeatureVec_t> grad(
417  pooled_features_gradient + in_channels * grad_idx,
418  in_channels);
419  feat_bp = grad;
420  }
421  }
422 
423  if (FEAT_FN == MAX) {
424  for (const auto point : voxelindex_to_accpoint) {
425  size_t grad_idx = voxelindex_to_gradindex[point.first];
426  Eigen::Map<const FeatureVec_t> grad(
427  pooled_features_gradient + in_channels * grad_idx,
428  in_channels);
429  for (int i = 0; i < in_channels; ++i) {
430  size_t idx = point.second.Index()(i);
431  Eigen::Map<FeatureVec_t> feat_bp(
432  features_backprop + in_channels * idx, in_channels);
433  feat_bp(i) = grad(i);
434  }
435  }
436  }
437 }
438 
483 template <class TReal, class TFeat, class OUTPUT_ALLOCATOR>
484 void VoxelPooling(size_t num_inp,
485  const TReal* const inp_positions,
486  int in_channels,
487  const TFeat* inp_features,
488  TReal voxel_size,
489  OUTPUT_ALLOCATOR& output_allocator,
490  AccumulationFn position_fn,
491  AccumulationFn feature_fn) {
492 #define CALL_TEMPLATE(POS_FN, FEAT_FN) \
493  if (POS_FN == position_fn && FEAT_FN == feature_fn) { \
494  _VoxelPooling<TReal, TFeat, \
495  Accumulator<TReal, TFeat, POS_FN, FEAT_FN>>( \
496  num_inp, inp_positions, in_channels, inp_features, voxel_size, \
497  output_allocator); \
498  }
499 
509 
510 #undef CALL_TEMPLATE
511 }
512 
528 template <class TReal, class TFeat>
529 void VoxelPoolingBackprop(TFeat* features_backprop,
530  size_t num_inp,
531  const TReal* const inp_positions,
532  int in_channels,
533  const TFeat* const inp_features,
534  size_t num_pooled,
535  const TReal* const pooled_positions,
536  const TFeat* const pooled_features_gradient,
537  TReal voxel_size,
538  AccumulationFn position_fn,
539  AccumulationFn feature_fn) {
540 #define CALL_TEMPLATE(POS_FN, FEAT_FN) \
541  if (POS_FN == position_fn && FEAT_FN == feature_fn) { \
542  _VoxelPoolingBackprop< \
543  TReal, TFeat, \
544  AccumulatorBackprop<TReal, TFeat, POS_FN, FEAT_FN>, FEAT_FN>( \
545  features_backprop, num_inp, inp_positions, in_channels, \
546  inp_features, num_pooled, pooled_positions, \
547  pooled_features_gradient, voxel_size); \
548  }
549 
559 
560 #undef CALL_TEMPLATE
561 }
562 
563 } // namespace impl
564 } // namespace ml
565 } // namespace cloudViewer
int count
#define CALL_TEMPLATE(POS_FN, FEAT_FN)
Eigen::Array< TFeat, Eigen::Dynamic, 1 > Features() const
Definition: VoxelPooling.h:179
void AddPoint(const Eigen::MatrixBase< Derived > &pos, const Eigen::MatrixBase< Derived2 > &voxel_center, const Eigen::ArrayBase< Derived3 > &feat, const size_t idx)
Definition: VoxelPooling.h:115
Eigen::Array< TReal, 3, 1 > Position() const
Definition: VoxelPooling.h:170
Eigen::Array< size_t, Eigen::Dynamic, 1 > Index() const
Definition: VoxelPooling.h:190
Eigen::Array< TFeat, Eigen::Dynamic, 1 > Features() const
Definition: VoxelPooling.h:81
Eigen::Array< TReal, 3, 1 > Position() const
Definition: VoxelPooling.h:72
void AddPoint(const Eigen::MatrixBase< Derived > &pos, const Eigen::MatrixBase< Derived2 > &voxel_center, const Eigen::ArrayBase< Derived3 > &feat)
Definition: VoxelPooling.h:38
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
Helper functions for the ml ops.
bool CheckVoxelSize(std::string &err, const size_t num_positions, const T *const positions, const T voxel_size)
Function for debugging. Checks if the voxel size is too small.
Definition: VoxelPooling.h:204
void VoxelPoolingBackprop(TFeat *features_backprop, size_t num_inp, const TReal *const inp_positions, int in_channels, const TFeat *const inp_features, size_t num_pooled, const TReal *const pooled_positions, const TFeat *const pooled_features_gradient, TReal voxel_size, AccumulationFn position_fn, AccumulationFn feature_fn)
Definition: VoxelPooling.h:529
void _VoxelPoolingBackprop(TFeat *features_backprop, size_t num_inp, const TReal *const inp_positions, int in_channels, const TFeat *const inp_features, size_t num_pooled, const TReal *const pooled_positions, const TFeat *const pooled_features_gradient, TReal voxel_size)
Definition: VoxelPooling.h:324
void _VoxelPooling(size_t num_inp, const TReal *const inp_positions, int in_channels, const TFeat *inp_features, TReal voxel_size, OUTPUT_ALLOCATOR &output_allocator)
Definition: VoxelPooling.h:261
utility::MiniVec< int, 3 > ComputeVoxelIndex(const TVecf &pos, const typename TVecf::Scalar_t &inv_voxel_size)
void VoxelPooling(size_t num_inp, const TReal *const inp_positions, int in_channels, const TFeat *inp_features, TReal voxel_size, OUTPUT_ALLOCATOR &output_allocator, AccumulationFn position_fn, AccumulationFn feature_fn)
Definition: VoxelPooling.h:484
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
Definition: knncpp.h:30
Definition: Eigen.h:85
Definition: lsd.c:149