opencv on mbed

Dependencies:   mbed

Committer:
joeverbout
Date:
Thu Mar 31 21:16:38 2016 +0000
Revision:
0:ea44dc9ed014
OpenCV on mbed attempt

Who changed what in which revision?

UserRevisionLine numberNew contents of line
joeverbout 0:ea44dc9ed014 1 /***********************************************************************
joeverbout 0:ea44dc9ed014 2 * Software License Agreement (BSD License)
joeverbout 0:ea44dc9ed014 3 *
joeverbout 0:ea44dc9ed014 4 * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
joeverbout 0:ea44dc9ed014 5 * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
joeverbout 0:ea44dc9ed014 6 *
joeverbout 0:ea44dc9ed014 7 * THE BSD LICENSE
joeverbout 0:ea44dc9ed014 8 *
joeverbout 0:ea44dc9ed014 9 * Redistribution and use in source and binary forms, with or without
joeverbout 0:ea44dc9ed014 10 * modification, are permitted provided that the following conditions
joeverbout 0:ea44dc9ed014 11 * are met:
joeverbout 0:ea44dc9ed014 12 *
joeverbout 0:ea44dc9ed014 13 * 1. Redistributions of source code must retain the above copyright
joeverbout 0:ea44dc9ed014 14 * notice, this list of conditions and the following disclaimer.
joeverbout 0:ea44dc9ed014 15 * 2. Redistributions in binary form must reproduce the above copyright
joeverbout 0:ea44dc9ed014 16 * notice, this list of conditions and the following disclaimer in the
joeverbout 0:ea44dc9ed014 17 * documentation and/or other materials provided with the distribution.
joeverbout 0:ea44dc9ed014 18 *
joeverbout 0:ea44dc9ed014 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
joeverbout 0:ea44dc9ed014 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
joeverbout 0:ea44dc9ed014 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
joeverbout 0:ea44dc9ed014 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
joeverbout 0:ea44dc9ed014 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
joeverbout 0:ea44dc9ed014 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
joeverbout 0:ea44dc9ed014 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
joeverbout 0:ea44dc9ed014 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
joeverbout 0:ea44dc9ed014 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
joeverbout 0:ea44dc9ed014 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
joeverbout 0:ea44dc9ed014 29 *************************************************************************/
joeverbout 0:ea44dc9ed014 30
joeverbout 0:ea44dc9ed014 31 #ifndef OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_
joeverbout 0:ea44dc9ed014 32 #define OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_
joeverbout 0:ea44dc9ed014 33
joeverbout 0:ea44dc9ed014 34 #include <algorithm>
joeverbout 0:ea44dc9ed014 35 #include <map>
joeverbout 0:ea44dc9ed014 36 #include <cassert>
joeverbout 0:ea44dc9ed014 37 #include <cstring>
joeverbout 0:ea44dc9ed014 38
joeverbout 0:ea44dc9ed014 39 #include "general.h"
joeverbout 0:ea44dc9ed014 40 #include "nn_index.h"
joeverbout 0:ea44dc9ed014 41 #include "matrix.h"
joeverbout 0:ea44dc9ed014 42 #include "result_set.h"
joeverbout 0:ea44dc9ed014 43 #include "heap.h"
joeverbout 0:ea44dc9ed014 44 #include "allocator.h"
joeverbout 0:ea44dc9ed014 45 #include "random.h"
joeverbout 0:ea44dc9ed014 46 #include "saving.h"
joeverbout 0:ea44dc9ed014 47
joeverbout 0:ea44dc9ed014 48 namespace cvflann
joeverbout 0:ea44dc9ed014 49 {
joeverbout 0:ea44dc9ed014 50
joeverbout 0:ea44dc9ed014 51 struct KDTreeSingleIndexParams : public IndexParams
joeverbout 0:ea44dc9ed014 52 {
joeverbout 0:ea44dc9ed014 53 KDTreeSingleIndexParams(int leaf_max_size = 10, bool reorder = true, int dim = -1)
joeverbout 0:ea44dc9ed014 54 {
joeverbout 0:ea44dc9ed014 55 (*this)["algorithm"] = FLANN_INDEX_KDTREE_SINGLE;
joeverbout 0:ea44dc9ed014 56 (*this)["leaf_max_size"] = leaf_max_size;
joeverbout 0:ea44dc9ed014 57 (*this)["reorder"] = reorder;
joeverbout 0:ea44dc9ed014 58 (*this)["dim"] = dim;
joeverbout 0:ea44dc9ed014 59 }
joeverbout 0:ea44dc9ed014 60 };
joeverbout 0:ea44dc9ed014 61
joeverbout 0:ea44dc9ed014 62
joeverbout 0:ea44dc9ed014 63 /**
joeverbout 0:ea44dc9ed014 64 * Randomized kd-tree index
joeverbout 0:ea44dc9ed014 65 *
joeverbout 0:ea44dc9ed014 66 * Contains the k-d trees and other information for indexing a set of points
joeverbout 0:ea44dc9ed014 67 * for nearest-neighbor matching.
joeverbout 0:ea44dc9ed014 68 */
joeverbout 0:ea44dc9ed014 69 template <typename Distance>
joeverbout 0:ea44dc9ed014 70 class KDTreeSingleIndex : public NNIndex<Distance>
joeverbout 0:ea44dc9ed014 71 {
joeverbout 0:ea44dc9ed014 72 public:
joeverbout 0:ea44dc9ed014 73 typedef typename Distance::ElementType ElementType;
joeverbout 0:ea44dc9ed014 74 typedef typename Distance::ResultType DistanceType;
joeverbout 0:ea44dc9ed014 75
joeverbout 0:ea44dc9ed014 76
joeverbout 0:ea44dc9ed014 77 /**
joeverbout 0:ea44dc9ed014 78 * KDTree constructor
joeverbout 0:ea44dc9ed014 79 *
joeverbout 0:ea44dc9ed014 80 * Params:
joeverbout 0:ea44dc9ed014 81 * inputData = dataset with the input features
joeverbout 0:ea44dc9ed014 82 * params = parameters passed to the kdtree algorithm
joeverbout 0:ea44dc9ed014 83 */
joeverbout 0:ea44dc9ed014 84 KDTreeSingleIndex(const Matrix<ElementType>& inputData, const IndexParams& params = KDTreeSingleIndexParams(),
joeverbout 0:ea44dc9ed014 85 Distance d = Distance() ) :
joeverbout 0:ea44dc9ed014 86 dataset_(inputData), index_params_(params), distance_(d)
joeverbout 0:ea44dc9ed014 87 {
joeverbout 0:ea44dc9ed014 88 size_ = dataset_.rows;
joeverbout 0:ea44dc9ed014 89 dim_ = dataset_.cols;
joeverbout 0:ea44dc9ed014 90 int dim_param = get_param(params,"dim",-1);
joeverbout 0:ea44dc9ed014 91 if (dim_param>0) dim_ = dim_param;
joeverbout 0:ea44dc9ed014 92 leaf_max_size_ = get_param(params,"leaf_max_size",10);
joeverbout 0:ea44dc9ed014 93 reorder_ = get_param(params,"reorder",true);
joeverbout 0:ea44dc9ed014 94
joeverbout 0:ea44dc9ed014 95 // Create a permutable array of indices to the input vectors.
joeverbout 0:ea44dc9ed014 96 vind_.resize(size_);
joeverbout 0:ea44dc9ed014 97 for (size_t i = 0; i < size_; i++) {
joeverbout 0:ea44dc9ed014 98 vind_[i] = (int)i;
joeverbout 0:ea44dc9ed014 99 }
joeverbout 0:ea44dc9ed014 100 }
joeverbout 0:ea44dc9ed014 101
joeverbout 0:ea44dc9ed014 102 KDTreeSingleIndex(const KDTreeSingleIndex&);
joeverbout 0:ea44dc9ed014 103 KDTreeSingleIndex& operator=(const KDTreeSingleIndex&);
joeverbout 0:ea44dc9ed014 104
joeverbout 0:ea44dc9ed014 105 /**
joeverbout 0:ea44dc9ed014 106 * Standard destructor
joeverbout 0:ea44dc9ed014 107 */
joeverbout 0:ea44dc9ed014 108 ~KDTreeSingleIndex()
joeverbout 0:ea44dc9ed014 109 {
joeverbout 0:ea44dc9ed014 110 if (reorder_) delete[] data_.data;
joeverbout 0:ea44dc9ed014 111 }
joeverbout 0:ea44dc9ed014 112
joeverbout 0:ea44dc9ed014 113 /**
joeverbout 0:ea44dc9ed014 114 * Builds the index
joeverbout 0:ea44dc9ed014 115 */
joeverbout 0:ea44dc9ed014 116 void buildIndex()
joeverbout 0:ea44dc9ed014 117 {
joeverbout 0:ea44dc9ed014 118 computeBoundingBox(root_bbox_);
joeverbout 0:ea44dc9ed014 119 root_node_ = divideTree(0, (int)size_, root_bbox_ ); // construct the tree
joeverbout 0:ea44dc9ed014 120
joeverbout 0:ea44dc9ed014 121 if (reorder_) {
joeverbout 0:ea44dc9ed014 122 delete[] data_.data;
joeverbout 0:ea44dc9ed014 123 data_ = cvflann::Matrix<ElementType>(new ElementType[size_*dim_], size_, dim_);
joeverbout 0:ea44dc9ed014 124 for (size_t i=0; i<size_; ++i) {
joeverbout 0:ea44dc9ed014 125 for (size_t j=0; j<dim_; ++j) {
joeverbout 0:ea44dc9ed014 126 data_[i][j] = dataset_[vind_[i]][j];
joeverbout 0:ea44dc9ed014 127 }
joeverbout 0:ea44dc9ed014 128 }
joeverbout 0:ea44dc9ed014 129 }
joeverbout 0:ea44dc9ed014 130 else {
joeverbout 0:ea44dc9ed014 131 data_ = dataset_;
joeverbout 0:ea44dc9ed014 132 }
joeverbout 0:ea44dc9ed014 133 }
joeverbout 0:ea44dc9ed014 134
joeverbout 0:ea44dc9ed014 135 flann_algorithm_t getType() const
joeverbout 0:ea44dc9ed014 136 {
joeverbout 0:ea44dc9ed014 137 return FLANN_INDEX_KDTREE_SINGLE;
joeverbout 0:ea44dc9ed014 138 }
joeverbout 0:ea44dc9ed014 139
joeverbout 0:ea44dc9ed014 140
joeverbout 0:ea44dc9ed014 141 void saveIndex(FILE* stream)
joeverbout 0:ea44dc9ed014 142 {
joeverbout 0:ea44dc9ed014 143 save_value(stream, size_);
joeverbout 0:ea44dc9ed014 144 save_value(stream, dim_);
joeverbout 0:ea44dc9ed014 145 save_value(stream, root_bbox_);
joeverbout 0:ea44dc9ed014 146 save_value(stream, reorder_);
joeverbout 0:ea44dc9ed014 147 save_value(stream, leaf_max_size_);
joeverbout 0:ea44dc9ed014 148 save_value(stream, vind_);
joeverbout 0:ea44dc9ed014 149 if (reorder_) {
joeverbout 0:ea44dc9ed014 150 save_value(stream, data_);
joeverbout 0:ea44dc9ed014 151 }
joeverbout 0:ea44dc9ed014 152 save_tree(stream, root_node_);
joeverbout 0:ea44dc9ed014 153 }
joeverbout 0:ea44dc9ed014 154
joeverbout 0:ea44dc9ed014 155
joeverbout 0:ea44dc9ed014 156 void loadIndex(FILE* stream)
joeverbout 0:ea44dc9ed014 157 {
joeverbout 0:ea44dc9ed014 158 load_value(stream, size_);
joeverbout 0:ea44dc9ed014 159 load_value(stream, dim_);
joeverbout 0:ea44dc9ed014 160 load_value(stream, root_bbox_);
joeverbout 0:ea44dc9ed014 161 load_value(stream, reorder_);
joeverbout 0:ea44dc9ed014 162 load_value(stream, leaf_max_size_);
joeverbout 0:ea44dc9ed014 163 load_value(stream, vind_);
joeverbout 0:ea44dc9ed014 164 if (reorder_) {
joeverbout 0:ea44dc9ed014 165 load_value(stream, data_);
joeverbout 0:ea44dc9ed014 166 }
joeverbout 0:ea44dc9ed014 167 else {
joeverbout 0:ea44dc9ed014 168 data_ = dataset_;
joeverbout 0:ea44dc9ed014 169 }
joeverbout 0:ea44dc9ed014 170 load_tree(stream, root_node_);
joeverbout 0:ea44dc9ed014 171
joeverbout 0:ea44dc9ed014 172
joeverbout 0:ea44dc9ed014 173 index_params_["algorithm"] = getType();
joeverbout 0:ea44dc9ed014 174 index_params_["leaf_max_size"] = leaf_max_size_;
joeverbout 0:ea44dc9ed014 175 index_params_["reorder"] = reorder_;
joeverbout 0:ea44dc9ed014 176 }
joeverbout 0:ea44dc9ed014 177
joeverbout 0:ea44dc9ed014 178 /**
joeverbout 0:ea44dc9ed014 179 * Returns size of index.
joeverbout 0:ea44dc9ed014 180 */
joeverbout 0:ea44dc9ed014 181 size_t size() const
joeverbout 0:ea44dc9ed014 182 {
joeverbout 0:ea44dc9ed014 183 return size_;
joeverbout 0:ea44dc9ed014 184 }
joeverbout 0:ea44dc9ed014 185
joeverbout 0:ea44dc9ed014 186 /**
joeverbout 0:ea44dc9ed014 187 * Returns the length of an index feature.
joeverbout 0:ea44dc9ed014 188 */
joeverbout 0:ea44dc9ed014 189 size_t veclen() const
joeverbout 0:ea44dc9ed014 190 {
joeverbout 0:ea44dc9ed014 191 return dim_;
joeverbout 0:ea44dc9ed014 192 }
joeverbout 0:ea44dc9ed014 193
joeverbout 0:ea44dc9ed014 194 /**
joeverbout 0:ea44dc9ed014 195 * Computes the inde memory usage
joeverbout 0:ea44dc9ed014 196 * Returns: memory used by the index
joeverbout 0:ea44dc9ed014 197 */
joeverbout 0:ea44dc9ed014 198 int usedMemory() const
joeverbout 0:ea44dc9ed014 199 {
joeverbout 0:ea44dc9ed014 200 return (int)(pool_.usedMemory+pool_.wastedMemory+dataset_.rows*sizeof(int)); // pool memory and vind array memory
joeverbout 0:ea44dc9ed014 201 }
joeverbout 0:ea44dc9ed014 202
joeverbout 0:ea44dc9ed014 203
joeverbout 0:ea44dc9ed014 204 /**
joeverbout 0:ea44dc9ed014 205 * \brief Perform k-nearest neighbor search
joeverbout 0:ea44dc9ed014 206 * \param[in] queries The query points for which to find the nearest neighbors
joeverbout 0:ea44dc9ed014 207 * \param[out] indices The indices of the nearest neighbors found
joeverbout 0:ea44dc9ed014 208 * \param[out] dists Distances to the nearest neighbors found
joeverbout 0:ea44dc9ed014 209 * \param[in] knn Number of nearest neighbors to return
joeverbout 0:ea44dc9ed014 210 * \param[in] params Search parameters
joeverbout 0:ea44dc9ed014 211 */
joeverbout 0:ea44dc9ed014 212 void knnSearch(const Matrix<ElementType>& queries, Matrix<int>& indices, Matrix<DistanceType>& dists, int knn, const SearchParams& params)
joeverbout 0:ea44dc9ed014 213 {
joeverbout 0:ea44dc9ed014 214 assert(queries.cols == veclen());
joeverbout 0:ea44dc9ed014 215 assert(indices.rows >= queries.rows);
joeverbout 0:ea44dc9ed014 216 assert(dists.rows >= queries.rows);
joeverbout 0:ea44dc9ed014 217 assert(int(indices.cols) >= knn);
joeverbout 0:ea44dc9ed014 218 assert(int(dists.cols) >= knn);
joeverbout 0:ea44dc9ed014 219
joeverbout 0:ea44dc9ed014 220 KNNSimpleResultSet<DistanceType> resultSet(knn);
joeverbout 0:ea44dc9ed014 221 for (size_t i = 0; i < queries.rows; i++) {
joeverbout 0:ea44dc9ed014 222 resultSet.init(indices[i], dists[i]);
joeverbout 0:ea44dc9ed014 223 findNeighbors(resultSet, queries[i], params);
joeverbout 0:ea44dc9ed014 224 }
joeverbout 0:ea44dc9ed014 225 }
joeverbout 0:ea44dc9ed014 226
joeverbout 0:ea44dc9ed014 227 IndexParams getParameters() const
joeverbout 0:ea44dc9ed014 228 {
joeverbout 0:ea44dc9ed014 229 return index_params_;
joeverbout 0:ea44dc9ed014 230 }
joeverbout 0:ea44dc9ed014 231
joeverbout 0:ea44dc9ed014 232 /**
joeverbout 0:ea44dc9ed014 233 * Find set of nearest neighbors to vec. Their indices are stored inside
joeverbout 0:ea44dc9ed014 234 * the result object.
joeverbout 0:ea44dc9ed014 235 *
joeverbout 0:ea44dc9ed014 236 * Params:
joeverbout 0:ea44dc9ed014 237 * result = the result object in which the indices of the nearest-neighbors are stored
joeverbout 0:ea44dc9ed014 238 * vec = the vector for which to search the nearest neighbors
joeverbout 0:ea44dc9ed014 239 * maxCheck = the maximum number of restarts (in a best-bin-first manner)
joeverbout 0:ea44dc9ed014 240 */
joeverbout 0:ea44dc9ed014 241 void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams)
joeverbout 0:ea44dc9ed014 242 {
joeverbout 0:ea44dc9ed014 243 float epsError = 1+get_param(searchParams,"eps",0.0f);
joeverbout 0:ea44dc9ed014 244
joeverbout 0:ea44dc9ed014 245 std::vector<DistanceType> dists(dim_,0);
joeverbout 0:ea44dc9ed014 246 DistanceType distsq = computeInitialDistances(vec, dists);
joeverbout 0:ea44dc9ed014 247 searchLevel(result, vec, root_node_, distsq, dists, epsError);
joeverbout 0:ea44dc9ed014 248 }
joeverbout 0:ea44dc9ed014 249
joeverbout 0:ea44dc9ed014 250 private:
joeverbout 0:ea44dc9ed014 251
joeverbout 0:ea44dc9ed014 252
joeverbout 0:ea44dc9ed014 253 /*--------------------- Internal Data Structures --------------------------*/
joeverbout 0:ea44dc9ed014 254 struct Node
joeverbout 0:ea44dc9ed014 255 {
joeverbout 0:ea44dc9ed014 256 /**
joeverbout 0:ea44dc9ed014 257 * Indices of points in leaf node
joeverbout 0:ea44dc9ed014 258 */
joeverbout 0:ea44dc9ed014 259 int left, right;
joeverbout 0:ea44dc9ed014 260 /**
joeverbout 0:ea44dc9ed014 261 * Dimension used for subdivision.
joeverbout 0:ea44dc9ed014 262 */
joeverbout 0:ea44dc9ed014 263 int divfeat;
joeverbout 0:ea44dc9ed014 264 /**
joeverbout 0:ea44dc9ed014 265 * The values used for subdivision.
joeverbout 0:ea44dc9ed014 266 */
joeverbout 0:ea44dc9ed014 267 DistanceType divlow, divhigh;
joeverbout 0:ea44dc9ed014 268 /**
joeverbout 0:ea44dc9ed014 269 * The child nodes.
joeverbout 0:ea44dc9ed014 270 */
joeverbout 0:ea44dc9ed014 271 Node* child1, * child2;
joeverbout 0:ea44dc9ed014 272 };
joeverbout 0:ea44dc9ed014 273 typedef Node* NodePtr;
joeverbout 0:ea44dc9ed014 274
joeverbout 0:ea44dc9ed014 275
joeverbout 0:ea44dc9ed014 276 struct Interval
joeverbout 0:ea44dc9ed014 277 {
joeverbout 0:ea44dc9ed014 278 DistanceType low, high;
joeverbout 0:ea44dc9ed014 279 };
joeverbout 0:ea44dc9ed014 280
joeverbout 0:ea44dc9ed014 281 typedef std::vector<Interval> BoundingBox;
joeverbout 0:ea44dc9ed014 282
joeverbout 0:ea44dc9ed014 283 typedef BranchStruct<NodePtr, DistanceType> BranchSt;
joeverbout 0:ea44dc9ed014 284 typedef BranchSt* Branch;
joeverbout 0:ea44dc9ed014 285
joeverbout 0:ea44dc9ed014 286
joeverbout 0:ea44dc9ed014 287
joeverbout 0:ea44dc9ed014 288
joeverbout 0:ea44dc9ed014 289 void save_tree(FILE* stream, NodePtr tree)
joeverbout 0:ea44dc9ed014 290 {
joeverbout 0:ea44dc9ed014 291 save_value(stream, *tree);
joeverbout 0:ea44dc9ed014 292 if (tree->child1!=NULL) {
joeverbout 0:ea44dc9ed014 293 save_tree(stream, tree->child1);
joeverbout 0:ea44dc9ed014 294 }
joeverbout 0:ea44dc9ed014 295 if (tree->child2!=NULL) {
joeverbout 0:ea44dc9ed014 296 save_tree(stream, tree->child2);
joeverbout 0:ea44dc9ed014 297 }
joeverbout 0:ea44dc9ed014 298 }
joeverbout 0:ea44dc9ed014 299
joeverbout 0:ea44dc9ed014 300
joeverbout 0:ea44dc9ed014 301 void load_tree(FILE* stream, NodePtr& tree)
joeverbout 0:ea44dc9ed014 302 {
joeverbout 0:ea44dc9ed014 303 tree = pool_.allocate<Node>();
joeverbout 0:ea44dc9ed014 304 load_value(stream, *tree);
joeverbout 0:ea44dc9ed014 305 if (tree->child1!=NULL) {
joeverbout 0:ea44dc9ed014 306 load_tree(stream, tree->child1);
joeverbout 0:ea44dc9ed014 307 }
joeverbout 0:ea44dc9ed014 308 if (tree->child2!=NULL) {
joeverbout 0:ea44dc9ed014 309 load_tree(stream, tree->child2);
joeverbout 0:ea44dc9ed014 310 }
joeverbout 0:ea44dc9ed014 311 }
joeverbout 0:ea44dc9ed014 312
joeverbout 0:ea44dc9ed014 313
joeverbout 0:ea44dc9ed014 314 void computeBoundingBox(BoundingBox& bbox)
joeverbout 0:ea44dc9ed014 315 {
joeverbout 0:ea44dc9ed014 316 bbox.resize(dim_);
joeverbout 0:ea44dc9ed014 317 for (size_t i=0; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 318 bbox[i].low = (DistanceType)dataset_[0][i];
joeverbout 0:ea44dc9ed014 319 bbox[i].high = (DistanceType)dataset_[0][i];
joeverbout 0:ea44dc9ed014 320 }
joeverbout 0:ea44dc9ed014 321 for (size_t k=1; k<dataset_.rows; ++k) {
joeverbout 0:ea44dc9ed014 322 for (size_t i=0; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 323 if (dataset_[k][i]<bbox[i].low) bbox[i].low = (DistanceType)dataset_[k][i];
joeverbout 0:ea44dc9ed014 324 if (dataset_[k][i]>bbox[i].high) bbox[i].high = (DistanceType)dataset_[k][i];
joeverbout 0:ea44dc9ed014 325 }
joeverbout 0:ea44dc9ed014 326 }
joeverbout 0:ea44dc9ed014 327 }
joeverbout 0:ea44dc9ed014 328
joeverbout 0:ea44dc9ed014 329
joeverbout 0:ea44dc9ed014 330 /**
joeverbout 0:ea44dc9ed014 331 * Create a tree node that subdivides the list of vecs from vind[first]
joeverbout 0:ea44dc9ed014 332 * to vind[last]. The routine is called recursively on each sublist.
joeverbout 0:ea44dc9ed014 333 * Place a pointer to this new tree node in the location pTree.
joeverbout 0:ea44dc9ed014 334 *
joeverbout 0:ea44dc9ed014 335 * Params: pTree = the new node to create
joeverbout 0:ea44dc9ed014 336 * first = index of the first vector
joeverbout 0:ea44dc9ed014 337 * last = index of the last vector
joeverbout 0:ea44dc9ed014 338 */
joeverbout 0:ea44dc9ed014 339 NodePtr divideTree(int left, int right, BoundingBox& bbox)
joeverbout 0:ea44dc9ed014 340 {
joeverbout 0:ea44dc9ed014 341 NodePtr node = pool_.allocate<Node>(); // allocate memory
joeverbout 0:ea44dc9ed014 342
joeverbout 0:ea44dc9ed014 343 /* If too few exemplars remain, then make this a leaf node. */
joeverbout 0:ea44dc9ed014 344 if ( (right-left) <= leaf_max_size_) {
joeverbout 0:ea44dc9ed014 345 node->child1 = node->child2 = NULL; /* Mark as leaf node. */
joeverbout 0:ea44dc9ed014 346 node->left = left;
joeverbout 0:ea44dc9ed014 347 node->right = right;
joeverbout 0:ea44dc9ed014 348
joeverbout 0:ea44dc9ed014 349 // compute bounding-box of leaf points
joeverbout 0:ea44dc9ed014 350 for (size_t i=0; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 351 bbox[i].low = (DistanceType)dataset_[vind_[left]][i];
joeverbout 0:ea44dc9ed014 352 bbox[i].high = (DistanceType)dataset_[vind_[left]][i];
joeverbout 0:ea44dc9ed014 353 }
joeverbout 0:ea44dc9ed014 354 for (int k=left+1; k<right; ++k) {
joeverbout 0:ea44dc9ed014 355 for (size_t i=0; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 356 if (bbox[i].low>dataset_[vind_[k]][i]) bbox[i].low=(DistanceType)dataset_[vind_[k]][i];
joeverbout 0:ea44dc9ed014 357 if (bbox[i].high<dataset_[vind_[k]][i]) bbox[i].high=(DistanceType)dataset_[vind_[k]][i];
joeverbout 0:ea44dc9ed014 358 }
joeverbout 0:ea44dc9ed014 359 }
joeverbout 0:ea44dc9ed014 360 }
joeverbout 0:ea44dc9ed014 361 else {
joeverbout 0:ea44dc9ed014 362 int idx;
joeverbout 0:ea44dc9ed014 363 int cutfeat;
joeverbout 0:ea44dc9ed014 364 DistanceType cutval;
joeverbout 0:ea44dc9ed014 365 middleSplit_(&vind_[0]+left, right-left, idx, cutfeat, cutval, bbox);
joeverbout 0:ea44dc9ed014 366
joeverbout 0:ea44dc9ed014 367 node->divfeat = cutfeat;
joeverbout 0:ea44dc9ed014 368
joeverbout 0:ea44dc9ed014 369 BoundingBox left_bbox(bbox);
joeverbout 0:ea44dc9ed014 370 left_bbox[cutfeat].high = cutval;
joeverbout 0:ea44dc9ed014 371 node->child1 = divideTree(left, left+idx, left_bbox);
joeverbout 0:ea44dc9ed014 372
joeverbout 0:ea44dc9ed014 373 BoundingBox right_bbox(bbox);
joeverbout 0:ea44dc9ed014 374 right_bbox[cutfeat].low = cutval;
joeverbout 0:ea44dc9ed014 375 node->child2 = divideTree(left+idx, right, right_bbox);
joeverbout 0:ea44dc9ed014 376
joeverbout 0:ea44dc9ed014 377 node->divlow = left_bbox[cutfeat].high;
joeverbout 0:ea44dc9ed014 378 node->divhigh = right_bbox[cutfeat].low;
joeverbout 0:ea44dc9ed014 379
joeverbout 0:ea44dc9ed014 380 for (size_t i=0; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 381 bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low);
joeverbout 0:ea44dc9ed014 382 bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high);
joeverbout 0:ea44dc9ed014 383 }
joeverbout 0:ea44dc9ed014 384 }
joeverbout 0:ea44dc9ed014 385
joeverbout 0:ea44dc9ed014 386 return node;
joeverbout 0:ea44dc9ed014 387 }
joeverbout 0:ea44dc9ed014 388
joeverbout 0:ea44dc9ed014 389 void computeMinMax(int* ind, int count, int dim, ElementType& min_elem, ElementType& max_elem)
joeverbout 0:ea44dc9ed014 390 {
joeverbout 0:ea44dc9ed014 391 min_elem = dataset_[ind[0]][dim];
joeverbout 0:ea44dc9ed014 392 max_elem = dataset_[ind[0]][dim];
joeverbout 0:ea44dc9ed014 393 for (int i=1; i<count; ++i) {
joeverbout 0:ea44dc9ed014 394 ElementType val = dataset_[ind[i]][dim];
joeverbout 0:ea44dc9ed014 395 if (val<min_elem) min_elem = val;
joeverbout 0:ea44dc9ed014 396 if (val>max_elem) max_elem = val;
joeverbout 0:ea44dc9ed014 397 }
joeverbout 0:ea44dc9ed014 398 }
joeverbout 0:ea44dc9ed014 399
joeverbout 0:ea44dc9ed014 400 void middleSplit(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
joeverbout 0:ea44dc9ed014 401 {
joeverbout 0:ea44dc9ed014 402 // find the largest span from the approximate bounding box
joeverbout 0:ea44dc9ed014 403 ElementType max_span = bbox[0].high-bbox[0].low;
joeverbout 0:ea44dc9ed014 404 cutfeat = 0;
joeverbout 0:ea44dc9ed014 405 cutval = (bbox[0].high+bbox[0].low)/2;
joeverbout 0:ea44dc9ed014 406 for (size_t i=1; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 407 ElementType span = bbox[i].high-bbox[i].low;
joeverbout 0:ea44dc9ed014 408 if (span>max_span) {
joeverbout 0:ea44dc9ed014 409 max_span = span;
joeverbout 0:ea44dc9ed014 410 cutfeat = i;
joeverbout 0:ea44dc9ed014 411 cutval = (bbox[i].high+bbox[i].low)/2;
joeverbout 0:ea44dc9ed014 412 }
joeverbout 0:ea44dc9ed014 413 }
joeverbout 0:ea44dc9ed014 414
joeverbout 0:ea44dc9ed014 415 // compute exact span on the found dimension
joeverbout 0:ea44dc9ed014 416 ElementType min_elem, max_elem;
joeverbout 0:ea44dc9ed014 417 computeMinMax(ind, count, cutfeat, min_elem, max_elem);
joeverbout 0:ea44dc9ed014 418 cutval = (min_elem+max_elem)/2;
joeverbout 0:ea44dc9ed014 419 max_span = max_elem - min_elem;
joeverbout 0:ea44dc9ed014 420
joeverbout 0:ea44dc9ed014 421 // check if a dimension of a largest span exists
joeverbout 0:ea44dc9ed014 422 size_t k = cutfeat;
joeverbout 0:ea44dc9ed014 423 for (size_t i=0; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 424 if (i==k) continue;
joeverbout 0:ea44dc9ed014 425 ElementType span = bbox[i].high-bbox[i].low;
joeverbout 0:ea44dc9ed014 426 if (span>max_span) {
joeverbout 0:ea44dc9ed014 427 computeMinMax(ind, count, i, min_elem, max_elem);
joeverbout 0:ea44dc9ed014 428 span = max_elem - min_elem;
joeverbout 0:ea44dc9ed014 429 if (span>max_span) {
joeverbout 0:ea44dc9ed014 430 max_span = span;
joeverbout 0:ea44dc9ed014 431 cutfeat = i;
joeverbout 0:ea44dc9ed014 432 cutval = (min_elem+max_elem)/2;
joeverbout 0:ea44dc9ed014 433 }
joeverbout 0:ea44dc9ed014 434 }
joeverbout 0:ea44dc9ed014 435 }
joeverbout 0:ea44dc9ed014 436 int lim1, lim2;
joeverbout 0:ea44dc9ed014 437 planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
joeverbout 0:ea44dc9ed014 438
joeverbout 0:ea44dc9ed014 439 if (lim1>count/2) index = lim1;
joeverbout 0:ea44dc9ed014 440 else if (lim2<count/2) index = lim2;
joeverbout 0:ea44dc9ed014 441 else index = count/2;
joeverbout 0:ea44dc9ed014 442 }
joeverbout 0:ea44dc9ed014 443
joeverbout 0:ea44dc9ed014 444
joeverbout 0:ea44dc9ed014 445 void middleSplit_(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
joeverbout 0:ea44dc9ed014 446 {
joeverbout 0:ea44dc9ed014 447 const float EPS=0.00001f;
joeverbout 0:ea44dc9ed014 448 DistanceType max_span = bbox[0].high-bbox[0].low;
joeverbout 0:ea44dc9ed014 449 for (size_t i=1; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 450 DistanceType span = bbox[i].high-bbox[i].low;
joeverbout 0:ea44dc9ed014 451 if (span>max_span) {
joeverbout 0:ea44dc9ed014 452 max_span = span;
joeverbout 0:ea44dc9ed014 453 }
joeverbout 0:ea44dc9ed014 454 }
joeverbout 0:ea44dc9ed014 455 DistanceType max_spread = -1;
joeverbout 0:ea44dc9ed014 456 cutfeat = 0;
joeverbout 0:ea44dc9ed014 457 for (size_t i=0; i<dim_; ++i) {
joeverbout 0:ea44dc9ed014 458 DistanceType span = bbox[i].high-bbox[i].low;
joeverbout 0:ea44dc9ed014 459 if (span>(DistanceType)((1-EPS)*max_span)) {
joeverbout 0:ea44dc9ed014 460 ElementType min_elem, max_elem;
joeverbout 0:ea44dc9ed014 461 computeMinMax(ind, count, cutfeat, min_elem, max_elem);
joeverbout 0:ea44dc9ed014 462 DistanceType spread = (DistanceType)(max_elem-min_elem);
joeverbout 0:ea44dc9ed014 463 if (spread>max_spread) {
joeverbout 0:ea44dc9ed014 464 cutfeat = (int)i;
joeverbout 0:ea44dc9ed014 465 max_spread = spread;
joeverbout 0:ea44dc9ed014 466 }
joeverbout 0:ea44dc9ed014 467 }
joeverbout 0:ea44dc9ed014 468 }
joeverbout 0:ea44dc9ed014 469 // split in the middle
joeverbout 0:ea44dc9ed014 470 DistanceType split_val = (bbox[cutfeat].low+bbox[cutfeat].high)/2;
joeverbout 0:ea44dc9ed014 471 ElementType min_elem, max_elem;
joeverbout 0:ea44dc9ed014 472 computeMinMax(ind, count, cutfeat, min_elem, max_elem);
joeverbout 0:ea44dc9ed014 473
joeverbout 0:ea44dc9ed014 474 if (split_val<min_elem) cutval = (DistanceType)min_elem;
joeverbout 0:ea44dc9ed014 475 else if (split_val>max_elem) cutval = (DistanceType)max_elem;
joeverbout 0:ea44dc9ed014 476 else cutval = split_val;
joeverbout 0:ea44dc9ed014 477
joeverbout 0:ea44dc9ed014 478 int lim1, lim2;
joeverbout 0:ea44dc9ed014 479 planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
joeverbout 0:ea44dc9ed014 480
joeverbout 0:ea44dc9ed014 481 if (lim1>count/2) index = lim1;
joeverbout 0:ea44dc9ed014 482 else if (lim2<count/2) index = lim2;
joeverbout 0:ea44dc9ed014 483 else index = count/2;
joeverbout 0:ea44dc9ed014 484 }
joeverbout 0:ea44dc9ed014 485
joeverbout 0:ea44dc9ed014 486
joeverbout 0:ea44dc9ed014 487 /**
joeverbout 0:ea44dc9ed014 488 * Subdivide the list of points by a plane perpendicular on axe corresponding
joeverbout 0:ea44dc9ed014 489 * to the 'cutfeat' dimension at 'cutval' position.
joeverbout 0:ea44dc9ed014 490 *
joeverbout 0:ea44dc9ed014 491 * On return:
joeverbout 0:ea44dc9ed014 492 * dataset[ind[0..lim1-1]][cutfeat]<cutval
joeverbout 0:ea44dc9ed014 493 * dataset[ind[lim1..lim2-1]][cutfeat]==cutval
joeverbout 0:ea44dc9ed014 494 * dataset[ind[lim2..count]][cutfeat]>cutval
joeverbout 0:ea44dc9ed014 495 */
joeverbout 0:ea44dc9ed014 496 void planeSplit(int* ind, int count, int cutfeat, DistanceType cutval, int& lim1, int& lim2)
joeverbout 0:ea44dc9ed014 497 {
joeverbout 0:ea44dc9ed014 498 /* Move vector indices for left subtree to front of list. */
joeverbout 0:ea44dc9ed014 499 int left = 0;
joeverbout 0:ea44dc9ed014 500 int right = count-1;
joeverbout 0:ea44dc9ed014 501 for (;; ) {
joeverbout 0:ea44dc9ed014 502 while (left<=right && dataset_[ind[left]][cutfeat]<cutval) ++left;
joeverbout 0:ea44dc9ed014 503 while (left<=right && dataset_[ind[right]][cutfeat]>=cutval) --right;
joeverbout 0:ea44dc9ed014 504 if (left>right) break;
joeverbout 0:ea44dc9ed014 505 std::swap(ind[left], ind[right]); ++left; --right;
joeverbout 0:ea44dc9ed014 506 }
joeverbout 0:ea44dc9ed014 507 /* If either list is empty, it means that all remaining features
joeverbout 0:ea44dc9ed014 508 * are identical. Split in the middle to maintain a balanced tree.
joeverbout 0:ea44dc9ed014 509 */
joeverbout 0:ea44dc9ed014 510 lim1 = left;
joeverbout 0:ea44dc9ed014 511 right = count-1;
joeverbout 0:ea44dc9ed014 512 for (;; ) {
joeverbout 0:ea44dc9ed014 513 while (left<=right && dataset_[ind[left]][cutfeat]<=cutval) ++left;
joeverbout 0:ea44dc9ed014 514 while (left<=right && dataset_[ind[right]][cutfeat]>cutval) --right;
joeverbout 0:ea44dc9ed014 515 if (left>right) break;
joeverbout 0:ea44dc9ed014 516 std::swap(ind[left], ind[right]); ++left; --right;
joeverbout 0:ea44dc9ed014 517 }
joeverbout 0:ea44dc9ed014 518 lim2 = left;
joeverbout 0:ea44dc9ed014 519 }
joeverbout 0:ea44dc9ed014 520
joeverbout 0:ea44dc9ed014 521 DistanceType computeInitialDistances(const ElementType* vec, std::vector<DistanceType>& dists)
joeverbout 0:ea44dc9ed014 522 {
joeverbout 0:ea44dc9ed014 523 DistanceType distsq = 0.0;
joeverbout 0:ea44dc9ed014 524
joeverbout 0:ea44dc9ed014 525 for (size_t i = 0; i < dim_; ++i) {
joeverbout 0:ea44dc9ed014 526 if (vec[i] < root_bbox_[i].low) {
joeverbout 0:ea44dc9ed014 527 dists[i] = distance_.accum_dist(vec[i], root_bbox_[i].low, (int)i);
joeverbout 0:ea44dc9ed014 528 distsq += dists[i];
joeverbout 0:ea44dc9ed014 529 }
joeverbout 0:ea44dc9ed014 530 if (vec[i] > root_bbox_[i].high) {
joeverbout 0:ea44dc9ed014 531 dists[i] = distance_.accum_dist(vec[i], root_bbox_[i].high, (int)i);
joeverbout 0:ea44dc9ed014 532 distsq += dists[i];
joeverbout 0:ea44dc9ed014 533 }
joeverbout 0:ea44dc9ed014 534 }
joeverbout 0:ea44dc9ed014 535
joeverbout 0:ea44dc9ed014 536 return distsq;
joeverbout 0:ea44dc9ed014 537 }
joeverbout 0:ea44dc9ed014 538
joeverbout 0:ea44dc9ed014 539 /**
joeverbout 0:ea44dc9ed014 540 * Performs an exact search in the tree starting from a node.
joeverbout 0:ea44dc9ed014 541 */
joeverbout 0:ea44dc9ed014 542 void searchLevel(ResultSet<DistanceType>& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq,
joeverbout 0:ea44dc9ed014 543 std::vector<DistanceType>& dists, const float epsError)
joeverbout 0:ea44dc9ed014 544 {
joeverbout 0:ea44dc9ed014 545 /* If this is a leaf node, then do check and return. */
joeverbout 0:ea44dc9ed014 546 if ((node->child1 == NULL)&&(node->child2 == NULL)) {
joeverbout 0:ea44dc9ed014 547 DistanceType worst_dist = result_set.worstDist();
joeverbout 0:ea44dc9ed014 548 for (int i=node->left; i<node->right; ++i) {
joeverbout 0:ea44dc9ed014 549 int index = reorder_ ? i : vind_[i];
joeverbout 0:ea44dc9ed014 550 DistanceType dist = distance_(vec, data_[index], dim_, worst_dist);
joeverbout 0:ea44dc9ed014 551 if (dist<worst_dist) {
joeverbout 0:ea44dc9ed014 552 result_set.addPoint(dist,vind_[i]);
joeverbout 0:ea44dc9ed014 553 }
joeverbout 0:ea44dc9ed014 554 }
joeverbout 0:ea44dc9ed014 555 return;
joeverbout 0:ea44dc9ed014 556 }
joeverbout 0:ea44dc9ed014 557
joeverbout 0:ea44dc9ed014 558 /* Which child branch should be taken first? */
joeverbout 0:ea44dc9ed014 559 int idx = node->divfeat;
joeverbout 0:ea44dc9ed014 560 ElementType val = vec[idx];
joeverbout 0:ea44dc9ed014 561 DistanceType diff1 = val - node->divlow;
joeverbout 0:ea44dc9ed014 562 DistanceType diff2 = val - node->divhigh;
joeverbout 0:ea44dc9ed014 563
joeverbout 0:ea44dc9ed014 564 NodePtr bestChild;
joeverbout 0:ea44dc9ed014 565 NodePtr otherChild;
joeverbout 0:ea44dc9ed014 566 DistanceType cut_dist;
joeverbout 0:ea44dc9ed014 567 if ((diff1+diff2)<0) {
joeverbout 0:ea44dc9ed014 568 bestChild = node->child1;
joeverbout 0:ea44dc9ed014 569 otherChild = node->child2;
joeverbout 0:ea44dc9ed014 570 cut_dist = distance_.accum_dist(val, node->divhigh, idx);
joeverbout 0:ea44dc9ed014 571 }
joeverbout 0:ea44dc9ed014 572 else {
joeverbout 0:ea44dc9ed014 573 bestChild = node->child2;
joeverbout 0:ea44dc9ed014 574 otherChild = node->child1;
joeverbout 0:ea44dc9ed014 575 cut_dist = distance_.accum_dist( val, node->divlow, idx);
joeverbout 0:ea44dc9ed014 576 }
joeverbout 0:ea44dc9ed014 577
joeverbout 0:ea44dc9ed014 578 /* Call recursively to search next level down. */
joeverbout 0:ea44dc9ed014 579 searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError);
joeverbout 0:ea44dc9ed014 580
joeverbout 0:ea44dc9ed014 581 DistanceType dst = dists[idx];
joeverbout 0:ea44dc9ed014 582 mindistsq = mindistsq + cut_dist - dst;
joeverbout 0:ea44dc9ed014 583 dists[idx] = cut_dist;
joeverbout 0:ea44dc9ed014 584 if (mindistsq*epsError<=result_set.worstDist()) {
joeverbout 0:ea44dc9ed014 585 searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError);
joeverbout 0:ea44dc9ed014 586 }
joeverbout 0:ea44dc9ed014 587 dists[idx] = dst;
joeverbout 0:ea44dc9ed014 588 }
joeverbout 0:ea44dc9ed014 589
joeverbout 0:ea44dc9ed014 590 private:
joeverbout 0:ea44dc9ed014 591
joeverbout 0:ea44dc9ed014 592 /**
joeverbout 0:ea44dc9ed014 593 * The dataset used by this index
joeverbout 0:ea44dc9ed014 594 */
joeverbout 0:ea44dc9ed014 595 const Matrix<ElementType> dataset_;
joeverbout 0:ea44dc9ed014 596
joeverbout 0:ea44dc9ed014 597 IndexParams index_params_;
joeverbout 0:ea44dc9ed014 598
joeverbout 0:ea44dc9ed014 599 int leaf_max_size_;
joeverbout 0:ea44dc9ed014 600 bool reorder_;
joeverbout 0:ea44dc9ed014 601
joeverbout 0:ea44dc9ed014 602
joeverbout 0:ea44dc9ed014 603 /**
joeverbout 0:ea44dc9ed014 604 * Array of indices to vectors in the dataset.
joeverbout 0:ea44dc9ed014 605 */
joeverbout 0:ea44dc9ed014 606 std::vector<int> vind_;
joeverbout 0:ea44dc9ed014 607
joeverbout 0:ea44dc9ed014 608 Matrix<ElementType> data_;
joeverbout 0:ea44dc9ed014 609
joeverbout 0:ea44dc9ed014 610 size_t size_;
joeverbout 0:ea44dc9ed014 611 size_t dim_;
joeverbout 0:ea44dc9ed014 612
joeverbout 0:ea44dc9ed014 613 /**
joeverbout 0:ea44dc9ed014 614 * Array of k-d trees used to find neighbours.
joeverbout 0:ea44dc9ed014 615 */
joeverbout 0:ea44dc9ed014 616 NodePtr root_node_;
joeverbout 0:ea44dc9ed014 617
joeverbout 0:ea44dc9ed014 618 BoundingBox root_bbox_;
joeverbout 0:ea44dc9ed014 619
joeverbout 0:ea44dc9ed014 620 /**
joeverbout 0:ea44dc9ed014 621 * Pooled memory allocator.
joeverbout 0:ea44dc9ed014 622 *
joeverbout 0:ea44dc9ed014 623 * Using a pooled memory allocator is more efficient
joeverbout 0:ea44dc9ed014 624 * than allocating memory directly when there is a large
joeverbout 0:ea44dc9ed014 625 * number small of memory allocations.
joeverbout 0:ea44dc9ed014 626 */
joeverbout 0:ea44dc9ed014 627 PooledAllocator pool_;
joeverbout 0:ea44dc9ed014 628
joeverbout 0:ea44dc9ed014 629 Distance distance_;
joeverbout 0:ea44dc9ed014 630 }; // class KDTree
joeverbout 0:ea44dc9ed014 631
joeverbout 0:ea44dc9ed014 632 }
joeverbout 0:ea44dc9ed014 633
joeverbout 0:ea44dc9ed014 634 #endif //OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_
joeverbout 0:ea44dc9ed014 635