ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
knncpp.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 <Eigen/Geometry>
11 #include <map>
12 #include <set>
13 #include <vector>
14 
15 #ifdef KNNCPP_FLANN
16 
17 #include <flann/flann.hpp>
18 
19 #endif
20 
21 namespace knncpp {
22 /********************************************************
23  * Matrix Definitions
24  *******************************************************/
25 
26 typedef typename Eigen::MatrixXd::Index Index;
27 
28 typedef Eigen::Matrix<Index, Eigen::Dynamic, 1> Vectori;
29 typedef Eigen::Matrix<Index, 2, 1> Vector2i;
30 typedef Eigen::Matrix<Index, 3, 1> Vector3i;
31 typedef Eigen::Matrix<Index, 4, 1> Vector4i;
32 typedef Eigen::Matrix<Index, 5, 1> Vector5i;
33 
34 typedef Eigen::Matrix<Index, Eigen::Dynamic, Eigen::Dynamic> Matrixi;
35 typedef Eigen::Matrix<Index, 2, 2> Matrix2i;
36 typedef Eigen::Matrix<Index, 3, 3> Matrix3i;
37 typedef Eigen::Matrix<Index, 4, 4> Matrix4i;
38 typedef Eigen::Matrix<Index, 5, 5> Matrix5i;
39 
40 typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> Matrixf;
41 typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Matrixd;
42 
43 /********************************************************
44  * Distance Functors
45  *******************************************************/
46 
50 template <typename Scalar>
55  template <typename DerivedA, typename DerivedB>
56  Scalar operator()(const Eigen::MatrixBase<DerivedA> &lhs,
57  const Eigen::MatrixBase<DerivedB> &rhs) const {
58  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedA>::Scalar,
59  Scalar>::value,
60  "distance scalar and input matrix A must have same type");
61  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedB>::Scalar,
62  Scalar>::value,
63  "distance scalar and input matrix B must have same type");
64 
65  return (lhs - rhs).cwiseAbs().sum();
66  }
67 
71  Scalar operator()(const Scalar lhs, const Scalar rhs) const {
72  return std::abs(lhs - rhs);
73  }
74 
77  Scalar operator()(const Scalar val) const { return val; }
78 };
79 
83 template <typename Scalar>
88  template <typename DerivedA, typename DerivedB>
89  Scalar operator()(const Eigen::MatrixBase<DerivedA> &lhs,
90  const Eigen::MatrixBase<DerivedB> &rhs) const {
91  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedA>::Scalar,
92  Scalar>::value,
93  "distance scalar and input matrix A must have same type");
94  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedB>::Scalar,
95  Scalar>::value,
96  "distance scalar and input matrix B must have same type");
97 
98  return (lhs - rhs).cwiseAbs2().sum();
99  }
100 
104  Scalar operator()(const Scalar lhs, const Scalar rhs) const {
105  Scalar diff = lhs - rhs;
106  return diff * diff;
107  }
108 
111  Scalar operator()(const Scalar val) const { return std::sqrt(val); }
112 };
113 
117 template <typename Scalar, int P>
119  struct Pow {
120  Scalar operator()(const Scalar val) const {
121  Scalar result = 1;
122  for (int i = 0; i < P; ++i) result *= val;
123  return result;
124  }
125  };
126 
130  template <typename DerivedA, typename DerivedB>
131  Scalar operator()(const Eigen::MatrixBase<DerivedA> &lhs,
132  const Eigen::MatrixBase<DerivedB> &rhs) const {
133  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedA>::Scalar,
134  Scalar>::value,
135  "distance scalar and input matrix A must have same type");
136  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedB>::Scalar,
137  Scalar>::value,
138  "distance scalar and input matrix B must have same type");
139 
140  return (lhs - rhs).cwiseAbs().unaryExpr(MinkowskiDistance::Pow()).sum();
141  }
142 
146  Scalar operator()(const Scalar lhs, const Scalar rhs) const {
147  return std::pow(std::abs(lhs - rhs), P);
148  ;
149  }
150 
153  Scalar operator()(const Scalar val) const {
154  return std::pow(val, 1 / static_cast<Scalar>(P));
155  }
156 };
157 
161 template <typename Scalar>
166  template <typename DerivedA, typename DerivedB>
167  Scalar operator()(const Eigen::MatrixBase<DerivedA> &lhs,
168  const Eigen::MatrixBase<DerivedB> &rhs) const {
169  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedA>::Scalar,
170  Scalar>::value,
171  "distance scalar and input matrix A must have same type");
172  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedB>::Scalar,
173  Scalar>::value,
174  "distance scalar and input matrix B must have same type");
175 
176  return (lhs - rhs).cwiseAbs().maxCoeff();
177  }
178 
182  Scalar operator()(const Scalar lhs, const Scalar rhs) const {
183  return std::abs(lhs - rhs);
184  }
185 
188  Scalar operator()(const Scalar val) const { return val; }
189 };
190 
196 template <typename Scalar>
198  static_assert(std::is_integral<Scalar>::value,
199  "HammingDistance requires integral Scalar type");
200 
201  struct XOR {
202  Scalar operator()(const Scalar lhs, const Scalar rhs) const {
203  return lhs ^ rhs;
204  }
205  };
206 
207  struct BitCount {
208  Scalar operator()(const Scalar lhs) const {
209  Scalar copy = lhs;
210  Scalar result = 0;
211  while (copy != static_cast<Scalar>(0)) {
212  ++result;
213  copy &= (copy - 1);
214  }
215 
216  return result;
217  }
218  };
219 
223  template <typename DerivedA, typename DerivedB>
224  Scalar operator()(const Eigen::MatrixBase<DerivedA> &lhs,
225  const Eigen::MatrixBase<DerivedB> &rhs) const {
226  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedA>::Scalar,
227  Scalar>::value,
228  "distance scalar and input matrix A must have same type");
229  static_assert(std::is_same<typename Eigen::MatrixBase<DerivedB>::Scalar,
230  Scalar>::value,
231  "distance scalar and input matrix B must have same type");
232 
233  return lhs.binaryExpr(rhs, XOR()).unaryExpr(BitCount()).sum();
234  }
235 
239  Scalar operator()(const Scalar lhs, const Scalar rhs) const {
240  BitCount cnt;
241  XOR xOr;
242  return cnt(xOr(lhs, rhs));
243  }
244 
247  Scalar operator()(const Scalar value) const { return value; }
248 };
249 
251 template <typename Scalar>
252 class QueryHeap {
253 private:
254  Index *indices_ = nullptr;
255  Scalar *distances_ = nullptr;
256  size_t maxSize_ = 0;
257  size_t size_ = 0;
258 
259 public:
262  QueryHeap(Index *indices, Scalar *distances, const size_t maxSize)
263  : indices_(indices), distances_(distances), maxSize_(maxSize) {}
264 
271  void push(const Index idx, const Scalar dist) {
272  assert(!full());
273 
274  // add new value at the end
275  indices_[size_] = idx;
276  distances_[size_] = dist;
277  ++size_;
278 
279  // upheap
280  size_t k = size_ - 1;
281  size_t tmp = (k - 1) / 2;
282  while (k > 0 && distances_[tmp] < dist) {
283  distances_[k] = distances_[tmp];
284  indices_[k] = indices_[tmp];
285  k = tmp;
286  tmp = (k - 1) / 2;
287  }
288  distances_[k] = dist;
289  indices_[k] = idx;
290  }
291 
294  void pop() {
295  assert(!empty());
296 
297  // replace first element with last
298  --size_;
299  distances_[0] = distances_[size_];
300  indices_[0] = indices_[size_];
301 
302  // downheap
303  size_t k = 0;
304  size_t j;
305  Scalar dist = distances_[0];
306  Index idx = indices_[0];
307  while (2 * k + 1 < size_) {
308  j = 2 * k + 1;
309  if (j + 1 < size_ && distances_[j + 1] > distances_[j]) ++j;
310  // j references now greatest child
311  if (dist >= distances_[j]) break;
312  distances_[k] = distances_[j];
313  indices_[k] = indices_[j];
314  k = j;
315  }
316  distances_[k] = dist;
317  indices_[k] = idx;
318  }
319 
321  Scalar front() const {
322  assert(!empty());
323  return distances_[0];
324  }
325 
330  bool full() const { return size_ >= maxSize_; }
331 
334  bool empty() const { return size_ == 0; }
335 
338  size_t size() const { return size_; }
339 
341  void clear() { size_ = 0; }
342 
345  void sort() {
346  size_t cnt = size_;
347  for (size_t i = 0; i < cnt; ++i) {
348  Index idx = indices_[0];
349  Scalar dist = distances_[0];
350  pop();
351  indices_[cnt - i - 1] = idx;
352  distances_[cnt - i - 1] = dist;
353  }
354  }
355 };
356 
358 template <typename Scalar, typename Distance = EuclideanDistance<Scalar>>
359 class BruteForce {
360 public:
361  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
362  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Vector;
364 
365 private:
366  Distance distance_ = Distance();
367  Matrix dataCopy_ = Matrix();
368  const Matrix *data_ = nullptr;
369 
370  bool sorted_ = true;
371  bool takeRoot_ = true;
372  Index threads_ = 1;
373  Scalar maxDist_ = 0;
374 
375 public:
376  BruteForce() = default;
377 
381  BruteForce(const Matrix &data, const bool copy = false) : BruteForce() {
382  setData(data, copy);
383  }
384 
388  void setSorted(const bool sorted) { sorted_ = sorted; }
389 
395  void setTakeRoot(const bool takeRoot) { takeRoot_ = takeRoot; }
396 
400  void setThreads(const unsigned int threads) { threads_ = threads; }
401 
406  void setMaxDistance(const Scalar maxDist) { maxDist_ = maxDist; }
407 
412  void setData(const Matrix &data, const bool copy = false) {
413  if (copy) {
414  dataCopy_ = data;
415  data_ = &dataCopy_;
416  } else {
417  data_ = &data;
418  }
419  }
420 
421  void setDistance(const Distance &distance) { distance_ = distance; }
422 
423  void build() {}
424 
425  template <typename Derived>
426  void query(const Eigen::MatrixBase<Derived> &queryPoints,
427  const size_t knn,
428  Matrixi &indices,
429  Matrix &distances) const {
430  if (data_ == nullptr)
431  throw std::runtime_error("cannot query BruteForce: data not set");
432  if (data_->size() == 0)
433  throw std::runtime_error("cannot query BruteForce: data is empty");
434  if (queryPoints.rows() != dimension())
435  throw std::runtime_error(
436  "cannot query BruteForce: data and query descriptors do "
437  "not have same dimension");
438 
439  const Matrix &dataPoints = *data_;
440 
441  indices.setConstant(knn, queryPoints.cols(), -1);
442  distances.setConstant(knn, queryPoints.cols(), -1);
443 
444 #pragma omp parallel for num_threads(threads_)
445  for (Index i = 0; i < queryPoints.cols(); ++i) {
446  Index *idxPoint = &indices.data()[i * knn];
447  Scalar *distPoint = &distances.data()[i * knn];
448 
449  QueryHeap<Scalar> heap(idxPoint, distPoint, knn);
450 
451  for (Index j = 0; j < dataPoints.cols(); ++j) {
452  Scalar dist = distance_(queryPoints.col(i), dataPoints.col(j));
453 
454  // check if point is in range if max distance was set
455  bool isInRange = maxDist_ <= 0 || dist <= maxDist_;
456  // check if this node was an improvement if heap is already full
457  bool isImprovement = !heap.full() || dist < heap.front();
458  if (isInRange && isImprovement) {
459  if (heap.full()) heap.pop();
460  heap.push(j, dist);
461  }
462  }
463 
464  if (sorted_) heap.sort();
465 
466  if (takeRoot_) {
467  for (size_t j = 0; j < knn; ++j) {
468  if (idxPoint[j] < 0) break;
469  distPoint[j] = distance_(distPoint[j]);
470  }
471  }
472  }
473  }
474 
477  Index size() const { return data_ == nullptr ? 0 : data_->cols(); }
478 
481  Index dimension() const { return data_ == nullptr ? 0 : data_->rows(); }
482 };
483 
484 // template<typename Scalar>
485 // struct MeanMidpointRule
486 // {
487 // typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
488 // typedef knncpp::Matrixi Matrixi;
489 
490 // void operator(const Matrix &data, const Matrixi &indices, Index split)
491 // };
492 
498 template <typename _Scalar, int _Dimension, typename _Distance>
500 public:
501  typedef _Scalar Scalar;
502  typedef _Distance Distance;
503  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
504  typedef Eigen::Matrix<Scalar, _Dimension, Eigen::Dynamic> DataMatrix;
505  typedef Eigen::Matrix<Scalar, _Dimension, 1> DataVector;
507 
508 private:
509  typedef Eigen::Matrix<Scalar, 2, 1> Bounds;
510  typedef Eigen::Matrix<Scalar, 2, _Dimension> BoundingBox;
511 
514  struct Node {
516  Index startIdx = 0;
517  Index length = 0;
518 
520  Index left = -1;
522  Index right = -1;
524  Index splitaxis = -1;
526  Scalar splitpoint = 0;
528  Scalar splitlower = 0;
530  Scalar splitupper = 0;
531 
532  Node() = default;
533 
535  Node(const Index startIdx, const Index length)
536  : startIdx(startIdx), length(length) {}
537 
539  Node(const Index splitaxis,
540  const Scalar splitpoint,
541  const Index left,
542  const Index right)
543  : left(left),
544  right(right),
545  splitaxis(splitaxis),
546  splitpoint(splitpoint) {}
547 
548  bool isLeaf() const { return !hasLeft() && !hasRight(); }
549 
550  bool isInner() const { return hasLeft() && hasRight(); }
551 
552  bool hasLeft() const { return left >= 0; }
553 
554  bool hasRight() const { return right >= 0; }
555  };
556 
557  DataMatrix dataCopy_ = DataMatrix();
558  const DataMatrix *data_ = nullptr;
559  std::vector<Index> indices_ = std::vector<Index>();
560  std::vector<Node> nodes_ = std::vector<Node>();
561 
562  Index bucketSize_ = 16;
563  bool sorted_ = true;
564  bool compact_ = false;
565  bool balanced_ = false;
566  bool takeRoot_ = true;
567  Index threads_ = 0;
568  Scalar maxDist_ = 0;
569 
570  Distance distance_ = Distance();
571 
572  BoundingBox bbox_ = BoundingBox();
573 
574  Index buildLeafNode(const Index startIdx,
575  const Index length,
576  BoundingBox &bbox) {
577  nodes_.push_back(Node(startIdx, length));
578  calculateBoundingBox(startIdx, length, bbox);
579  return static_cast<Index>(nodes_.size() - 1);
580  }
581 
588  void calculateBoundingBox(const Index startIdx,
589  const Index length,
590  BoundingBox &bbox) const {
591  assert(length > 0);
592  assert(startIdx >= 0);
593  assert(static_cast<size_t>(startIdx + length) <= indices_.size());
594  assert(data_->rows() == bbox.cols());
595 
596  const DataMatrix &data = *data_;
597 
598  // initialize bounds of the bounding box
599  Index first = indices_[startIdx];
600  for (Index i = 0; i < bbox.cols(); ++i) {
601  bbox(0, i) = data(i, first);
602  bbox(1, i) = data(i, first);
603  }
604 
605  // search for min / max values in data
606  for (Index i = 1; i < length; ++i) {
607  // retrieve data index
608  Index col = indices_[startIdx + i];
609  assert(col >= 0 && col < data.cols());
610 
611  // check min and max for each dimension individually
612  for (Index j = 0; j < data.rows(); ++j) {
613  bbox(0, j) = std::min(bbox(0, j), data(j, col));
614  bbox(1, j) = std::max(bbox(1, j), data(j, col));
615  }
616  }
617  }
618 
621  void calculateBounds(const Index startIdx,
622  const Index length,
623  const Index dim,
624  Bounds &bounds) const {
625  assert(length > 0);
626  assert(startIdx >= 0);
627  assert(static_cast<size_t>(startIdx + length) <= indices_.size());
628 
629  const DataMatrix &data = *data_;
630 
631  bounds(0) = data(dim, indices_[startIdx]);
632  bounds(1) = data(dim, indices_[startIdx]);
633 
634  for (Index i = 1; i < length; ++i) {
635  Index col = indices_[startIdx + i];
636  assert(col >= 0 && col < data.cols());
637 
638  bounds(0) = std::min(bounds(0), data(dim, col));
639  bounds(1) = std::max(bounds(1), data(dim, col));
640  }
641  }
642 
643  void calculateSplittingMidpoint(const Index startIdx,
644  const Index length,
645  const BoundingBox &bbox,
646  Index &splitaxis,
647  Scalar &splitpoint,
648  Index &splitoffset) {
649  const DataMatrix &data = *data_;
650 
651  // search for axis with longest distance
652  splitaxis = 0;
653  Scalar splitsize = static_cast<Scalar>(0);
654  for (Index i = 0; i < data.rows(); ++i) {
655  Scalar diff = bbox(1, i) - bbox(0, i);
656  if (diff > splitsize) {
657  splitaxis = i;
658  splitsize = diff;
659  }
660  }
661 
662  // calculate the bounds in this axis and update our data
663  // accordingly
664  Bounds bounds;
665  calculateBounds(startIdx, length, splitaxis, bounds);
666  splitsize = bounds(1) - bounds(0);
667 
668  const Index origSplitaxis = splitaxis;
669  for (Index i = 0; i < data.rows(); ++i) {
670  // skip the dimension of the previously found splitaxis
671  if (i == origSplitaxis) continue;
672  Scalar diff = bbox(1, i) - bbox(0, i);
673  // check if the split for this dimension would be potentially larger
674  if (diff > splitsize) {
675  Bounds newBounds;
676  // update the bounds to their actual current value
677  calculateBounds(startIdx, length, splitaxis, newBounds);
678  diff = newBounds(1) - newBounds(0);
679  if (diff > splitsize) {
680  splitaxis = i;
681  splitsize = diff;
682  bounds = newBounds;
683  }
684  }
685  }
686 
687  // use the sliding midpoint rule
688  splitpoint = (bounds(0) + bounds(1)) / static_cast<Scalar>(2);
689 
690  Index leftIdx = startIdx;
691  Index rightIdx = startIdx + length - 1;
692 
693  // first loop checks left < splitpoint and right >= splitpoint
694  while (leftIdx <= rightIdx) {
695  // increment left as long as left has not reached right and
696  // the value of the left element is less than the splitpoint
697  while (leftIdx <= rightIdx &&
698  data(splitaxis, indices_[leftIdx]) < splitpoint)
699  ++leftIdx;
700 
701  // decrement right as long as left has not reached right and
702  // the value of the right element is greater than the splitpoint
703  while (leftIdx <= rightIdx &&
704  data(splitaxis, indices_[rightIdx]) >= splitpoint)
705  --rightIdx;
706 
707  if (leftIdx <= rightIdx) {
708  std::swap(indices_[leftIdx], indices_[rightIdx]);
709  ++leftIdx;
710  --rightIdx;
711  }
712  }
713 
714  // remember this offset from starting index
715  const Index offset1 = leftIdx - startIdx;
716 
717  rightIdx = startIdx + length - 1;
718  // second loop checks left <= splitpoint and right > splitpoint
719  while (leftIdx <= rightIdx) {
720  // increment left as long as left has not reached right and
721  // the value of the left element is less than the splitpoint
722  while (leftIdx <= rightIdx &&
723  data(splitaxis, indices_[leftIdx]) <= splitpoint)
724  ++leftIdx;
725 
726  // decrement right as long as left has not reached right and
727  // the value of the right element is greater than the splitpoint
728  while (leftIdx <= rightIdx &&
729  data(splitaxis, indices_[rightIdx]) > splitpoint)
730  --rightIdx;
731 
732  if (leftIdx <= rightIdx) {
733  std::swap(indices_[leftIdx], indices_[rightIdx]);
734  ++leftIdx;
735  --rightIdx;
736  }
737  }
738 
739  // remember this offset from starting index
740  const Index offset2 = leftIdx - startIdx;
741 
742  const Index halfLength = length / static_cast<Index>(2);
743 
744  // find a separation of points such that is best balanced
745  // offset1 denotes separation where equal points are all on the right
746  // offset2 denots separation where equal points are all on the left
747  if (offset1 > halfLength)
748  splitoffset = offset1;
749  else if (offset2 < halfLength)
750  splitoffset = offset2;
751  // when we get here offset1 < halflength and offset2 > halflength
752  // so simply split the equal elements in the middle
753  else
754  splitoffset = halfLength;
755  }
756 
757  Index buildInnerNode(const Index startIdx,
758  const Index length,
759  BoundingBox &bbox) {
760  assert(length > 0);
761  assert(startIdx >= 0);
762  assert(static_cast<size_t>(startIdx + length) <= indices_.size());
763  assert(data_->rows() == bbox.cols());
764 
765  // create node
766  const Index nodeIdx = nodes_.size();
767  nodes_.push_back(Node());
768 
769  Index splitaxis;
770  Index splitoffset;
771  Scalar splitpoint;
772  calculateSplittingMidpoint(startIdx, length, bbox, splitaxis,
773  splitpoint, splitoffset);
774 
775  nodes_[nodeIdx].splitaxis = splitaxis;
776  nodes_[nodeIdx].splitpoint = splitpoint;
777 
778  const Index leftStart = startIdx;
779  const Index leftLength = splitoffset;
780  const Index rightStart = startIdx + splitoffset;
781  const Index rightLength = length - splitoffset;
782 
783  BoundingBox bboxLeft = bbox;
784  BoundingBox bboxRight = bbox;
785 
786  // do left build
787  bboxLeft(1, splitaxis) = splitpoint;
788  Index left = buildR(leftStart, leftLength, bboxLeft);
789  nodes_[nodeIdx].left = left;
790 
791  // do right build
792  bboxRight(0, splitaxis) = splitpoint;
793  Index right = buildR(rightStart, rightLength, bboxRight);
794  nodes_[nodeIdx].right = right;
795 
796  // extract the range of the splitpoint
797  nodes_[nodeIdx].splitlower = bboxLeft(1, splitaxis);
798  nodes_[nodeIdx].splitupper = bboxRight(0, splitaxis);
799 
800  // update the bounding box to the values of the new bounding boxes
801  for (Index i = 0; i < bbox.cols(); ++i) {
802  bbox(0, i) = std::min(bboxLeft(0, i), bboxRight(0, i));
803  bbox(1, i) = std::max(bboxLeft(1, i), bboxRight(1, i));
804  }
805 
806  return nodeIdx;
807  }
808 
809  Index buildR(const Index startIdx, const Index length, BoundingBox &bbox) {
810  // check for base case
811  if (length <= bucketSize_)
812  return buildLeafNode(startIdx, length, bbox);
813  else
814  return buildInnerNode(startIdx, length, bbox);
815  }
816 
817  bool isDistanceInRange(const Scalar dist) const {
818  return maxDist_ <= 0 || dist <= maxDist_;
819  }
820 
821  bool isDistanceImprovement(const Scalar dist,
822  const QueryHeap<Scalar> &dataHeap) const {
823  return !dataHeap.full() || dist < dataHeap.front();
824  }
825 
826  template <typename Derived>
827  void queryLeafNode(const Node &node,
828  const Eigen::MatrixBase<Derived> &queryPoint,
829  QueryHeap<Scalar> &dataHeap) const {
830  assert(node.isLeaf());
831 
832  const DataMatrix &data = *data_;
833 
834  // go through all points in this leaf node and do brute force search
835  for (Index i = 0; i < node.length; ++i) {
836  const Index idx = node.startIdx + i;
837  assert(idx >= 0 && idx < static_cast<Index>(indices_.size()));
838 
839  // retrieve index of the current data point
840  const Index dataIdx = indices_[idx];
841  const Scalar dist = distance_(queryPoint, data.col(dataIdx));
842 
843  // check if point is within max distance and if the value would be
844  // an improvement
845  if (isDistanceInRange(dist) &&
846  isDistanceImprovement(dist, dataHeap)) {
847  if (dataHeap.full()) dataHeap.pop();
848  dataHeap.push(dataIdx, dist);
849  }
850  }
851  }
852 
853  template <typename Derived>
854  void queryInnerNode(const Node &node,
855  const Eigen::MatrixBase<Derived> &queryPoint,
856  QueryHeap<Scalar> &dataHeap,
857  DataVector &splitdists,
858  const Scalar mindist) const {
859  assert(node.isInner());
860 
861  const Index splitaxis = node.splitaxis;
862  const Scalar splitval = queryPoint(splitaxis, 0);
863  Scalar splitdist;
864  Index firstNode;
865  Index secondNode;
866  // check if right or left child should be visited
867  const bool visitLeft =
868  (splitval - node.splitlower + splitval - node.splitupper) < 0;
869  if (visitLeft) {
870  firstNode = node.left;
871  secondNode = node.right;
872  splitdist = distance_(splitval, node.splitupper);
873  } else {
874  firstNode = node.right;
875  secondNode = node.left;
876  splitdist = distance_(splitval, node.splitlower);
877  }
878 
879  queryR(nodes_[firstNode], queryPoint, dataHeap, splitdists, mindist);
880 
881  const Scalar mindistNew = mindist + splitdist - splitdists(splitaxis);
882 
883  // check if node is in range if max distance was set
884  // check if this node was an improvement if heap is already full
885  if (isDistanceInRange(mindistNew) &&
886  isDistanceImprovement(mindistNew, dataHeap)) {
887  const Scalar splitdistOld = splitdists(splitaxis);
888  splitdists(splitaxis) = splitdist;
889  queryR(nodes_[secondNode], queryPoint, dataHeap, splitdists,
890  mindistNew);
891  splitdists(splitaxis) = splitdistOld;
892  }
893  }
894 
895  template <typename Derived>
896  void queryR(const Node &node,
897  const Eigen::MatrixBase<Derived> &queryPoint,
898  QueryHeap<Scalar> &dataHeap,
899  DataVector &splitdists,
900  const Scalar mindist) const {
901  if (node.isLeaf())
902  queryLeafNode(node, queryPoint, dataHeap);
903  else
904  queryInnerNode(node, queryPoint, dataHeap, splitdists, mindist);
905  }
906 
908  Index depthR(const Node &node) const {
909  if (node.isLeaf())
910  return 1;
911  else {
912  Index left = depthR(nodes_[node.left]);
913  Index right = depthR(nodes_[node.right]);
914  return std::max(left, right) + 1;
915  }
916  }
917 
918 public:
921 
926  KDTreeMinkowski(const DataMatrix &data, const bool copy = false) {
927  setData(data, copy);
928  }
929 
933  void setBucketSize(const Index bucketSize) { bucketSize_ = bucketSize; }
934 
938  void setSorted(const bool sorted) { sorted_ = sorted; }
939 
943  void setBalanced(const bool balanced) { balanced_ = balanced; }
944 
950  void setTakeRoot(const bool takeRoot) { takeRoot_ = takeRoot; }
951 
956  void setCompact(const bool compact) { compact_ = compact; }
957 
962  void setThreads(const unsigned int threads) { threads_ = threads; }
963 
968  void setMaxDistance(const Scalar maxDist) { maxDist_ = maxDist; }
969 
974  void setData(const DataMatrix &data, const bool copy = false) {
975  clear();
976  if (copy) {
977  dataCopy_ = data;
978  data_ = &dataCopy_;
979  } else {
980  data_ = &data;
981  }
982  }
983 
984  void setDistance(const Distance &distance) { distance_ = distance; }
985 
988  void build() {
989  if (data_ == nullptr)
990  throw std::runtime_error("cannot build KDTree; data not set");
991 
992  if (data_->size() == 0)
993  throw std::runtime_error("cannot build KDTree; data is empty");
994 
995  clear();
996  nodes_.reserve((data_->cols() / bucketSize_) + 1);
997 
998  // initialize indices in simple sequence
999  indices_.resize(data_->cols());
1000  for (size_t i = 0; i < indices_.size(); ++i) indices_[i] = i;
1001 
1002  bbox_.resize(2, data_->rows());
1003  Index startIdx = 0;
1004  Index length = data_->cols();
1005 
1006  calculateBoundingBox(startIdx, length, bbox_);
1007 
1008  buildR(startIdx, length, bbox_);
1009  }
1010 
1028  template <typename Derived>
1029  void query(const Eigen::MatrixBase<Derived> &queryPoints,
1030  const size_t knn,
1031  Matrixi &indices,
1032  Matrix &distances) const {
1033  if (nodes_.size() == 0)
1034  throw std::runtime_error("cannot query KDTree; not built yet");
1035 
1036  if (queryPoints.rows() != dimension())
1037  throw std::runtime_error(
1038  "cannot query KDTree; data and query points do not have "
1039  "same dimension");
1040 
1041  distances.setConstant(knn, queryPoints.cols(), -1);
1042  indices.setConstant(knn, queryPoints.cols(), -1);
1043 
1044  Index *indicesRaw = indices.data();
1045  Scalar *distsRaw = distances.data();
1046 
1047 #pragma omp parallel for num_threads(threads_)
1048  for (Index i = 0; i < queryPoints.cols(); ++i) {
1049  Scalar *distPoint = &distsRaw[i * knn];
1050  Index *idxPoint = &indicesRaw[i * knn];
1051 
1052  // create heap to find nearest neighbours
1053  QueryHeap<Scalar> dataHeap(idxPoint, distPoint, knn);
1054 
1055  Scalar mindist = static_cast<Scalar>(0);
1056  DataVector splitdists(queryPoints.rows());
1057 
1058  for (Index j = 0; j < splitdists.rows(); ++j) {
1059  const Scalar value = queryPoints(j, i);
1060  const Scalar lower = bbox_(0, j);
1061  const Scalar upper = bbox_(1, j);
1062  if (value < lower) {
1063  splitdists(j) = distance_(value, lower);
1064  } else if (value > upper) {
1065  splitdists(j) = distance_(value, upper);
1066  } else {
1067  splitdists(j) = static_cast<Scalar>(0);
1068  }
1069 
1070  mindist += splitdists(j);
1071  }
1072 
1073  queryR(nodes_[0], queryPoints.col(i), dataHeap, splitdists,
1074  mindist);
1075 
1076  if (sorted_) dataHeap.sort();
1077 
1078  if (takeRoot_) {
1079  for (size_t j = 0; j < knn; ++j) {
1080  if (distPoint[j] < 0) break;
1081  distPoint[j] = distance_(distPoint[j]);
1082  }
1083  }
1084  }
1085  }
1086 
1088  void clear() { nodes_.clear(); }
1089 
1092  Index size() const { return data_ == nullptr ? 0 : data_->cols(); }
1093 
1096  Index dimension() const { return data_ == nullptr ? 0 : data_->rows(); }
1097 
1100  Index depth() const {
1101  return nodes_.size() == 0 ? 0 : depthR(nodes_.front());
1102  }
1103 };
1104 
1105 template <typename _Scalar, typename _Distance = EuclideanDistance<_Scalar>>
1107 template <typename _Scalar, typename _Distance = EuclideanDistance<_Scalar>>
1109 template <typename _Scalar, typename _Distance = EuclideanDistance<_Scalar>>
1111 template <typename _Scalar, typename _Distance = EuclideanDistance<_Scalar>>
1113 template <typename _Scalar, typename _Distance = EuclideanDistance<_Scalar>>
1115 
1117 template <typename Scalar>
1119 public:
1120  static_assert(std::is_integral<Scalar>::value,
1121  "MultiIndexHashing Scalar has to be integral");
1122 
1123  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
1124  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Vector;
1126 
1127 private:
1128  HammingDistance<Scalar> distance_;
1129 
1130  Matrix dataCopy_;
1131  const Matrix *data_;
1132 
1133  bool sorted_;
1134  Scalar maxDist_;
1135  Index substrLen_;
1136  Index threads_;
1137  std::vector<std::map<Scalar, std::vector<Index>>> buckets_;
1138 
1139  template <typename Derived>
1140  Scalar extractCode(const Eigen::MatrixBase<Derived> &data,
1141  const Index idx,
1142  const Index offset) const {
1143  Index leftShift = std::max<Index>(
1144  0, static_cast<Index>(sizeof(Scalar)) - offset - substrLen_);
1145  Index rightShift = leftShift + offset;
1146 
1147  Scalar code = (data(idx, 0) << (leftShift * 8)) >> (rightShift * 8);
1148 
1149  if (static_cast<Index>(sizeof(Scalar)) - offset < substrLen_ &&
1150  idx + 1 < data.rows()) {
1151  Index shift = 2 * static_cast<Index>(sizeof(Scalar)) - substrLen_ -
1152  offset;
1153  code |= data(idx + 1, 0) << (shift * 8);
1154  }
1155 
1156  return code;
1157  }
1158 
1159 public:
1161  : distance_(),
1162  dataCopy_(),
1163  data_(nullptr),
1164  sorted_(true),
1165  maxDist_(0),
1166  substrLen_(1),
1167  threads_(1) {}
1168 
1173  MultiIndexHashing(const Matrix &data, const bool copy = false)
1174  : MultiIndexHashing() {
1175  setData(data, copy);
1176  }
1177 
1182  void setMaxDistance(const Scalar maxDist) { maxDist_ = maxDist; }
1183 
1187  void setSorted(const bool sorted) { sorted_ = sorted; }
1188 
1193  void setThreads(const unsigned int threads) { threads_ = threads; }
1194 
1197  void setSubstringLength(const Index len) { substrLen_ = len; }
1198 
1202  void setData(const Matrix &data, const bool copy = false) {
1203  clear();
1204  if (copy) {
1205  dataCopy_ = data;
1206  data_ = &dataCopy_;
1207  } else {
1208  data_ = &data;
1209  }
1210  }
1211 
1212  void build() {
1213  if (data_ == nullptr)
1214  throw std::runtime_error(
1215  "cannot build MultiIndexHashing; data not set");
1216  if (data_->size() == 0)
1217  throw std::runtime_error(
1218  "cannot build MultiIndexHashing; data is empty");
1219 
1220  const Matrix &data = *data_;
1221  const Index bytesPerVec =
1222  data.rows() * static_cast<Index>(sizeof(Scalar));
1223  if (bytesPerVec % substrLen_ != 0)
1224  throw std::runtime_error(
1225  "cannot build MultiIndexHashing; cannot divide byte count "
1226  "per vector by substring length without remainings");
1227 
1228  buckets_.clear();
1229  buckets_.resize(bytesPerVec / substrLen_);
1230 
1231  for (size_t i = 0; i < buckets_.size(); ++i) {
1232  Index start = static_cast<Index>(i) * substrLen_;
1233  Index idx = start / static_cast<Index>(sizeof(Scalar));
1234  Index offset = start % static_cast<Index>(sizeof(Scalar));
1235  std::map<Scalar, std::vector<Index>> &map = buckets_[i];
1236 
1237  for (Index c = 0; c < data.cols(); ++c) {
1238  Scalar code = extractCode(data.col(c), idx, offset);
1239  if (map.find(code) == map.end())
1240  map[code] = std::vector<Index>();
1241  map[code].push_back(c);
1242  }
1243  }
1244  }
1245 
1246  template <typename Derived>
1247  void query(const Eigen::MatrixBase<Derived> &queryPoints,
1248  const size_t knn,
1249  Matrixi &indices,
1250  Matrix &distances) const {
1251  if (buckets_.size() == 0)
1252  throw std::runtime_error(
1253  "cannot query MultiIndexHashing; not built yet");
1254  if (queryPoints.rows() != dimension())
1255  throw std::runtime_error(
1256  "cannot query MultiIndexHashing; data and query points do "
1257  "not have same dimension");
1258 
1259  const Matrix &data = *data_;
1260 
1261  indices.setConstant(knn, queryPoints.cols(), -1);
1262  distances.setConstant(knn, queryPoints.cols(), -1);
1263 
1264  Index *indicesRaw = indices.data();
1265  Scalar *distsRaw = distances.data();
1266 
1267  Scalar maxDistPart = maxDist_ / buckets_.size();
1268 
1269 #pragma omp parallel for num_threads(threads_)
1270  for (Index c = 0; c < queryPoints.cols(); ++c) {
1271  std::set<Index> candidates;
1272  for (size_t i = 0; i < buckets_.size(); ++i) {
1273  Index start = static_cast<Index>(i) * substrLen_;
1274  Index idx = start / static_cast<Index>(sizeof(Scalar));
1275  Index offset = start % static_cast<Index>(sizeof(Scalar));
1276  const std::map<Scalar, std::vector<Index>> &map = buckets_[i];
1277 
1278  Scalar code = extractCode(queryPoints.col(c), idx, offset);
1279  for (const auto &x : map) {
1280  Scalar dist = distance_(x.first, code);
1281  if (maxDistPart <= 0 || dist <= maxDistPart) {
1282  for (size_t j = 0; j < x.second.size(); ++j)
1283  candidates.insert(x.second[j]);
1284  }
1285  }
1286  }
1287 
1288  Scalar *distPoint = &distsRaw[c * knn];
1289  Index *idxPoint = &indicesRaw[c * knn];
1290  // create heap to find nearest neighbours
1291  QueryHeap<Scalar> dataHeap(idxPoint, distPoint, knn);
1292 
1293  for (Index idx : candidates) {
1294  Scalar dist = distance_(data.col(idx), queryPoints.col(c));
1295 
1296  bool isInRange = maxDist_ <= 0 || dist <= maxDist_;
1297  bool isImprovement =
1298  !dataHeap.full() || dist < dataHeap.front();
1299  if (isInRange && isImprovement) {
1300  if (dataHeap.full()) dataHeap.pop();
1301  dataHeap.push(idx, dist);
1302  }
1303  }
1304 
1305  if (sorted_) dataHeap.sort();
1306  }
1307  }
1308 
1311  Index size() const { return data_ == nullptr ? 0 : data_->cols(); }
1312 
1315  Index dimension() const { return data_ == nullptr ? 0 : data_->rows(); }
1316 
1317  void clear() {
1318  data_ = nullptr;
1319  dataCopy_.resize(0, 0);
1320  buckets_.clear();
1321  }
1322 };
1323 
1324 #ifdef KNNCPP_FLANN
1325 
1327 template <typename Scalar, typename Distance = flann::L2_Simple<Scalar>>
1328 class KDTreeFlann {
1329 public:
1330  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
1331  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Vector;
1332  typedef Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic> Matrixi;
1333 
1334 private:
1335  typedef flann::Index<Distance> FlannIndex;
1336 
1337  Matrix dataCopy_;
1338  Matrix *dataPoints_;
1339 
1340  FlannIndex *index_;
1341  flann::SearchParams searchParams_;
1342  flann::IndexParams indexParams_;
1343  Scalar maxDist_;
1344 
1345 public:
1346  KDTreeFlann()
1347  : dataCopy_(),
1348  dataPoints_(nullptr),
1349  index_(nullptr),
1350  searchParams_(32, 0, false),
1351  indexParams_(flann::KDTreeSingleIndexParams(15)),
1352  maxDist_(0) {}
1353 
1354  KDTreeFlann(Matrix &data, const bool copy = false) : KDTreeFlann() {
1355  setData(data, copy);
1356  }
1357 
1358  ~KDTreeFlann() { clear(); }
1359 
1360  void setIndexParams(const flann::IndexParams &params) {
1361  indexParams_ = params;
1362  }
1363 
1364  void setChecks(const int checks) { searchParams_.checks = checks; }
1365 
1366  void setSorted(const bool sorted) { searchParams_.sorted = sorted; }
1367 
1368  void setThreads(const int threads) { searchParams_.cores = threads; }
1369 
1370  void setEpsilon(const float eps) { searchParams_.eps = eps; }
1371 
1372  void setMaxDistance(const Scalar dist) { maxDist_ = dist; }
1373 
1374  void setData(Matrix &data, const bool copy = false) {
1375  if (copy) {
1376  dataCopy_ = data;
1377  dataPoints_ = &dataCopy_;
1378  } else {
1379  dataPoints_ = &data;
1380  }
1381 
1382  clear();
1383  }
1384 
1385  void build() {
1386  if (dataPoints_ == nullptr)
1387  throw std::runtime_error("cannot build KDTree; data not set");
1388  if (dataPoints_->size() == 0)
1389  throw std::runtime_error("cannot build KDTree; data is empty");
1390 
1391  if (index_ != nullptr) delete index_;
1392 
1393  flann::Matrix<Scalar> dataPts(dataPoints_->data(), dataPoints_->cols(),
1394  dataPoints_->rows());
1395 
1396  index_ = new FlannIndex(dataPts, indexParams_);
1397  index_->buildIndex();
1398  }
1399 
1400  void query(Matrix &queryPoints,
1401  const size_t knn,
1402  Matrixi &indices,
1403  Matrix &distances) const {
1404  if (index_ == nullptr)
1405  throw std::runtime_error("cannot query KDTree; not built yet");
1406  if (dataPoints_->rows() != queryPoints.rows())
1407  throw std::runtime_error(
1408  "cannot query KDTree; KDTree has different dimension than "
1409  "query data");
1410 
1411  // resize result matrices
1412  distances.resize(knn, queryPoints.cols());
1413  indices.resize(knn, queryPoints.cols());
1414 
1415  // wrap matrices into flann matrices
1416  flann::Matrix<Scalar> queryPts(queryPoints.data(), queryPoints.cols(),
1417  queryPoints.rows());
1418  flann::Matrix<int> indicesF(indices.data(), indices.cols(),
1419  indices.rows());
1420  flann::Matrix<Scalar> distancesF(distances.data(), distances.cols(),
1421  distances.rows());
1422 
1423  // if maximum distance was set then use radius search
1424  if (maxDist_ > 0)
1425  index_->radiusSearch(queryPts, indicesF, distancesF, maxDist_,
1426  searchParams_);
1427  else
1428  index_->knnSearch(queryPts, indicesF, distancesF, knn,
1429  searchParams_);
1430 
1431 // make result matrices compatible to API
1432 #pragma omp parallel for num_threads(searchParams_.cores)
1433  for (Index i = 0; i < indices.cols(); ++i) {
1434  bool found = false;
1435  for (Index j = 0; j < indices.rows(); ++j) {
1436  if (indices(j, i) == -1) found = true;
1437 
1438  if (found) {
1439  indices(j, i) = -1;
1440  distances(j, i) = -1;
1441  }
1442  }
1443  }
1444  }
1445 
1446  Index size() const {
1447  return dataPoints_ == nullptr ? 0 : dataPoints_->cols();
1448  }
1449 
1450  Index dimension() const {
1451  return dataPoints_ == nullptr ? 0 : dataPoints_->rows();
1452  }
1453 
1454  void clear() {
1455  if (index_ != nullptr) {
1456  delete index_;
1457  index_ = nullptr;
1458  }
1459  }
1460 
1461  FlannIndex &flannIndex() { return index_; }
1462 };
1463 
1464 typedef KDTreeFlann<double> KDTreeFlannd;
1465 typedef KDTreeFlann<float> KDTreeFlannf;
1466 
1467 #endif
1468 } // namespace knncpp
int size
int offset
int64_t size_
Definition: FilePLY.cpp:37
double Distance(const Point3D< Real > &p1, const Point3D< Real > &p2)
cmdLineReadable * params[]
core::Tensor result
Definition: VtkUtils.cpp:76
bool copy
Definition: VtkUtils.cpp:74
Generic handle to any of the 8 types of E57 element objects.
void setDistance(const Distance &distance)
Definition: knncpp.h:421
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > Matrix
Definition: knncpp.h:361
Index size() const
Definition: knncpp.h:477
void setSorted(const bool sorted)
Definition: knncpp.h:388
BruteForce(const Matrix &data, const bool copy=false)
Definition: knncpp.h:381
void setThreads(const unsigned int threads)
Definition: knncpp.h:400
void setData(const Matrix &data, const bool copy=false)
Definition: knncpp.h:412
void setMaxDistance(const Scalar maxDist)
Definition: knncpp.h:406
void query(const Eigen::MatrixBase< Derived > &queryPoints, const size_t knn, Matrixi &indices, Matrix &distances) const
Definition: knncpp.h:426
BruteForce()=default
Index dimension() const
Definition: knncpp.h:481
knncpp::Matrixi Matrixi
Definition: knncpp.h:363
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Definition: knncpp.h:362
void setTakeRoot(const bool takeRoot)
Definition: knncpp.h:395
void setDistance(const Distance &distance)
Definition: knncpp.h:984
void query(const Eigen::MatrixBase< Derived > &queryPoints, const size_t knn, Matrixi &indices, Matrix &distances) const
Definition: knncpp.h:1029
KDTreeMinkowski(const DataMatrix &data, const bool copy=false)
Definition: knncpp.h:926
void setThreads(const unsigned int threads)
Definition: knncpp.h:962
void setData(const DataMatrix &data, const bool copy=false)
Definition: knncpp.h:974
void setBucketSize(const Index bucketSize)
Definition: knncpp.h:933
Index dimension() const
Definition: knncpp.h:1096
_Distance Distance
Definition: knncpp.h:502
knncpp::Matrixi Matrixi
Definition: knncpp.h:506
Index depth() const
Definition: knncpp.h:1100
void setTakeRoot(const bool takeRoot)
Definition: knncpp.h:950
void setBalanced(const bool balanced)
Definition: knncpp.h:943
Eigen::Matrix< Scalar, _Dimension, 1 > DataVector
Definition: knncpp.h:505
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > Matrix
Definition: knncpp.h:503
void setMaxDistance(const Scalar maxDist)
Definition: knncpp.h:968
void setCompact(const bool compact)
Definition: knncpp.h:956
void setSorted(const bool sorted)
Definition: knncpp.h:938
Eigen::Matrix< Scalar, _Dimension, Eigen::Dynamic > DataMatrix
Definition: knncpp.h:504
Index size() const
Definition: knncpp.h:1092
void query(const Eigen::MatrixBase< Derived > &queryPoints, const size_t knn, Matrixi &indices, Matrix &distances) const
Definition: knncpp.h:1247
MultiIndexHashing(const Matrix &data, const bool copy=false)
Definition: knncpp.h:1173
void setThreads(const unsigned int threads)
Definition: knncpp.h:1193
void setMaxDistance(const Scalar maxDist)
Definition: knncpp.h:1182
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Definition: knncpp.h:1124
knncpp::Matrixi Matrixi
Definition: knncpp.h:1125
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > Matrix
Definition: knncpp.h:1121
Index dimension() const
Definition: knncpp.h:1315
Index size() const
Definition: knncpp.h:1311
void setData(const Matrix &data, const bool copy=false)
Definition: knncpp.h:1202
void setSorted(const bool sorted)
Definition: knncpp.h:1187
void setSubstringLength(const Index len)
Definition: knncpp.h:1197
size_t size() const
Definition: knncpp.h:338
bool full() const
Definition: knncpp.h:330
Scalar front() const
Definition: knncpp.h:321
void push(const Index idx, const Scalar dist)
Definition: knncpp.h:271
QueryHeap(Index *indices, Scalar *distances, const size_t maxSize)
Definition: knncpp.h:262
bool empty() const
Definition: knncpp.h:334
void clear()
Definition: knncpp.h:341
__host__ __device__ float length(float2 v)
Definition: cutil_math.h:1162
int min(int a, int b)
Definition: cutil_math.h:53
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
int max(int a, int b)
Definition: cutil_math.h:48
static double dist(double x1, double y1, double x2, double y2)
Definition: lsd.c:207
#define XOR(a, b)
Definition: macros.h:29
BoundingBoxTpl< PointCoordinateType > BoundingBox
Default bounding-box type.
Definition: BoundingBox.h:304
std::map< std::string, any > IndexParams
Definition: params.h:51
Definition: knncpp.h:21
Eigen::Matrix< Index, 3, 1 > Vector3i
Definition: knncpp.h:30
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > Matrixd
Definition: knncpp.h:41
Eigen::Matrix< Index, 5, 1 > Vector5i
Definition: knncpp.h:32
Eigen::Matrix< Index, 4, 4 > Matrix4i
Definition: knncpp.h:37
Eigen::Matrix< Index, 4, 1 > Vector4i
Definition: knncpp.h:31
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic > Matrixf
Definition: knncpp.h:40
Eigen::Matrix< Index, 2, 1 > Vector2i
Definition: knncpp.h:29
Eigen::Matrix< Index, 3, 3 > Matrix3i
Definition: knncpp.h:36
Eigen::MatrixXd::Index Index
Definition: knncpp.h:26
Eigen::Matrix< Index, 2, 2 > Matrix2i
Definition: knncpp.h:35
Eigen::Matrix< Index, Eigen::Dynamic, Eigen::Dynamic > Matrixi
Definition: knncpp.h:34
Eigen::Matrix< Index, Eigen::Dynamic, 1 > Vectori
Definition: knncpp.h:28
Eigen::Matrix< Index, 5, 5 > Matrix5i
Definition: knncpp.h:38
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
struct Index Index
Definition: sqlite3.c:14646
Scalar operator()(const Eigen::MatrixBase< DerivedA > &lhs, const Eigen::MatrixBase< DerivedB > &rhs) const
Definition: knncpp.h:167
Scalar operator()(const Scalar lhs, const Scalar rhs) const
Definition: knncpp.h:182
Scalar operator()(const Scalar val) const
Definition: knncpp.h:188
Scalar operator()(const Scalar lhs, const Scalar rhs) const
Definition: knncpp.h:104
Scalar operator()(const Eigen::MatrixBase< DerivedA > &lhs, const Eigen::MatrixBase< DerivedB > &rhs) const
Definition: knncpp.h:89
Scalar operator()(const Scalar val) const
Definition: knncpp.h:111
Scalar operator()(const Scalar lhs) const
Definition: knncpp.h:208
Scalar operator()(const Scalar lhs, const Scalar rhs) const
Definition: knncpp.h:202
Scalar operator()(const Scalar value) const
Definition: knncpp.h:247
Scalar operator()(const Eigen::MatrixBase< DerivedA > &lhs, const Eigen::MatrixBase< DerivedB > &rhs) const
Definition: knncpp.h:224
Scalar operator()(const Scalar lhs, const Scalar rhs) const
Definition: knncpp.h:239
Scalar operator()(const Scalar val) const
Definition: knncpp.h:77
Scalar operator()(const Eigen::MatrixBase< DerivedA > &lhs, const Eigen::MatrixBase< DerivedB > &rhs) const
Definition: knncpp.h:56
Scalar operator()(const Scalar lhs, const Scalar rhs) const
Definition: knncpp.h:71
Scalar operator()(const Scalar val) const
Definition: knncpp.h:120
Scalar operator()(const Eigen::MatrixBase< DerivedA > &lhs, const Eigen::MatrixBase< DerivedB > &rhs) const
Definition: knncpp.h:131
Scalar operator()(const Scalar val) const
Definition: knncpp.h:153
Scalar operator()(const Scalar lhs, const Scalar rhs) const
Definition: knncpp.h:146