temporarily reverted to FLANN 1.5 (FLANN 1.6 is put to a separate branch FLANN_1.6 until it's stabilized)
parent
310ed83343
commit
848be8dfe1
36 changed files with 3676 additions and 7634 deletions
@ -1,277 +0,0 @@ |
||||
#ifndef OPENCV_FLANN_ANY_H_ |
||||
#define OPENCV_FLANN_ANY_H_ |
||||
/*
|
||||
* (C) Copyright Christopher Diggins 2005-2011 |
||||
* (C) Copyright Pablo Aguilar 2005 |
||||
* (C) Copyright Kevlin Henney 2001 |
||||
* |
||||
* Distributed under the Boost Software License, Version 1.0. (See |
||||
* accompanying file LICENSE_1_0.txt or copy at |
||||
* http://www.boost.org/LICENSE_1_0.txt
|
||||
* |
||||
* Adapted for FLANN by Marius Muja |
||||
*/ |
||||
|
||||
|
||||
|
||||
#include <stdexcept> |
||||
#include <ostream> |
||||
|
||||
namespace cdiggins |
||||
{ |
||||
|
||||
namespace anyimpl |
||||
{ |
||||
|
||||
struct bad_any_cast |
||||
{ |
||||
}; |
||||
|
||||
struct empty_any |
||||
{ |
||||
}; |
||||
|
||||
struct base_any_policy |
||||
{ |
||||
virtual void static_delete(void** x) = 0; |
||||
virtual void copy_from_value(void const* src, void** dest) = 0; |
||||
virtual void clone(void* const* src, void** dest) = 0; |
||||
virtual void move(void* const* src, void** dest) = 0; |
||||
virtual void* get_value(void** src) = 0; |
||||
virtual size_t get_size() = 0; |
||||
virtual void print(std::ostream& out, void* const* src) = 0; |
||||
}; |
||||
|
||||
template<typename T> |
||||
struct typed_base_any_policy : base_any_policy |
||||
{ |
||||
virtual size_t get_size() { return sizeof(T); } |
||||
|
||||
}; |
||||
|
||||
template<typename T> |
||||
struct small_any_policy : typed_base_any_policy<T> |
||||
{ |
||||
virtual void static_delete(void**) { } |
||||
virtual void copy_from_value(void const* src, void** dest) |
||||
{ |
||||
new (dest) T(* reinterpret_cast<T const*>(src)); |
||||
} |
||||
virtual void clone(void* const* src, void** dest) { *dest = *src; } |
||||
virtual void move(void* const* src, void** dest) { *dest = *src; } |
||||
virtual void* get_value(void** src) { return reinterpret_cast<void*>(src); } |
||||
virtual void print(std::ostream& out, void* const* src) { out << *reinterpret_cast<T const*>(src); } |
||||
}; |
||||
|
||||
template<typename T> |
||||
struct big_any_policy : typed_base_any_policy<T> |
||||
{ |
||||
virtual void static_delete(void** x) |
||||
{ |
||||
if (* x) delete (* reinterpret_cast<T**>(x)); *x = NULL; |
||||
} |
||||
virtual void copy_from_value(void const* src, void** dest) |
||||
{ |
||||
*dest = new T(*reinterpret_cast<T const*>(src)); |
||||
} |
||||
virtual void clone(void* const* src, void** dest) |
||||
{ |
||||
*dest = new T(**reinterpret_cast<T* const*>(src)); |
||||
} |
||||
virtual void move(void* const* src, void** dest) |
||||
{ |
||||
(*reinterpret_cast<T**>(dest))->~T(); |
||||
**reinterpret_cast<T**>(dest) = **reinterpret_cast<T* const*>(src); |
||||
} |
||||
virtual void* get_value(void** src) { return *src; } |
||||
virtual void print(std::ostream& out, void* const* src) { out << *reinterpret_cast<T const*>(*src); } |
||||
}; |
||||
|
||||
template<typename T> |
||||
struct choose_policy |
||||
{ |
||||
typedef big_any_policy<T> type; |
||||
}; |
||||
|
||||
template<typename T> |
||||
struct choose_policy<T*> |
||||
{ |
||||
typedef small_any_policy<T*> type; |
||||
}; |
||||
|
||||
struct any; |
||||
|
||||
/// Choosing the policy for an any type is illegal, but should never happen.
|
||||
/// This is designed to throw a compiler error.
|
||||
template<> |
||||
struct choose_policy<any> |
||||
{ |
||||
typedef void type; |
||||
}; |
||||
|
||||
/// Specializations for small types.
|
||||
#define SMALL_POLICY(TYPE) \ |
||||
template<> \
|
||||
struct choose_policy<TYPE> { typedef small_any_policy<TYPE> type; \
|
||||
}; |
||||
|
||||
SMALL_POLICY(signed char); |
||||
SMALL_POLICY(unsigned char); |
||||
SMALL_POLICY(signed short); |
||||
SMALL_POLICY(unsigned short); |
||||
SMALL_POLICY(signed int); |
||||
SMALL_POLICY(unsigned int); |
||||
SMALL_POLICY(signed long); |
||||
SMALL_POLICY(unsigned long); |
||||
SMALL_POLICY(float); |
||||
SMALL_POLICY(bool); |
||||
|
||||
#undef SMALL_POLICY |
||||
|
||||
/// This function will return a different policy for each type.
|
||||
template<typename T> |
||||
base_any_policy* get_policy() |
||||
{ |
||||
static typename choose_policy<T>::type policy; |
||||
return &policy; |
||||
} |
||||
} // namespace anyimpl
|
||||
|
||||
struct any |
||||
{ |
||||
private: |
||||
// fields
|
||||
anyimpl::base_any_policy* policy; |
||||
void* object; |
||||
|
||||
public: |
||||
/// Initializing constructor.
|
||||
template <typename T> |
||||
any(const T& x) |
||||
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL) |
||||
{ |
||||
assign(x); |
||||
} |
||||
|
||||
/// Empty constructor.
|
||||
any() |
||||
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL) |
||||
{ } |
||||
|
||||
/// Special initializing constructor for string literals.
|
||||
any(const char* x) |
||||
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL) |
||||
{ |
||||
assign(x); |
||||
} |
||||
|
||||
/// Copy constructor.
|
||||
any(const any& x) |
||||
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL) |
||||
{ |
||||
assign(x); |
||||
} |
||||
|
||||
/// Destructor.
|
||||
~any() |
||||
{ |
||||
policy->static_delete(&object); |
||||
} |
||||
|
||||
/// Assignment function from another any.
|
||||
any& assign(const any& x) |
||||
{ |
||||
reset(); |
||||
policy = x.policy; |
||||
policy->clone(&x.object, &object); |
||||
return *this; |
||||
} |
||||
|
||||
/// Assignment function.
|
||||
template <typename T> |
||||
any& assign(const T& x) |
||||
{ |
||||
reset(); |
||||
policy = anyimpl::get_policy<T>(); |
||||
policy->copy_from_value(&x, &object); |
||||
return *this; |
||||
} |
||||
|
||||
/// Assignment operator.
|
||||
template<typename T> |
||||
any& operator=(const T& x) |
||||
{ |
||||
return assign(x); |
||||
} |
||||
|
||||
/// Assignment operator, specialed for literal strings.
|
||||
/// They have types like const char [6] which don't work as expected.
|
||||
any& operator=(const char* x) |
||||
{ |
||||
return assign(x); |
||||
} |
||||
|
||||
/// Utility functions
|
||||
any& swap(any& x) |
||||
{ |
||||
std::swap(policy, x.policy); |
||||
std::swap(object, x.object); |
||||
return *this; |
||||
} |
||||
|
||||
/// Cast operator. You can only cast to the original type.
|
||||
template<typename T> |
||||
T& cast() |
||||
{ |
||||
if (policy != anyimpl::get_policy<T>()) throw anyimpl::bad_any_cast(); |
||||
T* r = reinterpret_cast<T*>(policy->get_value(&object)); |
||||
return *r; |
||||
} |
||||
|
||||
/// Cast operator. You can only cast to the original type.
|
||||
template<typename T> |
||||
const T& cast() const |
||||
{ |
||||
if (policy != anyimpl::get_policy<T>()) throw anyimpl::bad_any_cast(); |
||||
T* r = reinterpret_cast<T*>(policy->get_value((void**)&object)); |
||||
return *r; |
||||
} |
||||
|
||||
/// Returns true if the any contains no value.
|
||||
bool empty() const |
||||
{ |
||||
return policy == anyimpl::get_policy<anyimpl::empty_any>(); |
||||
} |
||||
|
||||
/// Frees any allocated memory, and sets the value to NULL.
|
||||
void reset() |
||||
{ |
||||
policy->static_delete(&object); |
||||
policy = anyimpl::get_policy<anyimpl::empty_any>(); |
||||
} |
||||
|
||||
/// Returns true if the two types are the same.
|
||||
bool compatible(const any& x) const |
||||
{ |
||||
return policy == x.policy; |
||||
} |
||||
|
||||
/// Returns if the type is compatible with the policy
|
||||
template<typename T> |
||||
bool has_type() |
||||
{ |
||||
return policy == anyimpl::get_policy<T>(); |
||||
} |
||||
|
||||
friend std::ostream& operator <<(std::ostream& out, const any& any_val); |
||||
}; |
||||
|
||||
inline std::ostream& operator <<(std::ostream& out, const any& any_val) |
||||
{ |
||||
any_val.policy->print(out,&any_val.object); |
||||
return out; |
||||
} |
||||
|
||||
} |
||||
|
||||
#endif // OPENCV_FLANN_ANY_H_
|
@ -1,35 +0,0 @@ |
||||
/***********************************************************************
|
||||
* Software License Agreement (BSD License) |
||||
* |
||||
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. |
||||
* Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. |
||||
* |
||||
* 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_CONFIG_H_ |
||||
#define OPENCV_FLANN_CONFIG_H_ |
||||
|
||||
#define FLANN_VERSION "1.6.10" |
||||
|
||||
#endif /* OPENCV_FLANN_CONFIG_H_ */ |
@ -1,160 +0,0 @@ |
||||
/***********************************************************************
|
||||
* Software License Agreement (BSD License) |
||||
* |
||||
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. |
||||
* Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. |
||||
* |
||||
* 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_DEFINES_H_ |
||||
#define OPENCV_FLANN_DEFINES_H_ |
||||
|
||||
#include "config.h" |
||||
|
||||
#ifdef WIN32 |
||||
/* win32 dll export/import directives */ |
||||
#ifdef FLANN_EXPORTS |
||||
#define FLANN_EXPORT __declspec(dllexport) |
||||
#elif defined(FLANN_STATIC) |
||||
#define FLANN_EXPORT |
||||
#else |
||||
#define FLANN_EXPORT __declspec(dllimport) |
||||
#endif |
||||
#else |
||||
/* unix needs nothing */ |
||||
#define FLANN_EXPORT |
||||
#endif |
||||
|
||||
|
||||
#ifdef __GNUC__ |
||||
#define FLANN_DEPRECATED __attribute__ ((deprecated)) |
||||
#elif defined(_MSC_VER) |
||||
#define FLANN_DEPRECATED __declspec(deprecated) |
||||
#else |
||||
#pragma message("WARNING: You need to implement FLANN_DEPRECATED for this compiler") |
||||
#define FLANN_DEPRECATED |
||||
#endif |
||||
|
||||
|
||||
#if __amd64__ || __x86_64__ || _WIN64 || _M_X64 |
||||
#define FLANN_PLATFORM_64_BIT |
||||
#else |
||||
#define FLANN_PLATFORM_32_BIT |
||||
#endif |
||||
|
||||
|
||||
#define FLANN_ARRAY_LEN(a) (sizeof(a)/sizeof(a[0])) |
||||
|
||||
/* Nearest neighbour index algorithms */ |
||||
enum flann_algorithm_t |
||||
{ |
||||
FLANN_INDEX_LINEAR = 0, |
||||
FLANN_INDEX_KDTREE = 1, |
||||
FLANN_INDEX_KMEANS = 2, |
||||
FLANN_INDEX_COMPOSITE = 3, |
||||
FLANN_INDEX_KDTREE_SINGLE = 4, |
||||
FLANN_INDEX_HIERARCHICAL = 5, |
||||
FLANN_INDEX_LSH = 6, |
||||
FLANN_INDEX_SAVED = 254, |
||||
FLANN_INDEX_AUTOTUNED = 255, |
||||
|
||||
// deprecated constants, should use the FLANN_INDEX_* ones instead
|
||||
LINEAR = 0, |
||||
KDTREE = 1, |
||||
KMEANS = 2, |
||||
COMPOSITE = 3, |
||||
KDTREE_SINGLE = 4, |
||||
SAVED = 254, |
||||
AUTOTUNED = 255 |
||||
}; |
||||
|
||||
|
||||
|
||||
enum flann_centers_init_t |
||||
{ |
||||
FLANN_CENTERS_RANDOM = 0, |
||||
FLANN_CENTERS_GONZALES = 1, |
||||
FLANN_CENTERS_KMEANSPP = 2, |
||||
|
||||
// deprecated constants, should use the FLANN_CENTERS_* ones instead
|
||||
CENTERS_RANDOM = 0, |
||||
CENTERS_GONZALES = 1, |
||||
CENTERS_KMEANSPP = 2 |
||||
}; |
||||
|
||||
enum flann_log_level_t |
||||
{ |
||||
FLANN_LOG_NONE = 0, |
||||
FLANN_LOG_FATAL = 1, |
||||
FLANN_LOG_ERROR = 2, |
||||
FLANN_LOG_WARN = 3, |
||||
FLANN_LOG_INFO = 4, |
||||
}; |
||||
|
||||
enum flann_distance_t |
||||
{ |
||||
FLANN_DIST_EUCLIDEAN = 1, |
||||
FLANN_DIST_L2 = 1, |
||||
FLANN_DIST_MANHATTAN = 2, |
||||
FLANN_DIST_L1 = 2, |
||||
FLANN_DIST_MINKOWSKI = 3, |
||||
FLANN_DIST_MAX = 4, |
||||
FLANN_DIST_HIST_INTERSECT = 5, |
||||
FLANN_DIST_HELLINGER = 6, |
||||
FLANN_DIST_CHI_SQUARE = 7, |
||||
FLANN_DIST_CS = 7, |
||||
FLANN_DIST_KULLBACK_LEIBLER = 8, |
||||
FLANN_DIST_KL = 8, |
||||
|
||||
// deprecated constants, should use the FLANN_DIST_* ones instead
|
||||
EUCLIDEAN = 1, |
||||
MANHATTAN = 2, |
||||
MINKOWSKI = 3, |
||||
MAX_DIST = 4, |
||||
HIST_INTERSECT = 5, |
||||
HELLINGER = 6, |
||||
CS = 7, |
||||
KL = 8, |
||||
KULLBACK_LEIBLER = 8 |
||||
}; |
||||
|
||||
enum flann_datatype_t |
||||
{ |
||||
FLANN_INT8 = 0, |
||||
FLANN_INT16 = 1, |
||||
FLANN_INT32 = 2, |
||||
FLANN_INT64 = 3, |
||||
FLANN_UINT8 = 4, |
||||
FLANN_UINT16 = 5, |
||||
FLANN_UINT32 = 6, |
||||
FLANN_UINT64 = 7, |
||||
FLANN_FLOAT32 = 8, |
||||
FLANN_FLOAT64 = 9 |
||||
}; |
||||
|
||||
const int FLANN_CHECKS_UNLIMITED = -1; |
||||
const int FLANN_CHECKS_AUTOTUNED = -2; |
||||
|
||||
|
||||
#endif /* OPENCV_FLANN_DEFINES_H_ */ |
File diff suppressed because it is too large
Load Diff
@ -1,152 +0,0 @@ |
||||
/***********************************************************************
|
||||
* 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. |
||||
*************************************************************************/ |
||||
|
||||
/***********************************************************************
|
||||
* Author: Vincent Rabaud |
||||
*************************************************************************/ |
||||
|
||||
#ifndef OPENCV_FLANN_DYNAMIC_BITSET_H_ |
||||
#define OPENCV_FLANN_DYNAMIC_BITSET_H_ |
||||
|
||||
//#define FLANN_USE_BOOST 1
|
||||
#if FLANN_USE_BOOST |
||||
#include <boost/dynamic_bitset.hpp> |
||||
typedef boost::dynamic_bitset<> DynamicBitset; |
||||
#else |
||||
|
||||
#include <limits.h> |
||||
|
||||
#include "dist.h" |
||||
|
||||
/** Class re-implementing the boost version of it
|
||||
* This helps not depending on boost, it also does not do the bound checks |
||||
* and has a way to reset a block for speed |
||||
*/ |
||||
class DynamicBitset |
||||
{ |
||||
public: |
||||
/** @param default constructor
|
||||
*/ |
||||
DynamicBitset() |
||||
{ |
||||
} |
||||
|
||||
/** @param only constructor we use in our code
|
||||
* @param the size of the bitset (in bits) |
||||
*/ |
||||
DynamicBitset(size_t size) |
||||
{ |
||||
resize(size); |
||||
reset(); |
||||
} |
||||
|
||||
/** Sets all the bits to 0
|
||||
*/ |
||||
void clear() |
||||
{ |
||||
std::fill(bitset_.begin(), bitset_.end(), 0); |
||||
} |
||||
|
||||
/** @brief checks if the bitset is empty
|
||||
* @return true if the bitset is empty |
||||
*/ |
||||
bool empty() const |
||||
{ |
||||
return bitset_.empty(); |
||||
} |
||||
|
||||
/** @param set all the bits to 0
|
||||
*/ |
||||
void reset() |
||||
{ |
||||
std::fill(bitset_.begin(), bitset_.end(), 0); |
||||
} |
||||
|
||||
/** @brief set one bit to 0
|
||||
* @param |
||||
*/ |
||||
void reset(size_t index) |
||||
{ |
||||
bitset_[index / cell_bit_size_] &= ~(size_t(1) << (index % cell_bit_size_)); |
||||
} |
||||
|
||||
/** @brief sets a specific bit to 0, and more bits too
|
||||
* This function is useful when resetting a given set of bits so that the |
||||
* whole bitset ends up being 0: if that's the case, we don't care about setting |
||||
* other bits to 0 |
||||
* @param |
||||
*/ |
||||
void reset_block(size_t index) |
||||
{ |
||||
bitset_[index / cell_bit_size_] = 0; |
||||
} |
||||
|
||||
/** @param resize the bitset so that it contains at least size bits
|
||||
* @param size |
||||
*/ |
||||
void resize(size_t size) |
||||
{ |
||||
size_ = size; |
||||
bitset_.resize(size / cell_bit_size_ + 1); |
||||
} |
||||
|
||||
/** @param set a bit to true
|
||||
* @param index the index of the bit to set to 1 |
||||
*/ |
||||
void set(size_t index) |
||||
{ |
||||
bitset_[index / cell_bit_size_] |= size_t(1) << (index % cell_bit_size_); |
||||
} |
||||
|
||||
/** @param gives the number of contained bits
|
||||
*/ |
||||
size_t size() const |
||||
{ |
||||
return size_; |
||||
} |
||||
|
||||
/** @param check if a bit is set
|
||||
* @param index the index of the bit to check |
||||
* @return true if the bit is set |
||||
*/ |
||||
bool test(size_t index) const |
||||
{ |
||||
return (bitset_[index / cell_bit_size_] & (size_t(1) << (index % cell_bit_size_))) != 0; |
||||
} |
||||
|
||||
private: |
||||
std::vector<size_t> bitset_; |
||||
size_t size_; |
||||
static const unsigned int cell_bit_size_ = CHAR_BIT * sizeof(size_t); |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
#endif // OPENCV_FLANN_DYNAMIC_BITSET_H_
|
@ -1,717 +0,0 @@ |
||||
/***********************************************************************
|
||||
* Software License Agreement (BSD License) |
||||
* |
||||
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. |
||||
* Copyright 2008-2011 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_HIERARCHICAL_CLUSTERING_INDEX_H_ |
||||
#define OPENCV_FLANN_HIERARCHICAL_CLUSTERING_INDEX_H_ |
||||
|
||||
#include <algorithm> |
||||
#include <string> |
||||
#include <map> |
||||
#include <cassert> |
||||
#include <limits> |
||||
#include <cmath> |
||||
|
||||
#include "general.h" |
||||
#include "nn_index.h" |
||||
#include "dist.h" |
||||
#include "matrix.h" |
||||
#include "result_set.h" |
||||
#include "heap.h" |
||||
#include "allocator.h" |
||||
#include "random.h" |
||||
#include "saving.h" |
||||
|
||||
|
||||
namespace cvflann |
||||
{ |
||||
|
||||
struct HierarchicalClusteringIndexParams : public IndexParams |
||||
{ |
||||
HierarchicalClusteringIndexParams(int branching = 32, |
||||
flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, |
||||
int trees = 4, int leaf_size = 100) |
||||
{ |
||||
(*this)["algorithm"] = FLANN_INDEX_HIERARCHICAL; |
||||
// The branching factor used in the hierarchical clustering
|
||||
(*this)["branching"] = branching; |
||||
// Algorithm used for picking the initial cluster centers
|
||||
(*this)["centers_init"] = centers_init; |
||||
// number of parallel trees to build
|
||||
(*this)["trees"] = trees; |
||||
// maximum leaf size
|
||||
(*this)["leaf_size"] = leaf_size; |
||||
} |
||||
}; |
||||
|
||||
|
||||
/**
|
||||
* Hierarchical index |
||||
* |
||||
* Contains a tree constructed through a hierarchical clustering |
||||
* and other information for indexing a set of points for nearest-neighbour matching. |
||||
*/ |
||||
template <typename Distance> |
||||
class HierarchicalClusteringIndex : public NNIndex<Distance> |
||||
{ |
||||
public: |
||||
typedef typename Distance::ElementType ElementType; |
||||
typedef typename Distance::ResultType DistanceType; |
||||
|
||||
private: |
||||
|
||||
|
||||
typedef void (HierarchicalClusteringIndex::* centersAlgFunction)(int, int*, int, int*, int&); |
||||
|
||||
/**
|
||||
* The function used for choosing the cluster centers. |
||||
*/ |
||||
centersAlgFunction chooseCenters; |
||||
|
||||
|
||||
|
||||
/**
|
||||
* Chooses the initial centers in the k-means clustering in a random manner. |
||||
* |
||||
* Params: |
||||
* k = number of centers |
||||
* vecs = the dataset of points |
||||
* indices = indices in the dataset |
||||
* indices_length = length of indices vector |
||||
* |
||||
*/ |
||||
void chooseCentersRandom(int k, int* indices, int indices_length, int* centers, int& centers_length) |
||||
{ |
||||
UniqueRandom r(indices_length); |
||||
|
||||
int index; |
||||
for (index=0; index<k; ++index) { |
||||
bool duplicate = true; |
||||
int rnd; |
||||
while (duplicate) { |
||||
duplicate = false; |
||||
rnd = r.next(); |
||||
if (rnd<0) { |
||||
centers_length = index; |
||||
return; |
||||
} |
||||
|
||||
centers[index] = indices[rnd]; |
||||
|
||||
for (int j=0; j<index; ++j) { |
||||
float sq = distance(dataset[centers[index]], dataset[centers[j]], dataset.cols); |
||||
if (sq<1e-16) { |
||||
duplicate = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
centers_length = index; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Chooses the initial centers in the k-means using Gonzales' algorithm |
||||
* so that the centers are spaced apart from each other. |
||||
* |
||||
* Params: |
||||
* k = number of centers |
||||
* vecs = the dataset of points |
||||
* indices = indices in the dataset |
||||
* Returns: |
||||
*/ |
||||
void chooseCentersGonzales(int k, int* indices, int indices_length, int* centers, int& centers_length) |
||||
{ |
||||
int n = indices_length; |
||||
|
||||
int rnd = rand_int(n); |
||||
assert(rnd >=0 && rnd < n); |
||||
|
||||
centers[0] = indices[rnd]; |
||||
|
||||
int index; |
||||
for (index=1; index<k; ++index) { |
||||
|
||||
int best_index = -1; |
||||
float best_val = 0; |
||||
for (int j=0; j<n; ++j) { |
||||
float dist = distance(dataset[centers[0]],dataset[indices[j]],dataset.cols); |
||||
for (int i=1; i<index; ++i) { |
||||
float tmp_dist = distance(dataset[centers[i]],dataset[indices[j]],dataset.cols); |
||||
if (tmp_dist<dist) { |
||||
dist = tmp_dist; |
||||
} |
||||
} |
||||
if (dist>best_val) { |
||||
best_val = dist; |
||||
best_index = j; |
||||
} |
||||
} |
||||
if (best_index!=-1) { |
||||
centers[index] = indices[best_index]; |
||||
} |
||||
else { |
||||
break; |
||||
} |
||||
} |
||||
centers_length = index; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Chooses the initial centers in the k-means using the algorithm |
||||
* proposed in the KMeans++ paper: |
||||
* Arthur, David; Vassilvitskii, Sergei - k-means++: The Advantages of Careful Seeding |
||||
* |
||||
* Implementation of this function was converted from the one provided in Arthur's code. |
||||
* |
||||
* Params: |
||||
* k = number of centers |
||||
* vecs = the dataset of points |
||||
* indices = indices in the dataset |
||||
* Returns: |
||||
*/ |
||||
void chooseCentersKMeanspp(int k, int* indices, int indices_length, int* centers, int& centers_length) |
||||
{ |
||||
int n = indices_length; |
||||
|
||||
double currentPot = 0; |
||||
DistanceType* closestDistSq = new DistanceType[n]; |
||||
|
||||
// Choose one random center and set the closestDistSq values
|
||||
int index = rand_int(n); |
||||
assert(index >=0 && index < n); |
||||
centers[0] = indices[index]; |
||||
|
||||
for (int i = 0; i < n; i++) { |
||||
closestDistSq[i] = distance(dataset[indices[i]], dataset[indices[index]], dataset.cols); |
||||
currentPot += closestDistSq[i]; |
||||
} |
||||
|
||||
|
||||
const int numLocalTries = 1; |
||||
|
||||
// Choose each center
|
||||
int centerCount; |
||||
for (centerCount = 1; centerCount < k; centerCount++) { |
||||
|
||||
// Repeat several trials
|
||||
double bestNewPot = -1; |
||||
int bestNewIndex = 0; |
||||
for (int localTrial = 0; localTrial < numLocalTries; localTrial++) { |
||||
|
||||
// Choose our center - have to be slightly careful to return a valid answer even accounting
|
||||
// for possible rounding errors
|
||||
double randVal = rand_double(currentPot); |
||||
for (index = 0; index < n-1; index++) { |
||||
if (randVal <= closestDistSq[index]) break; |
||||
else randVal -= closestDistSq[index]; |
||||
} |
||||
|
||||
// Compute the new potential
|
||||
double newPot = 0; |
||||
for (int i = 0; i < n; i++) newPot += std::min( distance(dataset[indices[i]], dataset[indices[index]], dataset.cols), closestDistSq[i] ); |
||||
|
||||
// Store the best result
|
||||
if ((bestNewPot < 0)||(newPot < bestNewPot)) { |
||||
bestNewPot = newPot; |
||||
bestNewIndex = index; |
||||
} |
||||
} |
||||
|
||||
// Add the appropriate center
|
||||
centers[centerCount] = indices[bestNewIndex]; |
||||
currentPot = bestNewPot; |
||||
for (int i = 0; i < n; i++) closestDistSq[i] = std::min( distance(dataset[indices[i]], dataset[indices[bestNewIndex]], dataset.cols), closestDistSq[i] ); |
||||
} |
||||
|
||||
centers_length = centerCount; |
||||
|
||||
delete[] closestDistSq; |
||||
} |
||||
|
||||
|
||||
public: |
||||
|
||||
|
||||
/**
|
||||
* Index constructor |
||||
* |
||||
* Params: |
||||
* inputData = dataset with the input features |
||||
* params = parameters passed to the hierarchical k-means algorithm |
||||
*/ |
||||
HierarchicalClusteringIndex(const Matrix<ElementType>& inputData, const IndexParams& index_params = HierarchicalClusteringIndexParams(), |
||||
Distance d = Distance()) |
||||
: dataset(inputData), params(index_params), root(NULL), indices(NULL), distance(d) |
||||
{ |
||||
memoryCounter = 0; |
||||
|
||||
size_ = dataset.rows; |
||||
veclen_ = dataset.cols; |
||||
|
||||
branching_ = get_param(params,"branching",32); |
||||
centers_init_ = get_param(params,"centers_init", FLANN_CENTERS_RANDOM); |
||||
trees_ = get_param(params,"trees",4); |
||||
leaf_size_ = get_param(params,"leaf_size",100); |
||||
|
||||
if (centers_init_==FLANN_CENTERS_RANDOM) { |
||||
chooseCenters = &HierarchicalClusteringIndex::chooseCentersRandom; |
||||
} |
||||
else if (centers_init_==FLANN_CENTERS_GONZALES) { |
||||
chooseCenters = &HierarchicalClusteringIndex::chooseCentersGonzales; |
||||
} |
||||
else if (centers_init_==FLANN_CENTERS_KMEANSPP) { |
||||
chooseCenters = &HierarchicalClusteringIndex::chooseCentersKMeanspp; |
||||
} |
||||
else { |
||||
throw FLANNException("Unknown algorithm for choosing initial centers."); |
||||
} |
||||
|
||||
trees_ = get_param(params,"trees",4); |
||||
root = new NodePtr[trees_]; |
||||
indices = new int*[trees_]; |
||||
} |
||||
|
||||
HierarchicalClusteringIndex(const HierarchicalClusteringIndex&); |
||||
HierarchicalClusteringIndex& operator=(const HierarchicalClusteringIndex&); |
||||
|
||||
/**
|
||||
* Index destructor. |
||||
* |
||||
* Release the memory used by the index. |
||||
*/ |
||||
virtual ~HierarchicalClusteringIndex() |
||||
{ |
||||
if (indices!=NULL) { |
||||
delete[] indices; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Returns size of index. |
||||
*/ |
||||
size_t size() const |
||||
{ |
||||
return size_; |
||||
} |
||||
|
||||
/**
|
||||
* Returns the length of an index feature. |
||||
*/ |
||||
size_t veclen() const |
||||
{ |
||||
return veclen_; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Computes the inde memory usage |
||||
* Returns: memory used by the index |
||||
*/ |
||||
int usedMemory() const |
||||
{ |
||||
return pool.usedMemory+pool.wastedMemory+memoryCounter; |
||||
} |
||||
|
||||
/**
|
||||
* Builds the index |
||||
*/ |
||||
void buildIndex() |
||||
{ |
||||
if (branching_<2) { |
||||
throw FLANNException("Branching factor must be at least 2"); |
||||
} |
||||
for (int i=0; i<trees_; ++i) { |
||||
indices[i] = new int[size_]; |
||||
for (size_t j=0; j<size_; ++j) { |
||||
indices[i][j] = j; |
||||
} |
||||
root[i] = pool.allocate<Node>(); |
||||
computeClustering(root[i], indices[i], size_, branching_,0); |
||||
} |
||||
} |
||||
|
||||
|
||||
flann_algorithm_t getType() const |
||||
{ |
||||
return FLANN_INDEX_HIERARCHICAL; |
||||
} |
||||
|
||||
|
||||
void saveIndex(FILE* stream) |
||||
{ |
||||
save_value(stream, branching_); |
||||
save_value(stream, trees_); |
||||
save_value(stream, centers_init_); |
||||
save_value(stream, leaf_size_); |
||||
save_value(stream, memoryCounter); |
||||
for (int i=0; i<trees_; ++i) { |
||||
save_value(stream, *indices[i], size_); |
||||
save_tree(stream, root[i], i); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
void loadIndex(FILE* stream) |
||||
{ |
||||
load_value(stream, branching_); |
||||
load_value(stream, trees_); |
||||
load_value(stream, centers_init_); |
||||
load_value(stream, leaf_size_); |
||||
load_value(stream, memoryCounter); |
||||
indices = new int*[trees_]; |
||||
root = new NodePtr[trees_]; |
||||
for (int i=0; i<trees_; ++i) { |
||||
indices[i] = new int[size_]; |
||||
load_value(stream, *indices[i], size_); |
||||
load_tree(stream, root[i], i); |
||||
} |
||||
|
||||
params["algorithm"] = getType(); |
||||
params["branching"] = branching_; |
||||
params["trees"] = trees_; |
||||
params["centers_init"] = centers_init_; |
||||
params["leaf_size"] = leaf_size_; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* 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 |
||||
* searchParams = parameters that influence the search algorithm (checks) |
||||
*/ |
||||
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) |
||||
{ |
||||
|
||||
int maxChecks = get_param(searchParams,"checks",32); |
||||
|
||||
// Priority queue storing intermediate branches in the best-bin-first search
|
||||
Heap<BranchSt>* heap = new Heap<BranchSt>(size_); |
||||
|
||||
std::vector<bool> checked(size_,false); |
||||
int checks = 0; |
||||
for (int i=0; i<trees_; ++i) { |
||||
findNN(root[i], result, vec, checks, maxChecks, heap, checked); |
||||
} |
||||
|
||||
BranchSt branch; |
||||
while (heap->popMin(branch) && (checks<maxChecks || !result.full())) { |
||||
NodePtr node = branch.node; |
||||
findNN(node, result, vec, checks, maxChecks, heap, checked); |
||||
} |
||||
assert(result.full()); |
||||
|
||||
delete heap; |
||||
|
||||
} |
||||
|
||||
IndexParams getParameters() const |
||||
{ |
||||
return params; |
||||
} |
||||
|
||||
|
||||
private: |
||||
|
||||
/**
|
||||
* Struture representing a node in the hierarchical k-means tree. |
||||
*/ |
||||
struct Node |
||||
{ |
||||
/**
|
||||
* The cluster center index |
||||
*/ |
||||
int pivot; |
||||
/**
|
||||
* The cluster size (number of points in the cluster) |
||||
*/ |
||||
int size; |
||||
/**
|
||||
* Child nodes (only for non-terminal nodes) |
||||
*/ |
||||
Node** childs; |
||||
/**
|
||||
* Node points (only for terminal nodes) |
||||
*/ |
||||
int* indices; |
||||
/**
|
||||
* Level |
||||
*/ |
||||
int level; |
||||
}; |
||||
typedef Node* NodePtr; |
||||
|
||||
|
||||
|
||||
/**
|
||||
* Alias definition for a nicer syntax. |
||||
*/ |
||||
typedef BranchStruct<NodePtr, DistanceType> BranchSt; |
||||
|
||||
|
||||
|
||||
void save_tree(FILE* stream, NodePtr node, int num) |
||||
{ |
||||
save_value(stream, *node); |
||||
if (node->childs==NULL) { |
||||
int indices_offset = node->indices - indices[num]; |
||||
save_value(stream, indices_offset); |
||||
} |
||||
else { |
||||
for(int i=0; i<branching_; ++i) { |
||||
save_tree(stream, node->childs[i], num); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
void load_tree(FILE* stream, NodePtr& node, int num) |
||||
{ |
||||
node = pool.allocate<Node>(); |
||||
load_value(stream, *node); |
||||
if (node->childs==NULL) { |
||||
int indices_offset; |
||||
load_value(stream, indices_offset); |
||||
node->indices = indices[num] + indices_offset; |
||||
} |
||||
else { |
||||
node->childs = pool.allocate<NodePtr>(branching_); |
||||
for(int i=0; i<branching_; ++i) { |
||||
load_tree(stream, node->childs[i], num); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
void computeLabels(int* indices, int indices_length, int* centers, int centers_length, int* labels, DistanceType& cost) |
||||
{ |
||||
cost = 0; |
||||
for (int i=0; i<indices_length; ++i) { |
||||
ElementType* point = dataset[indices[i]]; |
||||
DistanceType dist = distance(point, dataset[centers[0]], veclen_); |
||||
labels[i] = 0; |
||||
for (int j=1; j<centers_length; ++j) { |
||||
DistanceType new_dist = distance(point, dataset[centers[j]], veclen_); |
||||
if (dist>new_dist) { |
||||
labels[i] = j; |
||||
dist = new_dist; |
||||
} |
||||
} |
||||
cost += dist; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* The method responsible with actually doing the recursive hierarchical |
||||
* clustering |
||||
* |
||||
* Params: |
||||
* node = the node to cluster |
||||
* indices = indices of the points belonging to the current node |
||||
* branching = the branching factor to use in the clustering |
||||
* |
||||
* TODO: for 1-sized clusters don't store a cluster center (it's the same as the single cluster point) |
||||
*/ |
||||
void computeClustering(NodePtr node, int* indices, int indices_length, int branching, int level) |
||||
{ |
||||
node->size = indices_length; |
||||
node->level = level; |
||||
|
||||
if (indices_length < leaf_size_) { // leaf node
|
||||
node->indices = indices; |
||||
std::sort(node->indices,node->indices+indices_length); |
||||
node->childs = NULL; |
||||
return; |
||||
} |
||||
|
||||
std::vector<int> centers(branching); |
||||
std::vector<int> labels(indices_length); |
||||
|
||||
int centers_length; |
||||
(this->*chooseCenters)(branching, indices, indices_length, ¢ers[0], centers_length); |
||||
|
||||
if (centers_length<branching) { |
||||
node->indices = indices; |
||||
std::sort(node->indices,node->indices+indices_length); |
||||
node->childs = NULL; |
||||
return; |
||||
} |
||||
|
||||
|
||||
// assign points to clusters
|
||||
DistanceType cost; |
||||
computeLabels(indices, indices_length, ¢ers[0], centers_length, &labels[0], cost); |
||||
|
||||
node->childs = pool.allocate<NodePtr>(branching); |
||||
int start = 0; |
||||
int end = start; |
||||
for (int i=0; i<branching; ++i) { |
||||
for (int j=0; j<indices_length; ++j) { |
||||
if (labels[j]==i) { |
||||
std::swap(indices[j],indices[end]); |
||||
std::swap(labels[j],labels[end]); |
||||
end++; |
||||
} |
||||
} |
||||
|
||||
node->childs[i] = pool.allocate<Node>(); |
||||
node->childs[i]->pivot = centers[i]; |
||||
node->childs[i]->indices = NULL; |
||||
computeClustering(node->childs[i],indices+start, end-start, branching, level+1); |
||||
start=end; |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
/**
|
||||
* Performs one descent in the hierarchical k-means tree. The branches not |
||||
* visited are stored in a priority queue. |
||||
* |
||||
* Params: |
||||
* node = node to explore |
||||
* result = container for the k-nearest neighbors found |
||||
* vec = query points |
||||
* checks = how many points in the dataset have been checked so far |
||||
* maxChecks = maximum dataset points to checks |
||||
*/ |
||||
|
||||
|
||||
void findNN(NodePtr node, ResultSet<DistanceType>& result, const ElementType* vec, int& checks, int maxChecks, |
||||
Heap<BranchSt>* heap, std::vector<bool>& checked) |
||||
{ |
||||
if (node->childs==NULL) { |
||||
if (checks>=maxChecks) { |
||||
if (result.full()) return; |
||||
} |
||||
checks += node->size; |
||||
for (int i=0; i<node->size; ++i) { |
||||
int index = node->indices[i]; |
||||
if (!checked[index]) { |
||||
DistanceType dist = distance(dataset[index], vec, veclen_); |
||||
result.addPoint(dist, index); |
||||
checked[index] = true; |
||||
} |
||||
} |
||||
} |
||||
else { |
||||
DistanceType* domain_distances = new DistanceType[branching_]; |
||||
int best_index = 0; |
||||
domain_distances[best_index] = distance(vec, dataset[node->childs[best_index]->pivot], veclen_); |
||||
for (int i=1; i<branching_; ++i) { |
||||
domain_distances[i] = distance(vec, dataset[node->childs[i]->pivot], veclen_); |
||||
if (domain_distances[i]<domain_distances[best_index]) { |
||||
best_index = i; |
||||
} |
||||
} |
||||
for (int i=0; i<branching_; ++i) { |
||||
if (i!=best_index) { |
||||
heap->insert(BranchSt(node->childs[i],domain_distances[i])); |
||||
} |
||||
} |
||||
delete[] domain_distances; |
||||
findNN(node->childs[best_index],result,vec, checks, maxChecks, heap, checked); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
|
||||
|
||||
/**
|
||||
* The dataset used by this index |
||||
*/ |
||||
const Matrix<ElementType> dataset; |
||||
|
||||
/**
|
||||
* Parameters used by this index |
||||
*/ |
||||
IndexParams params; |
||||
|
||||
|
||||
/**
|
||||
* Number of features in the dataset. |
||||
*/ |
||||
size_t size_; |
||||
|
||||
/**
|
||||
* Length of each feature. |
||||
*/ |
||||
size_t veclen_; |
||||
|
||||
/**
|
||||
* The root node in the tree. |
||||
*/ |
||||
NodePtr* root; |
||||
|
||||
/**
|
||||
* Array of indices to vectors in the dataset. |
||||
*/ |
||||
int** indices; |
||||
|
||||
|
||||
/**
|
||||
* The distance |
||||
*/ |
||||
Distance distance; |
||||
|
||||
/**
|
||||
* 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; |
||||
|
||||
/**
|
||||
* Memory occupied by the index. |
||||
*/ |
||||
int memoryCounter; |
||||
|
||||
/** index parameters */ |
||||
int branching_; |
||||
int trees_; |
||||
flann_centers_init_t centers_init_; |
||||
int leaf_size_; |
||||
|
||||
|
||||
}; |
||||
|
||||
} |
||||
|
||||
#endif /* OPENCV_FLANN_HIERARCHICAL_CLUSTERING_INDEX_H_ */ |
File diff suppressed because it is too large
Load Diff
@ -1,642 +0,0 @@ |
||||
/***********************************************************************
|
||||
* 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 <algorithm> |
||||
#include <map> |
||||
#include <cassert> |
||||
#include <cstring> |
||||
|
||||
#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 <typename Distance> |
||||
class KDTreeSingleIndex : public NNIndex<Distance> |
||||
{ |
||||
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<ElementType>& inputData, const IndexParams& params = KDTreeSingleIndexParams(), |
||||
Distance d = Distance() ) : |
||||
dataset_(inputData), index_params_(params), distance_(d) |
||||
{ |
||||
size_ = dataset_.rows; |
||||
dim_ = dataset_.cols; |
||||
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] = 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, size_, root_bbox_ ); // construct the tree
|
||||
|
||||
if (reorder_) { |
||||
delete[] data_.data; |
||||
data_ = cvflann::Matrix<ElementType>(new ElementType[size_*dim_], size_, dim_); |
||||
for (size_t i=0; i<size_; ++i) { |
||||
for (size_t j=0; j<dim_; ++j) { |
||||
data_[i][j] = dataset_[vind_[i]][j]; |
||||
} |
||||
} |
||||
} |
||||
else { |
||||
data_ = dataset_; |
||||
} |
||||
} |
||||
|
||||
flann_algorithm_t getType() const |
||||
{ |
||||
return FLANN_INDEX_KDTREE_SINGLE; |
||||
} |
||||
|
||||
|
||||
void saveIndex(FILE* stream) |
||||
{ |
||||
save_value(stream, size_); |
||||
save_value(stream, dim_); |
||||
save_value(stream, root_bbox_); |
||||
save_value(stream, reorder_); |
||||
save_value(stream, leaf_max_size_); |
||||
save_value(stream, vind_); |
||||
if (reorder_) { |
||||
save_value(stream, data_); |
||||
} |
||||
save_tree(stream, root_node_); |
||||
} |
||||
|
||||
|
||||
void loadIndex(FILE* stream) |
||||
{ |
||||
load_value(stream, size_); |
||||
load_value(stream, dim_); |
||||
load_value(stream, root_bbox_); |
||||
load_value(stream, reorder_); |
||||
load_value(stream, leaf_max_size_); |
||||
load_value(stream, vind_); |
||||
if (reorder_) { |
||||
load_value(stream, data_); |
||||
} |
||||
else { |
||||
data_ = dataset_; |
||||
} |
||||
load_tree(stream, root_node_); |
||||
|
||||
|
||||
index_params_["algorithm"] = getType(); |
||||
index_params_["leaf_max_size"] = leaf_max_size_; |
||||
index_params_["reorder"] = reorder_; |
||||
} |
||||
|
||||
/**
|
||||
* Returns size of index. |
||||
*/ |
||||
size_t size() const |
||||
{ |
||||
return size_; |
||||
} |
||||
|
||||
/**
|
||||
* Returns the length of an index feature. |
||||
*/ |
||||
size_t veclen() const |
||||
{ |
||||
return dim_; |
||||
} |
||||
|
||||
/**
|
||||
* Computes the inde memory usage |
||||
* Returns: memory used by the index |
||||
*/ |
||||
int usedMemory() const |
||||
{ |
||||
return pool_.usedMemory+pool_.wastedMemory+dataset_.rows*sizeof(int); // pool memory and vind array memory
|
||||
} |
||||
|
||||
|
||||
/**
|
||||
* \brief Perform k-nearest neighbor search |
||||
* \param[in] queries The query points for which to find the nearest neighbors |
||||
* \param[out] indices The indices of the nearest neighbors found |
||||
* \param[out] dists Distances to the nearest neighbors found |
||||
* \param[in] knn Number of nearest neighbors to return |
||||
* \param[in] params Search parameters |
||||
*/ |
||||
void knnSearch(const Matrix<ElementType>& queries, Matrix<int>& indices, Matrix<DistanceType>& 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<DistanceType> 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<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) |
||||
{ |
||||
float epsError = 1+get_param(searchParams,"eps",0.0f); |
||||
|
||||
std::vector<DistanceType> dists(dim_,0); |
||||
DistanceType distsq = computeInitialDistances(vec, dists); |
||||
searchLevel(result, vec, root_node_, distsq, dists, epsError); |
||||
} |
||||
|
||||
private: |
||||
|
||||
|
||||
/*--------------------- 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<Interval> BoundingBox; |
||||
|
||||
typedef BranchStruct<NodePtr, DistanceType> 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<Node>(); |
||||
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; i<dim_; ++i) { |
||||
bbox[i].low = dataset_[0][i]; |
||||
bbox[i].high = dataset_[0][i]; |
||||
} |
||||
for (size_t k=1; k<dataset_.rows; ++k) { |
||||
for (size_t i=0; i<dim_; ++i) { |
||||
if (dataset_[k][i]<bbox[i].low) bbox[i].low = dataset_[k][i]; |
||||
if (dataset_[k][i]>bbox[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<Node>(); // 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; i<dim_; ++i) { |
||||
bbox[i].low = dataset_[vind_[left]][i]; |
||||
bbox[i].high = dataset_[vind_[left]][i]; |
||||
} |
||||
for (int k=left+1; k<right; ++k) { |
||||
for (size_t i=0; i<dim_; ++i) { |
||||
if (bbox[i].low>dataset_[vind_[k]][i]) bbox[i].low=dataset_[vind_[k]][i]; |
||||
if (bbox[i].high<dataset_[vind_[k]][i]) bbox[i].high=dataset_[vind_[k]][i]; |
||||
} |
||||
} |
||||
} |
||||
else { |
||||
int idx; |
||||
int cutfeat; |
||||
DistanceType cutval; |
||||
middleSplit_(&vind_[0]+left, right-left, idx, cutfeat, cutval, bbox); |
||||
|
||||
node->divfeat = 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; i<dim_; ++i) { |
||||
bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low); |
||||
bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high); |
||||
} |
||||
} |
||||
|
||||
return node; |
||||
} |
||||
|
||||
void computeMinMax(int* ind, int count, int dim, ElementType& min_elem, ElementType& max_elem) |
||||
{ |
||||
min_elem = dataset_[ind[0]][dim]; |
||||
max_elem = dataset_[ind[0]][dim]; |
||||
for (int i=1; i<count; ++i) { |
||||
ElementType val = dataset_[ind[i]][dim]; |
||||
if (val<min_elem) min_elem = val; |
||||
if (val>max_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; i<dim_; ++i) { |
||||
ElementType span = bbox[i].low-bbox[i].low; |
||||
if (span>max_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; i<dim_; ++i) { |
||||
if (i==k) continue; |
||||
ElementType span = bbox[i].high-bbox[i].low; |
||||
if (span>max_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 (lim2<count/2) index = lim2; |
||||
else index = count/2; |
||||
} |
||||
|
||||
|
||||
void middleSplit_(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox) |
||||
{ |
||||
const float EPS=0.00001f; |
||||
ElementType max_span = bbox[0].high-bbox[0].low; |
||||
for (size_t i=1; i<dim_; ++i) { |
||||
ElementType span = bbox[i].high-bbox[i].low; |
||||
if (span>max_span) { |
||||
max_span = span; |
||||
} |
||||
} |
||||
ElementType max_spread = -1; |
||||
cutfeat = 0; |
||||
for (size_t i=0; i<dim_; ++i) { |
||||
ElementType span = bbox[i].high-bbox[i].low; |
||||
if (span>(ElementType)((1-EPS)*max_span)) { |
||||
ElementType min_elem, max_elem; |
||||
computeMinMax(ind, count, cutfeat, min_elem, max_elem); |
||||
ElementType spread = max_elem-min_elem; |
||||
if (spread>max_spread) { |
||||
cutfeat = 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_val<min_elem) cutval = min_elem; |
||||
else if (split_val>max_elem) cutval = max_elem; |
||||
else cutval = split_val; |
||||
|
||||
int lim1, lim2; |
||||
planeSplit(ind, count, cutfeat, cutval, lim1, lim2); |
||||
|
||||
if (lim1>count/2) index = lim1; |
||||
else if (lim2<count/2) index = lim2; |
||||
else index = count/2; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* Subdivide the list of points by a plane perpendicular on axe corresponding |
||||
* to the 'cutfeat' dimension at 'cutval' position. |
||||
* |
||||
* On return: |
||||
* dataset[ind[0..lim1-1]][cutfeat]<cutval |
||||
* dataset[ind[lim1..lim2-1]][cutfeat]==cutval |
||||
* dataset[ind[lim2..count]][cutfeat]>cutval |
||||
*/ |
||||
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) ++left; |
||||
while (left<=right && dataset_[ind[right]][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<DistanceType>& 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<DistanceType>& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq, |
||||
std::vector<DistanceType>& 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; i<node->right; ++i) { |
||||
int index = reorder_ ? i : vind_[i]; |
||||
DistanceType dist = distance_(vec, data_[index], dim_, worst_dist); |
||||
if (dist<worst_dist) { |
||||
result_set.addPoint(dist,vind_[i]); |
||||
} |
||||
} |
||||
return; |
||||
} |
||||
|
||||
/* Which child branch should be taken first? */ |
||||
int idx = node->divfeat; |
||||
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<ElementType> dataset_; |
||||
|
||||
IndexParams index_params_; |
||||
|
||||
int leaf_max_size_; |
||||
bool reorder_; |
||||
|
||||
|
||||
/**
|
||||
* Array of indices to vectors in the dataset. |
||||
*/ |
||||
std::vector<int> vind_; |
||||
|
||||
Matrix<ElementType> 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_
|
File diff suppressed because it is too large
Load Diff
@ -1,388 +0,0 @@ |
||||
/***********************************************************************
|
||||
* 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. |
||||
*************************************************************************/ |
||||
|
||||
/***********************************************************************
|
||||
* Author: Vincent Rabaud |
||||
*************************************************************************/ |
||||
|
||||
#ifndef OPENCV_FLANN_LSH_INDEX_H_ |
||||
#define OPENCV_FLANN_LSH_INDEX_H_ |
||||
|
||||
#include <algorithm> |
||||
#include <cassert> |
||||
#include <cstring> |
||||
#include <map> |
||||
#include <vector> |
||||
|
||||
#include "general.h" |
||||
#include "nn_index.h" |
||||
#include "matrix.h" |
||||
#include "result_set.h" |
||||
#include "heap.h" |
||||
#include "lsh_table.h" |
||||
#include "allocator.h" |
||||
#include "random.h" |
||||
#include "saving.h" |
||||
|
||||
namespace cvflann |
||||
{ |
||||
|
||||
struct LshIndexParams : public IndexParams |
||||
{ |
||||
LshIndexParams(unsigned int table_number, unsigned int key_size, unsigned int multi_probe_level) |
||||
{ |
||||
(* this)["algorithm"] = FLANN_INDEX_LSH; |
||||
// The number of hash tables to use
|
||||
(*this)["table_number"] = table_number; |
||||
// The length of the key in the hash tables
|
||||
(*this)["key_size"] = key_size; |
||||
// Number of levels to use in multi-probe (0 for standard LSH)
|
||||
(*this)["multi_probe_level"] = multi_probe_level; |
||||
} |
||||
}; |
||||
|
||||
/**
|
||||
* Randomized kd-tree index |
||||
* |
||||
* Contains the k-d trees and other information for indexing a set of points |
||||
* for nearest-neighbor matching. |
||||
*/ |
||||
template<typename Distance> |
||||
class LshIndex : public NNIndex<Distance> |
||||
{ |
||||
public: |
||||
typedef typename Distance::ElementType ElementType; |
||||
typedef typename Distance::ResultType DistanceType; |
||||
|
||||
/** Constructor
|
||||
* @param input_data dataset with the input features |
||||
* @param params parameters passed to the LSH algorithm |
||||
* @param d the distance used |
||||
*/ |
||||
LshIndex(const Matrix<ElementType>& input_data, const IndexParams& params = LshIndexParams(), |
||||
Distance d = Distance()) : |
||||
dataset_(input_data), index_params_(params), distance_(d) |
||||
{ |
||||
table_number_ = get_param<unsigned int>(index_params_,"table_number",12); |
||||
key_size_ = get_param<unsigned int>(index_params_,"key_size",20); |
||||
multi_probe_level_ = get_param<unsigned int>(index_params_,"multi_probe_level",2); |
||||
|
||||
feature_size_ = dataset_.cols; |
||||
fill_xor_mask(0, key_size_, multi_probe_level_, xor_masks_); |
||||
} |
||||
|
||||
|
||||
LshIndex(const LshIndex&); |
||||
LshIndex& operator=(const LshIndex&); |
||||
|
||||
/**
|
||||
* Builds the index |
||||
*/ |
||||
void buildIndex() |
||||
{ |
||||
tables_.resize(table_number_); |
||||
for (unsigned int i = 0; i < table_number_; ++i) { |
||||
lsh::LshTable<ElementType>& table = tables_[i]; |
||||
table = lsh::LshTable<ElementType>(feature_size_, key_size_); |
||||
|
||||
// Add the features to the table
|
||||
table.add(dataset_); |
||||
} |
||||
} |
||||
|
||||
flann_algorithm_t getType() const |
||||
{ |
||||
return FLANN_INDEX_LSH; |
||||
} |
||||
|
||||
|
||||
void saveIndex(FILE* stream) |
||||
{ |
||||
save_value(stream,table_number_); |
||||
save_value(stream,key_size_); |
||||
save_value(stream,multi_probe_level_); |
||||
save_value(stream, dataset_); |
||||
} |
||||
|
||||
void loadIndex(FILE* stream) |
||||
{ |
||||
load_value(stream, table_number_); |
||||
load_value(stream, key_size_); |
||||
load_value(stream, multi_probe_level_); |
||||
load_value(stream, dataset_); |
||||
// Building the index is so fast we can afford not storing it
|
||||
buildIndex(); |
||||
|
||||
index_params_["algorithm"] = getType(); |
||||
index_params_["table_number"] = table_number_; |
||||
index_params_["key_size"] = key_size_; |
||||
index_params_["multi_probe_level"] = multi_probe_level_; |
||||
} |
||||
|
||||
/**
|
||||
* Returns size of index. |
||||
*/ |
||||
size_t size() const |
||||
{ |
||||
return dataset_.rows; |
||||
} |
||||
|
||||
/**
|
||||
* Returns the length of an index feature. |
||||
*/ |
||||
size_t veclen() const |
||||
{ |
||||
return feature_size_; |
||||
} |
||||
|
||||
/**
|
||||
* Computes the index memory usage |
||||
* Returns: memory used by the index |
||||
*/ |
||||
int usedMemory() const |
||||
{ |
||||
return dataset_.rows * sizeof(int); |
||||
} |
||||
|
||||
|
||||
IndexParams getParameters() const |
||||
{ |
||||
return index_params_; |
||||
} |
||||
|
||||
/**
|
||||
* \brief Perform k-nearest neighbor search |
||||
* \param[in] queries The query points for which to find the nearest neighbors |
||||
* \param[out] indices The indices of the nearest neighbors found |
||||
* \param[out] dists Distances to the nearest neighbors found |
||||
* \param[in] knn Number of nearest neighbors to return |
||||
* \param[in] params Search parameters |
||||
*/ |
||||
virtual void knnSearch(const Matrix<ElementType>& queries, Matrix<int>& indices, Matrix<DistanceType>& 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); |
||||
|
||||
|
||||
KNNUniqueResultSet<DistanceType> resultSet(knn); |
||||
for (size_t i = 0; i < queries.rows; i++) { |
||||
resultSet.clear(); |
||||
findNeighbors(resultSet, queries[i], params); |
||||
if (get_param(params,"sorted",true)) resultSet.sortAndCopy(indices[i], dists[i], knn); |
||||
else resultSet.copy(indices[i], dists[i], knn); |
||||
} |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* 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<DistanceType>& result, const ElementType* vec, const SearchParams& /*searchParams*/) |
||||
{ |
||||
getNeighbors(vec, result); |
||||
} |
||||
|
||||
private: |
||||
/** Defines the comparator on score and index
|
||||
*/ |
||||
typedef std::pair<float, unsigned int> ScoreIndexPair; |
||||
struct SortScoreIndexPairOnSecond |
||||
{ |
||||
bool operator()(const ScoreIndexPair& left, const ScoreIndexPair& right) const |
||||
{ |
||||
return left.second < right.second; |
||||
} |
||||
}; |
||||
|
||||
/** Fills the different xor masks to use when getting the neighbors in multi-probe LSH
|
||||
* @param key the key we build neighbors from |
||||
* @param lowest_index the lowest index of the bit set |
||||
* @param level the multi-probe level we are at |
||||
* @param xor_masks all the xor mask |
||||
*/ |
||||
void fill_xor_mask(lsh::BucketKey key, int lowest_index, unsigned int level, |
||||
std::vector<lsh::BucketKey>& xor_masks) |
||||
{ |
||||
xor_masks.push_back(key); |
||||
if (level == 0) return; |
||||
for (int index = lowest_index - 1; index >= 0; --index) { |
||||
// Create a new key
|
||||
lsh::BucketKey new_key = key | (1 << index); |
||||
fill_xor_mask(new_key, index, level - 1, xor_masks); |
||||
} |
||||
} |
||||
|
||||
/** Performs the approximate nearest-neighbor search.
|
||||
* @param vec the feature to analyze |
||||
* @param do_radius flag indicating if we check the radius too |
||||
* @param radius the radius if it is a radius search |
||||
* @param do_k flag indicating if we limit the number of nn |
||||
* @param k_nn the number of nearest neighbors |
||||
* @param checked_average used for debugging |
||||
*/ |
||||
void getNeighbors(const ElementType* vec, bool do_radius, float radius, bool do_k, unsigned int k_nn, |
||||
float& checked_average) |
||||
{ |
||||
static std::vector<ScoreIndexPair> score_index_heap; |
||||
|
||||
if (do_k) { |
||||
unsigned int worst_score = std::numeric_limits<unsigned int>::max(); |
||||
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table = tables_.begin(); |
||||
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table_end = tables_.end(); |
||||
for (; table != table_end; ++table) { |
||||
size_t key = table->getKey(vec); |
||||
std::vector<lsh::BucketKey>::const_iterator xor_mask = xor_masks_.begin(); |
||||
std::vector<lsh::BucketKey>::const_iterator xor_mask_end = xor_masks_.end(); |
||||
for (; xor_mask != xor_mask_end; ++xor_mask) { |
||||
size_t sub_key = key ^ (*xor_mask); |
||||
const lsh::Bucket* bucket = table->getBucketFromKey(sub_key); |
||||
if (bucket == 0) continue; |
||||
|
||||
// Go over each descriptor index
|
||||
std::vector<lsh::FeatureIndex>::const_iterator training_index = bucket->begin(); |
||||
std::vector<lsh::FeatureIndex>::const_iterator last_training_index = bucket->end(); |
||||
DistanceType hamming_distance; |
||||
|
||||
// Process the rest of the candidates
|
||||
for (; training_index < last_training_index; ++training_index) { |
||||
hamming_distance = distance_(vec, dataset_[*training_index], dataset_.cols); |
||||
|
||||
if (hamming_distance < worst_score) { |
||||
// Insert the new element
|
||||
score_index_heap.push_back(ScoreIndexPair(hamming_distance, training_index)); |
||||
std::push_heap(score_index_heap.begin(), score_index_heap.end()); |
||||
|
||||
if (score_index_heap.size() > (unsigned int)k_nn) { |
||||
// Remove the highest distance value as we have too many elements
|
||||
std::pop_heap(score_index_heap.begin(), score_index_heap.end()); |
||||
score_index_heap.pop_back(); |
||||
// Keep track of the worst score
|
||||
worst_score = score_index_heap.front().first; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
else { |
||||
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table = tables_.begin(); |
||||
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table_end = tables_.end(); |
||||
for (; table != table_end; ++table) { |
||||
size_t key = table->getKey(vec); |
||||
std::vector<lsh::BucketKey>::const_iterator xor_mask = xor_masks_.begin(); |
||||
std::vector<lsh::BucketKey>::const_iterator xor_mask_end = xor_masks_.end(); |
||||
for (; xor_mask != xor_mask_end; ++xor_mask) { |
||||
size_t sub_key = key ^ (*xor_mask); |
||||
const lsh::Bucket* bucket = table->getBucketFromKey(sub_key); |
||||
if (bucket == 0) continue; |
||||
|
||||
// Go over each descriptor index
|
||||
std::vector<lsh::FeatureIndex>::const_iterator training_index = bucket->begin(); |
||||
std::vector<lsh::FeatureIndex>::const_iterator last_training_index = bucket->end(); |
||||
DistanceType hamming_distance; |
||||
|
||||
// Process the rest of the candidates
|
||||
for (; training_index < last_training_index; ++training_index) { |
||||
// Compute the Hamming distance
|
||||
hamming_distance = distance_(vec, dataset_[*training_index], dataset_.cols); |
||||
if (hamming_distance < radius) score_index_heap.push_back(ScoreIndexPair(hamming_distance, training_index)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Performs the approximate nearest-neighbor search.
|
||||
* This is a slower version than the above as it uses the ResultSet |
||||
* @param vec the feature to analyze |
||||
*/ |
||||
void getNeighbors(const ElementType* vec, ResultSet<DistanceType>& result) |
||||
{ |
||||
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table = tables_.begin(); |
||||
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table_end = tables_.end(); |
||||
for (; table != table_end; ++table) { |
||||
size_t key = table->getKey(vec); |
||||
std::vector<lsh::BucketKey>::const_iterator xor_mask = xor_masks_.begin(); |
||||
std::vector<lsh::BucketKey>::const_iterator xor_mask_end = xor_masks_.end(); |
||||
for (; xor_mask != xor_mask_end; ++xor_mask) { |
||||
size_t sub_key = key ^ (*xor_mask); |
||||
const lsh::Bucket* bucket = table->getBucketFromKey(sub_key); |
||||
if (bucket == 0) continue; |
||||
|
||||
// Go over each descriptor index
|
||||
std::vector<lsh::FeatureIndex>::const_iterator training_index = bucket->begin(); |
||||
std::vector<lsh::FeatureIndex>::const_iterator last_training_index = bucket->end(); |
||||
DistanceType hamming_distance; |
||||
|
||||
// Process the rest of the candidates
|
||||
for (; training_index < last_training_index; ++training_index) { |
||||
// Compute the Hamming distance
|
||||
hamming_distance = distance_(vec, dataset_[*training_index], dataset_.cols); |
||||
result.addPoint(hamming_distance, *training_index); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** The different hash tables */ |
||||
std::vector<lsh::LshTable<ElementType> > tables_; |
||||
|
||||
/** The data the LSH tables where built from */ |
||||
Matrix<ElementType> dataset_; |
||||
|
||||
/** The size of the features (as ElementType[]) */ |
||||
unsigned int feature_size_; |
||||
|
||||
IndexParams index_params_; |
||||
|
||||
/** table number */ |
||||
unsigned int table_number_; |
||||
/** key size */ |
||||
unsigned int key_size_; |
||||
/** How far should we look for neighbors in multi-probe LSH */ |
||||
unsigned int multi_probe_level_; |
||||
|
||||
/** The XOR masks to apply to a key to get the neighboring buckets */ |
||||
std::vector<lsh::BucketKey> xor_masks_; |
||||
|
||||
Distance distance_; |
||||
}; |
||||
} |
||||
|
||||
#endif //OPENCV_FLANN_LSH_INDEX_H_
|
@ -1,477 +0,0 @@ |
||||
/***********************************************************************
|
||||
* 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. |
||||
*************************************************************************/ |
||||
|
||||
/***********************************************************************
|
||||
* Author: Vincent Rabaud |
||||
*************************************************************************/ |
||||
|
||||
#ifndef OPENCV_FLANN_LSH_TABLE_H_ |
||||
#define OPENCV_FLANN_LSH_TABLE_H_ |
||||
|
||||
#include <algorithm> |
||||
#include <iostream> |
||||
#include <iomanip> |
||||
#include <limits.h> |
||||
// TODO as soon as we use C++0x, use the code in USE_UNORDERED_MAP
|
||||
#if USE_UNORDERED_MAP |
||||
#include <unordered_map> |
||||
#else |
||||
#include <map> |
||||
#endif |
||||
#include <math.h> |
||||
#include <stddef.h> |
||||
|
||||
#include "dynamic_bitset.h" |
||||
#include "matrix.h" |
||||
|
||||
namespace cvflann |
||||
{ |
||||
|
||||
namespace lsh |
||||
{ |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** What is stored in an LSH bucket
|
||||
*/ |
||||
typedef uint32_t FeatureIndex; |
||||
/** The id from which we can get a bucket back in an LSH table
|
||||
*/ |
||||
typedef unsigned int BucketKey; |
||||
|
||||
/** A bucket in an LSH table
|
||||
*/ |
||||
typedef std::vector<FeatureIndex> Bucket; |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** POD for stats about an LSH table
|
||||
*/ |
||||
struct LshStats |
||||
{ |
||||
std::vector<unsigned int> bucket_sizes_; |
||||
size_t n_buckets_; |
||||
size_t bucket_size_mean_; |
||||
size_t bucket_size_median_; |
||||
size_t bucket_size_min_; |
||||
size_t bucket_size_max_; |
||||
size_t bucket_size_std_dev; |
||||
/** Each contained vector contains three value: beginning/end for interval, number of elements in the bin
|
||||
*/ |
||||
std::vector<std::vector<unsigned int> > size_histogram_; |
||||
}; |
||||
|
||||
/** Overload the << operator for LshStats
|
||||
* @param out the streams |
||||
* @param stats the stats to display |
||||
* @return the streams |
||||
*/ |
||||
inline std::ostream& operator <<(std::ostream& out, const LshStats& stats) |
||||
{ |
||||
size_t w = 20; |
||||
out << "Lsh Table Stats:\n" << std::setw(w) << std::setiosflags(std::ios::right) << "N buckets : " |
||||
<< stats.n_buckets_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "mean size : " |
||||
<< std::setiosflags(std::ios::left) << stats.bucket_size_mean_ << "\n" << std::setw(w) |
||||
<< std::setiosflags(std::ios::right) << "median size : " << stats.bucket_size_median_ << "\n" << std::setw(w) |
||||
<< std::setiosflags(std::ios::right) << "min size : " << std::setiosflags(std::ios::left) |
||||
<< stats.bucket_size_min_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "max size : " |
||||
<< std::setiosflags(std::ios::left) << stats.bucket_size_max_; |
||||
|
||||
// Display the histogram
|
||||
out << std::endl << std::setw(w) << std::setiosflags(std::ios::right) << "histogram : " |
||||
<< std::setiosflags(std::ios::left); |
||||
for (std::vector<std::vector<unsigned int> >::const_iterator iterator = stats.size_histogram_.begin(), end = |
||||
stats.size_histogram_.end(); iterator != end; ++iterator) out << (*iterator)[0] << "-" << (*iterator)[1] << ": " << (*iterator)[2] << ", "; |
||||
|
||||
return out; |
||||
} |
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Lsh hash table. As its key is a sub-feature, and as usually
|
||||
* the size of it is pretty small, we keep it as a continuous memory array. |
||||
* The value is an index in the corpus of features (we keep it as an unsigned |
||||
* int for pure memory reasons, it could be a size_t) |
||||
*/ |
||||
template<typename ElementType> |
||||
class LshTable |
||||
{ |
||||
public: |
||||
/** A container of all the feature indices. Optimized for space
|
||||
*/ |
||||
#if USE_UNORDERED_MAP |
||||
typedef std::unordered_map<BucketKey, Bucket> BucketsSpace; |
||||
#else |
||||
typedef std::map<BucketKey, Bucket> BucketsSpace; |
||||
#endif |
||||
|
||||
/** A container of all the feature indices. Optimized for speed
|
||||
*/ |
||||
typedef std::vector<Bucket> BucketsSpeed; |
||||
|
||||
/** Default constructor
|
||||
*/ |
||||
LshTable() |
||||
{ |
||||
} |
||||
|
||||
/** Default constructor
|
||||
* Create the mask and allocate the memory |
||||
* @param feature_size is the size of the feature (considered as a ElementType[]) |
||||
* @param key_size is the number of bits that are turned on in the feature |
||||
*/ |
||||
LshTable(unsigned int /*feature_size*/, unsigned int /*key_size*/) |
||||
{ |
||||
std::cerr << "LSH is not implemented for that type" << std::endl; |
||||
throw; |
||||
} |
||||
|
||||
/** Add a feature to the table
|
||||
* @param value the value to store for that feature |
||||
* @param feature the feature itself |
||||
*/ |
||||
void add(unsigned int value, const ElementType* feature) |
||||
{ |
||||
// Add the value to the corresponding bucket
|
||||
BucketKey key = getKey(feature); |
||||
|
||||
switch (speed_level_) { |
||||
case kArray: |
||||
// That means we get the buckets from an array
|
||||
buckets_speed_[key].push_back(value); |
||||
break; |
||||
case kBitsetHash: |
||||
// That means we can check the bitset for the presence of a key
|
||||
key_bitset_.set(key); |
||||
buckets_space_[key].push_back(value); |
||||
break; |
||||
case kHash: |
||||
{ |
||||
// That means we have to check for the hash table for the presence of a key
|
||||
buckets_space_[key].push_back(value); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Add a set of features to the table
|
||||
* @param dataset the values to store |
||||
*/ |
||||
void add(Matrix<ElementType> dataset) |
||||
{ |
||||
#if USE_UNORDERED_MAP |
||||
if (!use_speed_) buckets_space_.rehash((buckets_space_.size() + dataset.rows) * 1.2); |
||||
#endif |
||||
// Add the features to the table
|
||||
for (unsigned int i = 0; i < dataset.rows; ++i) add(i, dataset[i]); |
||||
// Now that the table is full, optimize it for speed/space
|
||||
optimize(); |
||||
} |
||||
|
||||
/** Get a bucket given the key
|
||||
* @param key |
||||
* @return |
||||
*/ |
||||
inline const Bucket* getBucketFromKey(BucketKey key) const |
||||
{ |
||||
// Generate other buckets
|
||||
switch (speed_level_) { |
||||
case kArray: |
||||
// That means we get the buckets from an array
|
||||
return &buckets_speed_[key]; |
||||
break; |
||||
case kBitsetHash: |
||||
// That means we can check the bitset for the presence of a key
|
||||
if (key_bitset_.test(key)) return &buckets_space_.at(key); |
||||
else return 0; |
||||
break; |
||||
case kHash: |
||||
{ |
||||
// That means we have to check for the hash table for the presence of a key
|
||||
BucketsSpace::const_iterator bucket_it, bucket_end = buckets_space_.end(); |
||||
bucket_it = buckets_space_.find(key); |
||||
// Stop here if that bucket does not exist
|
||||
if (bucket_it == bucket_end) return 0; |
||||
else return &bucket_it->second; |
||||
break; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/** Compute the sub-signature of a feature
|
||||
*/ |
||||
size_t getKey(const ElementType* /*feature*/) const |
||||
{ |
||||
std::cerr << "LSH is not implemented for that type" << std::endl; |
||||
throw; |
||||
return 1; |
||||
} |
||||
|
||||
/** Get statistics about the table
|
||||
* @return |
||||
*/ |
||||
LshStats getStats() const; |
||||
|
||||
private: |
||||
/** defines the speed fo the implementation
|
||||
* kArray uses a vector for storing data |
||||
* kBitsetHash uses a hash map but checks for the validity of a key with a bitset |
||||
* kHash uses a hash map only |
||||
*/ |
||||
enum SpeedLevel |
||||
{ |
||||
kArray, kBitsetHash, kHash |
||||
}; |
||||
|
||||
/** Initialize some variables
|
||||
*/ |
||||
void initialize(size_t key_size) |
||||
{ |
||||
speed_level_ = kHash; |
||||
key_size_ = key_size; |
||||
} |
||||
|
||||
/** Optimize the table for speed/space
|
||||
*/ |
||||
void optimize() |
||||
{ |
||||
// If we are already using the fast storage, no need to do anything
|
||||
if (speed_level_ == kArray) return; |
||||
|
||||
// Use an array if it will be more than half full
|
||||
if (buckets_space_.size() > (unsigned int)((1 << key_size_) / 2)) { |
||||
speed_level_ = kArray; |
||||
// Fill the array version of it
|
||||
buckets_speed_.resize(1 << key_size_); |
||||
for (BucketsSpace::const_iterator key_bucket = buckets_space_.begin(); key_bucket != buckets_space_.end(); ++key_bucket) buckets_speed_[key_bucket->first] = key_bucket->second; |
||||
|
||||
// Empty the hash table
|
||||
buckets_space_.clear(); |
||||
return; |
||||
} |
||||
|
||||
// If the bitset is going to use less than 10% of the RAM of the hash map (at least 1 size_t for the key and two
|
||||
// for the vector) or less than 512MB (key_size_ <= 30)
|
||||
if (((std::max(buckets_space_.size(), buckets_speed_.size()) * CHAR_BIT * 3 * sizeof(BucketKey)) / 10 |
||||
>= size_t(1 << key_size_)) || (key_size_ <= 32)) { |
||||
speed_level_ = kBitsetHash; |
||||
key_bitset_.resize(1 << key_size_); |
||||
key_bitset_.reset(); |
||||
// Try with the BucketsSpace
|
||||
for (BucketsSpace::const_iterator key_bucket = buckets_space_.begin(); key_bucket != buckets_space_.end(); ++key_bucket) key_bitset_.set(key_bucket->first); |
||||
} |
||||
else { |
||||
speed_level_ = kHash; |
||||
key_bitset_.clear(); |
||||
} |
||||
} |
||||
|
||||
/** The vector of all the buckets if they are held for speed
|
||||
*/ |
||||
BucketsSpeed buckets_speed_; |
||||
|
||||
/** The hash table of all the buckets in case we cannot use the speed version
|
||||
*/ |
||||
BucketsSpace buckets_space_; |
||||
|
||||
/** What is used to store the data */ |
||||
SpeedLevel speed_level_; |
||||
|
||||
/** If the subkey is small enough, it will keep track of which subkeys are set through that bitset
|
||||
* That is just a speedup so that we don't look in the hash table (which can be mush slower that checking a bitset) |
||||
*/ |
||||
DynamicBitset key_bitset_; |
||||
|
||||
/** The size of the sub-signature in bits
|
||||
*/ |
||||
unsigned int key_size_; |
||||
|
||||
// Members only used for the unsigned char specialization
|
||||
/** The mask to apply to a feature to get the hash key
|
||||
* Only used in the unsigned char case |
||||
*/ |
||||
std::vector<size_t> mask_; |
||||
}; |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Specialization for unsigned char
|
||||
|
||||
template<> |
||||
inline LshTable<unsigned char>::LshTable(unsigned int feature_size, unsigned int subsignature_size) |
||||
{ |
||||
initialize(subsignature_size); |
||||
// Allocate the mask
|
||||
mask_ = std::vector<size_t>((size_t)ceil((float)(feature_size * sizeof(char)) / (float)sizeof(size_t)), 0); |
||||
|
||||
// A bit brutal but fast to code
|
||||
std::vector<size_t> indices(feature_size * CHAR_BIT); |
||||
for (size_t i = 0; i < feature_size * CHAR_BIT; ++i) indices[i] = i; |
||||
std::random_shuffle(indices.begin(), indices.end()); |
||||
|
||||
// Generate a random set of order of subsignature_size_ bits
|
||||
for (unsigned int i = 0; i < key_size_; ++i) { |
||||
size_t index = indices[i]; |
||||
|
||||
// Set that bit in the mask
|
||||
size_t divisor = CHAR_BIT * sizeof(size_t); |
||||
size_t idx = index / divisor; //pick the right size_t index
|
||||
mask_[idx] |= size_t(1) << (index % divisor); //use modulo to find the bit offset
|
||||
} |
||||
|
||||
// Set to 1 if you want to display the mask for debug
|
||||
#if 0 |
||||
{ |
||||
size_t bcount = 0; |
||||
BOOST_FOREACH(size_t mask_block, mask_){ |
||||
out << std::setw(sizeof(size_t) * CHAR_BIT / 4) << std::setfill('0') << std::hex << mask_block |
||||
<< std::endl; |
||||
bcount += __builtin_popcountll(mask_block); |
||||
} |
||||
out << "bit count : " << std::dec << bcount << std::endl; |
||||
out << "mask size : " << mask_.size() << std::endl; |
||||
return out; |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
/** Return the Subsignature of a feature
|
||||
* @param feature the feature to analyze |
||||
*/ |
||||
template<> |
||||
inline size_t LshTable<unsigned char>::getKey(const unsigned char* feature) const |
||||
{ |
||||
// no need to check if T is dividable by sizeof(size_t) like in the Hamming
|
||||
// distance computation as we have a mask
|
||||
const size_t* feature_block_ptr = reinterpret_cast<const size_t*> (feature); |
||||
|
||||
// Figure out the subsignature of the feature
|
||||
// Given the feature ABCDEF, and the mask 001011, the output will be
|
||||
// 000CEF
|
||||
size_t subsignature = 0; |
||||
size_t bit_index = 1; |
||||
|
||||
for (std::vector<size_t>::const_iterator pmask_block = mask_.begin(); pmask_block != mask_.end(); ++pmask_block) { |
||||
// get the mask and signature blocks
|
||||
size_t feature_block = *feature_block_ptr; |
||||
size_t mask_block = *pmask_block; |
||||
while (mask_block) { |
||||
// Get the lowest set bit in the mask block
|
||||
size_t lowest_bit = mask_block & (-(ptrdiff_t)mask_block); |
||||
// Add it to the current subsignature if necessary
|
||||
subsignature += (feature_block & lowest_bit) ? bit_index : 0; |
||||
// Reset the bit in the mask block
|
||||
mask_block ^= lowest_bit; |
||||
// increment the bit index for the subsignature
|
||||
bit_index <<= 1; |
||||
} |
||||
// Check the next feature block
|
||||
++feature_block_ptr; |
||||
} |
||||
return subsignature; |
||||
} |
||||
|
||||
template<> |
||||
inline LshStats LshTable<unsigned char>::getStats() const |
||||
{ |
||||
LshStats stats; |
||||
stats.bucket_size_mean_ = 0; |
||||
if ((buckets_speed_.empty()) && (buckets_space_.empty())) { |
||||
stats.n_buckets_ = 0; |
||||
stats.bucket_size_median_ = 0; |
||||
stats.bucket_size_min_ = 0; |
||||
stats.bucket_size_max_ = 0; |
||||
return stats; |
||||
} |
||||
|
||||
if (!buckets_speed_.empty()) { |
||||
for (BucketsSpeed::const_iterator pbucket = buckets_speed_.begin(); pbucket != buckets_speed_.end(); ++pbucket) { |
||||
stats.bucket_sizes_.push_back(pbucket->size()); |
||||
stats.bucket_size_mean_ += pbucket->size(); |
||||
} |
||||
stats.bucket_size_mean_ /= buckets_speed_.size(); |
||||
stats.n_buckets_ = buckets_speed_.size(); |
||||
} |
||||
else { |
||||
for (BucketsSpace::const_iterator x = buckets_space_.begin(); x != buckets_space_.end(); ++x) { |
||||
stats.bucket_sizes_.push_back(x->second.size()); |
||||
stats.bucket_size_mean_ += x->second.size(); |
||||
} |
||||
stats.bucket_size_mean_ /= buckets_space_.size(); |
||||
stats.n_buckets_ = buckets_space_.size(); |
||||
} |
||||
|
||||
std::sort(stats.bucket_sizes_.begin(), stats.bucket_sizes_.end()); |
||||
|
||||
// BOOST_FOREACH(int size, stats.bucket_sizes_)
|
||||
// std::cout << size << " ";
|
||||
// std::cout << std::endl;
|
||||
stats.bucket_size_median_ = stats.bucket_sizes_[stats.bucket_sizes_.size() / 2]; |
||||
stats.bucket_size_min_ = stats.bucket_sizes_.front(); |
||||
stats.bucket_size_max_ = stats.bucket_sizes_.back(); |
||||
|
||||
// TODO compute mean and std
|
||||
/*float mean, stddev;
|
||||
stats.bucket_size_mean_ = mean; |
||||
stats.bucket_size_std_dev = stddev;*/ |
||||
|
||||
// Include a histogram of the buckets
|
||||
unsigned int bin_start = 0; |
||||
unsigned int bin_end = 20; |
||||
bool is_new_bin = true; |
||||
for (std::vector<unsigned int>::iterator iterator = stats.bucket_sizes_.begin(), end = stats.bucket_sizes_.end(); iterator |
||||
!= end; ) |
||||
if (*iterator < bin_end) { |
||||
if (is_new_bin) { |
||||
stats.size_histogram_.push_back(std::vector<unsigned int>(3, 0)); |
||||
stats.size_histogram_.back()[0] = bin_start; |
||||
stats.size_histogram_.back()[1] = bin_end - 1; |
||||
is_new_bin = false; |
||||
} |
||||
++stats.size_histogram_.back()[2]; |
||||
++iterator; |
||||
} |
||||
else { |
||||
bin_start += 20; |
||||
bin_end += 20; |
||||
is_new_bin = true; |
||||
} |
||||
|
||||
return stats; |
||||
} |
||||
|
||||
// End the two namespaces
|
||||
} |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif /* OPENCV_FLANN_LSH_TABLE_H_ */ |
@ -1,97 +0,0 @@ |
||||
/***********************************************************************
|
||||
* Software License Agreement (BSD License) |
||||
* |
||||
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. |
||||
* Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. |
||||
* |
||||
* 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_PARAMS_H_ |
||||
#define OPENCV_FLANN_PARAMS_H_ |
||||
|
||||
#include "any.h" |
||||
#include "general.h" |
||||
#include <iostream> |
||||
#include <map> |
||||
|
||||
|
||||
namespace cvflann |
||||
{ |
||||
|
||||
typedef cdiggins::any any; |
||||
typedef std::map<std::string, any> IndexParams; |
||||
|
||||
struct SearchParams : public IndexParams |
||||
{ |
||||
SearchParams(int checks = 32, float eps = 0, bool sorted = true ) |
||||
{ |
||||
// how many leafs to visit when searching for neighbours (-1 for unlimited)
|
||||
(*this)["checks"] = checks; |
||||
// search for eps-approximate neighbours (default: 0)
|
||||
(*this)["eps"] = eps; |
||||
// only for radius search, require neighbours sorted by distance (default: true)
|
||||
(*this)["sorted"] = sorted; |
||||
} |
||||
}; |
||||
|
||||
|
||||
template<typename T> |
||||
T get_param(const IndexParams& params, std::string name, const T& default_value) |
||||
{ |
||||
IndexParams::const_iterator it = params.find(name); |
||||
if (it != params.end()) { |
||||
return it->second.cast<T>(); |
||||
} |
||||
else { |
||||
return default_value; |
||||
} |
||||
} |
||||
|
||||
template<typename T> |
||||
T get_param(const IndexParams& params, std::string name) |
||||
{ |
||||
IndexParams::const_iterator it = params.find(name); |
||||
if (it != params.end()) { |
||||
return it->second.cast<T>(); |
||||
} |
||||
else { |
||||
throw FLANNException(std::string("Missing parameter '")+name+std::string("' in the parameters given")); |
||||
} |
||||
} |
||||
|
||||
inline void print_params(const IndexParams& params) |
||||
{ |
||||
IndexParams::const_iterator it; |
||||
|
||||
for(it=params.begin(); it!=params.end(); ++it) { |
||||
std::cout << it->first << " : " << it->second << std::endl; |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
#endif /* OPENCV_FLANN_PARAMS_H_ */ |
Loading…
Reference in new issue