/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef KDTREESINGLE_H #define KDTREESINGLE_H #include #include #include #include #include "flann/general.h" #include "flann/algorithms/nn_index.h" #include "flann/util/matrix.h" #include "flann/util/result_set.h" #include "flann/util/heap.h" #include "flann/util/allocator.h" #include "flann/util/random.h" #include "flann/util/saving.h" namespace flann { struct KDTreeSingleIndexParams : public IndexParams { KDTreeSingleIndexParams(int leaf_max_size_ = 10, bool reorder_ = true, int dim_ = -1) : IndexParams(FLANN_INDEX_KDTREE_SINGLE), leaf_max_size(leaf_max_size_), reorder(reorder_), dim(dim_) {}; int leaf_max_size; bool reorder; int dim; flann_algorithm_t getIndexType() const { return algorithm; } void fromParameters(const FLANNParameters& p) { assert(p.algorithm==algorithm); } void toParameters(FLANNParameters& p) const { p.algorithm = algorithm; } void print() const { logger.info("Index type: %d\n",(int)algorithm); } }; /** * Randomized kd-tree index * * Contains the k-d trees and other information for indexing a set of points * for nearest-neighbor matching. */ template class KDTreeSingleIndex : public NNIndex { typedef typename Distance::ElementType ElementType; typedef typename Distance::ResultType DistanceType; /** * Array of indices to vectors in the dataset. */ std::vector vind; int leaf_max_size_; bool reorder_; /** * The dataset used by this index */ const Matrix dataset; Matrix data; const KDTreeSingleIndexParams index_params; size_t size_; size_t dim; /*--------------------- Internal Data Structures --------------------------*/ struct Node { union { struct { /** * Indices of points in leaf node */ int left, right; }; struct { /** * Dimension used for subdivision. */ int divfeat; /** * The values used for subdivision. */ DistanceType divlow, divhigh; }; }; /** * The child nodes. */ Node *child1, *child2; }; typedef Node* NodePtr; struct Interval { ElementType low, high; }; typedef std::vector BoundingBox; /** * Array of k-d trees used to find neighbours. */ NodePtr root_node; typedef BranchStruct BranchSt; typedef BranchSt* Branch; BoundingBox root_bbox; /** * Pooled memory allocator. * * Using a pooled memory allocator is more efficient * than allocating memory directly when there is a large * number small of memory allocations. */ PooledAllocator pool; public: Distance distance; int count_leaf; flann_algorithm_t getType() const { return FLANN_INDEX_KDTREE_SINGLE; } /** * KDTree constructor * * Params: * inputData = dataset with the input features * params = parameters passed to the kdtree algorithm */ KDTreeSingleIndex(const Matrix& inputData, const KDTreeSingleIndexParams& params = KDTreeSingleIndexParams(), Distance d = Distance() ) : dataset(inputData), index_params(params), distance(d) { size_ = dataset.rows; dim = dataset.cols; if (params.dim>0) dim = params.dim; leaf_max_size_ = params.leaf_max_size; reorder_ = params.reorder; // Create a permutable array of indices to the input vectors. vind.resize(size_); for (size_t i = 0; i < size_; i++) { vind[i] = i; } count_leaf = 0; } /** * Standard destructor */ ~KDTreeSingleIndex() { if (reorder_) data.free(); } /** * Builds the index */ void buildIndex() { computeBoundingBox(root_bbox); root_node = divideTree(0, size_, root_bbox ); // construct the tree if (reorder_) { data.free(); data = flann::Matrix(new ElementType[size_*dim], size_, dim); for (size_t i=0;i& result, const ElementType* vec, const SearchParams& searchParams) { float epsError = 1+searchParams.eps; std::vector dists(dim,0); DistanceType distsq = computeInitialDistances(vec, dists); searchLevel(result, vec, root_node, distsq, dists, epsError); } const IndexParams* getParameters() const { return &index_params; } private: void save_tree(FILE* stream, NodePtr tree) { save_value(stream, *tree); if (tree->child1!=NULL) { save_tree(stream, tree->child1); } if (tree->child2!=NULL) { save_tree(stream, tree->child2); } } void load_tree(FILE* stream, NodePtr& tree) { tree = pool.allocate(); load_value(stream, *tree); if (tree->child1!=NULL) { load_tree(stream, tree->child1); } if (tree->child2!=NULL) { load_tree(stream, tree->child2); } } void computeBoundingBox(BoundingBox& bbox) { bbox.resize(dim); for (size_t i=0;ibbox[i].high) bbox[i].high = dataset[k][i]; } } } /** * Create a tree node that subdivides the list of vecs from vind[first] * to vind[last]. The routine is called recursively on each sublist. * Place a pointer to this new tree node in the location pTree. * * Params: pTree = the new node to create * first = index of the first vector * last = index of the last vector */ NodePtr divideTree(int left, int right, BoundingBox& bbox) { NodePtr node = pool.allocate(); // allocate memory /* If too few exemplars remain, then make this a leaf node. */ if ( (right-left) <= leaf_max_size_) { node->child1 = node->child2 = NULL; /* Mark as leaf node. */ node->left = left; node->right = right; // compute bounding-box of leaf points for (size_t i=0;idataset[vind[k]][i]) bbox[i].low=dataset[vind[k]][i]; if (bbox[i].highdivfeat = cutfeat; BoundingBox left_bbox(bbox); left_bbox[cutfeat].high = cutval; node->child1 = divideTree(left, left+idx, left_bbox); BoundingBox right_bbox(bbox); right_bbox[cutfeat].low = cutval; node->child2 = divideTree(left+idx, right, right_bbox); node->divlow = left_bbox[cutfeat].high; node->divhigh = right_bbox[cutfeat].low; for (size_t i=0;imax_elem) max_elem = val; } } void middleSplit(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox) { // find the largest span from the approximate bounding box ElementType max_span = bbox[0].high-bbox[0].low; cutfeat = 0; cutval = (bbox[0].high+bbox[0].low)/2; for (size_t i=1;imax_span) { max_span = span; cutfeat = i; cutval = (bbox[i].high+bbox[i].low)/2; } } // compute exact span on the found dimension ElementType min_elem, max_elem; computeMinMax(ind, count, cutfeat, min_elem, max_elem); cutval = (min_elem+max_elem)/2; max_span = max_elem - min_elem; // check if a dimension of a largest span exists size_t k = cutfeat; for (size_t i=0;imax_span) { computeMinMax(ind, count, i, min_elem, max_elem); span = max_elem - min_elem; if (span>max_span) { max_span = span; cutfeat = i; cutval = (min_elem+max_elem)/2; } } } int lim1, lim2; planeSplit(ind, count, cutfeat, cutval, lim1, lim2); if (lim1>count/2) index = lim1; else if (lim2cutval */ void planeSplit(int* ind, int count, int cutfeat, DistanceType cutval, int& lim1, int& lim2) { /* Move vector indices for left subtree to front of list. */ int left = 0; int right = count-1; for (;;) { while (left<=right && dataset[ind[left]][cutfeat]=cutval) --right; if (left>right) break; std::swap(ind[left], ind[right]); ++left; --right; } /* If either list is empty, it means that all remaining features * are identical. Split in the middle to maintain a balanced tree. */ lim1 = left; right = count-1; for (;;) { while (left<=right && dataset[ind[left]][cutfeat]<=cutval) ++left; while (left<=right && dataset[ind[right]][cutfeat]>cutval) --right; if (left>right) break; std::swap(ind[left], ind[right]); ++left; --right; } lim2 = left; } DistanceType computeInitialDistances(const ElementType* vec, std::vector& dists) { DistanceType distsq = 0.0; for (size_t i = 0;i < dim;++i) { if (vec[i] < root_bbox[i].low) { dists[i] = distance.accum_dist(vec[i], root_bbox[i].low, i); distsq += dists[i]; } if (vec[i] > root_bbox[i].high) { dists[i] = distance.accum_dist(vec[i], root_bbox[i].high, i); distsq += dists[i]; } } return distsq; } /** * Performs an exact search in the tree starting from a node. */ void searchLevel(ResultSet& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq, std::vector& dists, const float epsError) { /* If this is a leaf node, then do check and return. */ if (node->child1 == NULL && node->child2 == NULL) { count_leaf += (node->right-node->left); DistanceType worst_dist = result_set.worstDist(); for (int i=node->left;iright;++i) { int index = reorder_ ? i : vind[i]; DistanceType dist = distance(vec, data[index], dim, worst_dist); if (distdivfeat; ElementType val = vec[idx]; DistanceType diff1 = val - node->divlow; DistanceType diff2 = val - node->divhigh; NodePtr bestChild; NodePtr otherChild; DistanceType cut_dist; if ((diff1+diff2)<0) { bestChild = node->child1; otherChild = node->child2; cut_dist = distance.accum_dist(val, node->divhigh, idx); } else { bestChild = node->child2; otherChild = node->child1; cut_dist = distance.accum_dist( val, node->divlow, idx); } /* Call recursively to search next level down. */ searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError); DistanceType dst = dists[idx]; mindistsq = mindistsq + cut_dist - dst; dists[idx] = cut_dist; if (mindistsq*epsError<=result_set.worstDist()) { searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError); } dists[idx] = dst; } }; // class KDTree } #endif //KDTREESINGLE_H