File: kdtree.h

package info (click to toggle)
meshlab 2020.09%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 45,132 kB
  • sloc: cpp: 400,238; ansic: 31,952; javascript: 1,578; sh: 387; yacc: 238; lex: 139; python: 86; makefile: 30
file content (532 lines) | stat: -rwxr-xr-x 18,476 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2016                                           \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program is free software; you can redistribute it and/or modify      *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation; either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* This program is distributed in the hope that it will be useful,           *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/

#ifndef KDTREE_VCG_H
#define KDTREE_VCG_H

#include <vcg/space/point3.h>
#include <vcg/space/box3.h>
#include <vcg/space/index/kdtree/priorityqueue.h>

#include <vector>
#include <limits>
#include <iostream>
#include <cstdint>

namespace vcg {

  template<typename _DataType>
  class ConstDataWrapper
  {
  public:
    typedef _DataType DataType;
    inline ConstDataWrapper()
      : mpData(0), mStride(0), mSize(0)
    {}
    inline ConstDataWrapper(const DataType* pData, int size, int64_t stride = sizeof(DataType))
      : mpData(reinterpret_cast<const unsigned char*>(pData)), mStride(stride), mSize(size)
    {}
    inline const DataType& operator[] (int i) const
    {
      return *reinterpret_cast<const DataType*>(mpData + i*mStride);
    }
    inline size_t size() const { return mSize; }
  protected:
    const unsigned char* mpData;
    int64_t mStride;
    size_t mSize;
  };

  template<class StdVectorType>
  class VectorConstDataWrapper :public  ConstDataWrapper<typename StdVectorType::value_type>
  {
  public:
    inline VectorConstDataWrapper(StdVectorType &vec) :
      ConstDataWrapper<typename StdVectorType::value_type>(&(vec[0]), vec.size(), sizeof(typename StdVectorType::value_type))
    {}
  };

  template<class MeshType>
  class VertexConstDataWrapper :public  ConstDataWrapper<typename MeshType::CoordType>
  {
  public:
    inline VertexConstDataWrapper(MeshType &m) :
      ConstDataWrapper<typename MeshType::CoordType>(&(m.vert[0].P()), m.vert.size(), sizeof(typename MeshType::VertexType))
    {}
  };

  /**
  * This class allows to create a Kd-Tree thought to perform the neighbour query (radius search, knn-nearest serach and closest search).
  * The class implemetantion is thread-safe.
  */
  template<typename _Scalar>
  class KdTree
  {
  public:

    typedef _Scalar Scalar;
    typedef vcg::Point3<Scalar> VectorType;
    typedef vcg::Box3<Scalar> AxisAlignedBoxType;

    typedef HeapMaxPriorityQueue<int, Scalar> PriorityQueue;

    struct Node
    {
      union {
        //standard node
        struct {
          Scalar splitValue;
          unsigned int firstChildId : 24;
          unsigned int dim : 2;
          unsigned int leaf : 1;
        };
        //leaf
        struct {
          unsigned int start;
          unsigned short size;
        };
      };
    };
    typedef std::vector<Node> NodeList;

    // return the protected members which store the nodes and the points list
    inline const NodeList& _getNodes(void) { return mNodes; }
    inline const std::vector<VectorType>& _getPoints(void) { return mPoints; }
    inline unsigned int _getNumLevel(void) { return numLevel; }
    inline const AxisAlignedBoxType& _getAABBox(void) { return mAABB; }

  public:

    KdTree(const ConstDataWrapper<VectorType>& points, unsigned int nofPointsPerCell = 16, unsigned int maxDepth = 64, bool balanced = false);

    ~KdTree();

    void doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue);

    void doQueryDist(const VectorType& queryPoint, float dist, std::vector<unsigned int>& points, std::vector<Scalar>& sqrareDists);

    void doQueryClosest(const VectorType& queryPoint, unsigned int& index, Scalar& dist);

  protected:

    // element of the stack
    struct QueryNode
    {
      QueryNode() {}
      QueryNode(unsigned int id) : nodeId(id) {}
      unsigned int nodeId;  // id of the next node
      Scalar sq;            // squared distance to the next node
    };

    // used to build the tree: split the subset [start..end[ according to dim and splitValue,
    // and returns the index of the first element of the second subset
    unsigned int split(int start, int end, unsigned int dim, float splitValue);

    int createTree(unsigned int nodeId, unsigned int start, unsigned int end, unsigned int level);

  protected:

    AxisAlignedBoxType mAABB; //BoundingBox
    NodeList mNodes; //kd-tree nodes
    std::vector<VectorType> mPoints; //points read from the input DataWrapper
    std::vector<unsigned int> mIndices; //points indices
    unsigned int targetCellSize; //min number of point in a leaf
    unsigned int targetMaxDepth; //max tree depth
    unsigned int numLevel; //actual tree depth
    bool isBalanced; //true if the tree is balanced
  };


  template<typename Scalar>
  KdTree<Scalar>::KdTree(const ConstDataWrapper<VectorType>& points, unsigned int nofPointsPerCell, unsigned int maxDepth, bool balanced)
    : mPoints(points.size()), mIndices(points.size())
  {
    // compute the AABB of the input
    mPoints[0] = points[0];
    mAABB.Set(mPoints[0]);
    for (unsigned int i = 1; i < mPoints.size(); ++i)
    {
      mPoints[i] = points[i];
      mIndices[i] = i;
      mAABB.Add(mPoints[i]);
    }

    targetMaxDepth = maxDepth;
    targetCellSize = nofPointsPerCell;
    isBalanced = balanced;
    //mNodes.reserve(4 * mPoints.size() / nofPointsPerCell);
    //first node inserted (no leaf). The others are made by the createTree function (recursively)
    mNodes.resize(1);
    mNodes.back().leaf = 0;
    numLevel = createTree(0, 0, mPoints.size(), 1);
  }

  template<typename Scalar>
  KdTree<Scalar>::~KdTree()
  {
  }


  /** Performs the kNN query.
  *
  * This algorithm uses the simple distance to the split plane to prune nodes.
  * A more elaborated approach consists to track the closest corner of the cell
  * relatively to the current query point. This strategy allows to save about 5%
  * of the leaves. However, in practice the slight overhead due to this tracking
  * reduces the overall performance.
  *
  * This algorithm also use a simple stack while a priority queue using the squared
  * distances to the cells as a priority values allows to save about 10% of the leaves.
  * But, again, priority queue insertions and deletions are quite involved, and therefore
  * a simple stack is by far much faster.
  *
  * The result of the query, the k-nearest neighbors, are stored into the stack mNeighborQueue, where the
  * topmost element [0] is NOT the nearest but the farthest!! (they are not sorted but arranged into a heap).
  */
  template<typename Scalar>
  void KdTree<Scalar>::doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue)
  {
    mNeighborQueue.setMaxSize(k);
    mNeighborQueue.init();

    std::vector<QueryNode> mNodeStack(numLevel + 1);
    mNodeStack[0].nodeId = 0;
    mNodeStack[0].sq = 0.f;
    unsigned int count = 1;

    while (count)
    {
      //we select the last node (AABB) inserted in the stack
      QueryNode& qnode = mNodeStack[count - 1];

      //while going down the tree qnode.nodeId is the nearest sub-tree, otherwise,
      //in backtracking, qnode.nodeId is the other sub-tree that will be visited iff
      //the actual nearest node is further than the split distance.
      Node& node = mNodes[qnode.nodeId];

      //if the distance is less than the top of the max-heap, it could be one of the k-nearest neighbours
      if (mNeighborQueue.getNofElements() < k || qnode.sq < mNeighborQueue.getTopWeight())
      {
        //when we arrive to a leaf
        if (node.leaf)
        {
          --count; //pop of the leaf

          //end is the index of the last element of the leaf in mPoints
          unsigned int end = node.start + node.size;
          //adding the element of the leaf to the heap
          for (unsigned int i = node.start; i < end; ++i)
            mNeighborQueue.insert(mIndices[i], vcg::SquaredNorm(queryPoint - mPoints[i]));
        }
        //otherwise, if we're not on a leaf
        else
        {
          // the new offset is the distance between the searched point and the actual split coordinate
          float new_off = queryPoint[node.dim] - node.splitValue;

          //left sub-tree
          if (new_off < 0.)
          {
            mNodeStack[count].nodeId = node.firstChildId;
            //in the father's nodeId we save the index of the other sub-tree (for backtracking)
            qnode.nodeId = node.firstChildId + 1;
          }
          //right sub-tree (same as above)
          else
          {
            mNodeStack[count].nodeId = node.firstChildId + 1;
            qnode.nodeId = node.firstChildId;
          }
          //distance is inherited from the father (while descending the tree it's equal to 0)
          mNodeStack[count].sq = qnode.sq;
          //distance of the father is the squared distance from the split plane
          qnode.sq = new_off*new_off;
          ++count;
        }
      }
      else
      {
        // pop
        --count;
      }
    }
  }


  /** Performs the distance query.
  *
  * The result of the query, all the points within the distance dist form the query point, is the vector of the indeces
  * and the vector of the squared distances from the query point.
  */
  template<typename Scalar>
  void KdTree<Scalar>::doQueryDist(const VectorType& queryPoint, float dist, std::vector<unsigned int>& points, std::vector<Scalar>& sqrareDists)
  {
    std::vector<QueryNode> mNodeStack(numLevel + 1);
    mNodeStack[0].nodeId = 0;
    mNodeStack[0].sq = 0.f;
    unsigned int count = 1;

    float sqrareDist = dist*dist;
    while (count)
    {
      QueryNode& qnode = mNodeStack[count - 1];
      Node   & node = mNodes[qnode.nodeId];

      if (qnode.sq < sqrareDist)
      {
        if (node.leaf)
        {
          --count; // pop
          unsigned int end = node.start + node.size;
          for (unsigned int i = node.start; i < end; ++i)
          {
            float pointSquareDist = vcg::SquaredNorm(queryPoint - mPoints[i]);
            if (pointSquareDist < sqrareDist)
            {
              points.push_back(mIndices[i]);
              sqrareDists.push_back(pointSquareDist);
            }
          }
        }
        else
        {
          // replace the stack top by the farthest and push the closest
          float new_off = queryPoint[node.dim] - node.splitValue;
          if (new_off < 0.)
          {
            mNodeStack[count].nodeId = node.firstChildId;
            qnode.nodeId = node.firstChildId + 1;
          }
          else
          {
            mNodeStack[count].nodeId = node.firstChildId + 1;
            qnode.nodeId = node.firstChildId;
          }
          mNodeStack[count].sq = qnode.sq;
          qnode.sq = new_off*new_off;
          ++count;
        }
      }
      else
      {
        // pop
        --count;
      }
    }
  }


  /** Searchs the closest point.
  *
  * The result of the query, the closest point to the query point, is the index of the point and
  * and the squared distance from the query point.
  */
  template<typename Scalar>
  void KdTree<Scalar>::doQueryClosest(const VectorType& queryPoint, unsigned int& index, Scalar& dist)
  {
    std::vector<QueryNode> mNodeStack(numLevel + 1);
    mNodeStack[0].nodeId = 0;
    mNodeStack[0].sq = 0.f;
    unsigned int count = 1;

    int minIndex = mIndices.size() / 2;
    Scalar minDist = vcg::SquaredNorm(queryPoint - mPoints[minIndex]);
    minIndex = mIndices[minIndex];

    while (count)
    {
      QueryNode& qnode = mNodeStack[count - 1];
      Node   & node = mNodes[qnode.nodeId];

      if (qnode.sq < minDist)
      {
        if (node.leaf)
        {
          --count; // pop
          unsigned int end = node.start + node.size;
          for (unsigned int i = node.start; i < end; ++i)
          {
            float pointSquareDist = vcg::SquaredNorm(queryPoint - mPoints[i]);
            if (pointSquareDist < minDist)
            {
              minDist = pointSquareDist;
              minIndex = mIndices[i];
            }
          }
        }
        else
        {
          // replace the stack top by the farthest and push the closest
          float new_off = queryPoint[node.dim] - node.splitValue;
          if (new_off < 0.)
          {
            mNodeStack[count].nodeId = node.firstChildId;
            qnode.nodeId = node.firstChildId + 1;
          }
          else
          {
            mNodeStack[count].nodeId = node.firstChildId + 1;
            qnode.nodeId = node.firstChildId;
          }
          mNodeStack[count].sq = qnode.sq;
          qnode.sq = new_off*new_off;
          ++count;
        }
      }
      else
      {
        // pop
        --count;
      }
    }
    index = minIndex;
    dist = minDist;
  }



  /**
  * Split the subarray between start and end in two part, one with the elements less than splitValue,
  * the other with the elements greater or equal than splitValue. The elements are compared
  * using the "dim" coordinate [0 = x, 1 = y, 2 = z].
  */
  template<typename Scalar>
  unsigned int KdTree<Scalar>::split(int start, int end, unsigned int dim, float splitValue)
  {
    int l(start), r(end - 1);
    for (; l < r; ++l, --r)
    {
      while (l < end && mPoints[l][dim] < splitValue)
        l++;
      while (r >= start && mPoints[r][dim] >= splitValue)
        r--;
      if (l > r)
        break;
      std::swap(mPoints[l], mPoints[r]);
      std::swap(mIndices[l], mIndices[r]);
    }
    //returns the index of the first element on the second part
    return (mPoints[l][dim] < splitValue ? l + 1 : l);
  }

  /** recursively builds the kdtree
  *
  *  The heuristic is the following:
  *   - if the number of points in the node is lower than targetCellsize then make a leaf
  *   - else compute the AABB of the points of the node and split it at the middle of
  *     the largest AABB dimension.
  *
  *  This strategy might look not optimal because it does not explicitly prune empty space,
  *  unlike more advanced SAH-like techniques used for RT. On the other hand it leads to a shorter tree,
  *  faster to traverse and our experience shown that in the special case of kNN queries,
  *  this strategy is indeed more efficient (and much faster to build). Moreover, for volume data
  *  (e.g., fluid simulation) pruning the empty space is useless.
  *
  *  Actually, storing at each node the exact AABB (we therefore have a binary BVH) allows
  *  to prune only about 10% of the leaves, but the overhead of this pruning (ball/ABBB intersection)
  *  is more expensive than the gain it provides and the memory consumption is x4 higher !
  */
  template<typename Scalar>
  int KdTree<Scalar>::createTree(unsigned int nodeId, unsigned int start, unsigned int end, unsigned int level)
  {
    //select the first node
    Node& node = mNodes[nodeId];
    AxisAlignedBoxType aabb;

    //putting all the points in the bounding box
    aabb.Set(mPoints[start]);
    for (unsigned int i = start + 1; i < end; ++i)
      aabb.Add(mPoints[i]);

    //bounding box diagonal
    VectorType diag = aabb.max - aabb.min;

    //the split "dim" is the dimension of the box with the biggest value
    unsigned int dim;
    if (diag.X() > diag.Y())
      dim = diag.X() > diag.Z() ? 0 : 2;
    else
      dim = diag.Y() > diag.Z() ? 1 : 2;

    node.dim = dim;
    if (isBalanced) //we divide the points using the median value along the "dim" dimension
    {
      std::vector<Scalar> tempVector;
      for (unsigned int i = start + 1; i < end; ++i)
        tempVector.push_back(mPoints[i][dim]);
      std::sort(tempVector.begin(), tempVector.end());
      node.splitValue = (tempVector[tempVector.size() / 2.0] + tempVector[tempVector.size() / 2.0 + 1]) / 2.0;
    }
    else //we divide the bounding box in 2 partitions, considering the average of the "dim" dimension
      node.splitValue = Scalar(0.5*(aabb.max[dim] + aabb.min[dim]));

    //midId is the index of the first element in the second partition
    unsigned int midId = split(start, end, dim, node.splitValue);

    node.firstChildId = mNodes.size();
    mNodes.resize(mNodes.size() + 2);
    bool flag = (midId == start) || (midId == end);
    int leftLevel, rightLevel;
    {
      // left child
      unsigned int childId = mNodes[nodeId].firstChildId;
      Node& child = mNodes[childId];
      if (flag || (midId - start) <= targetCellSize || level >= targetMaxDepth)
      {
        child.leaf = 1;
        child.start = start;
        child.size = midId - start;
        leftLevel = level;
      }
      else
      {
        child.leaf = 0;
        leftLevel = createTree(childId, start, midId, level + 1);
      }
    }

    {
      // right child
      unsigned int childId = mNodes[nodeId].firstChildId + 1;
      Node& child = mNodes[childId];
      if (flag || (end - midId) <= targetCellSize || level >= targetMaxDepth)
      {
        child.leaf = 1;
        child.start = midId;
        child.size = end - midId;
        rightLevel = level;
      }
      else
      {
        child.leaf = 0;
        rightLevel = createTree(childId, midId, end, level + 1);
      }
    }
    if (leftLevel > rightLevel)
      return leftLevel;
    return rightLevel;
  }

}

#endif