temporarily reverted to FLANN 1.5 (FLANN 1.6 is put to a separate branch FLANN_1.6 until it's stabilized)

This commit is contained in:
Vadim Pisarevsky 2011-06-20 09:20:17 +00:00
parent 310ed83343
commit 848be8dfe1
36 changed files with 3591 additions and 7549 deletions

View File

@ -27,129 +27,50 @@
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_ALL_INDICES_H_ #ifndef _OPENCV_ALL_INDICES_H_
#define OPENCV_FLANN_ALL_INDICES_H_ #define _OPENCV_ALL_INDICES_H_
#include "general.h" #include "opencv2/flann/general.h"
#include "nn_index.h"
#include "kdtree_index.h"
#include "kdtree_single_index.h"
#include "kmeans_index.h"
#include "composite_index.h"
#include "linear_index.h"
#include "hierarchical_clustering_index.h"
#include "lsh_index.h"
#include "autotuned_index.h"
#include "opencv2/flann/nn_index.h"
#include "opencv2/flann/kdtree_index.h"
#include "opencv2/flann/kmeans_index.h"
#include "opencv2/flann/composite_index.h"
#include "opencv2/flann/linear_index.h"
#include "opencv2/flann/autotuned_index.h"
namespace cvflann namespace cvflann
{ {
template<typename KDTreeCapability, typename VectorSpace, typename Distance> template<typename T>
struct index_creator NNIndex<T>* create_index_by_type(const Matrix<T>& dataset, const IndexParams& params)
{ {
static NNIndex<Distance>* create(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance) flann_algorithm_t index_type = params.getIndexType();
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params, "algorithm");
NNIndex<Distance>* nnIndex; NNIndex<T>* nnIndex;
switch (index_type) { switch (index_type) {
case FLANN_INDEX_LINEAR: case FLANN_INDEX_LINEAR:
nnIndex = new LinearIndex<Distance>(dataset, params, distance); nnIndex = new LinearIndex<T>(dataset, (const LinearIndexParams&)params);
break; break;
case FLANN_INDEX_KDTREE_SINGLE: case FLANN_INDEX_KDTREE:
nnIndex = new KDTreeSingleIndex<Distance>(dataset, params, distance); nnIndex = new KDTreeIndex<T>(dataset, (const KDTreeIndexParams&)params);
break; break;
case FLANN_INDEX_KDTREE: case FLANN_INDEX_KMEANS:
nnIndex = new KDTreeIndex<Distance>(dataset, params, distance); nnIndex = new KMeansIndex<T>(dataset, (const KMeansIndexParams&)params);
break; break;
case FLANN_INDEX_KMEANS: case FLANN_INDEX_COMPOSITE:
nnIndex = new KMeansIndex<Distance>(dataset, params, distance); nnIndex = new CompositeIndex<T>(dataset, (const CompositeIndexParams&) params);
break; break;
case FLANN_INDEX_COMPOSITE: case FLANN_INDEX_AUTOTUNED:
nnIndex = new CompositeIndex<Distance>(dataset, params, distance); nnIndex = new AutotunedIndex<T>(dataset, (const AutotunedIndexParams&) params);
break; break;
case FLANN_INDEX_AUTOTUNED: default:
nnIndex = new AutotunedIndex<Distance>(dataset, params, distance); throw FLANNException("Unknown index type");
break; }
case FLANN_INDEX_HIERARCHICAL:
nnIndex = new HierarchicalClusteringIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_LSH:
nnIndex = new LshIndex<Distance>(dataset, params, distance);
break;
default:
throw FLANNException("Unknown index type");
}
return nnIndex; return nnIndex;
}
};
template<typename VectorSpace, typename Distance>
struct index_creator<False,VectorSpace,Distance>
{
static NNIndex<Distance>* create(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance)
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params, "algorithm");
NNIndex<Distance>* nnIndex;
switch (index_type) {
case FLANN_INDEX_LINEAR:
nnIndex = new LinearIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_KMEANS:
nnIndex = new KMeansIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_HIERARCHICAL:
nnIndex = new HierarchicalClusteringIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_LSH:
nnIndex = new LshIndex<Distance>(dataset, params, distance);
break;
default:
throw FLANNException("Unknown index type");
}
return nnIndex;
}
};
template<typename Distance>
struct index_creator<False,False,Distance>
{
static NNIndex<Distance>* create(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance)
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params, "algorithm");
NNIndex<Distance>* nnIndex;
switch (index_type) {
case FLANN_INDEX_LINEAR:
nnIndex = new LinearIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_HIERARCHICAL:
nnIndex = new HierarchicalClusteringIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_LSH:
nnIndex = new LshIndex<Distance>(dataset, params, distance);
break;
default:
throw FLANNException("Unknown index type");
}
return nnIndex;
}
};
template<typename Distance>
NNIndex<Distance>* create_index_by_type(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance)
{
return index_creator<typename Distance::is_kdtree_distance,
typename Distance::is_vector_space_distance,
Distance>::create(dataset, params,distance);
} }
} } //namespace cvflann
#endif /* OPENCV_FLANN_ALL_INDICES_H_ */ #endif /* _OPENCV_ALL_INDICES_H_ */

View File

@ -28,13 +28,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_ALLOCATOR_H_ #ifndef _OPENCV_ALLOCATOR_H_
#define OPENCV_FLANN_ALLOCATOR_H_ #define _OPENCV_ALLOCATOR_H_
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
namespace cvflann namespace cvflann
{ {
@ -48,8 +47,8 @@ namespace cvflann
template <typename T> template <typename T>
T* allocate(size_t count = 1) T* allocate(size_t count = 1)
{ {
T* mem = (T*) ::malloc(sizeof(T)*count); T* mem = (T*) ::malloc(sizeof(T)*count);
return mem; return mem;
} }
@ -71,118 +70,118 @@ T* allocate(size_t count = 1)
const size_t WORDSIZE=16; const size_t WORDSIZE=16;
const size_t BLOCKSIZE=8192; const size_t BLOCKSIZE=8192;
class PooledAllocator class CV_EXPORTS PooledAllocator
{ {
/* We maintain memory alignment to word boundaries by requiring that all /* We maintain memory alignment to word boundaries by requiring that all
allocations be in multiples of the machine wordsize. */ allocations be in multiples of the machine wordsize. */
/* Size of machine word in bytes. Must be power of 2. */ /* Size of machine word in bytes. Must be power of 2. */
/* Minimum number of bytes requested at a time from the system. Must be multiple of WORDSIZE. */ /* Minimum number of bytes requested at a time from the system. Must be multiple of WORDSIZE. */
int remaining; /* Number of bytes left in current block of storage. */ int remaining; /* Number of bytes left in current block of storage. */
void* base; /* Pointer to base of current block of storage. */ void* base; /* Pointer to base of current block of storage. */
void* loc; /* Current location in block to next allocate memory. */ void* loc; /* Current location in block to next allocate memory. */
int blocksize; int blocksize;
public: public:
int usedMemory; int usedMemory;
int wastedMemory; int wastedMemory;
/** /**
Default constructor. Initializes a new pool. Default constructor. Initializes a new pool.
*/ */
PooledAllocator(int blocksize = BLOCKSIZE) PooledAllocator(int blocksize = BLOCKSIZE)
{ {
this->blocksize = blocksize; this->blocksize = blocksize;
remaining = 0; remaining = 0;
base = NULL; base = NULL;
usedMemory = 0; usedMemory = 0;
wastedMemory = 0; wastedMemory = 0;
} }
/** /**
* Destructor. Frees all the memory allocated in this pool. * Destructor. Frees all the memory allocated in this pool.
*/ */
~PooledAllocator() ~PooledAllocator()
{ {
void* prev; void *prev;
while (base != NULL) { while (base != NULL) {
prev = *((void**) base); /* Get pointer to prev block. */ prev = *((void **) base); /* Get pointer to prev block. */
::free(base); ::free(base);
base = prev; base = prev;
} }
} }
/** /**
* Returns a pointer to a piece of new memory of the given size in bytes * Returns a pointer to a piece of new memory of the given size in bytes
* allocated from the pool. * allocated from the pool.
*/ */
void* allocateMemory(int size) void* allocateBytes(int size)
{ {
int blocksize; int blocksize;
/* Round size up to a multiple of wordsize. The following expression /* Round size up to a multiple of wordsize. The following expression
only works for WORDSIZE that is a power of 2, by masking last bits of only works for WORDSIZE that is a power of 2, by masking last bits of
incremented size to zero. incremented size to zero.
*/ */
size = (size + (WORDSIZE - 1)) & ~(WORDSIZE - 1); size = (size + (WORDSIZE - 1)) & ~(WORDSIZE - 1);
/* Check whether a new block must be allocated. Note that the first word /* Check whether a new block must be allocated. Note that the first word
of a block is reserved for a pointer to the previous block. of a block is reserved for a pointer to the previous block.
*/ */
if (size > remaining) { if (size > remaining) {
wastedMemory += remaining; wastedMemory += remaining;
/* Allocate new storage. */ /* Allocate new storage. */
blocksize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ? blocksize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ?
size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE; size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE;
// use the standard C malloc to allocate memory // use the standard C malloc to allocate memory
void* m = ::malloc(blocksize); void* m = ::malloc(blocksize);
if (!m) { if (!m) {
fprintf(stderr,"Failed to allocate memory.\n"); fprintf(stderr,"Failed to allocate memory.\n");
return NULL; exit(1);
} }
/* Fill first word of new block with pointer to previous block. */ /* Fill first word of new block with pointer to previous block. */
((void**) m)[0] = base; ((void **) m)[0] = base;
base = m; base = m;
int shift = 0; int shift = 0;
//int shift = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1); //int shift = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1);
remaining = blocksize - sizeof(void*) - shift; remaining = blocksize - sizeof(void*) - shift;
loc = ((char*)m + sizeof(void*) + shift); loc = ((char*)m + sizeof(void*) + shift);
} }
void* rloc = loc; void* rloc = loc;
loc = (char*)loc + size; loc = (char*)loc + size;
remaining -= size; remaining -= size;
usedMemory += size; usedMemory += size;
return rloc; return rloc;
} }
/** /**
* Allocates (using this pool) a generic type T. * Allocates (using this pool) a generic type T.
* *
* Params: * Params:
* count = number of instances to allocate. * count = number of instances to allocate.
* Returns: pointer (of type T*) to memory buffer * Returns: pointer (of type T*) to memory buffer
*/ */
template <typename T> template <typename T>
T* allocate(size_t count = 1) T* allocate(size_t count = 1)
{ {
T* mem = (T*) this->allocateMemory((int)(sizeof(T)*count)); T* mem = (T*) this->allocateBytes((int)(sizeof(T)*count));
return mem; return mem;
} }
}; };
} } // namespace cvflann
#endif //OPENCV_FLANN_ALLOCATOR_H_ #endif //_OPENCV_ALLOCATOR_H_

View File

@ -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_

View File

@ -27,204 +27,216 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_AUTOTUNED_INDEX_H_
#define OPENCV_FLANN_AUTOTUNED_INDEX_H_
#include "general.h" #ifndef _OPENCV_AUTOTUNEDINDEX_H_
#include "nn_index.h" #define _OPENCV_AUTOTUNEDINDEX_H_
#include "ground_truth.h"
#include "index_testing.h" #include "opencv2/flann/general.h"
#include "sampling.h" #include "opencv2/flann/nn_index.h"
#include "kdtree_index.h" #include "opencv2/flann/ground_truth.h"
#include "kdtree_single_index.h" #include "opencv2/flann/index_testing.h"
#include "kmeans_index.h" #include "opencv2/flann/sampling.h"
#include "composite_index.h" #include "opencv2/flann/all_indices.h"
#include "linear_index.h"
#include "logger.h"
namespace cvflann namespace cvflann
{ {
template<typename Distance> struct AutotunedIndexParams : public IndexParams {
NNIndex<Distance>* create_index_by_type(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance); AutotunedIndexParams( float target_precision_ = 0.8, float build_weight_ = 0.01,
float memory_weight_ = 0, float sample_fraction_ = 0.1) :
IndexParams(FLANN_INDEX_AUTOTUNED),
target_precision(target_precision_),
build_weight(build_weight_),
memory_weight(memory_weight_),
sample_fraction(sample_fraction_) {};
float target_precision; // precision desired (used for autotuning, -1 otherwise)
float build_weight; // build tree time weighting factor
float memory_weight; // index memory weighting factor
float sample_fraction; // what fraction of the dataset to use for autotuning
struct AutotunedIndexParams : public IndexParams void print() const
{ {
AutotunedIndexParams(float target_precision = 0.8, float build_weight = 0.01, float memory_weight = 0, float sample_fraction = 0.1) logger().info("Index type: %d\n",(int)algorithm);
{ logger().info("logger(). precision: %g\n", target_precision);
(*this)["algorithm"] = FLANN_INDEX_AUTOTUNED; logger().info("Build weight: %g\n", build_weight);
// precision desired (used for autotuning, -1 otherwise) logger().info("Memory weight: %g\n", memory_weight);
(*this)["target_precision"] = target_precision; logger().info("Sample fraction: %g\n", sample_fraction);
// build tree time weighting factor }
(*this)["build_weight"] = build_weight;
// index memory weighting factor
(*this)["memory_weight"] = memory_weight;
// what fraction of the dataset to use for autotuning
(*this)["sample_fraction"] = sample_fraction;
}
}; };
template <typename Distance> template <typename ELEM_TYPE, typename DIST_TYPE = typename DistType<ELEM_TYPE>::type >
class AutotunedIndex : public NNIndex<Distance> class AutotunedIndex : public NNIndex<ELEM_TYPE>
{ {
NNIndex<ELEM_TYPE>* bestIndex;
IndexParams* bestParams;
SearchParams bestSearchParams;
Matrix<ELEM_TYPE> sampledDataset;
Matrix<ELEM_TYPE> testDataset;
Matrix<int> gt_matches;
float speedup;
/**
* The dataset used by this index
*/
const Matrix<ELEM_TYPE> dataset;
/**
* Index parameters
*/
const AutotunedIndexParams& index_params;
AutotunedIndex& operator=(const AutotunedIndex&);
AutotunedIndex(const AutotunedIndex&);
public: public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
AutotunedIndex(const Matrix<ElementType>& inputData, const IndexParams& params = AutotunedIndexParams(), Distance d = Distance()) : AutotunedIndex(const Matrix<ELEM_TYPE>& inputData, const AutotunedIndexParams& params = AutotunedIndexParams() ) :
dataset_(inputData), distance_(d) dataset(inputData), index_params(params)
{ {
target_precision_ = get_param(params, "target_precision",0.8f); bestIndex = NULL;
build_weight_ = get_param(params,"build_weight", 0.01f); bestParams = NULL;
memory_weight_ = get_param(params, "memory_weight", 0.0f); }
sample_fraction_ = get_param(params,"sample_fraction", 0.1f);
bestIndex_ = NULL;
}
AutotunedIndex(const AutotunedIndex&);
AutotunedIndex& operator=(const AutotunedIndex&);
virtual ~AutotunedIndex() virtual ~AutotunedIndex()
{ {
if (bestIndex_ != NULL) { if (bestIndex!=NULL) {
delete bestIndex_; delete bestIndex;
bestIndex_ = NULL; }
} if (bestParams!=NULL) {
} delete bestParams;
}
};
/** /**
* Method responsible with building the index. Method responsible with building the index.
*/ */
virtual void buildIndex() virtual void buildIndex()
{ {
bestParams_ = estimateBuildParams(); bestParams = estimateBuildParams();
Logger::info("----------------------------------------------------\n"); logger().info("----------------------------------------------------\n");
Logger::info("Autotuned parameters:\n"); logger().info("Autotuned parameters:\n");
print_params(bestParams_); bestParams->print();
Logger::info("----------------------------------------------------\n"); logger().info("----------------------------------------------------\n");
flann_algorithm_t index_type = bestParams->getIndexType();
bestIndex_ = create_index_by_type(dataset_, bestParams_, distance_); switch (index_type) {
bestIndex_->buildIndex(); case FLANN_INDEX_LINEAR:
speedup_ = estimateSearchParams(bestSearchParams_); bestIndex = new LinearIndex<ELEM_TYPE>(dataset, (const LinearIndexParams&)*bestParams);
Logger::info("----------------------------------------------------\n"); break;
Logger::info("Search parameters:\n"); case FLANN_INDEX_KDTREE:
print_params(bestSearchParams_); bestIndex = new KDTreeIndex<ELEM_TYPE>(dataset, (const KDTreeIndexParams&)*bestParams);
Logger::info("----------------------------------------------------\n"); break;
} case FLANN_INDEX_KMEANS:
bestIndex = new KMeansIndex<ELEM_TYPE>(dataset, (const KMeansIndexParams&)*bestParams);
break;
default:
throw FLANNException("Unknown algorithm choosen by the autotuning, most likely a bug.");
}
bestIndex->buildIndex();
speedup = estimateSearchParams(bestSearchParams);
}
/** /**
* Saves the index to a stream Saves the index to a stream
*/ */
virtual void saveIndex(FILE* stream) virtual void saveIndex(FILE* stream)
{ {
save_value(stream, (int)bestIndex_->getType()); save_value(stream, (int)bestIndex->getType());
bestIndex_->saveIndex(stream); bestIndex->saveIndex(stream);
save_value(stream, get_param<int>(bestSearchParams_, "checks")); save_value(stream, bestSearchParams);
} }
/** /**
* Loads the index from a stream Loads the index from a stream
*/ */
virtual void loadIndex(FILE* stream) virtual void loadIndex(FILE* stream)
{ {
int index_type; int index_type;
load_value(stream,index_type);
load_value(stream, index_type); IndexParams* params = ParamsFactory_instance().create((flann_algorithm_t)index_type);
IndexParams params; bestIndex = create_index_by_type(dataset, *params);
params["algorithm"] = (flann_algorithm_t)index_type; bestIndex->loadIndex(stream);
bestIndex_ = create_index_by_type<Distance>(dataset_, params, distance_); load_value(stream, bestSearchParams);
bestIndex_->loadIndex(stream);
int checks;
load_value(stream, checks);
bestSearchParams_["checks"] = checks;
} }
/**
Method that searches for nearest-neighbors
*/
virtual void findNeighbors(ResultSet<ELEM_TYPE>& result, const ELEM_TYPE* vec, const SearchParams& searchParams)
{
if (searchParams.checks==-2) {
bestIndex->findNeighbors(result, vec, bestSearchParams);
}
else {
bestIndex->findNeighbors(result, vec, searchParams);
}
}
const IndexParams* getParameters() const
{
return bestIndex->getParameters();
}
/**
Number of features in this index.
*/
virtual size_t size() const
{
return bestIndex->size();
}
/**
The length of each vector in this index.
*/
virtual size_t veclen() const
{
return bestIndex->veclen();
}
/**
The amount of memory (in bytes) this index uses.
*/
virtual int usedMemory() const
{
return bestIndex->usedMemory();
}
/** /**
* Method that searches for nearest-neighbors * Algorithm name
*/ */
virtual void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams)
{
int checks = get_param(searchParams,"checks",FLANN_CHECKS_AUTOTUNED);
if (checks == FLANN_CHECKS_AUTOTUNED) {
bestIndex_->findNeighbors(result, vec, bestSearchParams_);
}
else {
bestIndex_->findNeighbors(result, vec, searchParams);
}
}
IndexParams getParameters() const
{
return bestIndex_->getParameters();
}
SearchParams getSearchParameters() const
{
return bestSearchParams_;
}
float getSpeedup() const
{
return speedup_;
}
/**
* Number of features in this index.
*/
virtual size_t size() const
{
return bestIndex_->size();
}
/**
* The length of each vector in this index.
*/
virtual size_t veclen() const
{
return bestIndex_->veclen();
}
/**
* The amount of memory (in bytes) this index uses.
*/
virtual int usedMemory() const
{
return bestIndex_->usedMemory();
}
/**
* Algorithm name
*/
virtual flann_algorithm_t getType() const virtual flann_algorithm_t getType() const
{ {
return FLANN_INDEX_AUTOTUNED; return FLANN_INDEX_AUTOTUNED;
} }
private: private:
struct CostData struct CostData {
{
float searchTimeCost; float searchTimeCost;
float buildTimeCost; float buildTimeCost;
float timeCost;
float memoryCost; float memoryCost;
float totalCost; float totalCost;
IndexParams params;
}; };
void evaluate_kmeans(CostData& cost) typedef std::pair<CostData, KDTreeIndexParams> KDTreeCostData;
typedef std::pair<CostData, KMeansIndexParams> KMeansCostData;
void evaluate_kmeans(CostData& cost, const KMeansIndexParams& kmeans_params)
{ {
StartStopTimer t; StartStopTimer t;
int checks; int checks;
const int nn = 1; const int nn = 1;
Logger::info("KMeansTree using params: max_iterations=%d, branching=%d\n", logger().info("KMeansTree using params: max_iterations=%d, branching=%d\n", kmeans_params.iterations, kmeans_params.branching);
get_param<int>(cost.params,"iterations"), KMeansIndex<ELEM_TYPE> kmeans(sampledDataset, kmeans_params);
get_param<int>(cost.params,"branching"));
KMeansIndex<Distance> kmeans(sampledDataset_, cost.params, distance_);
// measure index build time // measure index build time
t.start(); t.start();
kmeans.buildIndex(); kmeans.buildIndex();
@ -232,24 +244,25 @@ private:
float buildTime = (float)t.value; float buildTime = (float)t.value;
// measure search time // measure search time
float searchTime = test_index_precision(kmeans, sampledDataset_, testDataset_, gt_matches_, target_precision_, checks, distance_, nn); float searchTime = test_index_precision(kmeans, sampledDataset, testDataset, gt_matches, index_params.target_precision, checks, nn);;
float datasetMemory = float(sampledDataset_.rows * sampledDataset_.cols * sizeof(float)); float datasetMemory = (float)(sampledDataset.rows*sampledDataset.cols*sizeof(float));
cost.memoryCost = (kmeans.usedMemory() + datasetMemory) / datasetMemory; cost.memoryCost = (kmeans.usedMemory()+datasetMemory)/datasetMemory;
cost.searchTimeCost = searchTime; cost.searchTimeCost = searchTime;
cost.buildTimeCost = buildTime; cost.buildTimeCost = buildTime;
Logger::info("KMeansTree buildTime=%g, searchTime=%g, build_weight=%g\n", buildTime, searchTime, build_weight_); cost.timeCost = (buildTime*index_params.build_weight+searchTime);
logger().info("KMeansTree buildTime=%g, searchTime=%g, timeCost=%g, buildTimeFactor=%g\n",buildTime, searchTime, cost.timeCost, index_params.build_weight);
} }
void evaluate_kdtree(CostData& cost) void evaluate_kdtree(CostData& cost, const KDTreeIndexParams& kdtree_params)
{ {
StartStopTimer t; StartStopTimer t;
int checks; int checks;
const int nn = 1; const int nn = 1;
Logger::info("KDTree using params: trees=%d\n", get_param<int>(cost.params,"trees")); logger().info("KDTree using params: trees=%d\n",kdtree_params.trees);
KDTreeIndex<Distance> kdtree(sampledDataset_, cost.params, distance_); KDTreeIndex<ELEM_TYPE> kdtree(sampledDataset, kdtree_params);
t.start(); t.start();
kdtree.buildIndex(); kdtree.buildIndex();
@ -257,220 +270,267 @@ private:
float buildTime = (float)t.value; float buildTime = (float)t.value;
//measure search time //measure search time
float searchTime = test_index_precision(kdtree, sampledDataset_, testDataset_, gt_matches_, target_precision_, checks, distance_, nn); float searchTime = test_index_precision(kdtree, sampledDataset, testDataset, gt_matches, index_params.target_precision, checks, nn);
float datasetMemory = float(sampledDataset_.rows * sampledDataset_.cols * sizeof(float)); float datasetMemory = (float)(sampledDataset.rows*sampledDataset.cols*sizeof(float));
cost.memoryCost = (kdtree.usedMemory() + datasetMemory) / datasetMemory; cost.memoryCost = (kdtree.usedMemory()+datasetMemory)/datasetMemory;
cost.searchTimeCost = searchTime; cost.searchTimeCost = searchTime;
cost.buildTimeCost = buildTime; cost.buildTimeCost = buildTime;
Logger::info("KDTree buildTime=%g, searchTime=%g\n", buildTime, searchTime); cost.timeCost = (buildTime*index_params.build_weight+searchTime);
logger().info("KDTree buildTime=%g, searchTime=%g, timeCost=%g\n",buildTime, searchTime, cost.timeCost);
} }
// struct KMeansSimpleDownhillFunctor { // struct KMeansSimpleDownhillFunctor {
// //
// Autotune& autotuner; // Autotune& autotuner;
// KMeansSimpleDownhillFunctor(Autotune& autotuner_) : autotuner(autotuner_) {}; // KMeansSimpleDownhillFunctor(Autotune& autotuner_) : autotuner(autotuner_) {};
// //
// float operator()(int* params) { // float operator()(int* params) {
// //
// float maxFloat = numeric_limits<float>::max(); // float maxFloat = numeric_limits<float>::max();
// //
// if (params[0]<2) return maxFloat; // if (params[0]<2) return maxFloat;
// if (params[1]<0) return maxFloat; // if (params[1]<0) return maxFloat;
// //
// CostData c; // CostData c;
// c.params["algorithm"] = KMEANS; // c.params["algorithm"] = KMEANS;
// c.params["centers-init"] = CENTERS_RANDOM; // c.params["centers-init"] = CENTERS_RANDOM;
// c.params["branching"] = params[0]; // c.params["branching"] = params[0];
// c.params["max-iterations"] = params[1]; // c.params["max-iterations"] = params[1];
// //
// autotuner.evaluate_kmeans(c); // autotuner.evaluate_kmeans(c);
// //
// return c.timeCost; // return c.timeCost;
// //
// } // }
// }; // };
// //
// struct KDTreeSimpleDownhillFunctor { // struct KDTreeSimpleDownhillFunctor {
// //
// Autotune& autotuner; // Autotune& autotuner;
// KDTreeSimpleDownhillFunctor(Autotune& autotuner_) : autotuner(autotuner_) {}; // KDTreeSimpleDownhillFunctor(Autotune& autotuner_) : autotuner(autotuner_) {};
// //
// float operator()(int* params) { // float operator()(int* params) {
// float maxFloat = numeric_limits<float>::max(); // float maxFloat = numeric_limits<float>::max();
// //
// if (params[0]<1) return maxFloat; // if (params[0]<1) return maxFloat;
// //
// CostData c; // CostData c;
// c.params["algorithm"] = KDTREE; // c.params["algorithm"] = KDTREE;
// c.params["trees"] = params[0]; // c.params["trees"] = params[0];
// //
// autotuner.evaluate_kdtree(c); // autotuner.evaluate_kdtree(c);
// //
// return c.timeCost; // return c.timeCost;
// //
// } // }
// }; // };
void optimizeKMeans(std::vector<CostData>& costs) KMeansCostData optimizeKMeans()
{ {
Logger::info("KMEANS, Step 1: Exploring parameter space\n"); logger().info("KMEANS, Step 1: Exploring parameter space\n");
// explore kmeans parameters space using combinations of the parameters below // explore kmeans parameters space using combinations of the parameters below
int maxIterations[] = { 1, 5, 10, 15 }; int maxIterations[] = { 1, 5, 10, 15 };
int branchingFactors[] = { 16, 32, 64, 128, 256 }; int branchingFactors[] = { 16, 32, 64, 128, 256 };
int kmeansParamSpaceSize = FLANN_ARRAY_LEN(maxIterations) * FLANN_ARRAY_LEN(branchingFactors); int kmeansParamSpaceSize = ARRAY_LEN(maxIterations)*ARRAY_LEN(branchingFactors);
costs.reserve(costs.size() + kmeansParamSpaceSize);
std::vector<KMeansCostData> kmeansCosts(kmeansParamSpaceSize);
// CostData* kmeansCosts = new CostData[kmeansParamSpaceSize];
// evaluate kmeans for all parameter combinations // evaluate kmeans for all parameter combinations
for (size_t i = 0; i < FLANN_ARRAY_LEN(maxIterations); ++i) { int cnt = 0;
for (size_t j = 0; j < FLANN_ARRAY_LEN(branchingFactors); ++j) { for (size_t i=0; i<ARRAY_LEN(maxIterations); ++i) {
CostData cost; for (size_t j=0; j<ARRAY_LEN(branchingFactors); ++j) {
cost.params["algorithm"] = FLANN_INDEX_KMEANS;
cost.params["centers_init"] = FLANN_CENTERS_RANDOM;
cost.params["iterations"] = maxIterations[i];
cost.params["branching"] = branchingFactors[j];
evaluate_kmeans(cost); kmeansCosts[cnt].second.centers_init = FLANN_CENTERS_RANDOM;
costs.push_back(cost); kmeansCosts[cnt].second.iterations = maxIterations[i];
kmeansCosts[cnt].second.branching = branchingFactors[j];
evaluate_kmeans(kmeansCosts[cnt].first, kmeansCosts[cnt].second);
int k = cnt;
// order by time cost
while (k>0 && kmeansCosts[k].first.timeCost < kmeansCosts[k-1].first.timeCost) {
swap(kmeansCosts[k],kmeansCosts[k-1]);
--k;
}
++cnt;
} }
} }
// Logger::info("KMEANS, Step 2: simplex-downhill optimization\n"); // logger().info("KMEANS, Step 2: simplex-downhill optimization\n");
// //
// const int n = 2; // const int n = 2;
// // choose initial simplex points as the best parameters so far // // choose initial simplex points as the best parameters so far
// int kmeansNMPoints[n*(n+1)]; // int kmeansNMPoints[n*(n+1)];
// float kmeansVals[n+1]; // float kmeansVals[n+1];
// for (int i=0;i<n+1;++i) { // for (int i=0;i<n+1;++i) {
// kmeansNMPoints[i*n] = (int)kmeansCosts[i].params["branching"]; // kmeansNMPoints[i*n] = (int)kmeansCosts[i].params["branching"];
// kmeansNMPoints[i*n+1] = (int)kmeansCosts[i].params["max-iterations"]; // kmeansNMPoints[i*n+1] = (int)kmeansCosts[i].params["max-iterations"];
// kmeansVals[i] = kmeansCosts[i].timeCost; // kmeansVals[i] = kmeansCosts[i].timeCost;
// } // }
// KMeansSimpleDownhillFunctor kmeans_cost_func(*this); // KMeansSimpleDownhillFunctor kmeans_cost_func(*this);
// // run optimization // // run optimization
// optimizeSimplexDownhill(kmeansNMPoints,n,kmeans_cost_func,kmeansVals); // optimizeSimplexDownhill(kmeansNMPoints,n,kmeans_cost_func,kmeansVals);
// // store results // // store results
// for (int i=0;i<n+1;++i) { // for (int i=0;i<n+1;++i) {
// kmeansCosts[i].params["branching"] = kmeansNMPoints[i*2]; // kmeansCosts[i].params["branching"] = kmeansNMPoints[i*2];
// kmeansCosts[i].params["max-iterations"] = kmeansNMPoints[i*2+1]; // kmeansCosts[i].params["max-iterations"] = kmeansNMPoints[i*2+1];
// kmeansCosts[i].timeCost = kmeansVals[i]; // kmeansCosts[i].timeCost = kmeansVals[i];
// } // }
float optTimeCost = kmeansCosts[0].first.timeCost;
// recompute total costs factoring in the memory costs
for (int i=0;i<kmeansParamSpaceSize;++i) {
kmeansCosts[i].first.totalCost = (kmeansCosts[i].first.timeCost/optTimeCost + index_params.memory_weight * kmeansCosts[i].first.memoryCost);
int k = i;
while (k>0 && kmeansCosts[k].first.totalCost < kmeansCosts[k-1].first.totalCost) {
swap(kmeansCosts[k],kmeansCosts[k-1]);
k--;
}
}
// display the costs obtained
for (int i=0;i<kmeansParamSpaceSize;++i) {
logger().info("KMeans, branching=%d, iterations=%d, time_cost=%g[%g] (build=%g, search=%g), memory_cost=%g, cost=%g\n",
kmeansCosts[i].second.branching, kmeansCosts[i].second.iterations,
kmeansCosts[i].first.timeCost,kmeansCosts[i].first.timeCost/optTimeCost,
kmeansCosts[i].first.buildTimeCost, kmeansCosts[i].first.searchTimeCost,
kmeansCosts[i].first.memoryCost,kmeansCosts[i].first.totalCost);
}
return kmeansCosts[0];
} }
void optimizeKDTree(std::vector<CostData>& costs) KDTreeCostData optimizeKDTree()
{ {
Logger::info("KD-TREE, Step 1: Exploring parameter space\n");
logger().info("KD-TREE, Step 1: Exploring parameter space\n");
// explore kd-tree parameters space using the parameters below // explore kd-tree parameters space using the parameters below
int testTrees[] = { 1, 4, 8, 16, 32 }; int testTrees[] = { 1, 4, 8, 16, 32 };
// evaluate kdtree for all parameter combinations size_t kdtreeParamSpaceSize = ARRAY_LEN(testTrees);
for (size_t i = 0; i < FLANN_ARRAY_LEN(testTrees); ++i) { std::vector<KDTreeCostData> kdtreeCosts(kdtreeParamSpaceSize);
CostData cost;
cost.params["trees"] = testTrees[i];
evaluate_kdtree(cost); // evaluate kdtree for all parameter combinations
costs.push_back(cost); int cnt = 0;
for (size_t i=0; i<ARRAY_LEN(testTrees); ++i) {
kdtreeCosts[cnt].second.trees = testTrees[i];
evaluate_kdtree(kdtreeCosts[cnt].first, kdtreeCosts[cnt].second);
int k = cnt;
// order by time cost
while (k>0 && kdtreeCosts[k].first.timeCost < kdtreeCosts[k-1].first.timeCost) {
swap(kdtreeCosts[k],kdtreeCosts[k-1]);
--k;
}
++cnt;
} }
// Logger::info("KD-TREE, Step 2: simplex-downhill optimization\n"); // logger().info("KD-TREE, Step 2: simplex-downhill optimization\n");
// //
// const int n = 1; // const int n = 1;
// // choose initial simplex points as the best parameters so far // // choose initial simplex points as the best parameters so far
// int kdtreeNMPoints[n*(n+1)]; // int kdtreeNMPoints[n*(n+1)];
// float kdtreeVals[n+1]; // float kdtreeVals[n+1];
// for (int i=0;i<n+1;++i) { // for (int i=0;i<n+1;++i) {
// kdtreeNMPoints[i] = (int)kdtreeCosts[i].params["trees"]; // kdtreeNMPoints[i] = (int)kdtreeCosts[i].params["trees"];
// kdtreeVals[i] = kdtreeCosts[i].timeCost; // kdtreeVals[i] = kdtreeCosts[i].timeCost;
// } // }
// KDTreeSimpleDownhillFunctor kdtree_cost_func(*this); // KDTreeSimpleDownhillFunctor kdtree_cost_func(*this);
// // run optimization // // run optimization
// optimizeSimplexDownhill(kdtreeNMPoints,n,kdtree_cost_func,kdtreeVals); // optimizeSimplexDownhill(kdtreeNMPoints,n,kdtree_cost_func,kdtreeVals);
// // store results // // store results
// for (int i=0;i<n+1;++i) { // for (int i=0;i<n+1;++i) {
// kdtreeCosts[i].params["trees"] = kdtreeNMPoints[i]; // kdtreeCosts[i].params["trees"] = kdtreeNMPoints[i];
// kdtreeCosts[i].timeCost = kdtreeVals[i]; // kdtreeCosts[i].timeCost = kdtreeVals[i];
// } // }
float optTimeCost = kdtreeCosts[0].first.timeCost;
// recompute costs for kd-tree factoring in memory cost
for (size_t i=0;i<kdtreeParamSpaceSize;++i) {
kdtreeCosts[i].first.totalCost = (kdtreeCosts[i].first.timeCost/optTimeCost + index_params.memory_weight * kdtreeCosts[i].first.memoryCost);
int k = (int)i;
while (k>0 && kdtreeCosts[k].first.totalCost < kdtreeCosts[k-1].first.totalCost) {
swap(kdtreeCosts[k],kdtreeCosts[k-1]);
k--;
}
}
// display costs obtained
for (size_t i=0;i<kdtreeParamSpaceSize;++i) {
logger().info("kd-tree, trees=%d, time_cost=%g[%g] (build=%g, search=%g), memory_cost=%g, cost=%g\n",
kdtreeCosts[i].second.trees,kdtreeCosts[i].first.timeCost,kdtreeCosts[i].first.timeCost/optTimeCost,
kdtreeCosts[i].first.buildTimeCost, kdtreeCosts[i].first.searchTimeCost,
kdtreeCosts[i].first.memoryCost,kdtreeCosts[i].first.totalCost);
}
return kdtreeCosts[0];
} }
/** /**
* Chooses the best nearest-neighbor algorithm and estimates the optimal Chooses the best nearest-neighbor algorithm and estimates the optimal
* parameters to use when building the index (for a given precision). parameters to use when building the index (for a given precision).
* Returns a dictionary with the optimal parameters. Returns a dictionary with the optimal parameters.
*/ */
IndexParams estimateBuildParams() IndexParams* estimateBuildParams()
{ {
std::vector<CostData> costs; int sampleSize = int(index_params.sample_fraction*dataset.rows);
int testSampleSize = std::min(sampleSize/10, 1000);
int sampleSize = int(sample_fraction_ * dataset_.rows); logger().info("Entering autotuning, dataset size: %d, sampleSize: %d, testSampleSize: %d\n",dataset.rows, sampleSize, testSampleSize);
int testSampleSize = std::min(sampleSize / 10, 1000);
Logger::info("Entering autotuning, dataset size: %d, sampleSize: %d, testSampleSize: %d, target precision: %g\n", dataset_.rows, sampleSize, testSampleSize, target_precision_);
// For a very small dataset, it makes no sense to build any fancy index, just // For a very small dataset, it makes no sense to build any fancy index, just
// use linear search // use linear search
if (testSampleSize < 10) { if (testSampleSize<10) {
Logger::info("Choosing linear, dataset too small\n"); logger().info("Choosing linear, dataset too small\n");
return LinearIndexParams(); return new LinearIndexParams();
} }
// We use a fraction of the original dataset to speedup the autotune algorithm // We use a fraction of the original dataset to speedup the autotune algorithm
sampledDataset_ = random_sample(dataset_, sampleSize); sampledDataset = random_sample(dataset,sampleSize);
// We use a cross-validation approach, first we sample a testset from the dataset // We use a cross-validation approach, first we sample a testset from the dataset
testDataset_ = random_sample(sampledDataset_, testSampleSize, true); testDataset = random_sample(sampledDataset,testSampleSize,true);
// We compute the ground truth using linear search // We compute the ground truth using linear search
Logger::info("Computing ground truth... \n"); logger().info("Computing ground truth... \n");
gt_matches_ = Matrix<int>(new int[testDataset_.rows], testDataset_.rows, 1); gt_matches = Matrix<int>(new int[testDataset.rows],(long)testDataset.rows, 1);
StartStopTimer t; StartStopTimer t;
t.start(); t.start();
compute_ground_truth<Distance>(sampledDataset_, testDataset_, gt_matches_, 0, distance_); compute_ground_truth(sampledDataset, testDataset, gt_matches, 0);
t.stop(); t.stop();
float bestCost = (float)t.value;
CostData linear_cost; IndexParams* bestParams = new LinearIndexParams();
linear_cost.searchTimeCost = (float)t.value;
linear_cost.buildTimeCost = 0;
linear_cost.memoryCost = 0;
linear_cost.params["algorithm"] = FLANN_INDEX_LINEAR;
costs.push_back(linear_cost);
// Start parameter autotune process // Start parameter autotune process
Logger::info("Autotuning parameters...\n"); logger().info("Autotuning parameters...\n");
optimizeKMeans(costs);
optimizeKDTree(costs);
float bestTimeCost = costs[0].searchTimeCost; KMeansCostData kmeansCost = optimizeKMeans();
for (size_t i = 0; i < costs.size(); ++i) { if (kmeansCost.first.totalCost<bestCost) {
float timeCost = costs[i].buildTimeCost * build_weight_ + costs[i].searchTimeCost; bestParams = new KMeansIndexParams(kmeansCost.second);
if (timeCost < bestTimeCost) { bestCost = kmeansCost.first.totalCost;
bestTimeCost = timeCost;
}
} }
float bestCost = costs[0].searchTimeCost / bestTimeCost; KDTreeCostData kdtreeCost = optimizeKDTree();
IndexParams bestParams = costs[0].params;
if (bestTimeCost > 0) { if (kdtreeCost.first.totalCost<bestCost) {
for (size_t i = 0; i < costs.size(); ++i) { bestParams = new KDTreeIndexParams(kdtreeCost.second);
float crtCost = (costs[i].buildTimeCost * build_weight_ + costs[i].searchTimeCost) / bestTimeCost + bestCost = kdtreeCost.first.totalCost;
memory_weight_ * costs[i].memoryCost;
if (crtCost < bestCost) {
bestCost = crtCost;
bestParams = costs[i].params;
}
}
} }
delete[] gt_matches_.data; gt_matches.release();
delete[] testDataset_.data; sampledDataset.release();
delete[] sampledDataset_.data; testDataset.release();
return bestParams; return bestParams;
} }
@ -478,48 +538,48 @@ private:
/** /**
* Estimates the search time parameters needed to get the desired precision. Estimates the search time parameters needed to get the desired precision.
* Precondition: the index is built Precondition: the index is built
* Postcondition: the searchParams will have the optimum params set, also the speedup obtained over linear search. Postcondition: the searchParams will have the optimum params set, also the speedup obtained over linear search.
*/ */
float estimateSearchParams(SearchParams& searchParams) float estimateSearchParams(SearchParams& searchParams)
{ {
const int nn = 1; const int nn = 1;
const size_t SAMPLE_COUNT = 1000; const size_t SAMPLE_COUNT = 1000;
assert(bestIndex_ != NULL); // must have a valid index assert(bestIndex!=NULL); // must have a valid index
float speedup = 0; float speedup = 0;
int samples = (int)std::min(dataset_.rows / 10, SAMPLE_COUNT); int samples = (int)std::min(dataset.rows/10, SAMPLE_COUNT);
if (samples > 0) { if (samples>0) {
Matrix<ElementType> testDataset = random_sample(dataset_, samples); Matrix<ELEM_TYPE> testDataset = random_sample(dataset,samples);
Logger::info("Computing ground truth\n"); logger().info("Computing ground truth\n");
// we need to compute the ground truth first // we need to compute the ground truth first
Matrix<int> gt_matches(new int[testDataset.rows], testDataset.rows, 1); Matrix<int> gt_matches(new int[testDataset.rows],(long)testDataset.rows,1);
StartStopTimer t; StartStopTimer t;
t.start(); t.start();
compute_ground_truth<Distance>(dataset_, testDataset, gt_matches, 1, distance_); compute_ground_truth(dataset, testDataset, gt_matches,1);
t.stop(); t.stop();
float linear = (float)t.value; float linear = (float)t.value;
int checks; int checks;
Logger::info("Estimating number of checks\n"); logger().info("Estimating number of checks\n");
float searchTime; float searchTime;
float cb_index; float cb_index;
if (bestIndex_->getType() == FLANN_INDEX_KMEANS) { if (bestIndex->getType() == FLANN_INDEX_KMEANS) {
Logger::info("KMeans algorithm, estimating cluster border factor\n"); logger().info("KMeans algorithm, estimating cluster border factor\n");
KMeansIndex<Distance>* kmeans = (KMeansIndex<Distance>*)bestIndex_; KMeansIndex<ELEM_TYPE>* kmeans = (KMeansIndex<ELEM_TYPE>*)bestIndex;
float bestSearchTime = -1; float bestSearchTime = -1;
float best_cb_index = -1; float best_cb_index = -1;
int best_checks = -1; int best_checks = -1;
for (cb_index = 0; cb_index < 1.1f; cb_index += 0.2f) { for (cb_index = 0;cb_index<1.1f; cb_index+=0.2f) {
kmeans->set_cb_index(cb_index); kmeans->set_cb_index(cb_index);
searchTime = test_index_precision(*kmeans, dataset_, testDataset, gt_matches, target_precision_, checks, distance_, nn, 1); searchTime = test_index_precision(*kmeans, dataset, testDataset, gt_matches, index_params.target_precision, checks, nn, 1);
if ((searchTime < bestSearchTime) || (bestSearchTime == -1)) { if (searchTime<bestSearchTime || bestSearchTime == -1) {
bestSearchTime = searchTime; bestSearchTime = searchTime;
best_cb_index = cb_index; best_cb_index = cb_index;
best_checks = checks; best_checks = checks;
@ -530,54 +590,26 @@ private:
checks = best_checks; checks = best_checks;
kmeans->set_cb_index(best_cb_index); kmeans->set_cb_index(best_cb_index);
Logger::info("Optimum cb_index: %g\n", cb_index); logger().info("Optimum cb_index: %g\n",cb_index);
bestParams_["cb_index"] = cb_index; ((KMeansIndexParams*)bestParams)->cb_index = cb_index;
} }
else { else {
searchTime = test_index_precision(*bestIndex_, dataset_, testDataset, gt_matches, target_precision_, checks, distance_, nn, 1); searchTime = test_index_precision(*bestIndex, dataset, testDataset, gt_matches, index_params.target_precision, checks, nn, 1);
} }
Logger::info("Required number of checks: %d \n", checks); logger().info("Required number of checks: %d \n",checks);;
searchParams["checks"] = checks; searchParams.checks = checks;
speedup = linear / searchTime; speedup = linear/searchTime;
delete[] gt_matches.data; gt_matches.release();
delete[] testDataset.data;
} }
return speedup; return speedup;
} }
private:
NNIndex<Distance>* bestIndex_;
IndexParams bestParams_;
SearchParams bestSearchParams_;
Matrix<ElementType> sampledDataset_;
Matrix<ElementType> testDataset_;
Matrix<int> gt_matches_;
float speedup_;
/**
* The dataset used by this index
*/
const Matrix<ElementType> dataset_;
/**
* Index parameters
*/
float target_precision_;
float build_weight_;
float memory_weight_;
float sample_fraction_;
Distance distance_;
}; };
}
#endif /* OPENCV_FLANN_AUTOTUNED_INDEX_H_ */ } // namespace cvflann
#endif /* _OPENCV_AUTOTUNEDINDEX_H_ */

View File

@ -28,167 +28,135 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_COMPOSITE_INDEX_H_ #ifndef _OPENCV_COMPOSITETREE_H_
#define OPENCV_FLANN_COMPOSITE_INDEX_H_ #define _OPENCV_COMPOSITETREE_H_
#include "general.h" #include "opencv2/flann/general.h"
#include "nn_index.h" #include "opencv2/flann/nn_index.h"
#include "kdtree_index.h"
#include "kmeans_index.h"
namespace cvflann namespace cvflann
{ {
/**
* Index parameters for the CompositeIndex. struct CompositeIndexParams : public IndexParams {
*/ CompositeIndexParams(int trees_ = 4, int branching_ = 32, int iterations_ = 11,
struct CompositeIndexParams : public IndexParams flann_centers_init_t centers_init_ = FLANN_CENTERS_RANDOM, float cb_index_ = 0.2 ) :
{ IndexParams(FLANN_INDEX_COMPOSITE),
CompositeIndexParams(int trees = 4, int branching = 32, int iterations = 11, trees(trees_),
flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, float cb_index = 0.2 ) branching(branching_),
{ iterations(iterations_),
(*this)["algorithm"] = FLANN_INDEX_KMEANS; centers_init(centers_init_),
// number of randomized trees to use (for kdtree) cb_index(cb_index_) {};
(*this)["trees"] = trees;
// branching factor int trees; // number of randomized trees to use (for kdtree)
(*this)["branching"] = branching; int branching; // branching factor (for kmeans tree)
// max iterations to perform in one kmeans clustering (kmeans tree) int iterations; // max iterations to perform in one kmeans clustering (kmeans tree)
(*this)["iterations"] = iterations; flann_centers_init_t centers_init; // algorithm used for picking the initial cluster centers for kmeans tree
// algorithm used for picking the initial cluster centers for kmeans tree float cb_index; // cluster boundary index. Used when searching the kmeans tree
(*this)["centers_init"] = centers_init;
// cluster boundary index. Used when searching the kmeans tree void print() const
(*this)["cb_index"] = cb_index; {
} logger().info("Index type: %d\n",(int)algorithm);
logger().info("Trees: %d\n", trees);
logger().info("Branching: %d\n", branching);
logger().info("Iterations: %d\n", iterations);
logger().info("Centres initialisation: %d\n", centers_init);
logger().info("Cluster boundary weight: %g\n", cb_index);
}
}; };
/**
* This index builds a kd-tree index and a k-means index and performs nearest template <typename ELEM_TYPE, typename DIST_TYPE = typename DistType<ELEM_TYPE>::type >
* neighbour search both indexes. This gives a slight boost in search performance class CompositeIndex : public NNIndex<ELEM_TYPE>
* as some of the neighbours that are missed by one index are found by the other.
*/
template <typename Distance>
class CompositeIndex : public NNIndex<Distance>
{ {
KMeansIndex<ELEM_TYPE, DIST_TYPE>* kmeans;
KDTreeIndex<ELEM_TYPE, DIST_TYPE>* kdtree;
const Matrix<ELEM_TYPE> dataset;
const IndexParams& index_params;
CompositeIndex& operator=(const CompositeIndex&);
CompositeIndex(const CompositeIndex&);
public: public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
/** CompositeIndex(const Matrix<ELEM_TYPE>& inputData, const CompositeIndexParams& params = CompositeIndexParams() ) :
* Index constructor dataset(inputData), index_params(params)
* @param inputData dataset containing the points to index {
* @param params Index parameters KDTreeIndexParams kdtree_params(params.trees);
* @param d Distance functor KMeansIndexParams kmeans_params(params.branching, params.iterations, params.centers_init, params.cb_index);
* @return
*/
CompositeIndex(const Matrix<ElementType>& inputData, const IndexParams& params = CompositeIndexParams(),
Distance d = Distance()) : index_params_(params)
{
kdtree_index_ = new KDTreeIndex<Distance>(inputData, params, d);
kmeans_index_ = new KMeansIndex<Distance>(inputData, params, d);
} kdtree = new KDTreeIndex<ELEM_TYPE, DIST_TYPE>(inputData,kdtree_params);
kmeans = new KMeansIndex<ELEM_TYPE, DIST_TYPE>(inputData,kmeans_params);
CompositeIndex(const CompositeIndex&); }
CompositeIndex& operator=(const CompositeIndex&);
virtual ~CompositeIndex()
{
delete kdtree;
delete kmeans;
}
virtual ~CompositeIndex()
{
delete kdtree_index_;
delete kmeans_index_;
}
/**
* @return The index type
*/
flann_algorithm_t getType() const flann_algorithm_t getType() const
{ {
return FLANN_INDEX_COMPOSITE; return FLANN_INDEX_COMPOSITE;
} }
/**
* @return Size of the index
*/
size_t size() const size_t size() const
{ {
return kdtree_index_->size(); return dataset.rows;
} }
/** size_t veclen() const
* \returns The dimensionality of the features in this index. {
*/ return dataset.cols;
size_t veclen() const }
{
return kdtree_index_->veclen();
}
/**
* \returns The amount of memory (in bytes) used by the index.
*/
int usedMemory() const
{
return kmeans_index_->usedMemory() + kdtree_index_->usedMemory();
}
/** int usedMemory() const
* \brief Builds the index {
*/ return kmeans->usedMemory()+kdtree->usedMemory();
void buildIndex() }
{
Logger::info("Building kmeans tree...\n"); void buildIndex()
kmeans_index_->buildIndex(); {
Logger::info("Building kdtree tree...\n"); logger().info("Building kmeans tree...\n");
kdtree_index_->buildIndex(); kmeans->buildIndex();
} logger().info("Building kdtree tree...\n");
kdtree->buildIndex();
}
/**
* \brief Saves the index to a stream
* \param stream The stream to save the index to
*/
void saveIndex(FILE* stream) void saveIndex(FILE* stream)
{ {
kmeans_index_->saveIndex(stream); kmeans->saveIndex(stream);
kdtree_index_->saveIndex(stream); kdtree->saveIndex(stream);
} }
/**
* \brief Loads the index from a stream
* \param stream The stream from which the index is loaded
*/
void loadIndex(FILE* stream) void loadIndex(FILE* stream)
{ {
kmeans_index_->loadIndex(stream); kmeans->loadIndex(stream);
kdtree_index_->loadIndex(stream); kdtree->loadIndex(stream);
} }
/** void findNeighbors(ResultSet<ELEM_TYPE>& result, const ELEM_TYPE* vec, const SearchParams& searchParams)
* \returns The index parameters {
*/ kmeans->findNeighbors(result,vec,searchParams);
IndexParams getParameters() const kdtree->findNeighbors(result,vec,searchParams);
{ }
return index_params_;
}
/** const IndexParams* getParameters() const
* \brief Method that searches for nearest-neighbours {
*/ return &index_params;
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) }
{
kmeans_index_->findNeighbors(result, vec, searchParams);
kdtree_index_->findNeighbors(result, vec, searchParams);
}
private:
/** The k-means index */
KMeansIndex<Distance>* kmeans_index_;
/** The kd-tree index */
KDTreeIndex<Distance>* kdtree_index_;
/** The index parameters */
const IndexParams index_params_;
}; };
} } // namespace cvflann
#endif //OPENCV_FLANN_COMPOSITE_INDEX_H_ #endif //_OPENCV_COMPOSITETREE_H_

View File

@ -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_ */

View File

@ -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

View File

@ -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_

View File

@ -47,21 +47,6 @@
#include "opencv2/flann/flann_base.hpp" #include "opencv2/flann/flann_base.hpp"
namespace cvflann
{
inline flann_distance_t& flann_distance_type_()
{
static flann_distance_t distance_type = FLANN_DIST_L2;
return distance_type;
}
FLANN_DEPRECATED inline void set_distance_type(flann_distance_t distance_type, int order = 0)
{
flann_distance_type_() = (flann_distance_t)((size_t)distance_type + order*0);
}
}
namespace cv namespace cv
{ {
namespace flann namespace flann
@ -77,359 +62,131 @@ template <> struct CvType<float> { static int type() { return CV_32F; } };
template <> struct CvType<double> { static int type() { return CV_64F; } }; template <> struct CvType<double> { static int type() { return CV_64F; } };
// bring the flann parameters into this namespace
using ::cvflann::IndexParams; using ::cvflann::IndexParams;
using ::cvflann::LinearIndexParams; using ::cvflann::LinearIndexParams;
using ::cvflann::KDTreeIndexParams; using ::cvflann::KDTreeIndexParams;
using ::cvflann::KMeansIndexParams; using ::cvflann::KMeansIndexParams;
using ::cvflann::CompositeIndexParams; using ::cvflann::CompositeIndexParams;
using ::cvflann::LshIndexParams;
using ::cvflann::HierarchicalClusteringIndexParams;
using ::cvflann::AutotunedIndexParams; using ::cvflann::AutotunedIndexParams;
using ::cvflann::SavedIndexParams; using ::cvflann::SavedIndexParams;
using ::cvflann::SearchParams; using ::cvflann::SearchParams;
using ::cvflann::get_param;
using ::cvflann::print_params;
// bring the flann distances into this namespace
using ::cvflann::L2_Simple;
using ::cvflann::L2;
using ::cvflann::L1;
using ::cvflann::MinkowskiDistance;
using ::cvflann::MaxDistance;
using ::cvflann::HammingLUT;
using ::cvflann::Hamming;
using ::cvflann::Hamming2;
using ::cvflann::HistIntersectionDistance;
using ::cvflann::HellingerDistance;
using ::cvflann::ChiSquareDistance;
using ::cvflann::KL_Divergence;
template <typename Distance>
class GenericIndex
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
GenericIndex(const Mat& features, const IndexParams& params, Distance distance = Distance());
~GenericIndex();
void knnSearch(const vector<ElementType>& query, vector<int>& indices,
vector<DistanceType>& dists, int knn, const SearchParams& params);
void knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const SearchParams& params);
int radiusSearch(const vector<ElementType>& query, vector<int>& indices,
vector<DistanceType>& dists, DistanceType radius, const SearchParams& params);
int radiusSearch(const Mat& query, Mat& indices, Mat& dists,
DistanceType radius, const SearchParams& params);
void save(std::string filename) { nnIndex->save(filename); }
int veclen() const { return nnIndex->veclen(); }
int size() const { return nnIndex->size(); }
IndexParams getParameters() { return nnIndex->getParameters(); }
FLANN_DEPRECATED const IndexParams* getIndexParameters() { return nnIndex->getIndexParameters(); }
private:
::cvflann::Index<Distance>* nnIndex;
};
#define FLANN_DISTANCE_CHECK \
if ( ::cvflann::flann_distance_type_() != FLANN_DIST_L2) { \
printf("[WARNING] You are using cv::flann::Index (or cv::flann::GenericIndex) and have also changed "\
"the distance using cvflann::set_distance_type. This is no longer working as expected "\
"(cv::flann::Index always uses L2). You should create the index templated on the distance, "\
"for example for L1 distance use: GenericIndex< L1<float> > \n"); \
}
template <typename Distance>
GenericIndex<Distance>::GenericIndex(const Mat& dataset, const IndexParams& params, Distance distance)
{
CV_Assert(dataset.type() == CvType<ElementType>::type());
CV_Assert(dataset.isContinuous());
::cvflann::Matrix<ElementType> m_dataset((ElementType*)dataset.ptr<ElementType>(0), dataset.rows, dataset.cols);
nnIndex = new ::cvflann::Index<Distance>(m_dataset, params, distance);
FLANN_DISTANCE_CHECK
nnIndex->buildIndex();
}
template <typename Distance>
GenericIndex<Distance>::~GenericIndex()
{
delete nnIndex;
}
template <typename Distance>
void GenericIndex<Distance>::knnSearch(const vector<ElementType>& query, vector<int>& indices, vector<DistanceType>& dists, int knn, const SearchParams& searchParams)
{
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size());
FLANN_DISTANCE_CHECK
nnIndex->knnSearch(m_query,m_indices,m_dists,knn,searchParams);
}
template <typename Distance>
void GenericIndex<Distance>::knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const SearchParams& searchParams)
{
CV_Assert(queries.type() == CvType<ElementType>::type());
CV_Assert(queries.isContinuous());
::cvflann::Matrix<ElementType> m_queries((ElementType*)queries.ptr<ElementType>(0), queries.rows, queries.cols);
CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type());
CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols);
FLANN_DISTANCE_CHECK
nnIndex->knnSearch(m_queries,m_indices,m_dists,knn, searchParams);
}
template <typename Distance>
int GenericIndex<Distance>::radiusSearch(const vector<ElementType>& query, vector<int>& indices, vector<DistanceType>& dists, DistanceType radius, const SearchParams& searchParams)
{
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size());
FLANN_DISTANCE_CHECK
return nnIndex->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
}
template <typename Distance>
int GenericIndex<Distance>::radiusSearch(const Mat& query, Mat& indices, Mat& dists, DistanceType radius, const SearchParams& searchParams)
{
CV_Assert(query.type() == CvType<ElementType>::type());
CV_Assert(query.isContinuous());
::cvflann::Matrix<ElementType> m_query((ElementType*)query.ptr<ElementType>(0), query.rows, query.cols);
CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type());
CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols);
FLANN_DISTANCE_CHECK
return nnIndex->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
}
typedef GenericIndex< L2<float> > Index;
/**
* @deprecated Use GenericIndex class instead
*/
template <typename T> template <typename T>
class FLANN_DEPRECATED Index_ { class CV_EXPORTS Index_ {
public: ::cvflann::Index<T>* nnIndex;
typedef typename L2<T>::ElementType ElementType;
typedef typename L2<T>::ResultType DistanceType;
public:
Index_(const Mat& features, const IndexParams& params); Index_(const Mat& features, const IndexParams& params);
~Index_(); ~Index_();
void knnSearch(const vector<ElementType>& query, vector<int>& indices, vector<DistanceType>& dists, int knn, const SearchParams& params); void knnSearch(const vector<T>& query, vector<int>& indices, vector<float>& dists, int knn, const SearchParams& params);
void knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const SearchParams& params); void knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const SearchParams& params);
int radiusSearch(const vector<ElementType>& query, vector<int>& indices, vector<DistanceType>& dists, DistanceType radius, const SearchParams& params); int radiusSearch(const vector<T>& query, vector<int>& indices, vector<float>& dists, float radius, const SearchParams& params);
int radiusSearch(const Mat& query, Mat& indices, Mat& dists, DistanceType radius, const SearchParams& params); int radiusSearch(const Mat& query, Mat& indices, Mat& dists, float radius, const SearchParams& params);
void save(std::string filename) void save(std::string filename) { nnIndex->save(filename); }
{
if (nnIndex_L1) nnIndex_L1->save(filename);
if (nnIndex_L2) nnIndex_L2->save(filename);
}
int veclen() const int veclen() const { return nnIndex->veclen(); }
{
if (nnIndex_L1) return nnIndex_L1->veclen();
if (nnIndex_L2) return nnIndex_L2->veclen();
}
int size() const int size() const { return nnIndex->size(); }
{
if (nnIndex_L1) return nnIndex_L1->size();
if (nnIndex_L2) return nnIndex_L2->size();
}
IndexParams getParameters() const IndexParams* getIndexParameters() { return nnIndex->getIndexParameters(); }
{
if (nnIndex_L1) return nnIndex_L1->getParameters();
if (nnIndex_L2) return nnIndex_L2->getParameters();
}
FLANN_DEPRECATED const IndexParams* getIndexParameters()
{
if (nnIndex_L1) return nnIndex_L1->getIndexParameters();
if (nnIndex_L2) return nnIndex_L2->getIndexParameters();
}
private:
// providing backwards compatibility for L2 and L1 distances (most common)
::cvflann::Index< L2<ElementType> >* nnIndex_L2;
::cvflann::Index< L1<ElementType> >* nnIndex_L1;
}; };
template <typename T> template <typename T>
Index_<T>::Index_(const Mat& dataset, const IndexParams& params) Index_<T>::Index_(const Mat& dataset, const IndexParams& params)
{ {
printf("[WARNING] The cv::flann::Index_<T> class is deperecated, use cv::flann::GenericIndex<Distance> instead\n"); CV_Assert(dataset.type() == CvType<T>::type());
CV_Assert(dataset.type() == CvType<ElementType>::type());
CV_Assert(dataset.isContinuous()); CV_Assert(dataset.isContinuous());
::cvflann::Matrix<ElementType> m_dataset((ElementType*)dataset.ptr<ElementType>(0), dataset.rows, dataset.cols); ::cvflann::Matrix<T> m_dataset((T*)dataset.ptr<T>(0), dataset.rows, dataset.cols);
if ( ::cvflann::flann_distance_type_() == FLANN_DIST_L2 ) { nnIndex = new ::cvflann::Index<T>(m_dataset, params);
nnIndex_L1 = NULL; nnIndex->buildIndex();
nnIndex_L2 = new ::cvflann::Index< L2<ElementType> >(m_dataset, params);
}
else if ( ::cvflann::flann_distance_type_() == FLANN_DIST_L1 ) {
nnIndex_L1 = new ::cvflann::Index< L1<ElementType> >(m_dataset, params);
nnIndex_L2 = NULL;
}
else {
printf("[ERROR] cv::flann::Index_<T> only provides backwards compatibility for the L1 and L2 distances. "
"For other distance types you must use cv::flann::GenericIndex<Distance>\n");
CV_Assert(0);
}
if (nnIndex_L1) nnIndex_L1->buildIndex();
if (nnIndex_L2) nnIndex_L2->buildIndex();
} }
template <typename T> template <typename T>
Index_<T>::~Index_() Index_<T>::~Index_()
{ {
if (nnIndex_L1) delete nnIndex_L1; delete nnIndex;
if (nnIndex_L2) delete nnIndex_L2;
} }
template <typename T> template <typename T>
void Index_<T>::knnSearch(const vector<ElementType>& query, vector<int>& indices, vector<DistanceType>& dists, int knn, const SearchParams& searchParams) void Index_<T>::knnSearch(const vector<T>& query, vector<int>& indices, vector<float>& dists, int knn, const SearchParams& searchParams)
{ {
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size()); ::cvflann::Matrix<T> m_query((T*)&query[0], 1, (int)query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size()); ::cvflann::Matrix<int> m_indices(&indices[0], 1, (int)indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size()); ::cvflann::Matrix<float> m_dists(&dists[0], 1, (int)dists.size());
if (nnIndex_L1) nnIndex_L1->knnSearch(m_query,m_indices,m_dists,knn,searchParams); nnIndex->knnSearch(m_query,m_indices,m_dists,knn,searchParams);
if (nnIndex_L2) nnIndex_L2->knnSearch(m_query,m_indices,m_dists,knn,searchParams);
} }
template <typename T> template <typename T>
void Index_<T>::knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const SearchParams& searchParams) void Index_<T>::knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const SearchParams& searchParams)
{ {
CV_Assert(queries.type() == CvType<ElementType>::type()); CV_Assert(queries.type() == CvType<T>::type());
CV_Assert(queries.isContinuous()); CV_Assert(queries.isContinuous());
::cvflann::Matrix<ElementType> m_queries((ElementType*)queries.ptr<ElementType>(0), queries.rows, queries.cols); ::cvflann::Matrix<T> m_queries((T*)queries.ptr<T>(0), queries.rows, queries.cols);
CV_Assert(indices.type() == CV_32S); CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous()); CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols); ::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type()); CV_Assert(dists.type() == CV_32F);
CV_Assert(dists.isContinuous()); CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols); ::cvflann::Matrix<float> m_dists((float*)dists.ptr<float>(0), dists.rows, dists.cols);
if (nnIndex_L1) nnIndex_L1->knnSearch(m_queries,m_indices,m_dists,knn, searchParams); nnIndex->knnSearch(m_queries,m_indices,m_dists,knn, searchParams);
if (nnIndex_L2) nnIndex_L2->knnSearch(m_queries,m_indices,m_dists,knn, searchParams);
} }
template <typename T> template <typename T>
int Index_<T>::radiusSearch(const vector<ElementType>& query, vector<int>& indices, vector<DistanceType>& dists, DistanceType radius, const SearchParams& searchParams) int Index_<T>::radiusSearch(const vector<T>& query, vector<int>& indices, vector<float>& dists, float radius, const SearchParams& searchParams)
{ {
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size()); ::cvflann::Matrix<T> m_query((T*)&query[0], 1, (int)query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size()); ::cvflann::Matrix<int> m_indices(&indices[0], 1, (int)indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size()); ::cvflann::Matrix<float> m_dists(&dists[0], 1, (int)dists.size());
if (nnIndex_L1) return nnIndex_L1->radiusSearch(m_query,m_indices,m_dists,radius,searchParams); return nnIndex->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
if (nnIndex_L2) return nnIndex_L2->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
} }
template <typename T> template <typename T>
int Index_<T>::radiusSearch(const Mat& query, Mat& indices, Mat& dists, DistanceType radius, const SearchParams& searchParams) int Index_<T>::radiusSearch(const Mat& query, Mat& indices, Mat& dists, float radius, const SearchParams& searchParams)
{ {
CV_Assert(query.type() == CvType<ElementType>::type()); CV_Assert(query.type() == CvType<T>::type());
CV_Assert(query.isContinuous()); CV_Assert(query.isContinuous());
::cvflann::Matrix<ElementType> m_query((ElementType*)query.ptr<ElementType>(0), query.rows, query.cols); ::cvflann::Matrix<T> m_query((T*)query.ptr<T>(0), query.rows, query.cols);
CV_Assert(indices.type() == CV_32S); CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous()); CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols); ::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type()); CV_Assert(dists.type() == CV_32F);
CV_Assert(dists.isContinuous()); CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols); ::cvflann::Matrix<float> m_dists((float*)dists.ptr<float>(0), dists.rows, dists.cols);
if (nnIndex_L1) return nnIndex_L1->radiusSearch(m_query,m_indices,m_dists,radius,searchParams); return nnIndex->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
if (nnIndex_L2) return nnIndex_L2->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
}
template <typename Distance>
int hierarchicalClustering(const Mat& features, Mat& centers, const KMeansIndexParams& params,
Distance d = Distance())
{
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
CV_Assert(features.type() == CvType<ElementType>::type());
CV_Assert(features.isContinuous());
::cvflann::Matrix<ElementType> m_features((ElementType*)features.ptr<ElementType>(0), features.rows, features.cols);
CV_Assert(centers.type() == CvType<DistanceType>::type());
CV_Assert(centers.isContinuous());
::cvflann::Matrix<DistanceType> m_centers((DistanceType*)centers.ptr<DistanceType>(0), centers.rows, centers.cols);
return ::cvflann::hierarchicalClustering<Distance>(m_features, m_centers, params, d);
} }
typedef Index_<float> Index;
template <typename ELEM_TYPE, typename DIST_TYPE> template <typename ELEM_TYPE, typename DIST_TYPE>
FLANN_DEPRECATED int hierarchicalClustering(const Mat& features, Mat& centers, const KMeansIndexParams& params) int hierarchicalClustering(const Mat& features, Mat& centers, const KMeansIndexParams& params)
{ {
printf("[WARNING] cv::flann::hierarchicalClustering<ELEM_TYPE,DIST_TYPE> is deprecated, use " CV_Assert(features.type() == CvType<ELEM_TYPE>::type());
"cv::flann::hierarchicalClustering<Distance> instead\n"); CV_Assert(features.isContinuous());
::cvflann::Matrix<ELEM_TYPE> m_features((ELEM_TYPE*)features.ptr<ELEM_TYPE>(0), features.rows, features.cols);
if ( ::cvflann::flann_distance_type_() == FLANN_DIST_L2 ) { CV_Assert(centers.type() == CvType<DIST_TYPE>::type());
return hierarchicalClustering< L2<ELEM_TYPE> >(features, centers, params); CV_Assert(centers.isContinuous());
} ::cvflann::Matrix<DIST_TYPE> m_centers((DIST_TYPE*)centers.ptr<DIST_TYPE>(0), centers.rows, centers.cols);
else if ( ::cvflann::flann_distance_type_() == FLANN_DIST_L1 ) {
return hierarchicalClustering< L1<ELEM_TYPE> >(features, centers, params); return ::cvflann::hierarchicalClustering<ELEM_TYPE,DIST_TYPE>(m_features, m_centers, params);
}
else {
printf("[ERROR] cv::flann::hierarchicalClustering<ELEM_TYPE,DIST_TYPE> only provides backwards "
"compatibility for the L1 and L2 distances. "
"For other distance types you must use cv::flann::hierarchicalClustering<Distance>\n");
CV_Assert(0);
}
} }
} } // namespace cv::flann } } // namespace cv::flann

View File

@ -28,264 +28,232 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef FLANN_BASE_HPP_ #ifndef _OPENCV_FLANN_BASE_HPP_
#define FLANN_BASE_HPP_ #define _OPENCV_FLANN_BASE_HPP_
#include <vector> #include <vector>
#include <string> #include <string>
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include "general.h" #include "opencv2/flann/general.h"
#include "matrix.h" #include "opencv2/flann/matrix.h"
#include "params.h" #include "opencv2/flann/result_set.h"
#include "saving.h" #include "opencv2/flann/index_testing.h"
#include "opencv2/flann/object_factory.h"
#include "opencv2/flann/saving.h"
#include "all_indices.h" #include "opencv2/flann/all_indices.h"
namespace cvflann namespace cvflann
{ {
/**
* Sets the log level used for all flann functions
* @param level Verbosity level
*/
inline void log_verbosity(int level)
{
if (level >= 0) {
Logger::setLevel(level);
}
}
/** /**
* (Deprecated) Index parameters for creating a saved index. Sets the log level used for all flann functions
Params:
level = verbosity level
*/
CV_EXPORTS void log_verbosity(int level);
/**
* Sets the distance type to use throughout FLANN.
* If distance type specified is MINKOWSKI, the second argument
* specifies which order the minkowski distance should have.
*/ */
struct SavedIndexParams : public IndexParams CV_EXPORTS void set_distance_type(flann_distance_t distance_type, int order);
{
SavedIndexParams(std::string filename)
{ struct CV_EXPORTS SavedIndexParams : public IndexParams {
(* this)["algorithm"] = FLANN_INDEX_SAVED; SavedIndexParams(std::string filename_) : IndexParams(FLANN_INDEX_SAVED), filename(filename_) {}
(*this)["filename"] = filename;
} std::string filename; // filename of the stored index
void print() const
{
logger().info("Index type: %d\n",(int)algorithm);
logger().info("Filename: %s\n", filename.c_str());
}
}; };
template<typename T>
class CV_EXPORTS Index {
NNIndex<T>* nnIndex;
bool built;
template<typename Distance>
NNIndex<Distance>* load_saved_index(const Matrix<typename Distance::ElementType>& dataset, const std::string& filename, Distance distance)
{
typedef typename Distance::ElementType ElementType;
FILE* fin = fopen(filename.c_str(), "rb");
if (fin == NULL) {
return NULL;
}
IndexHeader header = load_header(fin);
if (header.data_type != Datatype<ElementType>::type()) {
throw FLANNException("Datatype of saved index is different than of the one to be created.");
}
if ((size_t(header.rows) != dataset.rows)||(size_t(header.cols) != dataset.cols)) {
throw FLANNException("The index saved belongs to a different dataset");
}
IndexParams params;
params["algorithm"] = header.index_type;
NNIndex<Distance>* nnIndex = create_index_by_type<Distance>(dataset, params, distance);
nnIndex->loadIndex(fin);
fclose(fin);
return nnIndex;
}
template<typename Distance>
class Index : public NNIndex<Distance>
{
public: public:
typedef typename Distance::ElementType ElementType; Index(const Matrix<T>& features, const IndexParams& params);
typedef typename Distance::ResultType DistanceType;
Index(const Matrix<ElementType>& features, const IndexParams& params, Distance distance = Distance() ) ~Index();
: index_params_(params)
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params,"algorithm");
loaded_ = false;
if (index_type == FLANN_INDEX_SAVED) { void buildIndex();
nnIndex_ = load_saved_index<Distance>(features, get_param<std::string>(params,"filename"), distance);
loaded_ = true;
}
else {
nnIndex_ = create_index_by_type<Distance>(features, params, distance);
}
}
~Index() void knnSearch(const Matrix<T>& queries, Matrix<int>& indices, Matrix<float>& dists, int knn, const SearchParams& params);
{
delete nnIndex_;
}
/** int radiusSearch(const Matrix<T>& query, Matrix<int>& indices, Matrix<float>& dists, float radius, const SearchParams& params);
* Builds the index.
*/
void buildIndex()
{
if (!loaded_) {
nnIndex_->buildIndex();
}
}
void save(std::string filename) void save(std::string filename);
{
FILE* fout = fopen(filename.c_str(), "wb");
if (fout == NULL) {
throw FLANNException("Cannot open file");
}
save_header(fout, *nnIndex_);
saveIndex(fout);
fclose(fout);
}
/** int veclen() const;
* \brief Saves the index to a stream
* \param stream The stream to save the index to
*/
virtual void saveIndex(FILE* stream)
{
nnIndex_->saveIndex(stream);
}
/** int size() const;
* \brief Loads the index from a stream
* \param stream The stream from which the index is loaded
*/
virtual void loadIndex(FILE* stream)
{
nnIndex_->loadIndex(stream);
}
/** NNIndex<T>* getIndex() { return nnIndex; }
* \returns number of features in this index.
*/
size_t veclen() const
{
return nnIndex_->veclen();
}
/** const IndexParams* getIndexParameters() { return nnIndex->getParameters(); }
* \returns The dimensionality of the features in this index.
*/
size_t size() const
{
return nnIndex_->size();
}
/**
* \returns The index type (kdtree, kmeans,...)
*/
flann_algorithm_t getType() const
{
return nnIndex_->getType();
}
/**
* \returns The amount of memory (in bytes) used by the index.
*/
virtual int usedMemory() const
{
return nnIndex_->usedMemory();
}
/**
* \returns The index parameters
*/
IndexParams getParameters() const
{
return nnIndex_->getParameters();
}
/**
* \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)
{
nnIndex_->knnSearch(queries, indices, dists, knn, params);
}
/**
* \brief Perform radius search
* \param[in] query The query point
* \param[out] indices The indinces of the neighbors found within the given radius
* \param[out] dists The distances to the nearest neighbors found
* \param[in] radius The radius used for search
* \param[in] params Search parameters
* \returns Number of neighbors found
*/
int radiusSearch(const Matrix<ElementType>& query, Matrix<int>& indices, Matrix<DistanceType>& dists, float radius, const SearchParams& params)
{
return nnIndex_->radiusSearch(query, indices, dists, radius, params);
}
/**
* \brief Method that searches for nearest-neighbours
*/
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams)
{
nnIndex_->findNeighbors(result, vec, searchParams);
}
/**
* \brief Returns actual index
*/
FLANN_DEPRECATED NNIndex<Distance>* getIndex()
{
return nnIndex_;
}
/**
* \brief Returns index parameters.
* \deprecated use getParameters() instead.
*/
FLANN_DEPRECATED const IndexParams* getIndexParameters()
{
return &index_params_;
}
private:
/** Pointer to actual index class */
NNIndex<Distance>* nnIndex_;
/** Indices if the index was loaded from a file */
bool loaded_;
/** Parameters passed to the index */
IndexParams index_params_;
}; };
/**
* Performs a hierarchical clustering of the points passed as argument and then takes a cut in the template<typename T>
* the clustering tree to return a flat clustering. NNIndex<T>* load_saved_index(const Matrix<T>& dataset, const std::string& filename)
* @param[in] points Points to be clustered
* @param centers The computed cluster centres. Matrix should be preallocated and centers.rows is the
* number of clusters requested.
* @param params Clustering parameters (The same as for cvflann::KMeansIndex)
* @param d Distance to be used for clustering (eg: cvflann::L2)
* @return number of clusters computed (can be different than clusters.rows and is the highest number
* of the form (branching-1)*K+1 smaller than clusters.rows).
*/
template <typename Distance>
int hierarchicalClustering(const Matrix<typename Distance::ElementType>& points, Matrix<typename Distance::ResultType>& centers,
const KMeansIndexParams& params, Distance d = Distance())
{ {
KMeansIndex<Distance> kmeans(points, params, d); FILE* fin = fopen(filename.c_str(), "rb");
kmeans.buildIndex(); if (fin==NULL) {
return NULL;
}
IndexHeader header = load_header(fin);
if (header.data_type!=Datatype<T>::type()) {
throw FLANNException("Datatype of saved index is different than of the one to be created.");
}
if (size_t(header.rows)!=dataset.rows || size_t(header.cols)!=dataset.cols) {
throw FLANNException("The index saved belongs to a different dataset");
}
IndexParams* params = ParamsFactory_instance().create(header.index_type);
NNIndex<T>* nnIndex = create_index_by_type(dataset, *params);
nnIndex->loadIndex(fin);
fclose(fin);
return nnIndex;
}
template<typename T>
Index<T>::Index(const Matrix<T>& dataset, const IndexParams& params)
{
flann_algorithm_t index_type = params.getIndexType();
built = false;
if (index_type==FLANN_INDEX_SAVED) {
nnIndex = load_saved_index(dataset, ((const SavedIndexParams&)params).filename);
built = true;
}
else {
nnIndex = create_index_by_type(dataset, params);
}
}
template<typename T>
Index<T>::~Index()
{
delete nnIndex;
}
template<typename T>
void Index<T>::buildIndex()
{
if (!built) {
nnIndex->buildIndex();
built = true;
}
}
template<typename T>
void Index<T>::knnSearch(const Matrix<T>& queries, Matrix<int>& indices, Matrix<float>& dists, int knn, const SearchParams& searchParams)
{
if (!built) {
throw FLANNException("You must build the index before searching.");
}
assert(queries.cols==nnIndex->veclen());
assert(indices.rows>=queries.rows);
assert(dists.rows>=queries.rows);
assert(int(indices.cols)>=knn);
assert(int(dists.cols)>=knn);
KNNResultSet<T> resultSet(knn);
for (size_t i = 0; i < queries.rows; i++) {
T* target = queries[i];
resultSet.init(target, (int)queries.cols);
nnIndex->findNeighbors(resultSet, target, searchParams);
int* neighbors = resultSet.getNeighbors();
float* distances = resultSet.getDistances();
memcpy(indices[i], neighbors, knn*sizeof(int));
memcpy(dists[i], distances, knn*sizeof(float));
}
}
template<typename T>
int Index<T>::radiusSearch(const Matrix<T>& query, Matrix<int>& indices, Matrix<float>& dists, float radius, const SearchParams& searchParams)
{
if (!built) {
throw FLANNException("You must build the index before searching.");
}
if (query.rows!=1) {
fprintf(stderr, "I can only search one feature at a time for range search\n");
return -1;
}
assert(query.cols==nnIndex->veclen());
RadiusResultSet<T> resultSet(radius);
resultSet.init(query.data, (int)query.cols);
nnIndex->findNeighbors(resultSet,query.data,searchParams);
// TODO: optimise here
int* neighbors = resultSet.getNeighbors();
float* distances = resultSet.getDistances();
size_t count_nn = std::min(resultSet.size(), indices.cols);
assert (dists.cols>=count_nn);
for (size_t i=0;i<count_nn;++i) {
indices[0][i] = neighbors[i];
dists[0][i] = distances[i];
}
return (int)count_nn;
}
template<typename T>
void Index<T>::save(std::string filename)
{
FILE* fout = fopen(filename.c_str(), "wb");
if (fout==NULL) {
throw FLANNException("Cannot open file");
}
save_header(fout, *nnIndex);
nnIndex->saveIndex(fout);
fclose(fout);
}
template<typename T>
int Index<T>::size() const
{
return nnIndex->size();
}
template<typename T>
int Index<T>::veclen() const
{
return nnIndex->veclen();
}
template <typename ELEM_TYPE, typename DIST_TYPE>
int hierarchicalClustering(const Matrix<ELEM_TYPE>& features, Matrix<DIST_TYPE>& centers, const KMeansIndexParams& params)
{
KMeansIndex<ELEM_TYPE, DIST_TYPE> kmeans(features, params);
kmeans.buildIndex();
int clusterNum = kmeans.getClusterCenters(centers); int clusterNum = kmeans.getClusterCenters(centers);
return clusterNum; return clusterNum;
} }
} } // namespace cvflann
#endif /* FLANN_BASE_HPP_ */ #endif /* _OPENCV_FLANN_BASE_HPP_ */

View File

@ -28,30 +28,119 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_GENERAL_H_ #ifndef _OPENCV_GENERAL_H_
#define OPENCV_FLANN_GENERAL_H_ #define _OPENCV_GENERAL_H_
#ifdef __cplusplus
#include "defines.h"
#include <stdexcept> #include <stdexcept>
#include <cassert> #include <cassert>
#include "opencv2/flann/object_factory.h"
#include "opencv2/flann/logger.h"
namespace cvflann namespace cvflann {
{
class FLANNException : public std::runtime_error #undef ARRAY_LEN
{ #define ARRAY_LEN(a) (sizeof(a)/sizeof(a[0]))
public:
FLANNException(const char* message) : std::runtime_error(message) { }
FLANNException(const std::string& message) : std::runtime_error(message) { } /* 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_SAVED = 254,
FLANN_INDEX_AUTOTUNED = 255
}; };
#if (defined WIN32 || defined _WIN32 || defined WINCE) && defined CVAPI_EXPORTS enum flann_centers_init_t {
__declspec(dllexport) FLANN_CENTERS_RANDOM = 0,
FLANN_CENTERS_GONZALES = 1,
FLANN_CENTERS_KMEANSPP = 2
};
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
};
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
};
template <typename ELEM_TYPE>
struct DistType
{
typedef ELEM_TYPE type;
};
template <>
struct DistType<unsigned char>
{
typedef float type;
};
template <>
struct DistType<int>
{
typedef float type;
};
class FLANNException : public std::runtime_error {
public:
FLANNException(const char* message) : std::runtime_error(message) { }
FLANNException(const std::string& message) : std::runtime_error(message) { }
};
struct CV_EXPORTS IndexParams {
protected:
IndexParams(flann_algorithm_t algorithm_) : algorithm(algorithm_) {};
public:
virtual ~IndexParams() {}
virtual flann_algorithm_t getIndexType() const { return algorithm; }
virtual void print() const = 0;
flann_algorithm_t algorithm;
};
typedef ObjectFactory<IndexParams, flann_algorithm_t> ParamsFactory;
CV_EXPORTS ParamsFactory& ParamsFactory_instance();
struct CV_EXPORTS SearchParams {
SearchParams(int checks_ = 32) :
checks(checks_) {};
int checks;
};
} // namespace cvflann
#endif #endif
void dummyfunc();
} #endif /* _OPENCV_GENERAL_H_ */
#endif /* OPENCV_FLANN_GENERAL_H_ */

View File

@ -28,41 +28,39 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_GROUND_TRUTH_H_ #ifndef _OPENCV_GROUND_TRUTH_H_
#define OPENCV_FLANN_GROUND_TRUTH_H_ #define _OPENCV_GROUND_TRUTH_H_
#include "dist.h"
#include "matrix.h"
#include "opencv2/flann/dist.h"
#include "opencv2/flann/matrix.h"
namespace cvflann namespace cvflann
{ {
template <typename Distance> template <typename T>
void find_nearest(const Matrix<typename Distance::ElementType>& dataset, typename Distance::ElementType* query, int* matches, int nn, void find_nearest(const Matrix<T>& dataset, T* query, int* matches, int nn, int skip = 0)
int skip = 0, Distance distance = Distance())
{ {
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
int n = nn + skip; int n = nn + skip;
int* match = new int[n]; T* query_end = query + dataset.cols;
DistanceType* dists = new DistanceType[n];
dists[0] = distance(dataset[0], query, dataset.cols); long* match = new long[n];
T* dists = new T[n];
dists[0] = (float)flann_dist(query, query_end, dataset[0]);
match[0] = 0; match[0] = 0;
int dcnt = 1; int dcnt = 1;
for (size_t i=1; i<dataset.rows; ++i) { for (size_t i=1;i<dataset.rows;++i) {
DistanceType tmp = distance(dataset[i], query, dataset.cols); T tmp = (T)flann_dist(query, query_end, dataset[i]);
if (dcnt<n) { if (dcnt<n) {
match[dcnt] = i; match[dcnt] = (long)i;
dists[dcnt++] = tmp; dists[dcnt++] = tmp;
} }
else if (tmp < dists[dcnt-1]) { else if (tmp < dists[dcnt-1]) {
dists[dcnt-1] = tmp; dists[dcnt-1] = tmp;
match[dcnt-1] = i; match[dcnt-1] = (long)i;
} }
int j = dcnt-1; int j = dcnt-1;
@ -74,7 +72,7 @@ void find_nearest(const Matrix<typename Distance::ElementType>& dataset, typenam
} }
} }
for (int i=0; i<nn; ++i) { for (int i=0;i<nn;++i) {
matches[i] = match[i+skip]; matches[i] = match[i+skip];
} }
@ -83,16 +81,15 @@ void find_nearest(const Matrix<typename Distance::ElementType>& dataset, typenam
} }
template <typename Distance> template <typename T>
void compute_ground_truth(const Matrix<typename Distance::ElementType>& dataset, const Matrix<typename Distance::ElementType>& testset, Matrix<int>& matches, void compute_ground_truth(const Matrix<T>& dataset, const Matrix<T>& testset, Matrix<int>& matches, int skip=0)
int skip=0, Distance d = Distance())
{ {
for (size_t i=0; i<testset.rows; ++i) { for (size_t i=0;i<testset.rows;++i) {
find_nearest<Distance>(dataset, testset[i], matches[i], (int)matches.cols, skip, d); find_nearest(dataset, testset[i], matches[i], (int)matches.cols, skip);
} }
} }
} } // namespace cvflann
#endif //OPENCV_FLANN_GROUND_TRUTH_H_ #endif //_OPENCV_GROUND_TRUTH_H_

View File

@ -27,207 +27,137 @@
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_HDF5_H_ #ifndef _OPENCV_HDF5_H_
#define OPENCV_FLANN_HDF5_H_ #define _OPENCV_HDF5_H_
#include <hdf5.h> #include <H5Cpp.h>
#include "matrix.h" #include "opencv2/flann/matrix.h"
#ifndef H5_NO_NAMESPACE
using namespace H5;
#endif
namespace cvflann namespace cvflann
{ {
namespace
{ namespace {
template<typename T> template<typename T>
hid_t get_hdf5_type() PredType get_hdf5_type()
{ {
throw FLANNException("Unsupported type for IO operations"); throw FLANNException("Unsupported type for IO operations");
} }
template<> template<> PredType get_hdf5_type<char>() { return PredType::NATIVE_CHAR; }
hid_t get_hdf5_type<char>() { return H5T_NATIVE_CHAR; } template<> PredType get_hdf5_type<unsigned char>() { return PredType::NATIVE_UCHAR; }
template<> template<> PredType get_hdf5_type<short int>() { return PredType::NATIVE_SHORT; }
hid_t get_hdf5_type<unsigned char>() { return H5T_NATIVE_UCHAR; } template<> PredType get_hdf5_type<unsigned short int>() { return PredType::NATIVE_USHORT; }
template<> template<> PredType get_hdf5_type<int>() { return PredType::NATIVE_INT; }
hid_t get_hdf5_type<short int>() { return H5T_NATIVE_SHORT; } template<> PredType get_hdf5_type<unsigned int>() { return PredType::NATIVE_UINT; }
template<> template<> PredType get_hdf5_type<long>() { return PredType::NATIVE_LONG; }
hid_t get_hdf5_type<unsigned short int>() { return H5T_NATIVE_USHORT; } template<> PredType get_hdf5_type<unsigned long>() { return PredType::NATIVE_ULONG; }
template<> template<> PredType get_hdf5_type<float>() { return PredType::NATIVE_FLOAT; }
hid_t get_hdf5_type<int>() { return H5T_NATIVE_INT; } template<> PredType get_hdf5_type<double>() { return PredType::NATIVE_DOUBLE; }
template<> template<> PredType get_hdf5_type<long double>() { return PredType::NATIVE_LDOUBLE; }
hid_t get_hdf5_type<unsigned int>() { return H5T_NATIVE_UINT; }
template<>
hid_t get_hdf5_type<long>() { return H5T_NATIVE_LONG; }
template<>
hid_t get_hdf5_type<unsigned long>() { return H5T_NATIVE_ULONG; }
template<>
hid_t get_hdf5_type<float>() { return H5T_NATIVE_FLOAT; }
template<>
hid_t get_hdf5_type<double>() { return H5T_NATIVE_DOUBLE; }
template<>
hid_t get_hdf5_type<long double>() { return H5T_NATIVE_LDOUBLE; }
}
#define CHECK_ERROR(x,y) if ((x)<0) throw FLANNException((y));
template<typename T>
void save_to_file(const cvflann::Matrix<T>& dataset, const std::string& filename, const std::string& name)
{
#if H5Eset_auto_vers == 2
H5Eset_auto( H5E_DEFAULT, NULL, NULL );
#else
H5Eset_auto( NULL, NULL );
#endif
herr_t status;
hid_t file_id;
file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
if (file_id < 0) {
file_id = H5Fcreate(filename.c_str(), H5F_ACC_EXCL, H5P_DEFAULT, H5P_DEFAULT);
}
CHECK_ERROR(file_id,"Error creating hdf5 file.");
hsize_t dimsf[2]; // dataset dimensions
dimsf[0] = dataset.rows;
dimsf[1] = dataset.cols;
hid_t space_id = H5Screate_simple(2, dimsf, NULL);
hid_t memspace_id = H5Screate_simple(2, dimsf, NULL);
hid_t dataset_id;
#if H5Dcreate_vers == 2
dataset_id = H5Dcreate2(file_id, name.c_str(), get_hdf5_type<T>(), space_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
#else
dataset_id = H5Dcreate(file_id, name.c_str(), get_hdf5_type<T>(), space_id, H5P_DEFAULT);
#endif
if (dataset_id<0) {
#if H5Dopen_vers == 2
dataset_id = H5Dopen2(file_id, name.c_str(), H5P_DEFAULT);
#else
dataset_id = H5Dopen(file_id, name.c_str());
#endif
}
CHECK_ERROR(dataset_id,"Error creating or opening dataset in file.");
status = H5Dwrite(dataset_id, get_hdf5_type<T>(), memspace_id, space_id, H5P_DEFAULT, dataset.data );
CHECK_ERROR(status, "Error writing to dataset");
H5Sclose(memspace_id);
H5Sclose(space_id);
H5Dclose(dataset_id);
H5Fclose(file_id);
} }
template<typename T> template<typename T>
void load_from_file(cvflann::Matrix<T>& dataset, const std::string& filename, const std::string& name) void save_to_file(const cvflann::Matrix<T>& flann_dataset, const std::string& filename, const std::string& name)
{ {
herr_t status; // Try block to detect exceptions raised by any of the calls inside it
hid_t file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT); try
CHECK_ERROR(file_id,"Error opening hdf5 file."); {
/*
* Turn off the auto-printing when failure occurs so that we can
* handle the errors appropriately
*/
Exception::dontPrint();
hid_t dataset_id; /*
#if H5Dopen_vers == 2 * Create a new file using H5F_ACC_TRUNC access,
dataset_id = H5Dopen2(file_id, name.c_str(), H5P_DEFAULT); * default file creation properties, and default file
#else * access properties.
dataset_id = H5Dopen(file_id, name.c_str()); */
#endif H5File file( filename, H5F_ACC_TRUNC );
CHECK_ERROR(dataset_id,"Error opening dataset in file.");
hid_t space_id = H5Dget_space(dataset_id); /*
* Define the size of the array and create the data space for fixed
* size dataset.
*/
hsize_t dimsf[2]; // dataset dimensions
dimsf[0] = flann_dataset.rows;
dimsf[1] = flann_dataset.cols;
DataSpace dataspace( 2, dimsf );
hsize_t dims_out[2]; /*
H5Sget_simple_extent_dims(space_id, dims_out, NULL); * Create a new dataset within the file using defined dataspace and
* datatype and default dataset creation properties.
*/
DataSet dataset = file.createDataSet( name, get_hdf5_type<T>(), dataspace );
dataset = cvflann::Matrix<T>(new T[dims_out[0]*dims_out[1]], dims_out[0], dims_out[1]); /*
* Write the data to the dataset using default memory space, file
status = H5Dread(dataset_id, get_hdf5_type<T>(), H5S_ALL, H5S_ALL, H5P_DEFAULT, dataset[0]); * space, and transfer properties.
CHECK_ERROR(status, "Error reading dataset"); */
dataset.write( flann_dataset.data, get_hdf5_type<T>() );
H5Sclose(space_id); } // end of try block
H5Dclose(dataset_id); catch( H5::Exception& error )
H5Fclose(file_id); {
error.printError();
throw FLANNException(error.getDetailMsg());
}
} }
#ifdef HAVE_MPI
namespace mpi
{
/**
* Loads a the hyperslice corresponding to this processor from a hdf5 file.
* @param flann_dataset Dataset where the data is loaded
* @param filename HDF5 file name
* @param name Name of dataset inside file
*/
template<typename T> template<typename T>
void load_from_file(cvflann::Matrix<T>& dataset, const std::string& filename, const std::string& name) void load_from_file(cvflann::Matrix<T>& flann_dataset, const std::string& filename, const std::string& name)
{ {
MPI_Comm comm = MPI_COMM_WORLD; try
MPI_Info info = MPI_INFO_NULL; {
Exception::dontPrint();
int mpi_size, mpi_rank; H5File file( filename, H5F_ACC_RDONLY );
MPI_Comm_size(comm, &mpi_size); DataSet dataset = file.openDataSet( name );
MPI_Comm_rank(comm, &mpi_rank);
herr_t status; /*
* Check the type used by the dataset matches
*/
if ( !(dataset.getDataType()==get_hdf5_type<T>())) {
throw FLANNException("Dataset matrix type does not match the type to be read.");
}
hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS); /*
H5Pset_fapl_mpio(plist_id, comm, info); * Get dataspace of the dataset.
hid_t file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, plist_id); */
CHECK_ERROR(file_id,"Error opening hdf5 file."); DataSpace dataspace = dataset.getSpace();
H5Pclose(plist_id);
hid_t dataset_id;
#if H5Dopen_vers == 2
dataset_id = H5Dopen2(file_id, name.c_str(), H5P_DEFAULT);
#else
dataset_id = H5Dopen(file_id, name.c_str());
#endif
CHECK_ERROR(dataset_id,"Error opening dataset in file.");
hid_t space_id = H5Dget_space(dataset_id); /*
hsize_t dims[2]; * Get the dimension size of each dimension in the dataspace and
H5Sget_simple_extent_dims(space_id, dims, NULL); * display them.
*/
hsize_t dims_out[2];
dataspace.getSimpleExtentDims( dims_out, NULL);
hsize_t count[2]; flann_dataset.rows = dims_out[0];
hsize_t offset[2]; flann_dataset.cols = dims_out[1];
flann_dataset.data = new T[flann_dataset.rows*flann_dataset.cols];
hsize_t item_cnt = dims[0]/mpi_size+(dims[0]%mpi_size==0 ? 0 : 1); dataset.read( flann_dataset.data, get_hdf5_type<T>() );
hsize_t cnt = (mpi_rank<mpi_size-1 ? item_cnt : dims[0]-item_cnt*(mpi_size-1)); } // end of try block
catch( H5::Exception &error )
count[0] = cnt; {
count[1] = dims[1]; error.printError();
offset[0] = mpi_rank*item_cnt; throw FLANNException(error.getDetailMsg());
offset[1] = 0; }
hid_t memspace_id = H5Screate_simple(2,count,NULL);
H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, NULL, count, NULL);
dataset.rows = count[0];
dataset.cols = count[1];
dataset.data = new T[dataset.rows*dataset.cols];
plist_id = H5Pcreate(H5P_DATASET_XFER);
H5Pset_dxpl_mpio(plist_id, H5FD_MPIO_COLLECTIVE);
status = H5Dread(dataset_id, get_hdf5_type<T>(), memspace_id, space_id, plist_id, dataset.data);
CHECK_ERROR(status, "Error reading dataset");
H5Pclose(plist_id);
H5Sclose(space_id);
H5Sclose(memspace_id);
H5Dclose(dataset_id);
H5Fclose(file_id);
} }
}
#endif // HAVE_MPI
} // namespace cvflann::mpi
#endif /* OPENCV_FLANN_HDF5_H_ */
} // namespace cvflann
#endif /* _OPENCV_HDF5_H_ */

View File

@ -28,11 +28,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_HEAP_H_ #ifndef _OPENCV_HEAP_H_
#define OPENCV_FLANN_HEAP_H_ #define _OPENCV_HEAP_H_
#include <algorithm> #include <algorithm>
#include <vector>
namespace cvflann namespace cvflann
{ {
@ -43,123 +43,166 @@ namespace cvflann
* The priority queue is implemented with a heap. A heap is a complete * The priority queue is implemented with a heap. A heap is a complete
* (full) binary tree in which each parent is less than both of its * (full) binary tree in which each parent is less than both of its
* children, but the order of the children is unspecified. * children, but the order of the children is unspecified.
* Note that a heap uses 1-based indexing to allow for power-of-2
* location of parents and children. We ignore element 0 of Heap array.
*/ */
template <typename T> template <typename T>
class Heap class Heap {
{
/** /**
* Storage array for the heap. * Storage array for the heap.
* Type T must be comparable. * Type T must be comparable.
*/ */
std::vector<T> heap; T* heap;
int length; int length;
/** /**
* Number of element in the heap * Number of element in the heap
*/ */
int count; int count;
public: public:
/** /**
* Constructor. * Constructor.
* *
* Params: * Params:
* size = heap size * size = heap size
*/ */
Heap(int size) Heap(int size)
{ {
length = size; length = size+1;
heap.reserve(length); heap = new T[length]; // heap uses 1-based indexing
count = 0; count = 0;
} }
/**
*
* Returns: heap size
*/
int size()
{
return count;
}
/** /**
* Tests if the heap is empty * Destructor.
* *
* Returns: true is heap empty, false otherwise */
*/ ~Heap()
bool empty() {
{ delete[] heap;
return size()==0; }
}
/** /**
* Clears the heap. *
*/ * Returns: heap size
void clear() */
{ int size()
heap.clear(); {
count = 0; return count;
} }
struct CompareT /**
{ * Tests if the heap is empty
bool operator()(const T& t_1, const T& t_2) const *
{ * Returns: true is heap empty, false otherwise
return !(t_1 < t_2); */
} bool empty()
}; {
return size()==0;
}
/** /**
* Insert a new element in the heap. * Clears the heap.
* */
* We select the next empty leaf node, and then keep moving any larger void clear()
* parents down until the right location is found to store this element. {
* count = 0;
* Params: }
* value = the new element to be inserted in the heap
*/
void insert(T value)
{
/* If heap is full, then return without adding this element. */
if (count == length) {
return;
}
heap.push_back(value);
static CompareT compareT; /**
std::push_heap(heap.begin(), heap.end(), compareT); * Insert a new element in the heap.
++count; *
} * We select the next empty leaf node, and then keep moving any larger
* parents down until the right location is found to store this element.
*
* Params:
* value = the new element to be inserted in the heap
*/
void insert(T value)
{
/* If heap is full, then return without adding this element. */
if (count == length-1) {
return;
}
int loc = ++(count); /* Remember 1-based indexing. */
/* Keep moving parents down until a place is found for this node. */
int par = loc / 2; /* Location of parent. */
while (par > 0 && value < heap[par]) {
heap[loc] = heap[par]; /* Move parent down to loc. */
loc = par;
par = loc / 2;
}
/* Insert the element at the determined location. */
heap[loc] = value;
}
/** /**
* Returns the node of minimum value from the heap (top of the heap). * Returns the node of minimum value from the heap (top of the heap).
* *
* Params: * Params:
* value = out parameter used to return the min element * value = out parameter used to return the min element
* Returns: false if heap empty * Returns: false if heap empty
*/ */
bool popMin(T& value) bool popMin(T& value)
{ {
if (count == 0) { if (count == 0) {
return false; return false;
} }
value = heap[0]; /* Switch first node with last. */
static CompareT compareT; std::swap(heap[1],heap[count]);
std::pop_heap(heap.begin(), heap.end(), compareT);
heap.pop_back(); count -= 1;
--count; heapify(1); /* Move new node 1 to right position. */
value = heap[count + 1];
return true; /* Return old last node. */
}
/**
* Reorganizes the heap (a parent is smaller than its children)
* starting with a node.
*
* Params:
* parent = node form which to start heap reorganization.
*/
void heapify(int parent)
{
int minloc = parent;
/* Check the left child */
int left = 2 * parent;
if (left <= count && heap[left] < heap[parent]) {
minloc = left;
}
/* Check the right child */
int right = left + 1;
if (right <= count && heap[right] < heap[minloc]) {
minloc = right;
}
/* If a child was smaller, than swap parent with it and Heapify. */
if (minloc != parent) {
std::swap(heap[parent],heap[minloc]);
heapify(minloc);
}
}
return true; /* Return old last node. */
}
}; };
} } // namespace cvflann
#endif //OPENCV_FLANN_HEAP_H_ #endif //_OPENCV_HEAP_H_

View File

@ -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, &centers[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, &centers[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_ */

View File

@ -28,50 +28,36 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_INDEX_TESTING_H_ #ifndef _OPENCV_TESTING_H_
#define OPENCV_FLANN_INDEX_TESTING_H_ #define _OPENCV_TESTING_H_
#include <cstring> #include <cstring>
#include <cassert> #include <cassert>
#include <cmath>
#include "matrix.h" #include "opencv2/flann/matrix.h"
#include "nn_index.h" #include "opencv2/flann/nn_index.h"
#include "result_set.h" #include "opencv2/flann/result_set.h"
#include "logger.h" #include "opencv2/flann/logger.h"
#include "timer.h" #include "opencv2/flann/timer.h"
namespace cvflann namespace cvflann
{ {
inline int countCorrectMatches(int* neighbors, int* groundTruth, int n) CV_EXPORTS int countCorrectMatches(int* neighbors, int* groundTruth, int n);
template <typename ELEM_TYPE>
float computeDistanceRaport(const Matrix<ELEM_TYPE>& inputData, ELEM_TYPE* target, int* neighbors, int* groundTruth, int veclen, int n)
{ {
int count = 0; ELEM_TYPE* target_end = target + veclen;
for (int i=0; i<n; ++i) { float ret = 0;
for (int k=0; k<n; ++k) { for (int i=0;i<n;++i) {
if (neighbors[i]==groundTruth[k]) { float den = (float)flann_dist(target,target_end, inputData[groundTruth[i]]);
count++; float num = (float)flann_dist(target,target_end, inputData[neighbors[i]]);
break;
}
}
}
return count;
}
if (den==0 && num==0) {
template <typename Distance>
typename Distance::ResultType computeDistanceRaport(const Matrix<typename Distance::ElementType>& inputData, typename Distance::ElementType* target,
int* neighbors, int* groundTruth, int veclen, int n, const Distance& distance)
{
typedef typename Distance::ResultType DistanceType;
DistanceType ret = 0;
for (int i=0; i<n; ++i) {
DistanceType den = distance(inputData[groundTruth[i]], target, veclen);
DistanceType num = distance(inputData[neighbors[i]], target, veclen);
if ((den==0)&&(num==0)) {
ret += 1; ret += 1;
} }
else { else {
@ -82,28 +68,20 @@ typename Distance::ResultType computeDistanceRaport(const Matrix<typename Distan
return ret; return ret;
} }
template <typename Distance> template <typename ELEM_TYPE>
float search_with_ground_truth(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData, float search_with_ground_truth(NNIndex<ELEM_TYPE>& index, const Matrix<ELEM_TYPE>& inputData, const Matrix<ELEM_TYPE>& testData, const Matrix<int>& matches, int nn, int checks, float& time, float& dist, int skipMatches)
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches, int nn, int checks,
float& time, typename Distance::ResultType& dist, const Distance& distance, int skipMatches)
{ {
typedef typename Distance::ResultType DistanceType;
if (matches.cols<size_t(nn)) { if (matches.cols<size_t(nn)) {
Logger::info("matches.cols=%d, nn=%d\n",matches.cols,nn); logger().info("matches.cols=%d, nn=%d\n",matches.cols,nn);
throw FLANNException("Ground truth is not computed for as many neighbors as requested"); throw FLANNException("Ground truth is not computed for as many neighbors as requested");
} }
KNNResultSet<DistanceType> resultSet(nn+skipMatches); KNNResultSet<ELEM_TYPE> resultSet(nn+skipMatches);
SearchParams searchParams(checks); SearchParams searchParams(checks);
int* indices = new int[nn+skipMatches];
DistanceType* dists = new DistanceType[nn+skipMatches];
int* neighbors = indices + skipMatches;
int correct = 0; int correct = 0;
DistanceType distR = 0; float distR = 0;
StartStopTimer t; StartStopTimer t;
int repeats = 0; int repeats = 0;
while (t.value<0.2) { while (t.value<0.2) {
@ -112,69 +90,65 @@ float search_with_ground_truth(NNIndex<Distance>& index, const Matrix<typename D
correct = 0; correct = 0;
distR = 0; distR = 0;
for (size_t i = 0; i < testData.rows; i++) { for (size_t i = 0; i < testData.rows; i++) {
resultSet.init(indices, dists); ELEM_TYPE* target = testData[i];
index.findNeighbors(resultSet, testData[i], searchParams); resultSet.init(target, (int)testData.cols);
index.findNeighbors(resultSet,target, searchParams);
int* neighbors = resultSet.getNeighbors();
neighbors = neighbors+skipMatches;
correct += countCorrectMatches(neighbors,matches[i], nn); correct += countCorrectMatches(neighbors,matches[i], nn);
distR += computeDistanceRaport<Distance>(inputData, testData[i], neighbors, matches[i], testData.cols, nn, distance); distR += computeDistanceRaport(inputData, target,neighbors,matches[i], (int)testData.cols, nn);
} }
t.stop(); t.stop();
} }
time = float(t.value/repeats); time = (float)(t.value/repeats);
delete[] indices;
delete[] dists;
float precicion = (float)correct/(nn*testData.rows); float precicion = (float)correct/(nn*testData.rows);
dist = distR/(testData.rows*nn); dist = distR/(testData.rows*nn);
Logger::info("%8d %10.4g %10.5g %10.5g %10.5g\n", logger().info("%8d %10.4g %10.5g %10.5g %10.5g\n",
checks, precicion, time, 1000.0 * time / testData.rows, dist); checks, precicion, time, 1000.0 * time / testData.rows, dist);
return precicion; return precicion;
} }
template <typename Distance> template <typename ELEM_TYPE>
float test_index_checks(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData, float test_index_checks(NNIndex<ELEM_TYPE>& index, const Matrix<ELEM_TYPE>& inputData, const Matrix<ELEM_TYPE>& testData, const Matrix<int>& matches,
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches, int checks, float& precision, int nn = 1, int skipMatches = 0)
int checks, float& precision, const Distance& distance, int nn = 1, int skipMatches = 0)
{ {
typedef typename Distance::ResultType DistanceType; logger().info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n");
logger().info("---------------------------------------------------------\n");
Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n");
Logger::info("---------------------------------------------------------\n");
float time = 0; float time = 0;
DistanceType dist = 0; float dist = 0;
precision = search_with_ground_truth(index, inputData, testData, matches, nn, checks, time, dist, distance, skipMatches); precision = search_with_ground_truth(index, inputData, testData, matches, nn, checks, time, dist, skipMatches);
return time; return time;
} }
template <typename Distance> template <typename ELEM_TYPE>
float test_index_precision(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData, float test_index_precision(NNIndex<ELEM_TYPE>& index, const Matrix<ELEM_TYPE>& inputData, const Matrix<ELEM_TYPE>& testData, const Matrix<int>& matches,
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches, float precision, int& checks, int nn = 1, int skipMatches = 0)
float precision, int& checks, const Distance& distance, int nn = 1, int skipMatches = 0)
{ {
typedef typename Distance::ResultType DistanceType; const float SEARCH_EPS = 0.001f;
const float SEARCH_EPS = 0.001f;
Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n"); logger().info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n");
Logger::info("---------------------------------------------------------\n"); logger().info("---------------------------------------------------------\n");
int c2 = 1; int c2 = 1;
float p2; float p2;
int c1 = 1; int c1 = 1;
float p1; float p1;
float time; float time;
DistanceType dist; float dist;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, skipMatches);
if (p2>precision) { if (p2>precision) {
Logger::info("Got as close as I can\n"); logger().info("Got as close as I can\n");
checks = c2; checks = c2;
return time; return time;
} }
@ -183,18 +157,18 @@ float test_index_precision(NNIndex<Distance>& index, const Matrix<typename Dista
c1 = c2; c1 = c2;
p1 = p2; p1 = p2;
c2 *=2; c2 *=2;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, skipMatches);
} }
int cx; int cx;
float realPrecision; float realPrecision;
if (fabs(p2-precision)>SEARCH_EPS) { if (fabs(p2-precision)>SEARCH_EPS) {
Logger::info("Start linear estimation\n"); logger().info("Start linear estimation\n");
// after we got to values in the vecinity of the desired precision // after we got to values in the vecinity of the desired precision
// use linear approximation get a better estimation // use linear approximation get a better estimation
cx = (c1+c2)/2; cx = (c1+c2)/2;
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, skipMatches);
while (fabs(realPrecision-precision)>SEARCH_EPS) { while (fabs(realPrecision-precision)>SEARCH_EPS) {
if (realPrecision<precision) { if (realPrecision<precision) {
@ -205,18 +179,17 @@ float test_index_precision(NNIndex<Distance>& index, const Matrix<typename Dista
} }
cx = (c1+c2)/2; cx = (c1+c2)/2;
if (cx==c1) { if (cx==c1) {
Logger::info("Got as close as I can\n"); logger().info("Got as close as I can\n");
break; break;
} }
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, skipMatches);
} }
c2 = cx; c2 = cx;
p2 = realPrecision; p2 = realPrecision;
} } else {
else { logger().info("No need for linear estimation\n");
Logger::info("No need for linear estimation\n");
cx = c2; cx = c2;
realPrecision = p2; realPrecision = p2;
} }
@ -226,14 +199,11 @@ float test_index_precision(NNIndex<Distance>& index, const Matrix<typename Dista
} }
template <typename Distance> template <typename ELEM_TYPE>
void test_index_precisions(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData, float test_index_precisions(NNIndex<ELEM_TYPE>& index, const Matrix<ELEM_TYPE>& inputData, const Matrix<ELEM_TYPE>& testData, const Matrix<int>& matches,
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches, float* precisions, int precisions_length, int nn = 1, int skipMatches = 0, float maxTime = 0)
float* precisions, int precisions_length, const Distance& distance, int nn = 1, int skipMatches = 0, float maxTime = 0)
{ {
typedef typename Distance::ResultType DistanceType; const float SEARCH_EPS = 0.001;
const float SEARCH_EPS = 0.001;
// make sure precisions array is sorted // make sure precisions array is sorted
std::sort(precisions, precisions+precisions_length); std::sort(precisions, precisions+precisions_length);
@ -241,8 +211,8 @@ void test_index_precisions(NNIndex<Distance>& index, const Matrix<typename Dista
int pindex = 0; int pindex = 0;
float precision = precisions[pindex]; float precision = precisions[pindex];
Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n"); logger().info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist");
Logger::info("---------------------------------------------------------\n"); logger().info("---------------------------------------------------------");
int c2 = 1; int c2 = 1;
float p2; float p2;
@ -251,9 +221,9 @@ void test_index_precisions(NNIndex<Distance>& index, const Matrix<typename Dista
float p1; float p1;
float time; float time;
DistanceType dist; float dist;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, skipMatches);
// if precision for 1 run down the tree is already // if precision for 1 run down the tree is already
// better then some of the requested precisions, then // better then some of the requested precisions, then
@ -263,30 +233,30 @@ void test_index_precisions(NNIndex<Distance>& index, const Matrix<typename Dista
} }
if (pindex==precisions_length) { if (pindex==precisions_length) {
Logger::info("Got as close as I can\n"); logger().info("Got as close as I can\n");
return; return time;
} }
for (int i=pindex; i<precisions_length; ++i) { for (int i=pindex;i<precisions_length;++i) {
precision = precisions[i]; precision = precisions[i];
while (p2<precision) { while (p2<precision) {
c1 = c2; c1 = c2;
p1 = p2; p1 = p2;
c2 *=2; c2 *=2;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, skipMatches);
if ((maxTime> 0)&&(time > maxTime)&&(p2<precision)) return; if (maxTime> 0 && time > maxTime && p2<precision) return time;
} }
int cx; int cx;
float realPrecision; float realPrecision;
if (fabs(p2-precision)>SEARCH_EPS) { if (fabs(p2-precision)>SEARCH_EPS) {
Logger::info("Start linear estimation\n"); logger().info("Start linear estimation\n");
// after we got to values in the vecinity of the desired precision // after we got to values in the vecinity of the desired precision
// use linear approximation get a better estimation // use linear approximation get a better estimation
cx = (c1+c2)/2; cx = (c1+c2)/2;
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, skipMatches);
while (fabs(realPrecision-precision)>SEARCH_EPS) { while (fabs(realPrecision-precision)>SEARCH_EPS) {
if (realPrecision<precision) { if (realPrecision<precision) {
@ -297,25 +267,25 @@ void test_index_precisions(NNIndex<Distance>& index, const Matrix<typename Dista
} }
cx = (c1+c2)/2; cx = (c1+c2)/2;
if (cx==c1) { if (cx==c1) {
Logger::info("Got as close as I can\n"); logger().info("Got as close as I can\n");
break; break;
} }
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, skipMatches);
} }
c2 = cx; c2 = cx;
p2 = realPrecision; p2 = realPrecision;
} } else {
else { logger().info("No need for linear estimation\n");
Logger::info("No need for linear estimation\n");
cx = c2; cx = c2;
realPrecision = p2; realPrecision = p2;
} }
} }
return time;
} }
} } // namespace cvflann
#endif //OPENCV_FLANN_INDEX_TESTING_H_ #endif //_OPENCV_TESTING_H_

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -28,40 +28,41 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_LINEAR_INDEX_H_ #ifndef _OPENCV_LINEARSEARCH_H_
#define OPENCV_FLANN_LINEAR_INDEX_H_ #define _OPENCV_LINEARSEARCH_H_
#include "opencv2/flann/general.h"
#include "opencv2/flann/nn_index.h"
#include "general.h"
#include "nn_index.h"
namespace cvflann namespace cvflann
{ {
struct LinearIndexParams : public IndexParams struct CV_EXPORTS LinearIndexParams : public IndexParams {
{ LinearIndexParams() : IndexParams(FLANN_INDEX_LINEAR) {};
LinearIndexParams()
{ void print() const
(* this)["algorithm"] = FLANN_INDEX_LINEAR; {
} logger().info("Index type: %d\n",(int)algorithm);
}
}; };
template <typename Distance>
class LinearIndex : public NNIndex<Distance> template <typename ELEM_TYPE, typename DIST_TYPE = typename DistType<ELEM_TYPE>::type >
class LinearIndex : public NNIndex<ELEM_TYPE>
{ {
const Matrix<ELEM_TYPE> dataset;
const LinearIndexParams& index_params;
LinearIndex(const LinearIndex&);
LinearIndex& operator=(const LinearIndex&);
public: public:
typedef typename Distance::ElementType ElementType; LinearIndex(const Matrix<ELEM_TYPE>& inputData, const LinearIndexParams& params = LinearIndexParams() ) :
typedef typename Distance::ResultType DistanceType; dataset(inputData), index_params(params)
{
}
LinearIndex(const Matrix<ElementType>& inputData, const IndexParams& params = LinearIndexParams(),
Distance d = Distance()) :
dataset_(inputData), index_params_(params), distance_(d)
{
}
LinearIndex(const LinearIndex&);
LinearIndex& operator=(const LinearIndex&);
flann_algorithm_t getType() const flann_algorithm_t getType() const
{ {
@ -69,64 +70,52 @@ public:
} }
size_t size() const size_t size() const
{ {
return dataset_.rows; return dataset.rows;
} }
size_t veclen() const size_t veclen() const
{ {
return dataset_.cols; return dataset.cols;
} }
int usedMemory() const int usedMemory() const
{ {
return 0; return 0;
} }
void buildIndex() void buildIndex()
{ {
/* nothing to do here for linear search */ /* nothing to do here for linear search */
} }
void saveIndex(FILE*) void saveIndex(FILE*)
{ {
/* nothing to do here for linear search */ /* nothing to do here for linear search */
} }
void loadIndex(FILE*) void loadIndex(FILE*)
{ {
/* nothing to do here for linear search */ /* nothing to do here for linear search */
index_params_["algorithm"] = getType();
} }
void findNeighbors(ResultSet<DistanceType>& resultSet, const ElementType* vec, const SearchParams& /*searchParams*/) void findNeighbors(ResultSet<ELEM_TYPE>& resultSet, const ELEM_TYPE*, const SearchParams&)
{ {
ElementType* data = dataset_.data; for (size_t i=0;i<dataset.rows;++i) {
for (size_t i = 0; i < dataset_.rows; ++i, data += dataset_.cols) { resultSet.addPoint(dataset[i],(int)i);
DistanceType dist = distance_(data, vec, dataset_.cols); }
resultSet.addPoint(dist, i); }
}
}
IndexParams getParameters() const const IndexParams* getParameters() const
{ {
return index_params_; return &index_params;
} }
private:
/** The dataset */
const Matrix<ElementType> dataset_;
/** Index parameters */
IndexParams index_params_;
/** Index distance */
Distance distance_;
}; };
} } // namespace cvflann
#endif // OPENCV_FLANN_LINEAR_INDEX_H_ #endif // _OPENCV_LINEARSEARCH_H_

View File

@ -28,36 +28,40 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef FLANN_LOGGER_H #ifndef _OPENCV_LOGGER_H_
#define FLANN_LOGGER_H #define _OPENCV_LOGGER_H_
#include <stdio.h> #include <cstdio>
#include <stdarg.h> #include <stdarg.h>
#include "defines.h"
namespace cvflann namespace cvflann
{ {
class Logger 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
};
class CV_EXPORTS Logger
{ {
Logger() : stream(stdout), logLevel(FLANN_LOG_WARN) {} FILE* stream;
int logLevel;
public:
Logger() : stream(stdout), logLevel(FLANN_LOG_WARN) {};
~Logger() ~Logger()
{ {
if ((stream!=NULL)&&(stream!=stdout)) { if (stream!=NULL && stream!=stdout) {
fclose(stream); fclose(stream);
} }
} }
static Logger& instance() void setDestination(const char* name)
{
static Logger logger;
return logger;
}
void _setDestination(const char* name)
{ {
if (name==NULL) { if (name==NULL) {
stream = stdout; stream = stdout;
@ -70,61 +74,23 @@ class Logger
} }
} }
int _log(int level, const char* fmt, va_list arglist) void setLevel(int level) { logLevel = level; }
{
if (level > logLevel ) return -1;
int ret = vfprintf(stream, fmt, arglist);
return ret;
}
public: int log(int level, const char* fmt, ...);
/**
* Sets the logging level. All messages with lower priority will be ignored.
* @param level Logging level
*/
static void setLevel(int level) { instance().logLevel = level; }
/** int log(int level, const char* fmt, va_list arglist);
* Sets the logging destination
* @param name Filename or NULL for console
*/
static void setDestination(const char* name) { instance()._setDestination(name); }
/** int fatal(const char* fmt, ...);
* Print log message
* @param level Log level
* @param fmt Message format
* @return
*/
static int log(int level, const char* fmt, ...)
{
va_list arglist;
va_start(arglist, fmt);
int ret = instance()._log(level,fmt,arglist);
va_end(arglist);
return ret;
}
#define LOG_METHOD(NAME,LEVEL) \ int error(const char* fmt, ...);
static int NAME(const char* fmt, ...) \
{ \
va_list ap; \
va_start(ap, fmt); \
int ret = instance()._log(LEVEL, fmt, ap); \
va_end(ap); \
return ret; \
}
LOG_METHOD(fatal, FLANN_LOG_FATAL) int warn(const char* fmt, ...);
LOG_METHOD(error, FLANN_LOG_ERROR)
LOG_METHOD(warn, FLANN_LOG_WARN)
LOG_METHOD(info, FLANN_LOG_INFO)
private: int info(const char* fmt, ...);
FILE* stream;
int logLevel;
}; };
} CV_EXPORTS Logger& logger();
#endif //FLANN_LOGGER_H } // namespace cvflann
#endif //_OPENCV_LOGGER_H_

View File

@ -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_

View File

@ -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_ */

View File

@ -28,58 +28,60 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_DATASET_H_ #ifndef _OPENCV_DATASET_H_
#define OPENCV_FLANN_DATASET_H_ #define _OPENCV_DATASET_H_
#include <stdio.h> #include <stdio.h>
#include "general.h" #include "opencv2/flann/general.h"
namespace cvflann namespace cvflann
{ {
/** /**
* Class that implements a simple rectangular matrix stored in a memory buffer and * Class that implements a simple rectangular matrix stored in a memory buffer and
* provides convenient matrix-like access using the [] operators. * provides convenient matrix-like access using the [] operators.
*/ */
template <typename T> template <typename T>
class Matrix class Matrix {
{
public: public:
typedef T type;
size_t rows; size_t rows;
size_t cols; size_t cols;
size_t stride;
T* data; T* data;
Matrix() : rows(0), cols(0), stride(0), data(NULL) Matrix() : rows(0), cols(0), data(NULL)
{ {
} }
Matrix(T* data_, size_t rows_, size_t cols_, size_t stride_ = 0) : Matrix(T* data_, long rows_, long cols_) :
rows(rows_), cols(cols_), stride(stride_), data(data_) rows(rows_), cols(cols_), data(data_)
{ {
if (stride==0) stride = cols; }
}
/** /**
* Convenience function for deallocating the storage data. * Convenience function for deallocating the storage data.
*/ */
FLANN_DEPRECATED void free() void release()
{ {
fprintf(stderr, "The cvflann::Matrix<T>::free() method is deprecated " if (data!=NULL) delete[] data;
"and it does not do any memory deallocation any more. You are"
"responsible for deallocating the matrix memory (by doing"
"'delete[] matrix.data' for example)");
} }
~Matrix()
{
}
/** /**
* Operator that return a (pointer to a) row of the data. * Operator that return a (pointer to a) row of the data.
*/ */
T* operator[](size_t index)
{
return data+index*cols;
}
T* operator[](size_t index) const T* operator[](size_t index) const
{ {
return data+index*stride; return data+index*cols;
} }
}; };
@ -93,7 +95,7 @@ public:
flann_datatype_t type; flann_datatype_t type;
UntypedMatrix(void* data_, long rows_, long cols_) : UntypedMatrix(void* data_, long rows_, long cols_) :
rows(rows_), cols(cols_), data(data_) rows(rows_), cols(cols_), data(data_)
{ {
} }
@ -111,6 +113,6 @@ public:
} } // namespace cvflann
#endif //OPENCV_FLANN_DATASET_H_ #endif //_OPENCV_DATASET_H_

View File

@ -28,152 +28,79 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef FLANN_NNINDEX_H #ifndef _OPENCV_NNINDEX_H_
#define FLANN_NNINDEX_H #define _OPENCV_NNINDEX_H_
#include <string> #include <string>
#include "general.h" #include "opencv2/flann/general.h"
#include "matrix.h" #include "opencv2/flann/matrix.h"
#include "result_set.h"
#include "params.h"
namespace cvflann namespace cvflann
{ {
template <typename ELEM_TYPE>
class ResultSet;
/** /**
* Nearest-neighbour index base class * Nearest-neighbour index base class
*/ */
template <typename Distance> template <typename ELEM_TYPE>
class NNIndex class NNIndex
{ {
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
public: public:
virtual ~NNIndex() {} virtual ~NNIndex() {};
/** /**
* \brief Builds the index Method responsible with building the index.
*/ */
virtual void buildIndex() = 0; virtual void buildIndex() = 0;
/** /**
* \brief Perform k-nearest neighbor search Saves the index to a stream
* \param[in] queries The query points for which to find the nearest neighbors */
* \param[out] indices The indices of the nearest neighbors found virtual void saveIndex(FILE* stream) = 0;
* \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);
#if 0 /**
KNNResultSet<DistanceType> resultSet(knn); Loads the index from a stream
for (size_t i = 0; i < queries.rows; i++) { */
resultSet.init(indices[i], dists[i]); virtual void loadIndex(FILE* stream) = 0;
findNeighbors(resultSet, queries[i], params);
}
#else
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);
}
#endif
}
/** /**
* \brief Perform radius search Method that searches for nearest-neighbors
* \param[in] query The query point */
* \param[out] indices The indinces of the neighbors found within the given radius virtual void findNeighbors(ResultSet<ELEM_TYPE>& result, const ELEM_TYPE* vec, const SearchParams& searchParams) = 0;
* \param[out] dists The distances to the nearest neighbors found
* \param[in] radius The radius used for search
* \param[in] params Search parameters
* \returns Number of neighbors found
*/
virtual int radiusSearch(const Matrix<ElementType>& query, Matrix<int>& indices, Matrix<DistanceType>& dists, float radius, const SearchParams& params)
{
if (query.rows != 1) {
fprintf(stderr, "I can only search one feature at a time for range search\n");
return -1;
}
assert(query.cols == veclen());
assert(indices.cols == dists.cols);
int n = 0; /**
int* indices_ptr = NULL; Number of features in this index.
DistanceType* dists_ptr = NULL; */
if (indices.cols > 0) { virtual size_t size() const = 0;
n = indices.cols;
indices_ptr = indices[0];
dists_ptr = dists[0];
}
RadiusUniqueResultSet<DistanceType> resultSet(radius); /**
resultSet.clear(); The length of each vector in this index.
findNeighbors(resultSet, query[0], params); */
if (n>0) { virtual size_t veclen() const = 0;
if (get_param(params,"sorted",true)) resultSet.sortAndCopy(indices_ptr, dists_ptr, n);
else resultSet.copy(indices_ptr, dists_ptr, n);
}
return resultSet.size(); /**
} The amount of memory (in bytes) this index uses.
*/
virtual int usedMemory() const = 0;
/** /**
* \brief Saves the index to a stream * Algorithm name
* \param stream The stream to save the index to */
*/ virtual flann_algorithm_t getType() const = 0;
virtual void saveIndex(FILE* stream) = 0;
/** /**
* \brief Loads the index from a stream * Returns the parameters used for the index
* \param stream The stream from which the index is loaded */
*/ virtual const IndexParams* getParameters() const = 0;
virtual void loadIndex(FILE* stream) = 0;
/**
* \returns number of features in this index.
*/
virtual size_t size() const = 0;
/**
* \returns The dimensionality of the features in this index.
*/
virtual size_t veclen() const = 0;
/**
* \returns The amount of memory (in bytes) used by the index.
*/
virtual int usedMemory() const = 0;
/**
* \returns The index type (kdtree, kmeans,...)
*/
virtual flann_algorithm_t getType() const = 0;
/**
* \returns The index parameters
*/
virtual IndexParams getParameters() const = 0;
/**
* \brief Method that searches for nearest-neighbours
*/
virtual void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) = 0;
}; };
}
#endif //FLANN_NNINDEX_H } // namespace cvflann
#endif //_OPENCV_NNINDEX_H_

View File

@ -28,64 +28,67 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_OBJECT_FACTORY_H_ #ifndef _OPENCV_OBJECT_FACTORY_H_
#define OPENCV_FLANN_OBJECT_FACTORY_H_ #define _OPENCV_OBJECT_FACTORY_H_
#include "opencv2/core/types_c.h"
#include <map> #include <map>
namespace cvflann namespace cvflann
{ {
class CreatorNotFound template<typename BaseClass, typename DerivedClass>
BaseClass* createObject()
{ {
}; return new DerivedClass();
template<typename BaseClass,
typename UniqueIdType,
typename ObjectCreator = BaseClass* (*)()>
class ObjectFactory
{
typedef ObjectFactory<BaseClass,UniqueIdType,ObjectCreator> ThisClass;
typedef std::map<UniqueIdType, ObjectCreator> ObjectRegistry;
// singleton class, private constructor
ObjectFactory() {}
public:
bool subscribe(UniqueIdType id, ObjectCreator creator)
{
if (object_registry.find(id) != object_registry.end()) return false;
object_registry[id] = creator;
return true;
}
bool unregister(UniqueIdType id)
{
return object_registry.erase(id) == 1;
}
ObjectCreator create(UniqueIdType id)
{
typename ObjectRegistry::const_iterator iter = object_registry.find(id);
if (iter == object_registry.end()) {
throw CreatorNotFound();
}
return iter->second;
}
static ThisClass& instance()
{
static ThisClass the_factory;
return the_factory;
}
private:
ObjectRegistry object_registry;
};
} }
#endif /* OPENCV_FLANN_OBJECT_FACTORY_H_ */ template<typename BaseClass, typename UniqueIdType>
class ObjectFactory
{
typedef BaseClass* (*CreateObjectFunc)();
std::map<UniqueIdType, CreateObjectFunc> object_registry;
// singleton class, private constructor
//ObjectFactory() {};
public:
typedef typename std::map<UniqueIdType, CreateObjectFunc>::iterator Iterator;
template<typename DerivedClass>
bool register_(UniqueIdType id)
{
if (object_registry.find(id) != object_registry.end())
return false;
object_registry[id] = &createObject<BaseClass, DerivedClass>;
return true;
}
bool unregister(UniqueIdType id)
{
return (object_registry.erase(id) == 1);
}
BaseClass* create(UniqueIdType id)
{
Iterator iter = object_registry.find(id);
if (iter == object_registry.end())
return NULL;
return ((*iter).second)();
}
/*static ObjectFactory<BaseClass,UniqueIdType>& instance()
{
static ObjectFactory<BaseClass,UniqueIdType> the_factory;
return the_factory;
}*/
};
} // namespace cvflann
#endif /* OBJECT_FACTORY_H_ */

View File

@ -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_ */

View File

@ -28,108 +28,106 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef FLANN_RANDOM_H #ifndef _OPENCV_RANDOM_H_
#define FLANN_RANDOM_H #define _OPENCV_RANDOM_H_
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <vector> #include <cassert>
#include "general.h"
namespace cvflann namespace cvflann
{ {
/** /**
* Seeds the random number generator * Seeds the random number generator
* @param seed Random seed
*/ */
inline void seed_random(unsigned int seed) CV_EXPORTS void seed_random(unsigned int seed);
{
srand(seed);
}
/* /*
* Generates a random double value. * Generates a random double value.
*/ */
/** CV_EXPORTS double rand_double(double high = 1.0, double low=0);
* Generates a random double value.
* @param high Upper limit
* @param low Lower limit
* @return Random double value
*/
inline double rand_double(double high = 1.0, double low = 0)
{
return low + ((high-low) * (std::rand() / (RAND_MAX + 1.0)));
}
/** /*
* Generates a random integer value. * Generates a random integer value.
* @param high Upper limit
* @param low Lower limit
* @return Random integer value
*/ */
inline int rand_int(int high = RAND_MAX, int low = 0) CV_EXPORTS int rand_int(int high = RAND_MAX, int low = 0);
{
return low + (int) ( double(high-low) * (std::rand() / (RAND_MAX + 1.0)));
}
/** /**
* Random number generator that returns a distinct number from * Random number generator that returns a distinct number from
* the [0,n) interval each time. * the [0,n) interval each time.
*
* TODO: improve on this to use a generator function instead of an
* array of randomly permuted numbers
*/ */
class UniqueRandom class CV_EXPORTS UniqueRandom
{ {
std::vector<int> vals_; int* vals;
int size_; int size;
int counter_; int counter;
public: public:
/** /**
* Constructor. * Constructor.
* @param n Size of the interval from which to generate * Params:
* @return * n = the size of the interval from which to generate
*/ * random numbers.
UniqueRandom(int n) */
{ UniqueRandom(int n) : vals(NULL) {
init(n); init(n);
} }
/** ~UniqueRandom()
* Initializes the number generator. {
* @param n the size of the interval from which to generate random numbers. delete[] vals;
*/ }
void init(int n)
{
// create and initialize an array of size n
vals_.resize(n);
size_ = n;
for (int i = 0; i < size_; ++i) vals_[i] = i;
// shuffle the elements in the array /**
std::random_shuffle(vals_.begin(), vals_.end()); * Initializes the number generator.
* Params:
* n = the size of the interval from which to generate
* random numbers.
*/
void init(int n)
{
// create and initialize an array of size n
if (vals == NULL || n!=size) {
delete[] vals;
size = n;
vals = new int[size];
}
for(int i=0;i<size;++i) {
vals[i] = i;
}
counter_ = 0; // shuffle the elements in the array
} // Fisher-Yates shuffle
for (int i=size;i>0;--i) {
// int rand = cast(int) (drand48() * n);
int rnd = rand_int(i);
assert(rnd >=0 && rnd < i);
std::swap(vals[i-1], vals[rnd]);
}
/** counter = 0;
* Return a distinct random integer in greater or equal to 0 and less }
* than 'n' on each call. It should be called maximum 'n' times.
* Returns: a random integer /**
*/ * Return a distinct random integer in greater or equal to 0 and less
int next() * than 'n' on each call. It should be called maximum 'n' times.
{ * Returns: a random integer
if (counter_ == size_) { */
return -1; int next() {
} if (counter==size) {
else { return -1;
return vals_[counter_++]; } else {
} return vals[counter++];
} }
}
}; };
} } // namespace cvflann
#endif //FLANN_RANDOM_H
#endif //_OPENCV_RANDOM_H_

View File

@ -28,516 +28,291 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef FLANN_RESULTSET_H #ifndef _OPENCV_RESULTSET_H_
#define FLANN_RESULTSET_H #define _OPENCV_RESULTSET_H_
#include <algorithm> #include <algorithm>
#include <cstring>
#include <iostream>
#include <limits> #include <limits>
#include <set>
#include <vector> #include <vector>
#include "opencv2/flann/dist.h"
namespace cvflann namespace cvflann
{ {
/* This record represents a branch point when finding neighbors in /* This record represents a branch point when finding neighbors in
the tree. It contains a record of the minimum distance to the query the tree. It contains a record of the minimum distance to the query
point, as well as the node at which the search resumes. point, as well as the node at which the search resumes.
*/ */
template <typename T, typename DistanceType> template <typename T>
struct BranchStruct struct BranchStruct {
{ T node; /* Tree node at which search resumes */
T node; /* Tree node at which search resumes */ float mindistsq; /* Minimum distance to query for all nodes below. */
DistanceType mindist; /* Minimum distance to query for all nodes below. */
BranchStruct() {} bool operator<(const BranchStruct<T>& rhs)
BranchStruct(const T& aNode, DistanceType dist) : node(aNode), mindist(dist) {} {
return mindistsq<rhs.mindistsq;
}
bool operator<(const BranchStruct<T, DistanceType>& rhs) const static BranchStruct<T> make_branch(const T& aNode, float dist)
{ {
return mindist<rhs.mindist; BranchStruct<T> branch;
branch.node = aNode;
branch.mindistsq = dist;
return branch;
} }
}; };
template <typename DistanceType>
template <typename ELEM_TYPE>
class ResultSet class ResultSet
{ {
protected:
public: public:
virtual ~ResultSet() {}
virtual bool full() const = 0; virtual ~ResultSet() {};
virtual void addPoint(DistanceType dist, int index) = 0; virtual void init(const ELEM_TYPE* target_, int veclen_) = 0;
virtual DistanceType worstDist() const = 0; virtual int* getNeighbors() = 0;
virtual float* getDistances() = 0;
virtual size_t size() const = 0;
virtual bool full() const = 0;
virtual bool addPoint(const ELEM_TYPE* point, int index) = 0;
virtual float worstDist() const = 0;
}; };
/**
* KNNSimpleResultSet does not ensure that the element it holds are unique. template <typename ELEM_TYPE>
* Is used in those cases where the nearest neighbour algorithm used does not class KNNResultSet : public ResultSet<ELEM_TYPE>
* attempt to insert the same element multiple times.
*/
template <typename DistanceType>
class KNNSimpleResultSet : public ResultSet<DistanceType>
{ {
int* indices; const ELEM_TYPE* target;
DistanceType* dists; const ELEM_TYPE* target_end;
int veclen;
int* indices;
float* dists;
int capacity; int capacity;
int count;
DistanceType worst_distance_; int count;
public: public:
KNNSimpleResultSet(int capacity_) : capacity(capacity_), count(0) KNNResultSet(int capacity_, ELEM_TYPE* target_ = NULL, int veclen_ = 0 ) :
{ target(target_), veclen(veclen_), capacity(capacity_), count(0)
} {
target_end = target + veclen;
void init(int* indices_, DistanceType* dists_) indices = new int[capacity_];
{ dists = new float[capacity_];
indices = indices_; }
dists = dists_;
~KNNResultSet()
{
delete[] indices;
delete[] dists;
}
void init(const ELEM_TYPE* target_, int veclen_)
{
target = target_;
veclen = veclen_;
target_end = target + veclen;
count = 0; count = 0;
worst_distance_ = (std::numeric_limits<DistanceType>::max)(); }
dists[capacity-1] = worst_distance_;
int* getNeighbors()
{
return indices;
}
float* getDistances()
{
return dists;
} }
size_t size() const size_t size() const
{ {
return count; return count;
} }
bool full() const bool full() const
{ {
return count == capacity; return count == capacity;
} }
void addPoint(DistanceType dist, int index) bool addPoint(const ELEM_TYPE* point, int index)
{ {
if (dist >= worst_distance_) return; for (int i=0;i<count;++i) {
int i; if (indices[i]==index) return false;
for (i=count; i>0; --i) { }
#ifdef FLANN_FIRST_MATCH float dist = (float)flann_dist(target, target_end, point);
if ( (dists[i-1]>dist) || ((dist==dists[i-1])&&(indices[i-1]>index)) )
#else
if (dists[i-1]>dist)
#endif
{
if (i<capacity) {
dists[i] = dists[i-1];
indices[i] = indices[i-1];
}
}
else break;
}
if (count < capacity) ++count;
dists[i] = dist;
indices[i] = index;
worst_distance_ = dists[capacity-1];
}
DistanceType worstDist() const if (count<capacity) {
{ indices[count] = index;
return worst_distance_; dists[count] = dist;
} ++count;
}; }
else if (dist < dists[count-1] || (dist == dists[count-1] && index < indices[count-1])) {
// else if (dist < dists[count-1]) {
indices[count-1] = index;
dists[count-1] = dist;
}
else {
return false;
}
/** int i = count-1;
* K-Nearest neighbour result set. Ensures that the elements inserted are unique // bubble up
*/ while (i>=1 && (dists[i]<dists[i-1] || (dists[i]==dists[i-1] && indices[i]<indices[i-1]) ) ) {
template <typename DistanceType> // while (i>=1 && (dists[i]<dists[i-1]) ) {
class KNNResultSet : public ResultSet<DistanceType> std::swap(indices[i],indices[i-1]);
{ std::swap(dists[i],dists[i-1]);
int* indices; i--;
DistanceType* dists; }
int capacity;
int count;
DistanceType worst_distance_;
public: return true;
KNNResultSet(int capacity_) : capacity(capacity_), count(0) }
{
}
void init(int* indices_, DistanceType* dists_) float worstDist() const
{ {
indices = indices_; return (count<capacity) ? (std::numeric_limits<float>::max)() : dists[count-1];
dists = dists_; }
count = 0;
worst_distance_ = (std::numeric_limits<DistanceType>::max)();
dists[capacity-1] = worst_distance_;
}
size_t size() const
{
return count;
}
bool full() const
{
return count == capacity;
}
void addPoint(DistanceType dist, int index)
{
if (dist >= worst_distance_) return;
int i;
for (i = count; i > 0; --i) {
#ifdef FLANN_FIRST_MATCH
if ( (dists[i-1]<=dist) && ((dist!=dists[i-1])||(indices[i-1]<=index)) )
#else
if (dists[i-1]<=dist)
#endif
{
// Check for duplicate indices
int j = i - 1;
while ((j >= 0) && (dists[j] == dist)) {
if (indices[j] == index) {
return;
}
--j;
}
break;
}
}
if (count < capacity) ++count;
for (int j = count-1; j > i; --j) {
dists[j] = dists[j-1];
indices[j] = indices[j-1];
}
dists[i] = dist;
indices[i] = index;
worst_distance_ = dists[capacity-1];
}
DistanceType worstDist() const
{
return worst_distance_;
}
}; };
/** /**
* A result-set class used when performing a radius based search. * A result-set class used when performing a radius based search.
*/ */
template <typename DistanceType> template <typename ELEM_TYPE>
class RadiusResultSet : public ResultSet<DistanceType> class RadiusResultSet : public ResultSet<ELEM_TYPE>
{ {
DistanceType radius; const ELEM_TYPE* target;
int* indices; const ELEM_TYPE* target_end;
DistanceType* dists; int veclen;
size_t capacity;
size_t count; struct Item {
int index;
float dist;
bool operator<(Item rhs) {
return dist<rhs.dist;
}
};
std::vector<Item> items;
float radius;
bool sorted;
int* indices;
float* dists;
size_t count;
private:
void resize_vecs()
{
if (items.size()>count) {
if (indices!=NULL) delete[] indices;
if (dists!=NULL) delete[] dists;
count = items.size();
indices = new int[count];
dists = new float[count];
}
}
public: public:
RadiusResultSet(DistanceType radius_, int* indices_, DistanceType* dists_, int capacity_) : RadiusResultSet(float radius_) :
radius(radius_), indices(indices_), dists(dists_), capacity(capacity_) radius(radius_), indices(NULL), dists(NULL)
{ {
init(); sorted = false;
} items.reserve(16);
count = 0;
}
~RadiusResultSet() ~RadiusResultSet()
{ {
} if (indices!=NULL) delete[] indices;
if (dists!=NULL) delete[] dists;
}
void init() void init(const ELEM_TYPE* target_, int veclen_)
{
target = target_;
veclen = veclen_;
target_end = target + veclen;
items.clear();
sorted = false;
}
int* getNeighbors()
{
if (!sorted) {
sorted = true;
sort_heap(items.begin(), items.end());
}
resize_vecs();
for (size_t i=0;i<items.size();++i) {
indices[i] = items[i].index;
}
return indices;
}
float* getDistances()
{ {
count = 0; if (!sorted) {
sorted = true;
sort_heap(items.begin(), items.end());
}
resize_vecs();
for (size_t i=0;i<items.size();++i) {
dists[i] = items[i].dist;
}
return dists;
} }
size_t size() const size_t size() const
{ {
return count; return items.size();
} }
bool full() const bool full() const
{ {
return true; return true;
} }
void addPoint(DistanceType dist, int index) bool addPoint(const ELEM_TYPE* point, int index)
{ {
if (dist<radius) { Item it;
if ((capacity>0)&&(count < capacity)) { it.index = index;
dists[count] = dist; it.dist = (float)flann_dist(target, target_end, point);
indices[count] = index; if (it.dist<=radius) {
} items.push_back(it);
count++; push_heap(items.begin(), items.end());
} return true;
} }
return false;
}
DistanceType worstDist() const float worstDist() const
{ {
return radius; return radius;
} }
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace cvflann
/** Class that holds the k NN neighbors
* Faster than KNNResultSet as it uses a binary heap and does not maintain two arrays
*/
template<typename DistanceType>
class UniqueResultSet : public ResultSet<DistanceType>
{
public:
struct DistIndex
{
DistIndex(DistanceType dist, unsigned int index) :
dist_(dist), index_(index)
{
}
bool operator<(const DistIndex dist_index) const
{
return (dist_ < dist_index.dist_) || ((dist_ == dist_index.dist_) && index_ < dist_index.index_);
}
DistanceType dist_;
unsigned int index_;
};
/** Default cosntructor */
UniqueResultSet() :
worst_distance_(std::numeric_limits<DistanceType>::max())
{
}
/** Check the status of the set
* @return true if we have k NN
*/
inline bool full() const
{
return is_full_;
}
/** Remove all elements in the set
*/
virtual void clear() = 0;
/** Copy the set to two C arrays
* @param indices pointer to a C array of indices
* @param dist pointer to a C array of distances
* @param n_neighbors the number of neighbors to copy
*/
virtual void copy(int* indices, DistanceType* dist, int n_neighbors = -1) const
{
if (n_neighbors < 0) {
for (typename std::set<DistIndex>::const_iterator dist_index = dist_indices_.begin(), dist_index_end =
dist_indices_.end(); dist_index != dist_index_end; ++dist_index, ++indices, ++dist) {
*indices = dist_index->index_;
*dist = dist_index->dist_;
}
}
else {
int i = 0;
for (typename std::set<DistIndex>::const_iterator dist_index = dist_indices_.begin(), dist_index_end =
dist_indices_.end(); (dist_index != dist_index_end) && (i < n_neighbors); ++dist_index, ++indices, ++dist, ++i) {
*indices = dist_index->index_;
*dist = dist_index->dist_;
}
}
}
/** Copy the set to two C arrays but sort it according to the distance first
* @param indices pointer to a C array of indices
* @param dist pointer to a C array of distances
* @param n_neighbors the number of neighbors to copy
*/
virtual void sortAndCopy(int* indices, DistanceType* dist, int n_neighbors = -1) const
{
copy(indices, dist, n_neighbors);
}
/** The number of neighbors in the set
* @return
*/
size_t size() const
{
return dist_indices_.size();
}
/** The distance of the furthest neighbor
* If we don't have enough neighbors, it returns the max possible value
* @return
*/
inline DistanceType worstDist() const
{
return worst_distance_;
}
protected:
/** Flag to say if the set is full */
bool is_full_;
/** The worst distance found so far */
DistanceType worst_distance_;
/** The best candidates so far */
std::set<DistIndex> dist_indices_;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Class that holds the k NN neighbors
* Faster than KNNResultSet as it uses a binary heap and does not maintain two arrays
*/
template<typename DistanceType>
class KNNUniqueResultSet : public UniqueResultSet<DistanceType>
{
public:
/** Constructor
* @param capacity the number of neighbors to store at max
*/
KNNUniqueResultSet(unsigned int capacity) : capacity_(capacity)
{
this->is_full_ = false;
this->clear();
}
/** Add a possible candidate to the best neighbors
* @param dist distance for that neighbor
* @param index index of that neighbor
*/
inline void addPoint(DistanceType dist, int index)
{
// Don't do anything if we are worse than the worst
if (dist >= worst_distance_) return;
dist_indices_.insert(DistIndex(dist, index));
if (is_full_) {
if (dist_indices_.size() > capacity_) {
dist_indices_.erase(*dist_indices_.rbegin());
worst_distance_ = dist_indices_.rbegin()->dist_;
}
}
else if (dist_indices_.size() == capacity_) {
is_full_ = true;
worst_distance_ = dist_indices_.rbegin()->dist_;
}
}
/** Remove all elements in the set
*/
void clear()
{
dist_indices_.clear();
worst_distance_ = std::numeric_limits<DistanceType>::max();
is_full_ = false;
}
protected:
typedef typename UniqueResultSet<DistanceType>::DistIndex DistIndex;
using UniqueResultSet<DistanceType>::is_full_;
using UniqueResultSet<DistanceType>::worst_distance_;
using UniqueResultSet<DistanceType>::dist_indices_;
/** The number of neighbors to keep */
unsigned int capacity_;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Class that holds the radius nearest neighbors
* It is more accurate than RadiusResult as it is not limited in the number of neighbors
*/
template<typename DistanceType>
class RadiusUniqueResultSet : public UniqueResultSet<DistanceType>
{
public:
/** Constructor
* @param capacity the number of neighbors to store at max
*/
RadiusUniqueResultSet(DistanceType radius) :
radius_(radius)
{
is_full_ = true;
}
/** Add a possible candidate to the best neighbors
* @param dist distance for that neighbor
* @param index index of that neighbor
*/
void addPoint(DistanceType dist, int index)
{
if (dist <= radius_) dist_indices_.insert(DistIndex(dist, index));
}
/** Remove all elements in the set
*/
inline void clear()
{
dist_indices_.clear();
}
/** Check the status of the set
* @return alwys false
*/
inline bool full() const
{
return true;
}
/** The distance of the furthest neighbor
* If we don't have enough neighbors, it returns the max possible value
* @return
*/
inline DistanceType worstDist() const
{
return radius_;
}
private:
typedef typename UniqueResultSet<DistanceType>::DistIndex DistIndex;
using UniqueResultSet<DistanceType>::dist_indices_;
using UniqueResultSet<DistanceType>::is_full_;
/** The furthest distance a neighbor can be */
DistanceType radius_;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Class that holds the k NN neighbors within a radius distance
*/
template<typename DistanceType>
class KNNRadiusUniqueResultSet : public KNNUniqueResultSet<DistanceType>
{
public:
/** Constructor
* @param capacity the number of neighbors to store at max
*/
KNNRadiusUniqueResultSet(unsigned int capacity, DistanceType radius)
{
this->capacity_ = capacity;
this->radius_ = radius;
this->dist_indices_.reserve(capacity_);
this->clear();
}
/** Remove all elements in the set
*/
void clear()
{
dist_indices_.clear();
worst_distance_ = radius_;
is_full_ = false;
}
private:
using KNNUniqueResultSet<DistanceType>::dist_indices_;
using KNNUniqueResultSet<DistanceType>::is_full_;
using KNNUniqueResultSet<DistanceType>::worst_distance_;
/** The maximum number of neighbors to consider */
unsigned int capacity_;
/** The maximum distance of a neighbor */
DistanceType radius_;
};
}
#endif //FLANN_RESULTSET_H
#endif //_OPENCV_RESULTSET_H_

View File

@ -27,11 +27,13 @@
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_SAMPLING_H_ #ifndef _OPENCV_SAMPLING_H_
#define OPENCV_FLANN_SAMPLING_H_ #define _OPENCV_SAMPLING_H_
#include "opencv2/flann/matrix.h"
#include "opencv2/flann/random.h"
#include "matrix.h"
#include "random.h"
namespace cvflann namespace cvflann
{ {
@ -39,43 +41,54 @@ namespace cvflann
template<typename T> template<typename T>
Matrix<T> random_sample(Matrix<T>& srcMatrix, long size, bool remove = false) Matrix<T> random_sample(Matrix<T>& srcMatrix, long size, bool remove = false)
{ {
Matrix<T> newSet(new T[size * srcMatrix.cols], size,srcMatrix.cols); UniqueRandom rand((int)srcMatrix.rows);
Matrix<T> newSet(new T[size * srcMatrix.cols], size, (long)srcMatrix.cols);
T* src,* dest; T *src,*dest;
for (long i=0; i<size; ++i) { for (long i=0;i<size;++i) {
long r = rand_int(srcMatrix.rows-i); long r = rand.next();
dest = newSet[i]; dest = newSet[i];
src = srcMatrix[r]; src = srcMatrix[r];
std::copy(src, src+srcMatrix.cols, dest); for (size_t j=0;j<srcMatrix.cols;++j) {
dest[j] = src[j];
}
if (remove) { if (remove) {
src = srcMatrix[srcMatrix.rows-i-1]; dest = srcMatrix[srcMatrix.rows-i-1];
dest = srcMatrix[r]; src = srcMatrix[r];
std::copy(src, src+srcMatrix.cols, dest); for (size_t j=0;j<srcMatrix.cols;++j) {
std::swap(*src,*dest);
src++;
dest++;
}
} }
} }
if (remove) { if (remove) {
srcMatrix.rows -= size; srcMatrix.rows -= size;
} }
return newSet; return newSet;
} }
template<typename T> template<typename T>
Matrix<T> random_sample(const Matrix<T>& srcMatrix, size_t size) Matrix<T> random_sample(const Matrix<T>& srcMatrix, size_t size)
{ {
UniqueRandom rand(srcMatrix.rows); UniqueRandom rand((int)srcMatrix.rows);
Matrix<T> newSet(new T[size * srcMatrix.cols], size,srcMatrix.cols); Matrix<T> newSet(new T[size * srcMatrix.cols], (long)size, (long)srcMatrix.cols);
T* src,* dest; T *src,*dest;
for (size_t i=0; i<size; ++i) { for (size_t i=0;i<size;++i) {
long r = rand.next(); long r = rand.next();
dest = newSet[i]; dest = newSet[i];
src = srcMatrix[r]; src = srcMatrix[r];
std::copy(src, src+srcMatrix.cols, dest); for (size_t j=0;j<srcMatrix.cols;++j) {
dest[j] = src[j];
}
} }
return newSet; return newSet;
} }
} // namespace } // namespace cvflann
#endif /* _OPENCV_SAMPLING_H_ */
#endif /* OPENCV_FLANN_SAMPLING_H_ */

View File

@ -26,51 +26,41 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_SAVING_H_ #ifndef _OPENCV_SAVING_H_
#define OPENCV_FLANN_SAVING_H_ #define _OPENCV_SAVING_H_
#include "opencv2/flann/general.h"
#include "opencv2/flann/nn_index.h"
#include <cstdio>
#include <cstring> #include <cstring>
#include <vector>
#include "general.h"
#include "nn_index.h"
#define FLANN_SIGNATURE "FLANN_INDEX"
namespace cvflann namespace cvflann
{ {
template <typename T> struct Datatype {};
template<> struct Datatype<char> { static flann_datatype_t type() { return FLANN_INT8; } };
template<> struct Datatype<short> { static flann_datatype_t type() { return FLANN_INT16; } };
template<> struct Datatype<int> { static flann_datatype_t type() { return FLANN_INT32; } };
template<> struct Datatype<unsigned char> { static flann_datatype_t type() { return FLANN_UINT8; } };
template<> struct Datatype<unsigned short> { static flann_datatype_t type() { return FLANN_UINT16; } };
template<> struct Datatype<unsigned int> { static flann_datatype_t type() { return FLANN_UINT32; } };
template<> struct Datatype<float> { static flann_datatype_t type() { return FLANN_FLOAT32; } };
template<> struct Datatype<double> { static flann_datatype_t type() { return FLANN_FLOAT64; } };
template <typename T>
struct Datatype {};
template<>
struct Datatype<char> { static flann_datatype_t type() { return FLANN_INT8; } };
template<>
struct Datatype<short> { static flann_datatype_t type() { return FLANN_INT16; } };
template<>
struct Datatype<int> { static flann_datatype_t type() { return FLANN_INT32; } };
template<>
struct Datatype<unsigned char> { static flann_datatype_t type() { return FLANN_UINT8; } };
template<>
struct Datatype<unsigned short> { static flann_datatype_t type() { return FLANN_UINT16; } };
template<>
struct Datatype<unsigned int> { static flann_datatype_t type() { return FLANN_UINT32; } };
template<>
struct Datatype<float> { static flann_datatype_t type() { return FLANN_FLOAT32; } };
template<>
struct Datatype<double> { static flann_datatype_t type() { return FLANN_FLOAT64; } };
CV_EXPORTS const char* FLANN_SIGNATURE();
CV_EXPORTS const char* FLANN_VERSION();
/** /**
* Structure representing the index header. * Structure representing the index header.
*/ */
struct IndexHeader struct CV_EXPORTS IndexHeader
{ {
char signature[16]; char signature[16];
char version[16]; char version[16];
flann_datatype_t data_type; flann_datatype_t data_type;
flann_algorithm_t index_type; flann_algorithm_t index_type;
size_t rows; int rows;
size_t cols; int cols;
}; };
/** /**
@ -79,20 +69,20 @@ struct IndexHeader
* @param stream - Stream to save to * @param stream - Stream to save to
* @param index - The index to save * @param index - The index to save
*/ */
template<typename Distance> template<typename ELEM_TYPE>
void save_header(FILE* stream, const NNIndex<Distance>& index) void save_header(FILE* stream, const NNIndex<ELEM_TYPE>& index)
{ {
IndexHeader header; IndexHeader header;
memset(header.signature, 0, sizeof(header.signature)); memset(header.signature, 0 , sizeof(header.signature));
strcpy(header.signature, FLANN_SIGNATURE); strcpy(header.signature, FLANN_SIGNATURE());
memset(header.version, 0, sizeof(header.version)); memset(header.version, 0 , sizeof(header.version));
strcpy(header.version, FLANN_VERSION); strcpy(header.version, FLANN_VERSION());
header.data_type = Datatype<typename Distance::ElementType>::type(); header.data_type = Datatype<ELEM_TYPE>::type();
header.index_type = index.getType(); header.index_type = index.getType();
header.rows = index.size(); header.rows = (int)index.size();
header.cols = index.veclen(); header.cols = index.veclen();
std::fwrite(&header, sizeof(header),1,stream); std::fwrite(&header, sizeof(header),1,stream);
} }
@ -101,84 +91,25 @@ void save_header(FILE* stream, const NNIndex<Distance>& index)
* @param stream - Stream to load from * @param stream - Stream to load from
* @return Index header * @return Index header
*/ */
inline IndexHeader load_header(FILE* stream) CV_EXPORTS IndexHeader load_header(FILE* stream);
template<typename T>
void save_value(FILE* stream, const T& value, int count = 1)
{ {
IndexHeader header; fwrite(&value, 1, sizeof(value)*count, stream);
int read_size = fread(&header,sizeof(header),1,stream);
if (read_size!=1) {
throw FLANNException("Invalid index file, cannot read");
}
if (strcmp(header.signature,FLANN_SIGNATURE)!=0) {
throw FLANNException("Invalid index file, wrong signature");
}
return header;
} }
template<typename T> template<typename T>
void save_value(FILE* stream, const T& value, size_t count = 1) void load_value(FILE* stream, T& value, int count = 1)
{ {
fwrite(&value, sizeof(value),count, stream); int read_cnt = (int)fread(&value, sizeof(value),count, stream);
if (read_cnt!=count) {
throw FLANNException("Cannot read from file");
}
} }
template<typename T> } // namespace cvflann
void save_value(FILE* stream, const cvflann::Matrix<T>& value)
{
fwrite(&value, sizeof(value),1, stream);
fwrite(value.data, sizeof(T),value.rows*value.cols, stream);
}
template<typename T> #endif /* _OPENCV_SAVING_H_ */
void save_value(FILE* stream, const std::vector<T>& value)
{
size_t size = value.size();
fwrite(&size, sizeof(size_t), 1, stream);
fwrite(&value[0], sizeof(T), size, stream);
}
template<typename T>
void load_value(FILE* stream, T& value, size_t count = 1)
{
size_t read_cnt = fread(&value, sizeof(value), count, stream);
if (read_cnt != count) {
throw FLANNException("Cannot read from file");
}
}
template<typename T>
void load_value(FILE* stream, cvflann::Matrix<T>& value)
{
size_t read_cnt = fread(&value, sizeof(value), 1, stream);
if (read_cnt != 1) {
throw FLANNException("Cannot read from file");
}
value.data = new T[value.rows*value.cols];
read_cnt = fread(value.data, sizeof(T), value.rows*value.cols, stream);
if (read_cnt != int(value.rows*value.cols)) {
throw FLANNException("Cannot read from file");
}
}
template<typename T>
void load_value(FILE* stream, std::vector<T>& value)
{
size_t size;
size_t read_cnt = fread(&size, sizeof(size_t), 1, stream);
if (read_cnt!=1) {
throw FLANNException("Cannot read from file");
}
value.resize(size);
read_cnt = fread(&value[0], sizeof(T), size, stream);
if (read_cnt!=int(size)) {
throw FLANNException("Cannot read from file");
}
}
}
#endif /* OPENCV_FLANN_SAVING_H_ */

View File

@ -28,20 +28,20 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef OPENCV_FLANN_SIMPLEX_DOWNHILL_H_ #ifndef _OPENCV_SIMPLEX_DOWNHILL_H_
#define OPENCV_FLANN_SIMPLEX_DOWNHILL_H_ #define _OPENCV_SIMPLEX_DOWNHILL_H_
namespace cvflann namespace cvflann
{ {
/** /**
Adds val to array vals (and point to array points) and keeping the arrays sorted by vals. Adds val to array vals (and point to array points) and keeping the arrays sorted by vals.
*/ */
template <typename T> template <typename T>
void addValue(int pos, float val, float* vals, T* point, T* points, int n) void addValue(int pos, float val, float* vals, T* point, T* points, int n)
{ {
vals[pos] = val; vals[pos] = val;
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
points[pos*n+i] = point[i]; points[pos*n+i] = point[i];
} }
@ -49,7 +49,7 @@ void addValue(int pos, float val, float* vals, T* point, T* points, int n)
int j=pos; int j=pos;
while (j>0 && vals[j]<vals[j-1]) { while (j>0 && vals[j]<vals[j-1]) {
swap(vals[j],vals[j-1]); swap(vals[j],vals[j-1]);
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
swap(points[j*n+i],points[(j-1)*n+i]); swap(points[j*n+i],points[(j-1)*n+i]);
} }
--j; --j;
@ -64,7 +64,7 @@ void addValue(int pos, float val, float* vals, T* point, T* points, int n)
vals is the cost function in the n+1 simplex points, if NULL it will be computed vals is the cost function in the n+1 simplex points, if NULL it will be computed
Postcondition: returns optimum value and points[0..n] are the optimum parameters Postcondition: returns optimum value and points[0..n] are the optimum parameters
*/ */
template <typename T, typename F> template <typename T, typename F>
float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL ) float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL )
{ {
@ -84,7 +84,7 @@ float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL )
if (vals == NULL) { if (vals == NULL) {
ownVals = true; ownVals = true;
vals = new float[n+1]; vals = new float[n+1];
for (int i=0; i<n+1; ++i) { for (int i=0;i<n+1;++i) {
float val = func(points+i*n); float val = func(points+i*n);
addValue(i, val, vals, points+i*n, points, n); addValue(i, val, vals, points+i*n, points, n);
} }
@ -96,18 +96,18 @@ float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL )
if (iterations++ > MAX_ITERATIONS) break; if (iterations++ > MAX_ITERATIONS) break;
// compute average of simplex points (except the highest point) // compute average of simplex points (except the highest point)
for (int j=0; j<n; ++j) { for (int j=0;j<n;++j) {
p_o[j] = 0; p_o[j] = 0;
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
p_o[i] += points[j*n+i]; p_o[i] += points[j*n+i];
} }
} }
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
p_o[i] /= n; p_o[i] /= n;
} }
bool converged = true; bool converged = true;
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
if (p_o[i] != points[nn+i]) { if (p_o[i] != points[nn+i]) {
converged = false; converged = false;
} }
@ -115,15 +115,15 @@ float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL )
if (converged) break; if (converged) break;
// trying a reflection // trying a reflection
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
p_r[i] = p_o[i] + alpha*(p_o[i]-points[nn+i]); p_r[i] = p_o[i] + alpha*(p_o[i]-points[nn+i]);
} }
float val_r = func(p_r); float val_r = func(p_r);
if ((val_r>=vals[0])&&(val_r<vals[n])) { if (val_r>=vals[0] && val_r<vals[n]) {
// reflection between second highest and lowest // reflection between second highest and lowest
// add it to the simplex // add it to the simplex
Logger::info("Choosing reflection\n"); logger().info("Choosing reflection\n");
addValue(n, val_r,vals, p_r, points, n); addValue(n, val_r,vals, p_r, points, n);
continue; continue;
} }
@ -132,37 +132,37 @@ float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL )
// value is smaller than smalest in simplex // value is smaller than smalest in simplex
// expand some more to see if it drops further // expand some more to see if it drops further
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
p_e[i] = 2*p_r[i]-p_o[i]; p_e[i] = 2*p_r[i]-p_o[i];
} }
float val_e = func(p_e); float val_e = func(p_e);
if (val_e<val_r) { if (val_e<val_r) {
Logger::info("Choosing reflection and expansion\n"); logger().info("Choosing reflection and expansion\n");
addValue(n, val_e,vals,p_e,points,n); addValue(n, val_e,vals,p_e,points,n);
} }
else { else {
Logger::info("Choosing reflection\n"); logger().info("Choosing reflection\n");
addValue(n, val_r,vals,p_r,points,n); addValue(n, val_r,vals,p_r,points,n);
} }
continue; continue;
} }
if (val_r>=vals[n]) { if (val_r>=vals[n]) {
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
p_e[i] = (p_o[i]+points[nn+i])/2; p_e[i] = (p_o[i]+points[nn+i])/2;
} }
float val_e = func(p_e); float val_e = func(p_e);
if (val_e<vals[n]) { if (val_e<vals[n]) {
Logger::info("Choosing contraction\n"); logger().info("Choosing contraction\n");
addValue(n,val_e,vals,p_e,points,n); addValue(n,val_e,vals,p_e,points,n);
continue; continue;
} }
} }
{ {
Logger::info("Full contraction\n"); logger().info("Full contraction\n");
for (int j=1; j<=n; ++j) { for (int j=1;j<=n;++j) {
for (int i=0; i<n; ++i) { for (int i=0;i<n;++i) {
points[j*n+i] = (points[j*n+i]+points[i])/2; points[j*n+i] = (points[j*n+i]+points[i])/2;
} }
float val = func(points+j*n); float val = func(points+j*n);
@ -181,6 +181,6 @@ float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL )
return bestVal; return bestVal;
} }
} } // namespace cvflann
#endif //OPENCV_FLANN_SIMPLEX_DOWNHILL_H_ #endif //_OPENCV_SIMPLEX_DOWNHILL_H_

View File

@ -28,8 +28,8 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/ *************************************************************************/
#ifndef FLANN_TIMER_H #ifndef _OPENCV_TIMER_H_
#define FLANN_TIMER_H #define _OPENCV_TIMER_H_
#include <time.h> #include <time.h>
@ -42,7 +42,7 @@ namespace cvflann
* *
* Can be used to time portions of code. * Can be used to time portions of code.
*/ */
class StartStopTimer class CV_EXPORTS StartStopTimer
{ {
clock_t startTime; clock_t startTime;
@ -64,16 +64,14 @@ public:
/** /**
* Starts the timer. * Starts the timer.
*/ */
void start() void start() {
{
startTime = clock(); startTime = clock();
} }
/** /**
* Stops the timer and updates timer value. * Stops the timer and updates timer value.
*/ */
void stop() void stop() {
{
clock_t stopTime = clock(); clock_t stopTime = clock();
value += ( (double)stopTime - startTime) / CLOCKS_PER_SEC; value += ( (double)stopTime - startTime) / CLOCKS_PER_SEC;
} }
@ -81,13 +79,12 @@ public:
/** /**
* Resets the timer value to 0. * Resets the timer value to 0.
*/ */
void reset() void reset() {
{
value = 0; value = 0;
} }
}; };
} }// namespace cvflann
#endif // FLANN_TIMER_H #endif // _OPENCV_TIMER_H_

View File

@ -28,4 +28,198 @@
#include "precomp.hpp" #include "precomp.hpp"
void cvflann::dummyfunc() {} namespace cvflann
{
// ----------------------- dist.cpp ---------------------------
/** Global variable indicating the distance metric
* to be used.
*/
flann_distance_t flann_distance_type_ = FLANN_DIST_EUCLIDEAN;
flann_distance_t flann_distance_type() { return flann_distance_type_; }
/**
* Zero iterator that emulates a zero feature.
*/
ZeroIterator<float> zero_;
ZeroIterator<float>& zero() { return zero_; }
/**
* Order of Minkowski distance to use.
*/
int flann_minkowski_order_;
int flann_minkowski_order() { return flann_minkowski_order_; }
double euclidean_dist(const unsigned char* first1, const unsigned char* last1, unsigned char* first2, double acc)
{
double distsq = acc;
double diff0, diff1, diff2, diff3;
const unsigned char* lastgroup = last1 - 3;
while (first1 < lastgroup) {
diff0 = first1[0] - first2[0];
diff1 = first1[1] - first2[1];
diff2 = first1[2] - first2[2];
diff3 = first1[3] - first2[3];
distsq += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3;
first1 += 4;
first2 += 4;
}
while (first1 < last1) {
diff0 = *first1++ - *first2++;
distsq += diff0 * diff0;
}
return distsq;
}
// ----------------------- index_testing.cpp ---------------------------
int countCorrectMatches(int* neighbors, int* groundTruth, int n)
{
int count = 0;
for (int i=0;i<n;++i) {
for (int k=0;k<n;++k) {
if (neighbors[i]==groundTruth[k]) {
count++;
break;
}
}
}
return count;
}
// ----------------------- logger.cpp ---------------------------
Logger logger_;
Logger& logger() { return logger_; }
int Logger::log(int level, const char* fmt, ...)
{
if (level > logLevel ) return -1;
int ret;
va_list arglist;
va_start(arglist, fmt);
ret = vfprintf(stream, fmt, arglist);
va_end(arglist);
return ret;
}
int Logger::log(int level, const char* fmt, va_list arglist)
{
if (level > logLevel ) return -1;
int ret;
ret = vfprintf(stream, fmt, arglist);
return ret;
}
#define LOG_METHOD(NAME,LEVEL) \
int Logger::NAME(const char* fmt, ...) \
{ \
int ret; \
va_list ap; \
va_start(ap, fmt); \
ret = log(LEVEL, fmt, ap); \
va_end(ap); \
return ret; \
}
LOG_METHOD(fatal, FLANN_LOG_FATAL)
LOG_METHOD(error, FLANN_LOG_ERROR)
LOG_METHOD(warn, FLANN_LOG_WARN)
LOG_METHOD(info, FLANN_LOG_INFO)
// ----------------------- random.cpp ---------------------------
void seed_random(unsigned int seed)
{
srand(seed);
}
double rand_double(double high, double low)
{
return low + ((high-low) * (std::rand() / (RAND_MAX + 1.0)));
}
int rand_int(int high, int low)
{
return low + (int) ( double(high-low) * (std::rand() / (RAND_MAX + 1.0)));
}
// ----------------------- saving.cpp ---------------------------
const char FLANN_SIGNATURE_[] = "FLANN_INDEX";
const char FLANN_VERSION_[] = "1.5.0";
const char* FLANN_SIGNATURE() { return FLANN_SIGNATURE_; }
const char* FLANN_VERSION() { return FLANN_VERSION_; }
IndexHeader load_header(FILE* stream)
{
IndexHeader header;
size_t read_size = fread(&header,sizeof(header),1,stream);
if (read_size!=1) {
throw FLANNException("Invalid index file, cannot read");
}
if (strcmp(header.signature,FLANN_SIGNATURE())!=0) {
throw FLANNException("Invalid index file, wrong signature");
}
return header;
}
// ----------------------- flann.cpp ---------------------------
void log_verbosity(int level)
{
if (level>=0) {
logger().setLevel(level);
}
}
void set_distance_type(flann_distance_t distance_type, int order)
{
flann_distance_type_ = distance_type;
flann_minkowski_order_ = order;
}
static ParamsFactory the_factory;
ParamsFactory& ParamsFactory_instance()
{
return the_factory;
}
class StaticInit
{
public:
StaticInit()
{
ParamsFactory_instance().register_<LinearIndexParams>(FLANN_INDEX_LINEAR);
ParamsFactory_instance().register_<KDTreeIndexParams>(FLANN_INDEX_KDTREE);
ParamsFactory_instance().register_<KMeansIndexParams>(FLANN_INDEX_KMEANS);
ParamsFactory_instance().register_<CompositeIndexParams>(FLANN_INDEX_COMPOSITE);
ParamsFactory_instance().register_<AutotunedIndexParams>(FLANN_INDEX_AUTOTUNED);
// ParamsFactory::instance().register_<SavedIndexParams>(FLANN_INDEX_SAVED);
}
};
StaticInit __init;
} // namespace cvflann

View File

@ -7,7 +7,6 @@
#include "opencv2/flann/dist.h" #include "opencv2/flann/dist.h"
#include "opencv2/flann/index_testing.h" #include "opencv2/flann/index_testing.h"
#include "opencv2/flann/params.h"
#include "opencv2/flann/saving.h" #include "opencv2/flann/saving.h"
#include "opencv2/flann/general.h" #include "opencv2/flann/general.h"