9 #include <tbb/parallel_for.h>
13 #include <nanoflann.hpp>
25 template <
int METRIC,
class TReal,
class TIndex>
31 const TReal *
const data_ptr)
53 template <
int M,
typename fake =
void>
56 template <
typename fake>
58 typedef nanoflann::L2_Adaptor<TReal, DataAdaptor, TReal>
adaptor_t;
61 template <
typename fake>
63 typedef nanoflann::L1_Adaptor<TReal, DataAdaptor, TReal>
adaptor_t;
67 typedef nanoflann::KDTreeSingleIndexAdaptor<
76 const TReal *data_ptr) {
88 template <
class T,
class TIndex,
int METRIC>
89 void _BuildKdTree(
size_t num_points,
97 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR,
int METRIC>
99 int64_t *query_neighbors_row_splits,
103 const T *
const queries,
104 const size_t dimension,
106 bool ignore_query_point,
107 bool return_distances,
108 OUTPUT_ALLOCATOR &output_allocator) {
110 if (num_queries == 0 || num_points == 0 || holder ==
nullptr) {
111 std::fill(query_neighbors_row_splits,
112 query_neighbors_row_splits + num_queries + 1, 0);
114 output_allocator.AllocIndices(&indices_ptr, 0);
117 output_allocator.AllocDistances(&distances_ptr, 0);
121 std::vector<std::vector<TIndex>> neighbors_indices(num_queries);
122 std::vector<std::vector<T>> neighbors_distances(num_queries);
123 std::vector<uint32_t> neighbors_count(num_queries, 0);
130 tbb::blocked_range<size_t>(0, num_queries),
131 [&](
const tbb::blocked_range<size_t> &r) {
132 std::vector<TIndex> result_indices(knn);
133 std::vector<T> result_distances(knn);
134 for (
size_t i = r.begin(); i != r.end(); ++i) {
135 size_t num_valid = holder_->index_->knnSearch(
136 &queries[i * dimension], knn, result_indices.data(),
137 result_distances.data());
139 int num_neighbors = 0;
140 for (
size_t valid_i = 0; valid_i < num_valid; ++valid_i) {
141 TIndex idx = result_indices[valid_i];
142 if (ignore_query_point &&
143 std::equal(&queries[i * dimension],
144 &queries[i * dimension] + dimension,
145 &
points[idx * dimension])) {
148 neighbors_indices[i].push_back(idx);
149 if (return_distances) {
150 T dist = result_distances[valid_i];
151 neighbors_distances[i].push_back(dist);
155 neighbors_count[i] = num_neighbors;
159 query_neighbors_row_splits[0] = 0;
161 neighbors_count.data() + neighbors_count.size(),
162 query_neighbors_row_splits + 1);
164 int64_t num_indices = query_neighbors_row_splits[num_queries];
167 output_allocator.AllocIndices(&indices_ptr, num_indices);
169 if (return_distances)
170 output_allocator.AllocDistances(&distances_ptr, num_indices);
172 output_allocator.AllocDistances(&distances_ptr, 0);
174 std::fill(neighbors_count.begin(), neighbors_count.end(), 0);
177 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_queries),
178 [&](
const tbb::blocked_range<size_t> &r) {
179 for (
size_t i = r.begin(); i != r.end(); ++i) {
180 int64_t start_idx = query_neighbors_row_splits[i];
181 std::copy(neighbors_indices[i].begin(),
182 neighbors_indices[i].end(),
183 &indices_ptr[start_idx]);
185 if (return_distances) {
186 std::copy(neighbors_distances[i].begin(),
187 neighbors_distances[i].end(),
188 &distances_ptr[start_idx]);
194 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR,
int METRIC>
195 void _RadiusSearchCPU(NanoFlannIndexHolderBase *holder,
196 int64_t *query_neighbors_row_splits,
200 const T *
const queries,
201 const size_t dimension,
202 const T *
const radii,
203 bool ignore_query_point,
204 bool return_distances,
205 bool normalize_distances,
207 OUTPUT_ALLOCATOR &output_allocator) {
208 if (num_queries == 0 || num_points == 0 || holder ==
nullptr) {
209 std::fill(query_neighbors_row_splits,
210 query_neighbors_row_splits + num_queries + 1, 0);
212 output_allocator.AllocIndices(&indices_ptr, 0);
215 output_allocator.AllocDistances(&distances_ptr, 0);
219 std::vector<std::vector<TIndex>> neighbors_indices(num_queries);
220 std::vector<std::vector<T>> neighbors_distances(num_queries);
221 std::vector<uint32_t> neighbors_count(num_queries, 0);
223 nanoflann::SearchParameters params;
224 params.sorted = sort;
227 static_cast<NanoFlannIndexHolder<METRIC, T, TIndex> *
>(holder);
229 tbb::blocked_range<size_t>(0, num_queries),
230 [&](
const tbb::blocked_range<size_t> &r) {
231 std::vector<nanoflann::ResultItem<TIndex, T>> search_result;
232 for (
size_t i = r.begin(); i != r.end(); ++i) {
235 radius = radius * radius;
238 holder_->index_->radiusSearch(&queries[i * dimension],
239 radius, search_result,
242 int num_neighbors = 0;
243 for (
const auto &idx_dist : search_result) {
244 if (ignore_query_point &&
245 std::equal(&queries[i * dimension],
246 &queries[i * dimension] + dimension,
247 &
points[idx_dist.first * dimension])) {
250 neighbors_indices[i].push_back(idx_dist.first);
251 if (return_distances) {
252 neighbors_distances[i].push_back(idx_dist.second);
256 neighbors_count[i] = num_neighbors;
260 query_neighbors_row_splits[0] = 0;
262 neighbors_count.data() + neighbors_count.size(),
263 query_neighbors_row_splits + 1);
265 int64_t num_indices = query_neighbors_row_splits[num_queries];
268 output_allocator.AllocIndices(&indices_ptr, num_indices);
270 if (return_distances)
271 output_allocator.AllocDistances(&distances_ptr, num_indices);
273 output_allocator.AllocDistances(&distances_ptr, 0);
275 std::fill(neighbors_count.begin(), neighbors_count.end(), 0);
279 tbb::blocked_range<size_t>(0, num_queries),
280 [&](
const tbb::blocked_range<size_t> &r) {
281 for (
size_t i = r.begin(); i != r.end(); ++i) {
282 int64_t start_idx = query_neighbors_row_splits[i];
283 std::copy(neighbors_indices[i].begin(),
284 neighbors_indices[i].end(),
285 &indices_ptr[start_idx]);
286 if (return_distances) {
287 std::transform(neighbors_distances[i].begin(),
288 neighbors_distances[i].end(),
289 &distances_ptr[start_idx], [&](T dist) {
291 if (normalize_distances) {
293 d /= (radii[i] * radii[i]);
305 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR,
int METRIC>
306 void _HybridSearchCPU(NanoFlannIndexHolderBase *holder,
310 const T *
const queries,
311 const size_t dimension,
314 bool ignore_query_point,
315 bool return_distances,
316 OUTPUT_ALLOCATOR &output_allocator) {
317 if (num_queries == 0 || num_points == 0 || holder ==
nullptr) {
318 TIndex *indices_ptr, *counts_ptr;
319 output_allocator.AllocIndices(&indices_ptr, 0);
320 output_allocator.AllocCounts(&counts_ptr, 0);
323 output_allocator.AllocDistances(&distances_ptr, 0);
327 T radius_squared = radius * radius;
328 TIndex *indices_ptr, *counts_ptr;
331 size_t num_indices =
static_cast<size_t>(max_knn) * num_queries;
332 output_allocator.AllocIndices(&indices_ptr, num_indices);
333 output_allocator.AllocDistances(&distances_ptr, num_indices);
334 output_allocator.AllocCounts(&counts_ptr, num_queries);
336 nanoflann::SearchParameters params;
337 params.sorted =
true;
340 static_cast<NanoFlannIndexHolder<METRIC, T, TIndex> *
>(holder);
342 tbb::blocked_range<size_t>(0, num_queries),
343 [&](
const tbb::blocked_range<size_t> &r) {
344 std::vector<nanoflann::ResultItem<TIndex, T>> ret_matches;
345 for (
size_t i = r.begin(); i != r.end(); ++i) {
346 size_t num_results = holder_->index_->radiusSearch(
347 &queries[i * dimension], radius_squared,
348 ret_matches, params);
349 ret_matches.resize(num_results);
351 TIndex count_i = static_cast<TIndex>(num_results);
352 count_i = count_i < max_knn ? count_i : max_knn;
353 counts_ptr[i] = count_i;
355 int neighbor_idx = 0;
356 for (auto it = ret_matches.begin();
357 it < ret_matches.end() && neighbor_idx < max_knn;
358 it++, neighbor_idx++) {
359 indices_ptr[i * max_knn + neighbor_idx] = it->first;
360 distances_ptr[i * max_knn + neighbor_idx] = it->second;
363 while (neighbor_idx < max_knn) {
364 indices_ptr[i * max_knn + neighbor_idx] = -1;
365 distances_ptr[i * max_knn + neighbor_idx] = 0;
388 template <
class T,
class TIndex>
389 std::unique_ptr<NanoFlannIndexHolderBase>
BuildKdTree(
size_t num_points,
394 #define FN_PARAMETERS num_points, points, dimension, &holder
396 #define CALL_TEMPLATE(METRIC) \
397 if (METRIC == metric) { \
398 _BuildKdTree<T, TIndex, METRIC>(FN_PARAMETERS); \
401 #define CALL_TEMPLATE2 \
408 #undef CALL_TEMPLATE2
411 return std::unique_ptr<NanoFlannIndexHolderBase>(holder);
466 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR>
468 int64_t *query_neighbors_row_splits,
472 const T *
const queries,
473 const size_t dimension,
476 bool ignore_query_point,
477 bool return_distances,
478 OUTPUT_ALLOCATOR &output_allocator) {
479 #define FN_PARAMETERS \
480 holder, query_neighbors_row_splits, num_points, points, num_queries, \
481 queries, dimension, knn, ignore_query_point, return_distances, \
484 #define CALL_TEMPLATE(METRIC) \
485 if (METRIC == metric) { \
486 _KnnSearchCPU<T, TIndex, OUTPUT_ALLOCATOR, METRIC>(FN_PARAMETERS); \
489 #define CALL_TEMPLATE2 \
496 #undef CALL_TEMPLATE2
560 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR>
562 int64_t *query_neighbors_row_splits,
566 const T *
const queries,
567 const size_t dimension,
568 const T *
const radii,
570 bool ignore_query_point,
571 bool return_distances,
572 bool normalize_distances,
574 OUTPUT_ALLOCATOR &output_allocator) {
575 #define FN_PARAMETERS \
576 holder, query_neighbors_row_splits, num_points, points, num_queries, \
577 queries, dimension, radii, ignore_query_point, return_distances, \
578 normalize_distances, sort, output_allocator
580 #define CALL_TEMPLATE(METRIC) \
581 if (METRIC == metric) { \
582 _RadiusSearchCPU<T, TIndex, OUTPUT_ALLOCATOR, METRIC>(FN_PARAMETERS); \
585 #define CALL_TEMPLATE2 \
592 #undef CALL_TEMPLATE2
648 template <
class T,
class TIndex,
class OUTPUT_ALLOCATOR>
653 const T *
const queries,
654 const size_t dimension,
658 bool ignore_query_point,
659 bool return_distances,
660 OUTPUT_ALLOCATOR &output_allocator) {
661 #define FN_PARAMETERS \
662 holder, num_points, points, num_queries, queries, dimension, radius, \
663 max_knn, ignore_query_point, return_distances, output_allocator
665 #define CALL_TEMPLATE(METRIC) \
666 if (METRIC == metric) { \
667 _HybridSearchCPU<T, TIndex, OUTPUT_ALLOCATOR, METRIC>(FN_PARAMETERS); \
670 #define CALL_TEMPLATE2 \
677 #undef CALL_TEMPLATE2
std::unique_ptr< NanoFlannIndexHolderBase > BuildKdTree(size_t num_points, const T *const points, size_t dimension, const Metric metric)
Definition: NanoFlannImpl.h:389
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:561
void HybridSearchCPU(NanoFlannIndexHolderBase *holder, size_t num_points, const T *const points, size_t num_queries, const T *const queries, const size_t dimension, const T radius, const int max_knn, const Metric metric, bool ignore_query_point, bool return_distances, OUTPUT_ALLOCATOR &output_allocator)
Definition: NanoFlannImpl.h:649
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:467
Metric
Supported metrics.
Definition: NeighborSearchCommon.h:19
@ L1
Definition: NeighborSearchCommon.h:19
@ L2
Definition: NeighborSearchCommon.h:19
void InclusivePrefixSum(const Tin *first, const Tin *last, Tout *out)
Definition: ParallelScan.h:43
Definition: PinholeCameraIntrinsic.cpp:16
This class is the Adaptor for connecting Open3D Tensor and NanoFlann.
Definition: NanoFlannImpl.h:28
const TReal *const data_ptr_
Definition: NanoFlannImpl.h:49
size_t dataset_size_
Definition: NanoFlannImpl.h:47
int dimension_
Definition: NanoFlannImpl.h:48
TReal kdtree_get_pt(const size_t idx, const size_t dim) const
Definition: NanoFlannImpl.h:38
DataAdaptor(size_t dataset_size, int dimension, const TReal *const data_ptr)
Definition: NanoFlannImpl.h:29
bool kdtree_get_bbox(BBOX &) const
Definition: NanoFlannImpl.h:43
size_t kdtree_get_point_count() const
Definition: NanoFlannImpl.h:36
nanoflann::L1_Adaptor< TReal, DataAdaptor, TReal > adaptor_t
Definition: NanoFlannImpl.h:63
nanoflann::L2_Adaptor< TReal, DataAdaptor, TReal > adaptor_t
Definition: NanoFlannImpl.h:58
Adaptor Selector.
Definition: NanoFlannImpl.h:54
Base struct for NanoFlann index holder.
Definition: NeighborSearchCommon.h:53
NanoFlann Index Holder.
Definition: NanoFlannImpl.h:26
std::unique_ptr< KDTree_t > index_
Definition: NanoFlannImpl.h:82
nanoflann::KDTreeSingleIndexAdaptor< typename SelectNanoflannAdaptor< METRIC >::adaptor_t, DataAdaptor, -1, TIndex > KDTree_t
typedef for KDtree.
Definition: NanoFlannImpl.h:72
std::unique_ptr< DataAdaptor > adaptor_
Definition: NanoFlannImpl.h:83
NanoFlannIndexHolder(size_t dataset_size, int dimension, const TReal *data_ptr)
Definition: NanoFlannImpl.h:74