28 #include <tbb/parallel_for.h> 32 #include <nanoflann.hpp> 44 template <
int METRIC,
class TReal,
class TIndex>
50 const TReal *
const data_ptr)
72 template <
int M,
typename fake =
void>
75 template <
typename fake>
77 typedef nanoflann::L2_Adaptor<TReal, DataAdaptor, TReal>
adaptor_t;
80 template <
typename fake>
82 typedef nanoflann::L1_Adaptor<TReal, DataAdaptor, TReal>
adaptor_t;
86 typedef nanoflann::KDTreeSingleIndexAdaptor<
95 const TReal *data_ptr) {
107 template <
class T,
class TIndex,
int METRIC>
108 void _BuildKdTree(
size_t num_points,
116 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR,
int METRIC>
118 int64_t *query_neighbors_row_splits,
120 const T *
const points,
122 const T *
const queries,
123 const size_t dimension,
125 bool ignore_query_point,
126 bool return_distances,
127 OUTPUT_ALLOCATOR &output_allocator) {
129 if (num_queries == 0 || num_points == 0 || holder ==
nullptr) {
130 std::fill(query_neighbors_row_splits,
131 query_neighbors_row_splits + num_queries + 1, 0);
133 output_allocator.AllocIndices(&indices_ptr, 0);
136 output_allocator.AllocDistances(&distances_ptr, 0);
140 auto points_equal = [](
const T *
const p1,
const T *
const p2,
142 std::vector<T> p1_vec(p1, p1 + dimension);
143 std::vector<T> p2_vec(p2, p2 + dimension);
144 return p1_vec == p2_vec;
147 std::vector<std::vector<TIndex>> neighbors_indices(num_queries);
148 std::vector<std::vector<T>> neighbors_distances(num_queries);
149 std::vector<uint32_t> neighbors_count(num_queries, 0);
156 tbb::blocked_range<size_t>(0, num_queries),
157 [&](
const tbb::blocked_range<size_t> &r) {
158 std::vector<TIndex> result_indices(knn);
159 std::vector<T> result_distances(knn);
160 for (
size_t i = r.begin(); i != r.end(); ++i) {
161 size_t num_valid = holder_->index_->knnSearch(
162 &queries[i * dimension], knn, result_indices.data(),
163 result_distances.data());
165 int num_neighbors = 0;
166 for (
size_t valid_i = 0; valid_i < num_valid; ++valid_i) {
167 TIndex idx = result_indices[valid_i];
168 if (ignore_query_point &&
169 points_equal(&queries[i * dimension],
170 &points[idx * dimension], dimension)) {
173 neighbors_indices[i].push_back(idx);
174 if (return_distances) {
175 T dist = result_distances[valid_i];
176 neighbors_distances[i].push_back(dist);
180 neighbors_count[i] = num_neighbors;
184 query_neighbors_row_splits[0] = 0;
186 neighbors_count.data() + neighbors_count.size(),
187 query_neighbors_row_splits + 1);
189 int64_t num_indices = query_neighbors_row_splits[num_queries];
192 output_allocator.AllocIndices(&indices_ptr, num_indices);
194 if (return_distances)
195 output_allocator.AllocDistances(&distances_ptr, num_indices);
197 output_allocator.AllocDistances(&distances_ptr, 0);
199 std::fill(neighbors_count.begin(), neighbors_count.end(), 0);
202 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_queries),
203 [&](
const tbb::blocked_range<size_t> &r) {
204 for (
size_t i = r.begin(); i != r.end(); ++i) {
205 int64_t start_idx = query_neighbors_row_splits[i];
207 neighbors_indices[i].end(),
208 &indices_ptr[start_idx]);
210 if (return_distances) {
211 std::copy(neighbors_distances[i].begin(),
212 neighbors_distances[i].end(),
213 &distances_ptr[start_idx]);
219 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR,
int METRIC>
221 int64_t *query_neighbors_row_splits,
223 const T *
const points,
225 const T *
const queries,
226 const size_t dimension,
227 const T *
const radii,
228 bool ignore_query_point,
229 bool return_distances,
230 bool normalize_distances,
232 OUTPUT_ALLOCATOR &output_allocator) {
233 if (num_queries == 0 || num_points == 0 || holder ==
nullptr) {
234 std::fill(query_neighbors_row_splits,
235 query_neighbors_row_splits + num_queries + 1, 0);
237 output_allocator.AllocIndices(&indices_ptr, 0);
240 output_allocator.AllocDistances(&distances_ptr, 0);
244 auto points_equal = [](
const T *
const p1,
const T *
const p2,
246 std::vector<T> p1_vec(p1, p1 + dimension);
247 std::vector<T> p2_vec(p2, p2 + dimension);
248 return p1_vec == p2_vec;
251 std::vector<std::vector<TIndex>> neighbors_indices(num_queries);
252 std::vector<std::vector<T>> neighbors_distances(num_queries);
253 std::vector<uint32_t> neighbors_count(num_queries, 0);
255 nanoflann::SearchParams params;
256 params.sorted = sort;
261 tbb::blocked_range<size_t>(0, num_queries),
262 [&](
const tbb::blocked_range<size_t> &r) {
263 std::vector<std::pair<TIndex, T>> search_result;
264 for (
size_t i = r.begin(); i != r.end(); ++i) {
267 radius = radius * radius;
270 holder_->index_->radiusSearch(&queries[i * dimension],
271 radius, search_result,
274 int num_neighbors = 0;
275 for (
const auto &idx_dist : search_result) {
276 if (ignore_query_point &&
277 points_equal(&queries[i * dimension],
278 &points[idx_dist.first * dimension],
282 neighbors_indices[i].push_back(idx_dist.first);
283 if (return_distances) {
284 neighbors_distances[i].push_back(idx_dist.second);
288 neighbors_count[i] = num_neighbors;
292 query_neighbors_row_splits[0] = 0;
294 neighbors_count.data() + neighbors_count.size(),
295 query_neighbors_row_splits + 1);
297 int64_t num_indices = query_neighbors_row_splits[num_queries];
300 output_allocator.AllocIndices(&indices_ptr, num_indices);
302 if (return_distances)
303 output_allocator.AllocDistances(&distances_ptr, num_indices);
305 output_allocator.AllocDistances(&distances_ptr, 0);
307 std::fill(neighbors_count.begin(), neighbors_count.end(), 0);
311 tbb::blocked_range<size_t>(0, num_queries),
312 [&](
const tbb::blocked_range<size_t> &r) {
313 for (
size_t i = r.begin(); i != r.end(); ++i) {
314 int64_t start_idx = query_neighbors_row_splits[i];
316 neighbors_indices[i].end(),
317 &indices_ptr[start_idx]);
318 if (return_distances) {
319 std::transform(neighbors_distances[i].begin(),
320 neighbors_distances[i].end(),
321 &distances_ptr[start_idx], [&](T dist) {
323 if (normalize_distances) {
325 d /= (radii[i] * radii[i]);
337 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR,
int METRIC>
340 const T *
const points,
342 const T *
const queries,
343 const size_t dimension,
346 bool ignore_query_point,
347 bool return_distances,
348 OUTPUT_ALLOCATOR &output_allocator) {
349 if (num_queries == 0 || num_points == 0 || holder ==
nullptr) {
350 TIndex *indices_ptr, *counts_ptr;
351 output_allocator.AllocIndices(&indices_ptr, 0);
352 output_allocator.AllocCounts(&counts_ptr, 0);
355 output_allocator.AllocDistances(&distances_ptr, 0);
359 T radius_squared = radius * radius;
360 TIndex *indices_ptr, *counts_ptr;
363 size_t num_indices =
static_cast<size_t>(max_knn) * num_queries;
364 output_allocator.AllocIndices(&indices_ptr, num_indices);
365 output_allocator.AllocDistances(&distances_ptr, num_indices);
366 output_allocator.AllocCounts(&counts_ptr, num_queries);
368 nanoflann::SearchParams params;
369 params.sorted =
true;
374 tbb::blocked_range<size_t>(0, num_queries),
375 [&](
const tbb::blocked_range<size_t> &r) {
376 std::vector<std::pair<TIndex, T>> ret_matches;
377 for (
size_t i = r.begin(); i != r.end(); ++i) {
378 size_t num_results = holder_->index_->radiusSearch(
379 &queries[i * dimension], radius_squared,
380 ret_matches, params);
381 ret_matches.resize(num_results);
383 TIndex count_i =
static_cast<TIndex
>(num_results);
384 count_i = count_i < max_knn ? count_i : max_knn;
385 counts_ptr[i] = count_i;
387 int neighbor_idx = 0;
388 for (
auto it = ret_matches.begin();
389 it < ret_matches.end() && neighbor_idx < max_knn;
390 it++, neighbor_idx++) {
391 indices_ptr[i * max_knn + neighbor_idx] = it->first;
392 distances_ptr[i * max_knn + neighbor_idx] = it->second;
395 while (neighbor_idx < max_knn) {
396 indices_ptr[i * max_knn + neighbor_idx] = -1;
397 distances_ptr[i * max_knn + neighbor_idx] = 0;
420 template <
class T,
class TIndex>
421 std::unique_ptr<NanoFlannIndexHolderBase>
BuildKdTree(
size_t num_points,
426 #define FN_PARAMETERS num_points, points, dimension, &holder 428 #define CALL_TEMPLATE(METRIC) \ 429 if (METRIC == metric) { \ 430 _BuildKdTree<T, TIndex, METRIC>(FN_PARAMETERS); \ 433 #define CALL_TEMPLATE2 \ 440 #undef CALL_TEMPLATE2 443 return std::unique_ptr<NanoFlannIndexHolderBase>(holder);
498 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR>
500 int64_t *query_neighbors_row_splits,
504 const T *
const queries,
505 const size_t dimension,
508 bool ignore_query_point,
509 bool return_distances,
510 OUTPUT_ALLOCATOR &output_allocator) {
511 #define FN_PARAMETERS \ 512 holder, query_neighbors_row_splits, num_points, points, num_queries, \ 513 queries, dimension, knn, ignore_query_point, return_distances, \ 516 #define CALL_TEMPLATE(METRIC) \ 517 if (METRIC == metric) { \ 518 _KnnSearchCPU<T, TIndex, OUTPUT_ALLOCATOR, METRIC>(FN_PARAMETERS); \ 521 #define CALL_TEMPLATE2 \ 528 #undef CALL_TEMPLATE2 592 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR>
594 int64_t *query_neighbors_row_splits,
598 const T *
const queries,
599 const size_t dimension,
600 const T *
const radii,
602 bool ignore_query_point,
603 bool return_distances,
604 bool normalize_distances,
606 OUTPUT_ALLOCATOR &output_allocator) {
607 #define FN_PARAMETERS \ 608 holder, query_neighbors_row_splits, num_points, points, num_queries, \ 609 queries, dimension, radii, ignore_query_point, return_distances, \ 610 normalize_distances, sort, output_allocator 612 #define CALL_TEMPLATE(METRIC) \ 613 if (METRIC == metric) { \ 614 _RadiusSearchCPU<T, TIndex, OUTPUT_ALLOCATOR, METRIC>(FN_PARAMETERS); \ 617 #define CALL_TEMPLATE2 \ 624 #undef CALL_TEMPLATE2 680 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR>
685 const T *
const queries,
686 const size_t dimension,
690 bool ignore_query_point,
691 bool return_distances,
692 OUTPUT_ALLOCATOR &output_allocator) {
693 #define FN_PARAMETERS \ 694 holder, num_points, points, num_queries, queries, dimension, radius, \ 695 max_knn, ignore_query_point, return_distances, output_allocator 697 #define CALL_TEMPLATE(METRIC) \ 698 if (METRIC == metric) { \ 699 _HybridSearchCPU<T, TIndex, OUTPUT_ALLOCATOR, METRIC>(FN_PARAMETERS); \ 702 #define CALL_TEMPLATE2 \ 709 #undef CALL_TEMPLATE2 bool kdtree_get_bbox(BBOX &) const
Definition: NanoFlannImpl.h:62
void KnnSearchCPU(NanoFlannIndexHolderBase *holder, int64_t *query_neighbors_row_splits, size_t num_points, const T *const points, size_t num_queries, const T *const queries, const size_t dimension, int knn, const Metric metric, bool ignore_query_point, bool return_distances, OUTPUT_ALLOCATOR &output_allocator)
Definition: NanoFlannImpl.h:499
Definition: NeighborSearchCommon.h:38
std::unique_ptr< KDTree_t > index_
Definition: NanoFlannImpl.h:101
Base struct for NanoFlann index holder.
Definition: NeighborSearchCommon.h:72
std::unique_ptr< DataAdaptor > adaptor_
Definition: NanoFlannImpl.h:102
void InclusivePrefixSum(const Tin *first, const Tin *last, Tout *out)
Definition: ParallelScan.h:90
Metric
Supported metrics.
Definition: NeighborSearchCommon.h:38
const TReal *const data_ptr_
Definition: NanoFlannImpl.h:68
size_t dataset_size_
Definition: NanoFlannImpl.h:66
NanoFlann Index Holder.
Definition: NanoFlannImpl.h:45
Definition: NeighborSearchCommon.h:38
void RadiusSearchCPU(NanoFlannIndexHolderBase *holder, int64_t *query_neighbors_row_splits, size_t num_points, const T *const points, size_t num_queries, const T *const queries, const size_t dimension, const T *const radii, const Metric metric, bool ignore_query_point, bool return_distances, bool normalize_distances, bool sort, OUTPUT_ALLOCATOR &output_allocator)
Definition: NanoFlannImpl.h:593
Adaptor Selector.
Definition: NanoFlannImpl.h:73
This class is the Adaptor for connecting Open3D Tensor and NanoFlann.
Definition: NanoFlannImpl.h:47
void HybridSearchCPU(const Tensor &points, const Tensor &queries, double radius, int max_knn, const Tensor &points_row_splits, const Tensor &queries_row_splits, const Tensor &hash_table_splits, const Tensor &hash_table_index, const Tensor &hash_table_cell_splits, const Metric metric, Tensor &neighbors_index, Tensor &neighbors_count, Tensor &neighbors_distance)
Definition: FixedRadiusSearchOps.cpp:93
NanoFlannIndexHolder(size_t dataset_size, int dimension, const TReal *data_ptr)
Definition: NanoFlannImpl.h:93
nanoflann::L2_Adaptor< TReal, DataAdaptor, TReal > adaptor_t
Definition: NanoFlannImpl.h:77
DataAdaptor(size_t dataset_size, int dimension, const TReal *const data_ptr)
Definition: NanoFlannImpl.h:48
nanoflann::L1_Adaptor< TReal, DataAdaptor, TReal > adaptor_t
Definition: NanoFlannImpl.h:82
Definition: PinholeCameraIntrinsic.cpp:35
nanoflann::KDTreeSingleIndexAdaptor< typename SelectNanoflannAdaptor< METRIC >::adaptor_t, DataAdaptor, -1, TIndex > KDTree_t
typedef for KDtree.
Definition: NanoFlannImpl.h:91
size_t kdtree_get_point_count() const
Definition: NanoFlannImpl.h:55
TReal kdtree_get_pt(const size_t idx, const size_t dim) const
Definition: NanoFlannImpl.h:57
bool copy
Definition: VtkUtils.cpp:89
int dimension_
Definition: NanoFlannImpl.h:67
std::unique_ptr< NanoFlannIndexHolderBase > BuildKdTree(size_t num_points, const T *const points, size_t dimension, const Metric metric)
Definition: NanoFlannImpl.h:421