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