/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2008, Xavier Delacour, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's 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. // // * The name of Intel Corporation may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. // //M*/ // 2008-05-13, Xavier Delacour <xavier.delacour@gmail.com> #ifndef __cv_kdtree_h__ #define __cv_kdtree_h__ #include "_cv.h" #include <vector> #include <algorithm> #include <limits> #include <iostream> #include "assert.h" #include "math.h" // J.S. Beis and D.G. Lowe. Shape indexing using approximate nearest-neighbor search in highdimensional spaces. In Proc. IEEE Conf. Comp. Vision Patt. Recog., pages 1000--1006, 1997. http://citeseer.ist.psu.edu/beis97shape.html #undef __deref #undef __valuetype template < class __valuetype, class __deref > class CvKDTree { public: typedef __deref deref_type; typedef typename __deref::scalar_type scalar_type; typedef typename __deref::accum_type accum_type; private: struct node { int dim; // split dimension; >=0 for nodes, -1 for leaves __valuetype value; // if leaf, value of leaf int left, right; // node indices of left and right branches scalar_type boundary; // left if deref(value,dim)<=boundary, otherwise right }; typedef std::vector < node > node_array; __deref deref; // requires operator() (__valuetype lhs,int dim) node_array nodes; // node storage int point_dim; // dimension of points (the k in kd-tree) int root_node; // index of root node, -1 if empty tree // for given set of point indices, compute dimension of highest variance template < class __instype, class __valuector > int dimension_of_highest_variance(__instype * first, __instype * last, __valuector ctor) { assert(last - first > 0); accum_type maxvar = -std::numeric_limits < accum_type >::max(); int maxj = -1; for (int j = 0; j < point_dim; ++j) { accum_type mean = 0; for (__instype * k = first; k < last; ++k) mean += deref(ctor(*k), j); mean /= last - first; accum_type var = 0; for (__instype * k = first; k < last; ++k) { accum_type diff = accum_type(deref(ctor(*k), j)) - mean; var += diff * diff; } var /= last - first; assert(maxj != -1 || var >= maxvar); if (var >= maxvar) { maxvar = var; maxj = j; } } return maxj; } // given point indices and dimension, find index of median; (almost) modifies [first,last) // such that points_in[first,median]<=point[median], points_in(median,last)>point[median]. // implemented as partial quicksort; expected linear perf. template < class __instype, class __valuector > __instype * median_partition(__instype * first, __instype * last, int dim, __valuector ctor) { assert(last - first > 0); __instype *k = first + (last - first) / 2; median_partition(first, last, k, dim, ctor); return k; } template < class __instype, class __valuector > struct median_pr { const __instype & pivot; int dim; __deref deref; __valuector ctor; median_pr(const __instype & _pivot, int _dim, __deref _deref, __valuector _ctor) : pivot(_pivot), dim(_dim), deref(_deref), ctor(_ctor) { } bool operator() (const __instype & lhs) const { return deref(ctor(lhs), dim) <= deref(ctor(pivot), dim); } }; template < class __instype, class __valuector > void median_partition(__instype * first, __instype * last, __instype * k, int dim, __valuector ctor) { int pivot = (last - first) / 2; std::swap(first[pivot], last[-1]); __instype *middle = std::partition(first, last - 1, median_pr < __instype, __valuector > (last[-1], dim, deref, ctor)); std::swap(*middle, last[-1]); if (middle < k) median_partition(middle + 1, last, k, dim, ctor); else if (middle > k) median_partition(first, middle, k, dim, ctor); } // insert given points into the tree; return created node template < class __instype, class __valuector > int insert(__instype * first, __instype * last, __valuector ctor) { if (first == last) return -1; else { int dim = dimension_of_highest_variance(first, last, ctor); __instype *median = median_partition(first, last, dim, ctor); __instype *split = median; for (; split != last && deref(ctor(*split), dim) == deref(ctor(*median), dim); ++split); if (split == last) { // leaf int nexti = -1; for (--split; split >= first; --split) { int i = nodes.size(); node & n = *nodes.insert(nodes.end(), node()); n.dim = -1; n.value = ctor(*split); n.left = -1; n.right = nexti; nexti = i; } return nexti; } else { // node int i = nodes.size(); // note that recursive insert may invalidate this ref node & n = *nodes.insert(nodes.end(), node()); n.dim = dim; n.boundary = deref(ctor(*median), dim); int left = insert(first, split, ctor); nodes[i].left = left; int right = insert(split, last, ctor); nodes[i].right = right; return i; } } } // run to leaf; linear search for p; // if found, remove paths to empty leaves on unwind bool remove(int *i, const __valuetype & p) { if (*i == -1) return false; node & n = nodes[*i]; bool r; if (n.dim >= 0) { // node if (deref(p, n.dim) <= n.boundary) // left r = remove(&n.left, p); else // right r = remove(&n.right, p); // if terminal, remove this node if (n.left == -1 && n.right == -1) *i = -1; return r; } else { // leaf if (n.value == p) { *i = n.right; return true; } else return remove(&n.right, p); } } public: struct identity_ctor { const __valuetype & operator() (const __valuetype & rhs) const { return rhs; } }; // initialize an empty tree CvKDTree(__deref _deref = __deref()) : deref(_deref), root_node(-1) { } // given points, initialize a balanced tree CvKDTree(__valuetype * first, __valuetype * last, int _point_dim, __deref _deref = __deref()) : deref(_deref) { set_data(first, last, _point_dim, identity_ctor()); } // given points, initialize a balanced tree template < class __instype, class __valuector > CvKDTree(__instype * first, __instype * last, int _point_dim, __valuector ctor, __deref _deref = __deref()) : deref(_deref) { set_data(first, last, _point_dim, ctor); } void set_deref(__deref _deref) { deref = _deref; } void set_data(__valuetype * first, __valuetype * last, int _point_dim) { set_data(first, last, _point_dim, identity_ctor()); } template < class __instype, class __valuector > void set_data(__instype * first, __instype * last, int _point_dim, __valuector ctor) { point_dim = _point_dim; nodes.clear(); nodes.reserve(last - first); root_node = insert(first, last, ctor); } int dims() const { return point_dim; } // remove the given point bool remove(const __valuetype & p) { return remove(&root_node, p); } void print() const { print(root_node); } void print(int i, int indent = 0) const { if (i == -1) return; for (int j = 0; j < indent; ++j) std::cout << " "; const node & n = nodes[i]; if (n.dim >= 0) { std::cout << "node " << i << ", left " << nodes[i].left << ", right " << nodes[i].right << ", dim " << nodes[i].dim << ", boundary " << nodes[i].boundary << std::endl; print(n.left, indent + 3); print(n.right, indent + 3); } else std::cout << "leaf " << i << ", value = " << nodes[i].value << std::endl; } //////////////////////////////////////////////////////////////////////////////////////// // bbf search public: struct bbf_nn { // info on found neighbors (approx k nearest) const __valuetype *p; // nearest neighbor accum_type dist; // distance from d to query point bbf_nn(const __valuetype & _p, accum_type _dist) : p(&_p), dist(_dist) { } bool operator<(const bbf_nn & rhs) const { return dist < rhs.dist; } }; typedef std::vector < bbf_nn > bbf_nn_pqueue; private: struct bbf_node { // info on branches not taken int node; // corresponding node accum_type dist; // minimum distance from bounds to query point bbf_node(int _node, accum_type _dist) : node(_node), dist(_dist) { } bool operator<(const bbf_node & rhs) const { return dist > rhs.dist; } }; typedef std::vector < bbf_node > bbf_pqueue; mutable bbf_pqueue tmp_pq; // called for branches not taken, as bbf walks to leaf; // construct bbf_node given minimum distance to bounds of alternate branch void pq_alternate(int alt_n, bbf_pqueue & pq, scalar_type dist) const { if (alt_n == -1) return; // add bbf_node for alternate branch in priority queue pq.push_back(bbf_node(alt_n, dist)); push_heap(pq.begin(), pq.end()); } // called by bbf to walk to leaf; // takes one step down the tree towards query point d template < class __desctype > int bbf_branch(int i, const __desctype * d, bbf_pqueue & pq) const { const node & n = nodes[i]; // push bbf_node with bounds of alternate branch, then branch if (d[n.dim] <= n.boundary) { // left pq_alternate(n.right, pq, n.boundary - d[n.dim]); return n.left; } else { // right pq_alternate(n.left, pq, d[n.dim] - n.boundary); return n.right; } } // compute euclidean distance between two points template < class __desctype > accum_type distance(const __desctype * d, const __valuetype & p) const { accum_type dist = 0; for (int j = 0; j < point_dim; ++j) { accum_type diff = accum_type(d[j]) - accum_type(deref(p, j)); dist += diff * diff; } return (accum_type) sqrt(dist); } // called per candidate nearest neighbor; constructs new bbf_nn for // candidate and adds it to priority queue of all candidates; if // queue len exceeds k, drops the point furthest from query point d. template < class __desctype > void bbf_new_nn(bbf_nn_pqueue & nn_pq, int k, const __desctype * d, const __valuetype & p) const { bbf_nn nn(p, distance(d, p)); if ((int) nn_pq.size() < k) { nn_pq.push_back(nn); push_heap(nn_pq.begin(), nn_pq.end()); } else if (nn_pq[0].dist > nn.dist) { pop_heap(nn_pq.begin(), nn_pq.end()); nn_pq.end()[-1] = nn; push_heap(nn_pq.begin(), nn_pq.end()); } assert(nn_pq.size() < 2 || nn_pq[0].dist >= nn_pq[1].dist); } public: // finds (with high probability) the k nearest neighbors of d, // searching at most emax leaves/bins. // ret_nn_pq is an array containing the (at most) k nearest neighbors // (see bbf_nn structure def above). template < class __desctype > int find_nn_bbf(const __desctype * d, int k, int emax, bbf_nn_pqueue & ret_nn_pq) const { assert(k > 0); ret_nn_pq.clear(); if (root_node == -1) return 0; // add root_node to bbf_node priority queue; // iterate while queue non-empty and emax>0 tmp_pq.clear(); tmp_pq.push_back(bbf_node(root_node, 0)); while (tmp_pq.size() && emax > 0) { // from node nearest query point d, run to leaf pop_heap(tmp_pq.begin(), tmp_pq.end()); bbf_node bbf(tmp_pq.end()[-1]); tmp_pq.erase(tmp_pq.end() - 1); int i; for (i = bbf.node; i != -1 && nodes[i].dim >= 0; i = bbf_branch(i, d, tmp_pq)); if (i != -1) { // add points in leaf/bin to ret_nn_pq do { bbf_new_nn(ret_nn_pq, k, d, nodes[i].value); } while (-1 != (i = nodes[i].right)); --emax; } } tmp_pq.clear(); return ret_nn_pq.size(); } //////////////////////////////////////////////////////////////////////////////////////// // orthogonal range search private: void find_ortho_range(int i, scalar_type * bounds_min, scalar_type * bounds_max, std::vector < __valuetype > &inbounds) const { if (i == -1) return; const node & n = nodes[i]; if (n.dim >= 0) { // node if (bounds_min[n.dim] <= n.boundary) find_ortho_range(n.left, bounds_min, bounds_max, inbounds); if (bounds_max[n.dim] > n.boundary) find_ortho_range(n.right, bounds_min, bounds_max, inbounds); } else { // leaf do { inbounds.push_back(nodes[i].value); } while (-1 != (i = nodes[i].right)); } } public: // return all points that lie within the given bounds; inbounds is cleared int find_ortho_range(scalar_type * bounds_min, scalar_type * bounds_max, std::vector < __valuetype > &inbounds) const { inbounds.clear(); find_ortho_range(root_node, bounds_min, bounds_max, inbounds); return inbounds.size(); } }; #endif // __cv_kdtree_h__ // Local Variables: // mode:C++ // End: