/*********************************************************************** * 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 OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_ #define OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_ #include #include #include #include #include "general.h" #include "nn_index.h" #include "matrix.h" #include "result_set.h" #include "heap.h" #include "allocator.h" #include "random.h" #include "saving.h" namespace cvflann { struct KDTreeSingleIndexParams : public IndexParams { KDTreeSingleIndexParams(int leaf_max_size = 10, bool reorder = true, int dim = -1) { (*this)["algorithm"] = FLANN_INDEX_KDTREE_SINGLE; (*this)["leaf_max_size"] = leaf_max_size; (*this)["reorder"] = reorder; (*this)["dim"] = dim; } }; /** * 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 { public: typedef typename Distance::ElementType ElementType; typedef typename Distance::ResultType DistanceType; /** * KDTree constructor * * Params: * inputData = dataset with the input features * params = parameters passed to the kdtree algorithm */ KDTreeSingleIndex(const Matrix& inputData, const IndexParams& params = KDTreeSingleIndexParams(), Distance d = Distance() ) : dataset_(inputData), index_params_(params), distance_(d) { size_ = dataset_.rows; dim_ = dataset_.cols; root_node_ = 0; int dim_param = get_param(params,"dim",-1); if (dim_param>0) dim_ = dim_param; leaf_max_size_ = get_param(params,"leaf_max_size",10); reorder_ = get_param(params,"reorder",true); // Create a permutable array of indices to the input vectors. vind_.resize(size_); for (size_t i = 0; i < size_; i++) { vind_[i] = (int)i; } } KDTreeSingleIndex(const KDTreeSingleIndex&); KDTreeSingleIndex& operator=(const KDTreeSingleIndex&); /** * Standard destructor */ ~KDTreeSingleIndex() { if (reorder_) delete[] data_.data; } /** * Builds the index */ void buildIndex() { computeBoundingBox(root_bbox_); root_node_ = divideTree(0, (int)size_, root_bbox_ ); // construct the tree if (reorder_) { delete[] data_.data; data_ = cvflann::Matrix(new ElementType[size_*dim_], size_, dim_); for (size_t i=0; i& queries, Matrix& indices, Matrix& dists, int knn, const SearchParams& params) { assert(queries.cols == veclen()); assert(indices.rows >= queries.rows); assert(dists.rows >= queries.rows); assert(int(indices.cols) >= knn); assert(int(dists.cols) >= knn); KNNSimpleResultSet resultSet(knn); for (size_t i = 0; i < queries.rows; i++) { resultSet.init(indices[i], dists[i]); findNeighbors(resultSet, queries[i], params); } } IndexParams getParameters() const { return index_params_; } /** * Find set of nearest neighbors to vec. Their indices are stored inside * the result object. * * Params: * result = the result object in which the indices of the nearest-neighbors are stored * vec = the vector for which to search the nearest neighbors * maxCheck = the maximum number of restarts (in a best-bin-first manner) */ void findNeighbors(ResultSet& result, const ElementType* vec, const SearchParams& searchParams) { float epsError = 1+get_param(searchParams,"eps",0.0f); std::vector dists(dim_,0); DistanceType distsq = computeInitialDistances(vec, dists); searchLevel(result, vec, root_node_, distsq, dists, epsError); } private: /*--------------------- Internal Data Structures --------------------------*/ struct Node { /** * Indices of points in leaf node */ int left, right; /** * 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 { DistanceType low, high; }; typedef std::vector BoundingBox; typedef BranchStruct BranchSt; typedef BranchSt* Branch; 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 = (DistanceType)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=(DistanceType)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 (lim2max_span) { max_span = span; } } DistanceType max_spread = -1; cutfeat = 0; for (size_t i=0; i(DistanceType)((1-EPS)*max_span)) { ElementType min_elem, max_elem; computeMinMax(ind, count, cutfeat, min_elem, max_elem); DistanceType spread = (DistanceType)(max_elem-min_elem); if (spread>max_spread) { cutfeat = (int)i; max_spread = spread; } } } // split in the middle DistanceType split_val = (bbox[cutfeat].low+bbox[cutfeat].high)/2; ElementType min_elem, max_elem; computeMinMax(ind, count, cutfeat, min_elem, max_elem); if (split_valmax_elem) cutval = (DistanceType)max_elem; else cutval = split_val; 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, (int)i); distsq += dists[i]; } if (vec[i] > root_bbox_[i].high) { dists[i] = distance_.accum_dist(vec[i], root_bbox_[i].high, (int)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)) { 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; } private: /** * The dataset used by this index */ const Matrix dataset_; IndexParams index_params_; int leaf_max_size_; bool reorder_; /** * Array of indices to vectors in the dataset. */ std::vector vind_; Matrix data_; size_t size_; size_t dim_; /** * Array of k-d trees used to find neighbours. */ NodePtr root_node_; 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_; Distance distance_; }; // class KDTree } #endif //OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_