tesseract/classify/kdtree.cpp
Jim O'Regan 524a61452d Doxygen
Squashed commit from https://github.com/tesseract-ocr/tesseract/tree/more-doxygen
closes #14

Commits:
6317305  doxygen
9f42f69  doxygen
0fc4d52  doxygen
37b4b55  fix typo
bded8f1  some more doxy
020eb00  slight tweak
524666d  doxygenify
2a36a3e  doxygenify
229d218  doxygenify
7fd28ae  doxygenify
a8c64bc  doxygenify
f5d21b6  fix
5d8ede8  doxygenify
a58a4e0  language_model.cpp
fa85709  lm_pain_points.cpp lm_state.cpp
6418da3  merge
06190ba  Merge branch 'old_doxygen_merge' into more-doxygen
84acf08  Merge branch 'master' into more-doxygen
50fe1ff  pagewalk.cpp cube_reco_context.cpp
2982583  change to relative
192a24a  applybox.cpp, take one
8eeb053  delete docs for obsolete params
52e4c77  modernise classify/ocrfeatures.cpp
2a1cba6  modernise cutil/emalloc.cpp
773e006  silence doxygen warning
aeb1731  silence doxygen warning
f18387f  silence doxygen; new params are unused?
15ad6bd  doxygenify cutil/efio.cpp
c8b5dad  doxygenify cutil/danerror.cpp
784450f  the globals and exceptions parts are obsolete; remove
8bca324  doxygen classify/normfeat.cpp
9bcbe16  doxygen classify/normmatch.cpp
aa9a971  doxygen ccmain/cube_control.cpp
c083ff2  doxygen ccmain/cube_reco_context.cpp
f842850  params changed
5c94f12  doxygen ccmain/cubeclassifier.cpp
15ba750  case sensitive
f5c71d4  case sensitive
f85655b  doxygen classify/intproto.cpp
4bbc7aa  partial doxygen classify/mfx.cpp
dbb6041  partial doxygen classify/intproto.cpp
2aa72db  finish doxygen classify/intproto.cpp
0b8de99  doxygen training/mftraining.cpp
0b5b35c  partial doxygen ccstruct/coutln.cpp
b81c766  partial doxygen ccstruct/coutln.cpp
40fc415  finished? doxygen ccstruct/coutln.cpp
6e4165c  doxygen classify/clusttool.cpp
0267dec  doxygen classify/cutoffs.cpp
7f0c70c  doxygen classify/fpoint.cpp
512f3bd  ignore ~ files
5668a52  doxygen classify/intmatcher.cpp
84788d4  doxygen classify/kdtree.cpp
29f36ca  doxygen classify/mfoutline.cpp
40b94b1  silence doxygen warnings
6c511b9  doxygen classify/mfx.cpp
f9b4080  doxygen classify/outfeat.cpp
aa1df05  doxygen classify/picofeat.cpp
cc5f466  doxygen training/cntraining.cpp
cce044f  doxygen training/commontraining.cpp
167e216  missing param
9498383  renamed params
37eeac2  renamed param
d87b5dd  case
c8ee174  renamed params
b858db8  typo
4c2a838  h2 context?
81a2c0c  fix some param names; add some missing params, no docs
bcf8a4c  add some missing params, no docs
af77f86  add some missing params, no docs; fix some param names
01df24e  fix some params
6161056  fix some params
68508b6  fix some params
285aeb6  doxygen complains here no matter what
529bcfa  rm some missing params, typos
cd21226  rm some missing params, add some new ones
48a4bc2  fix params
c844628  missing param
312ce37  missing param; rename one
ec2fdec  missing param
05e15e0  missing params
d515858  change "<" to &lt; to make doxygen happy
b476a28  wrong place
2015-07-20 18:48:00 +01:00

563 lines
18 KiB
C++

/******************************************************************************
** Filename: kdtree.cpp
** Purpose: Routines for managing K-D search trees
** Author: Dan Johnson
** History: 3/10/89, DSJ, Created.
** 5/23/89, DSJ, Added circular feature capability.
** 7/13/89, DSJ, Made tree nodes invisible to outside.
**
** (c) Copyright Hewlett-Packard Company, 1988.
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
** http://www.apache.org/licenses/LICENSE-2.0
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
******************************************************************************/
/*-----------------------------------------------------------------------------
Include Files and Type Defines
-----------------------------------------------------------------------------*/
#include "kdtree.h"
#include "const.h"
#include "emalloc.h"
#include "freelist.h"
#include <stdio.h>
#include <math.h>
#define Magnitude(X) ((X) < 0 ? -(X) : (X))
#define NodeFound(N,K,D) (( (N)->Key == (K) ) && ( (N)->Data == (D) ))
/*-----------------------------------------------------------------------------
Global Data Definitions and Declarations
-----------------------------------------------------------------------------*/
#define MINSEARCH -MAX_FLOAT32
#define MAXSEARCH MAX_FLOAT32
// Helper function to find the next essential dimension in a cycle.
static int NextLevel(KDTREE *tree, int level) {
do {
++level;
if (level >= tree->KeySize)
level = 0;
} while (tree->KeyDesc[level].NonEssential);
return level;
}
//-----------------------------------------------------------------------------
/** Store the k smallest-keyed key-value pairs. */
template<typename Key, typename Value>
class MinK {
public:
MinK(Key max_key, int k);
~MinK();
struct Element {
Element() {}
Element(const Key& k, const Value& v) : key(k), value(v) {}
Key key;
Value value;
};
bool insert(Key k, Value v);
const Key& max_insertable_key();
int elements_count() { return elements_count_; }
const Element* elements() { return elements_; }
private:
const Key max_key_; //< the maximum possible Key
Element* elements_; //< unsorted array of elements
int elements_count_; //< the number of results collected so far
int k_; //< the number of results we want from the search
int max_index_; //< the index of the result with the largest key
};
template<typename Key, typename Value>
MinK<Key, Value>::MinK(Key max_key, int k) :
max_key_(max_key), elements_count_(0), k_(k < 1 ? 1 : k), max_index_(0) {
elements_ = new Element[k_];
}
template<typename Key, typename Value>
MinK<Key, Value>::~MinK() {
delete []elements_;
}
template<typename Key, typename Value>
const Key& MinK<Key, Value>::max_insertable_key() {
if (elements_count_ < k_)
return max_key_;
return elements_[max_index_].key;
}
template<typename Key, typename Value>
bool MinK<Key, Value>::insert(Key key, Value value) {
if (elements_count_ < k_) {
elements_[elements_count_++] = Element(key, value);
if (key > elements_[max_index_].key)
max_index_ = elements_count_ - 1;
return true;
} else if (key < elements_[max_index_].key) {
// evict the largest element.
elements_[max_index_] = Element(key, value);
// recompute max_index_
for (int i = 0; i < elements_count_; i++) {
if (elements_[i].key > elements_[max_index_].key)
max_index_ = i;
}
return true;
}
return false;
}
//-----------------------------------------------------------------------------
/** Helper class for searching for the k closest points to query_point in tree. */
class KDTreeSearch {
public:
KDTreeSearch(KDTREE* tree, FLOAT32 *query_point, int k_closest);
~KDTreeSearch();
/** Return the k nearest points' data. */
void Search(int *result_count, FLOAT32 *distances, void **results);
private:
void SearchRec(int Level, KDNODE *SubTree);
bool BoxIntersectsSearch(FLOAT32 *lower, FLOAT32 *upper);
KDTREE *tree_;
FLOAT32 *query_point_;
MinK<FLOAT32, void *>* results_;
FLOAT32 *sb_min_; //< search box minimum
FLOAT32 *sb_max_; //< search box maximum
};
KDTreeSearch::KDTreeSearch(KDTREE* tree, FLOAT32 *query_point, int k_closest) :
tree_(tree),
query_point_(query_point) {
results_ = new MinK<FLOAT32, void *>(MAXSEARCH, k_closest);
sb_min_ = new FLOAT32[tree->KeySize];
sb_max_ = new FLOAT32[tree->KeySize];
}
KDTreeSearch::~KDTreeSearch() {
delete results_;
delete[] sb_min_;
delete[] sb_max_;
}
/// Locate the k_closest points to query_point_, and return their distances and
/// data into the given buffers.
void KDTreeSearch::Search(int *result_count,
FLOAT32 *distances,
void **results) {
if (tree_->Root.Left == NULL) {
*result_count = 0;
} else {
for (int i = 0; i < tree_->KeySize; i++) {
sb_min_[i] = tree_->KeyDesc[i].Min;
sb_max_[i] = tree_->KeyDesc[i].Max;
}
SearchRec(0, tree_->Root.Left);
int count = results_->elements_count();
*result_count = count;
for (int j = 0; j < count; j++) {
distances[j] = (FLOAT32) sqrt((FLOAT64)results_->elements()[j].key);
results[j] = results_->elements()[j].value;
}
}
}
/*-----------------------------------------------------------------------------
Public Code
-----------------------------------------------------------------------------*/
/// @return a new KDTREE based on the specified parameters.
/// @param KeySize # of dimensions in the K-D tree
/// @param KeyDesc array of params to describe key dimensions
KDTREE *MakeKDTree(inT16 KeySize, const PARAM_DESC KeyDesc[]) {
KDTREE *KDTree = (KDTREE *) Emalloc(
sizeof(KDTREE) + (KeySize - 1) * sizeof(PARAM_DESC));
for (int i = 0; i < KeySize; i++) {
KDTree->KeyDesc[i].NonEssential = KeyDesc[i].NonEssential;
KDTree->KeyDesc[i].Circular = KeyDesc[i].Circular;
if (KeyDesc[i].Circular) {
KDTree->KeyDesc[i].Min = KeyDesc[i].Min;
KDTree->KeyDesc[i].Max = KeyDesc[i].Max;
KDTree->KeyDesc[i].Range = KeyDesc[i].Max - KeyDesc[i].Min;
KDTree->KeyDesc[i].HalfRange = KDTree->KeyDesc[i].Range / 2;
KDTree->KeyDesc[i].MidRange = (KeyDesc[i].Max + KeyDesc[i].Min) / 2;
} else {
KDTree->KeyDesc[i].Min = MINSEARCH;
KDTree->KeyDesc[i].Max = MAXSEARCH;
}
}
KDTree->KeySize = KeySize;
KDTree->Root.Left = NULL;
KDTree->Root.Right = NULL;
return KDTree;
}
/**
* This routine stores Data in the K-D tree specified by Tree
* using Key as an access key.
*
* @param Tree K-D tree in which data is to be stored
* @param Key ptr to key by which data can be retrieved
* @param Data ptr to data to be stored in the tree
*
* @note Exceptions: none
* @note History: 3/10/89, DSJ, Created.
* 7/13/89, DSJ, Changed return to void.
*/
void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data) {
int Level;
KDNODE *Node;
KDNODE **PtrToNode;
PtrToNode = &(Tree->Root.Left);
Node = *PtrToNode;
Level = NextLevel(Tree, -1);
while (Node != NULL) {
if (Key[Level] < Node->BranchPoint) {
PtrToNode = &(Node->Left);
if (Key[Level] > Node->LeftBranch)
Node->LeftBranch = Key[Level];
}
else {
PtrToNode = &(Node->Right);
if (Key[Level] < Node->RightBranch)
Node->RightBranch = Key[Level];
}
Level = NextLevel(Tree, Level);
Node = *PtrToNode;
}
*PtrToNode = MakeKDNode(Tree, Key, (void *) Data, Level);
} /* KDStore */
/**
* This routine deletes a node from Tree. The node to be
* deleted is specified by the Key for the node and the Data
* contents of the node. These two pointers must be identical
* to the pointers that were used for the node when it was
* originally stored in the tree. A node will be deleted from
* the tree only if its key and data pointers are identical
* to Key and Data respectively. The tree is re-formed by removing
* the affected subtree and inserting all elements but the root.
*
* @param Tree K-D tree to delete node from
* @param Key key of node to be deleted
* @param Data data contents of node to be deleted
*
* @note Exceptions: none
*
* @note History: 3/13/89, DSJ, Created.
* 7/13/89, DSJ, Specify node indirectly by key and data.
*/
void
KDDelete (KDTREE * Tree, FLOAT32 Key[], void *Data) {
int Level;
KDNODE *Current;
KDNODE *Father;
/* initialize search at root of tree */
Father = &(Tree->Root);
Current = Father->Left;
Level = NextLevel(Tree, -1);
/* search tree for node to be deleted */
while ((Current != NULL) && (!NodeFound (Current, Key, Data))) {
Father = Current;
if (Key[Level] < Current->BranchPoint)
Current = Current->Left;
else
Current = Current->Right;
Level = NextLevel(Tree, Level);
}
if (Current != NULL) { /* if node to be deleted was found */
if (Current == Father->Left) {
Father->Left = NULL;
Father->LeftBranch = Tree->KeyDesc[Level].Min;
} else {
Father->Right = NULL;
Father->RightBranch = Tree->KeyDesc[Level].Max;
}
InsertNodes(Tree, Current->Left);
InsertNodes(Tree, Current->Right);
FreeSubTree(Current);
}
} /* KDDelete */
/**
* This routine searches the K-D tree specified by Tree and
* finds the QuerySize nearest neighbors of Query. All neighbors
* must be within MaxDistance of Query. The data contents of
* the nearest neighbors
* are placed in NBuffer and their distances from Query are
* placed in DBuffer.
* @param Tree ptr to K-D tree to be searched
* @param Query ptr to query key (point in D-space)
* @param QuerySize number of nearest neighbors to be found
* @param MaxDistance all neighbors must be within this distance
* @param NBuffer ptr to QuerySize buffer to hold nearest neighbors
* @param DBuffer ptr to QuerySize buffer to hold distances
* from nearest neighbor to query point
* @param NumberOfResults [out] Number of nearest neighbors actually found
* @note Exceptions: none
* @note History:
* - 3/10/89, DSJ, Created.
* - 7/13/89, DSJ, Return contents of node instead of node itself.
*/
void KDNearestNeighborSearch(
KDTREE *Tree, FLOAT32 Query[], int QuerySize, FLOAT32 MaxDistance,
int *NumberOfResults, void **NBuffer, FLOAT32 DBuffer[]) {
KDTreeSearch search(Tree, Query, QuerySize);
search.Search(NumberOfResults, DBuffer, NBuffer);
}
/*---------------------------------------------------------------------------*/
/** Walk a given Tree with action. */
void KDWalk(KDTREE *Tree, void_proc action, void *context) {
if (Tree->Root.Left != NULL)
Walk(Tree, action, context, Tree->Root.Left, NextLevel(Tree, -1));
}
/*---------------------------------------------------------------------------*/
/**
* This routine frees all memory which is allocated to the
* specified KD-tree. This includes the data structure for
* the kd-tree itself plus the data structures for each node
* in the tree. It does not include the Key and Data items
* which are pointed to by the nodes. This memory is left
* untouched.
* @param Tree tree data structure to be released
* @return none
* @note Exceptions: none
* @note History: 5/26/89, DSJ, Created.
*/
void FreeKDTree(KDTREE *Tree) {
FreeSubTree(Tree->Root.Left);
memfree(Tree);
} /* FreeKDTree */
/*-----------------------------------------------------------------------------
Private Code
-----------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/**
* This routine allocates memory for a new K-D tree node
* and places the specified Key and Data into it. The
* left and right subtree pointers for the node are
* initialized to empty subtrees.
* @param tree The tree to create the node for
* @param Key Access key for new node in KD tree
* @param Data ptr to data to be stored in new node
* @param Index index of Key to branch on
* @return pointer to new K-D tree node
* @note Exceptions: None
* @note History: 3/11/89, DSJ, Created.
*/
KDNODE *MakeKDNode(KDTREE *tree, FLOAT32 Key[], void *Data, int Index) {
KDNODE *NewNode;
NewNode = (KDNODE *) Emalloc (sizeof (KDNODE));
NewNode->Key = Key;
NewNode->Data = Data;
NewNode->BranchPoint = Key[Index];
NewNode->LeftBranch = tree->KeyDesc[Index].Min;
NewNode->RightBranch = tree->KeyDesc[Index].Max;
NewNode->Left = NULL;
NewNode->Right = NULL;
return NewNode;
} /* MakeKDNode */
/*---------------------------------------------------------------------------*/
void FreeKDNode(KDNODE *Node) {
memfree ((char *)Node);
}
/*---------------------------------------------------------------------------*/
/**
* Recursively accumulate the k_closest points to query_point_ into results_.
* @param Level level in tree of sub-tree to be searched
* @param SubTree sub-tree to be searched
*/
void KDTreeSearch::SearchRec(int level, KDNODE *sub_tree) {
if (level >= tree_->KeySize)
level = 0;
if (!BoxIntersectsSearch(sb_min_, sb_max_))
return;
results_->insert(DistanceSquared(tree_->KeySize, tree_->KeyDesc,
query_point_, sub_tree->Key),
sub_tree->Data);
if (query_point_[level] < sub_tree->BranchPoint) {
if (sub_tree->Left != NULL) {
FLOAT32 tmp = sb_max_[level];
sb_max_[level] = sub_tree->LeftBranch;
SearchRec(NextLevel(tree_, level), sub_tree->Left);
sb_max_[level] = tmp;
}
if (sub_tree->Right != NULL) {
FLOAT32 tmp = sb_min_[level];
sb_min_[level] = sub_tree->RightBranch;
SearchRec(NextLevel(tree_, level), sub_tree->Right);
sb_min_[level] = tmp;
}
} else {
if (sub_tree->Right != NULL) {
FLOAT32 tmp = sb_min_[level];
sb_min_[level] = sub_tree->RightBranch;
SearchRec(NextLevel(tree_, level), sub_tree->Right);
sb_min_[level] = tmp;
}
if (sub_tree->Left != NULL) {
FLOAT32 tmp = sb_max_[level];
sb_max_[level] = sub_tree->LeftBranch;
SearchRec(NextLevel(tree_, level), sub_tree->Left);
sb_max_[level] = tmp;
}
}
}
/*---------------------------------------------------------------------------*/
/**
*Returns the Euclidean distance squared between p1 and p2 for all essential
* dimensions.
* @param k keys are in k-space
* @param dim dimension descriptions (essential, circular, etc)
* @param p1,p2 two different points in K-D space
*/
FLOAT32 DistanceSquared(int k, PARAM_DESC *dim, FLOAT32 p1[], FLOAT32 p2[]) {
FLOAT32 total_distance = 0;
for (; k > 0; k--, p1++, p2++, dim++) {
if (dim->NonEssential)
continue;
FLOAT32 dimension_distance = *p1 - *p2;
/* if this dimension is circular - check wraparound distance */
if (dim->Circular) {
dimension_distance = Magnitude(dimension_distance);
FLOAT32 wrap_distance = dim->Max - dim->Min - dimension_distance;
dimension_distance = MIN(dimension_distance, wrap_distance);
}
total_distance += dimension_distance * dimension_distance;
}
return total_distance;
}
FLOAT32 ComputeDistance(int k, PARAM_DESC *dim, FLOAT32 p1[], FLOAT32 p2[]) {
return sqrt(DistanceSquared(k, dim, p1, p2));
}
/*---------------------------------------------------------------------------*/
/// Return whether the query region (the smallest known circle about
/// query_point_ containing results->k_ points) intersects the box specified
/// between lower and upper. For circular dimensions, we also check the point
/// one wrap distance away from the query.
bool KDTreeSearch::BoxIntersectsSearch(FLOAT32 *lower, FLOAT32 *upper) {
FLOAT32 *query = query_point_;
FLOAT64 total_distance = 0.0;
FLOAT64 radius_squared =
results_->max_insertable_key() * results_->max_insertable_key();
PARAM_DESC *dim = tree_->KeyDesc;
for (int i = tree_->KeySize; i > 0; i--, dim++, query++, lower++, upper++) {
if (dim->NonEssential)
continue;
FLOAT32 dimension_distance;
if (*query < *lower)
dimension_distance = *lower - *query;
else if (*query > *upper)
dimension_distance = *query - *upper;
else
dimension_distance = 0;
/* if this dimension is circular - check wraparound distance */
if (dim->Circular) {
FLOAT32 wrap_distance = MAX_FLOAT32;
if (*query < *lower)
wrap_distance = *query + dim->Max - dim->Min - *upper;
else if (*query > *upper)
wrap_distance = *lower - (*query - (dim->Max - dim->Min));
dimension_distance = MIN(dimension_distance, wrap_distance);
}
total_distance += dimension_distance * dimension_distance;
if (total_distance >= radius_squared)
return FALSE;
}
return TRUE;
}
/*---------------------------------------------------------------------------*/
/**
* Walk a tree, calling action once on each node.
*
* Operation:
* This routine walks thru the specified sub_tree and invokes action
* action at each node as follows:
* action(context, data, level)
* data the data contents of the node being visited,
* level is the level of the node in the tree with the root being level 0.
* @param tree root of the tree being walked.
* @param action action to be performed at every node
* @param context action's context
* @param sub_tree ptr to root of subtree to be walked
* @param level current level in the tree for this node
*/
void Walk(KDTREE *tree, void_proc action, void *context,
KDNODE *sub_tree, inT32 level) {
(*action)(context, sub_tree->Data, level);
if (sub_tree->Left != NULL)
Walk(tree, action, context, sub_tree->Left, NextLevel(tree, level));
if (sub_tree->Right != NULL)
Walk(tree, action, context, sub_tree->Right, NextLevel(tree, level));
}
/** Given a subtree nodes, insert all of its elements into tree. */
void InsertNodes(KDTREE *tree, KDNODE *nodes) {
if (nodes == NULL)
return;
KDStore(tree, nodes->Key, nodes->Data);
InsertNodes(tree, nodes->Left);
InsertNodes(tree, nodes->Right);
}
/** Free all of the nodes of a sub tree. */
void FreeSubTree(KDNODE *sub_tree) {
if (sub_tree != NULL) {
FreeSubTree(sub_tree->Left);
FreeSubTree(sub_tree->Right);
memfree(sub_tree);
}
}