2009-07-11 10:20:33 +08:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// File: dict.h
|
|
|
|
// Description: dict class.
|
|
|
|
// Author: Samuel Charron
|
|
|
|
//
|
|
|
|
// (C) Copyright 2006, Google Inc.
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#ifndef TESSERACT_DICT_DICT_H_
|
|
|
|
#define TESSERACT_DICT_DICT_H_
|
|
|
|
|
|
|
|
#include "ambigs.h"
|
|
|
|
#include "dawg.h"
|
2013-09-23 23:26:50 +08:00
|
|
|
#include "dawg_cache.h"
|
2010-11-24 02:34:14 +08:00
|
|
|
#include "host.h"
|
2009-07-11 10:20:33 +08:00
|
|
|
#include "ratngs.h"
|
|
|
|
#include "stopper.h"
|
|
|
|
#include "trie.h"
|
|
|
|
#include "unicharset.h"
|
2013-09-23 23:26:50 +08:00
|
|
|
#include "params_training_featdef.h"
|
|
|
|
|
|
|
|
class MATRIX;
|
|
|
|
class WERD_RES;
|
2009-07-11 10:20:33 +08:00
|
|
|
|
2010-11-24 02:34:14 +08:00
|
|
|
#define MAX_WERD_LENGTH (inT64) 128
|
2009-07-11 10:20:33 +08:00
|
|
|
#define NO_RATING -1
|
|
|
|
|
2010-07-27 21:23:23 +08:00
|
|
|
/** Struct used to hold temporary information about fragments. */
|
2009-07-11 10:20:33 +08:00
|
|
|
struct CHAR_FRAGMENT_INFO {
|
|
|
|
UNICHAR_ID unichar_id;
|
|
|
|
const CHAR_FRAGMENT *fragment;
|
|
|
|
int num_fragments;
|
|
|
|
float rating;
|
|
|
|
float certainty;
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace tesseract {
|
|
|
|
|
|
|
|
typedef GenericVector<Dawg *> DawgVector;
|
|
|
|
|
2010-11-24 02:34:14 +08:00
|
|
|
//
|
|
|
|
// Constants
|
|
|
|
//
|
|
|
|
static const int kRatingPad = 4;
|
2013-09-23 23:26:50 +08:00
|
|
|
static const char kDictWildcard[] = "\u2606"; // WHITE STAR
|
|
|
|
static const int kDictMaxWildcards = 2; // max wildcards for a word
|
2010-11-24 02:34:14 +08:00
|
|
|
// TODO(daria): If hyphens are different in different languages and can be
|
|
|
|
// inferred from training data we should load their values dynamically.
|
|
|
|
static const char kHyphenSymbol[] = "-";
|
2013-09-23 23:26:50 +08:00
|
|
|
static const char kSlashSymbol[] = "/";
|
|
|
|
static const char kQuestionSymbol[] = "?";
|
|
|
|
static const char kApostropheSymbol[] = "'";
|
2010-11-24 02:34:14 +08:00
|
|
|
static const float kSimCertaintyScale = -10.0; // similarity matcher scaling
|
|
|
|
static const float kSimCertaintyOffset = -10.0; // similarity matcher offset
|
|
|
|
static const float kSimilarityFloor = 100.0; // worst E*L product to stop on
|
|
|
|
static const int kDocDictMaxRepChars = 4;
|
|
|
|
|
2013-09-23 23:26:50 +08:00
|
|
|
// Enum for describing whether the x-height for the word is consistent:
|
|
|
|
// 0 - everything is good.
|
|
|
|
// 1 - there are one or two secondary (but consistent) baselines
|
|
|
|
// [think subscript and superscript], or there is an oversized
|
|
|
|
// first character.
|
|
|
|
// 2 - the word is inconsistent.
|
|
|
|
enum XHeightConsistencyEnum {XH_GOOD, XH_SUBNORMAL, XH_INCONSISTENT};
|
|
|
|
|
2009-07-11 10:20:33 +08:00
|
|
|
struct DawgArgs {
|
2013-09-23 23:26:50 +08:00
|
|
|
DawgArgs(DawgPositionVector *d, DawgPositionVector *up, PermuterType p)
|
2016-11-08 07:38:07 +08:00
|
|
|
: active_dawgs(d), updated_dawgs(up), permuter(p), valid_end(false) {}
|
2010-11-24 02:34:14 +08:00
|
|
|
|
2013-09-23 23:26:50 +08:00
|
|
|
DawgPositionVector *active_dawgs;
|
|
|
|
DawgPositionVector *updated_dawgs;
|
|
|
|
PermuterType permuter;
|
2016-11-08 07:38:07 +08:00
|
|
|
// True if the current position is a valid word end.
|
|
|
|
bool valid_end;
|
2009-07-11 10:20:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class Dict {
|
|
|
|
public:
|
2014-01-10 01:31:29 +08:00
|
|
|
Dict(CCUtil* image_ptr);
|
2009-07-11 10:20:33 +08:00
|
|
|
~Dict();
|
2014-01-10 01:31:29 +08:00
|
|
|
const CCUtil* getCCUtil() const {
|
|
|
|
return ccutil_;
|
2012-02-02 10:56:18 +08:00
|
|
|
}
|
2014-01-10 01:31:29 +08:00
|
|
|
CCUtil* getCCUtil() {
|
|
|
|
return ccutil_;
|
2009-07-11 10:20:33 +08:00
|
|
|
}
|
2012-02-02 10:56:18 +08:00
|
|
|
const UNICHARSET& getUnicharset() const {
|
2014-01-10 01:31:29 +08:00
|
|
|
return getCCUtil()->unicharset;
|
2012-02-02 10:56:18 +08:00
|
|
|
}
|
2009-07-11 10:20:33 +08:00
|
|
|
UNICHARSET& getUnicharset() {
|
2014-01-10 01:31:29 +08:00
|
|
|
return getCCUtil()->unicharset;
|
2009-07-11 10:20:33 +08:00
|
|
|
}
|
2013-09-23 23:26:50 +08:00
|
|
|
const UnicharAmbigs &getUnicharAmbigs() const {
|
2014-01-10 01:31:29 +08:00
|
|
|
return getCCUtil()->unichar_ambigs;
|
2009-07-11 10:20:33 +08:00
|
|
|
}
|
|
|
|
|
2013-09-23 23:26:50 +08:00
|
|
|
// Returns true if unichar_id is a word compounding character like - or /.
|
2010-11-24 02:34:14 +08:00
|
|
|
inline bool compound_marker(UNICHAR_ID unichar_id) {
|
2013-09-23 23:26:50 +08:00
|
|
|
const GenericVector<UNICHAR_ID>& normed_ids =
|
|
|
|
getUnicharset().normed_ids(unichar_id);
|
|
|
|
return normed_ids.size() == 1 &&
|
|
|
|
(normed_ids[0] == hyphen_unichar_id_ ||
|
|
|
|
normed_ids[0] == slash_unichar_id_);
|
|
|
|
}
|
|
|
|
// Returns true if unichar_id is an apostrophe-like character that may
|
|
|
|
// separate prefix/suffix words from a main body word.
|
|
|
|
inline bool is_apostrophe(UNICHAR_ID unichar_id) {
|
|
|
|
const GenericVector<UNICHAR_ID>& normed_ids =
|
|
|
|
getUnicharset().normed_ids(unichar_id);
|
|
|
|
return normed_ids.size() == 1 && normed_ids[0] == apostrophe_unichar_id_;
|
2010-11-24 02:34:14 +08:00
|
|
|
}
|
|
|
|
|
2009-07-11 10:20:33 +08:00
|
|
|
/* hyphen.cpp ************************************************************/
|
|
|
|
|
2010-07-27 21:23:23 +08:00
|
|
|
/// Returns true if we've recorded the beginning of a hyphenated word.
|
2012-02-02 10:56:18 +08:00
|
|
|
inline bool hyphenated() const { return
|
2013-09-23 23:26:50 +08:00
|
|
|
!last_word_on_line_ && hyphen_word_;
|
2010-11-24 02:34:14 +08:00
|
|
|
}
|
2010-07-27 21:23:23 +08:00
|
|
|
/// Size of the base word (the part on the line before) of a hyphenated word.
|
2012-02-02 10:56:18 +08:00
|
|
|
inline int hyphen_base_size() const {
|
2009-07-11 10:20:33 +08:00
|
|
|
return this->hyphenated() ? hyphen_word_->length() : 0;
|
|
|
|
}
|
2010-07-27 21:23:23 +08:00
|
|
|
/// If this word is hyphenated copy the base word (the part on
|
|
|
|
/// the line before) of a hyphenated word into the given word.
|
|
|
|
/// This function assumes that word is not NULL.
|
2012-02-02 10:56:18 +08:00
|
|
|
inline void copy_hyphen_info(WERD_CHOICE *word) const {
|
2009-07-11 10:20:33 +08:00
|
|
|
if (this->hyphenated()) {
|
|
|
|
*word = *hyphen_word_;
|
|
|
|
if (hyphen_debug_level) word->print("copy_hyphen_info: ");
|
|
|
|
}
|
|
|
|
}
|
2010-07-27 21:23:23 +08:00
|
|
|
/// Check whether the word has a hyphen at the end.
|
2012-02-02 10:56:18 +08:00
|
|
|
inline bool has_hyphen_end(UNICHAR_ID unichar_id, bool first_pos) const {
|
2013-09-23 23:26:50 +08:00
|
|
|
if (!last_word_on_line_ || first_pos)
|
|
|
|
return false;
|
|
|
|
const GenericVector<UNICHAR_ID>& normed_ids =
|
|
|
|
getUnicharset().normed_ids(unichar_id);
|
|
|
|
return normed_ids.size() == 1 && normed_ids[0] == hyphen_unichar_id_;
|
2010-11-24 02:34:14 +08:00
|
|
|
}
|
|
|
|
/// Same as above, but check the unichar at the end of the word.
|
2012-02-02 10:56:18 +08:00
|
|
|
inline bool has_hyphen_end(const WERD_CHOICE &word) const {
|
2009-07-11 10:20:33 +08:00
|
|
|
int word_index = word.length() - 1;
|
2010-11-24 02:34:14 +08:00
|
|
|
return has_hyphen_end(word.unichar_id(word_index), word_index == 0);
|
2009-07-11 10:20:33 +08:00
|
|
|
}
|
2010-07-27 21:23:23 +08:00
|
|
|
/// Unless the previous word was the last one on the line, and the current
|
|
|
|
/// one is not (thus it is the first one on the line), erase hyphen_word_,
|
2013-09-23 23:26:50 +08:00
|
|
|
/// clear hyphen_active_dawgs_, update last_word_on_line_.
|
2009-07-11 10:20:33 +08:00
|
|
|
void reset_hyphen_vars(bool last_word_on_line);
|
2013-09-23 23:26:50 +08:00
|
|
|
/// Update hyphen_word_, and copy the given DawgPositionVectors into
|
|
|
|
/// hyphen_active_dawgs_ .
|
2009-07-11 10:20:33 +08:00
|
|
|
void set_hyphen_word(const WERD_CHOICE &word,
|
2013-09-23 23:26:50 +08:00
|
|
|
const DawgPositionVector &active_dawgs);
|
2009-07-11 10:20:33 +08:00
|
|
|
|
|
|
|
/* permdawg.cpp ************************************************************/
|
2013-09-23 23:26:50 +08:00
|
|
|
// Note: Functions in permdawg.cpp are only used by NoDangerousAmbig().
|
|
|
|
// When this function is refactored, permdawg.cpp can be removed.
|
|
|
|
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Copies word into best_choice if its rating is smaller
|
|
|
|
/// than that of best_choice.
|
|
|
|
inline void update_best_choice(const WERD_CHOICE &word,
|
|
|
|
WERD_CHOICE *best_choice) {
|
2013-09-23 23:26:50 +08:00
|
|
|
if (word.rating() < best_choice->rating()) {
|
|
|
|
*best_choice = word;
|
|
|
|
}
|
2009-07-11 10:20:33 +08:00
|
|
|
}
|
2010-07-27 21:23:23 +08:00
|
|
|
/// Fill the given active_dawgs vector with dawgs that could contain the
|
|
|
|
/// beginning of the word. If hyphenated() returns true, copy the entries
|
|
|
|
/// from hyphen_active_dawgs_ instead.
|
2013-09-23 23:26:50 +08:00
|
|
|
void init_active_dawgs(DawgPositionVector *active_dawgs,
|
2012-02-02 10:56:18 +08:00
|
|
|
bool ambigs_mode) const;
|
2013-09-23 23:26:50 +08:00
|
|
|
// Fill the given vector with the default collection of any-length dawgs
|
|
|
|
void default_dawgs(DawgPositionVector *anylength_dawgs,
|
|
|
|
bool suppress_patterns) const;
|
|
|
|
|
|
|
|
|
2010-07-27 21:23:23 +08:00
|
|
|
/// Recursively explore all the possible character combinations in
|
|
|
|
/// the given char_choices. Use go_deeper_dawg_fxn() to explore all the
|
|
|
|
/// dawgs in the dawgs_ vector in parallel and discard invalid words.
|
|
|
|
///
|
|
|
|
/// Allocate and return a WERD_CHOICE with the best valid word found.
|
2009-07-11 10:20:33 +08:00
|
|
|
WERD_CHOICE *dawg_permute_and_select(
|
2013-09-23 23:26:50 +08:00
|
|
|
const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit);
|
2010-07-27 21:23:23 +08:00
|
|
|
/// If the choice being composed so far could be a dictionary word
|
|
|
|
/// and we have not reached the end of the word keep exploring the
|
|
|
|
/// char_choices further.
|
2009-07-11 10:20:33 +08:00
|
|
|
void go_deeper_dawg_fxn(
|
|
|
|
const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
|
|
|
int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
|
|
|
bool word_ending, WERD_CHOICE *word, float certainties[],
|
2010-11-24 02:34:14 +08:00
|
|
|
float *limit, WERD_CHOICE *best_choice, int *attempts_left,
|
|
|
|
void *void_more_args);
|
2009-07-11 10:20:33 +08:00
|
|
|
|
2013-09-23 23:26:50 +08:00
|
|
|
/// Pointer to go_deeper function.
|
|
|
|
void (Dict::*go_deeper_fxn_)(const char *debug,
|
|
|
|
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
|
|
|
int char_choice_index,
|
|
|
|
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
|
|
|
bool word_ending, WERD_CHOICE *word,
|
|
|
|
float certainties[], float *limit,
|
|
|
|
WERD_CHOICE *best_choice, int *attempts_left,
|
|
|
|
void *void_more_args);
|
|
|
|
//
|
|
|
|
// Helper functions for dawg_permute_and_select().
|
|
|
|
//
|
2009-07-11 10:20:33 +08:00
|
|
|
void permute_choices(
|
|
|
|
const char *debug,
|
|
|
|
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
|
|
|
int char_choice_index,
|
|
|
|
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
|
|
|
WERD_CHOICE *word,
|
|
|
|
float certainties[],
|
|
|
|
float *limit,
|
|
|
|
WERD_CHOICE *best_choice,
|
2010-11-24 02:34:14 +08:00
|
|
|
int *attempts_left,
|
2009-07-11 10:20:33 +08:00
|
|
|
void *more_args);
|
|
|
|
|
|
|
|
void append_choices(
|
|
|
|
const char *debug,
|
|
|
|
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
|
|
|
const BLOB_CHOICE &blob_choice,
|
|
|
|
int char_choice_index,
|
|
|
|
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
|
|
|
WERD_CHOICE *word,
|
|
|
|
float certainties[],
|
|
|
|
float *limit,
|
|
|
|
WERD_CHOICE *best_choice,
|
2010-11-24 02:34:14 +08:00
|
|
|
int *attempts_left,
|
2009-07-11 10:20:33 +08:00
|
|
|
void *more_args);
|
2013-09-23 23:26:50 +08:00
|
|
|
|
|
|
|
bool fragment_state_okay(UNICHAR_ID curr_unichar_id,
|
|
|
|
float curr_rating, float curr_certainty,
|
|
|
|
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
|
|
|
const char *debug, int word_ending,
|
|
|
|
CHAR_FRAGMENT_INFO *char_frag_info);
|
|
|
|
|
2009-07-11 10:20:33 +08:00
|
|
|
/* stopper.cpp *************************************************************/
|
2010-11-24 02:34:14 +08:00
|
|
|
bool NoDangerousAmbig(WERD_CHOICE *BestChoice,
|
|
|
|
DANGERR *fixpt,
|
|
|
|
bool fix_replaceable,
|
2013-09-23 23:26:50 +08:00
|
|
|
MATRIX* ratings);
|
|
|
|
// Replaces the corresponding wrong ngram in werd_choice with the correct
|
|
|
|
// one. The whole correct n-gram is inserted into the ratings matrix and
|
|
|
|
// the werd_choice: no more fragments!. Rating and certainty of new entries
|
|
|
|
// in matrix and werd_choice are the sum and mean of the wrong ngram
|
|
|
|
// respectively.
|
|
|
|
// E.g. for werd_choice mystring'' and ambiguity ''->": werd_choice becomes
|
|
|
|
// mystring", with a new entry in the ratings matrix for ".
|
2009-07-11 10:20:33 +08:00
|
|
|
void ReplaceAmbig(int wrong_ngram_begin_index, int wrong_ngram_size,
|
|
|
|
UNICHAR_ID correct_ngram_id, WERD_CHOICE *werd_choice,
|
2013-09-23 23:26:50 +08:00
|
|
|
MATRIX *ratings);
|
2009-07-11 10:20:33 +08:00
|
|
|
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Returns the length of the shortest alpha run in WordChoice.
|
2009-07-11 10:20:33 +08:00
|
|
|
int LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice);
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Returns true if the certainty of the BestChoice word is within a
|
|
|
|
/// reasonable range of the average certainties for the best choices for
|
|
|
|
/// each character in the segmentation. This test is used to catch words
|
|
|
|
/// in which one character is much worse than the other characters in the
|
|
|
|
/// word (i.e. false will be returned in that case). The algorithm computes
|
|
|
|
/// the mean and std deviation of the certainties in the word with the worst
|
|
|
|
/// certainty thrown out.
|
2013-09-23 23:26:50 +08:00
|
|
|
int UniformCertainties(const WERD_CHOICE& word);
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Returns true if the given best_choice is good enough to stop.
|
2013-09-23 23:26:50 +08:00
|
|
|
bool AcceptableChoice(const WERD_CHOICE& best_choice,
|
|
|
|
XHeightConsistencyEnum xheight_consistency);
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Returns false if the best choice for the current word is questionable
|
|
|
|
/// and should be tried again on the second pass or should be flagged to
|
|
|
|
/// the user.
|
2013-09-23 23:26:50 +08:00
|
|
|
bool AcceptableResult(WERD_RES* word);
|
2009-07-11 10:20:33 +08:00
|
|
|
void EndDangerousAmbigs();
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Prints the current choices for this word to stdout.
|
2009-07-11 10:20:33 +08:00
|
|
|
void DebugWordChoices();
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Sets up stopper variables in preparation for the first pass.
|
2009-07-11 10:20:33 +08:00
|
|
|
void SettupStopperPass1();
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Sets up stopper variables in preparation for the second pass.
|
2009-07-11 10:20:33 +08:00
|
|
|
void SettupStopperPass2();
|
2010-11-24 02:34:14 +08:00
|
|
|
/* context.cpp *************************************************************/
|
|
|
|
/// Check a string to see if it matches a set of lexical rules.
|
|
|
|
int case_ok(const WERD_CHOICE &word, const UNICHARSET &unicharset);
|
|
|
|
/// Returns true if the word looks like an absolute garbage
|
|
|
|
/// (e.g. image mistakenly recognized as text).
|
|
|
|
bool absolute_garbage(const WERD_CHOICE &word, const UNICHARSET &unicharset);
|
|
|
|
|
|
|
|
/* dict.cpp ****************************************************************/
|
|
|
|
|
|
|
|
/// Initialize Dict class - load dawgs from [lang].traineddata and
|
|
|
|
/// user-specified wordlist and parttern list.
|
2013-09-23 23:26:50 +08:00
|
|
|
static DawgCache *GlobalDawgCache();
|
2016-11-08 07:38:07 +08:00
|
|
|
// Sets up ready for a Load or LoadLSTM.
|
|
|
|
void SetupForLoad(DawgCache *dawg_cache);
|
|
|
|
// Loads the dawgs needed by Tesseract. Call FinishLoad() after.
|
|
|
|
void Load(const char *data_file_name, const STRING &lang);
|
|
|
|
// Loads the dawgs needed by the LSTM model. Call FinishLoad() after.
|
|
|
|
void LoadLSTM(const char *data_file_name, const STRING &lang);
|
|
|
|
// Completes the loading process after Load() and/or LoadLSTM().
|
|
|
|
// Returns false if no dictionaries were loaded.
|
|
|
|
bool FinishLoad();
|
2010-11-24 02:34:14 +08:00
|
|
|
void End();
|
2009-07-11 10:20:33 +08:00
|
|
|
|
2011-03-22 05:46:35 +08:00
|
|
|
// Resets the document dictionary analogous to ResetAdaptiveClassifier.
|
|
|
|
void ResetDocumentDictionary() {
|
|
|
|
if (pending_words_ != NULL)
|
|
|
|
pending_words_->clear();
|
|
|
|
if (document_words_ != NULL)
|
|
|
|
document_words_->clear();
|
|
|
|
}
|
|
|
|
|
2010-07-27 21:23:23 +08:00
|
|
|
/**
|
|
|
|
* Returns the maximal permuter code (from ccstruct/ratngs.h) if in light
|
|
|
|
* of the current state the letter at word_index in the given word
|
|
|
|
* is allowed according to at least one of the dawgs in dawgs_,
|
|
|
|
* otherwise returns NO_PERM.
|
|
|
|
*
|
|
|
|
* The state is described by void_dawg_args, which are interpreted as
|
2013-09-23 23:26:50 +08:00
|
|
|
* DawgArgs and contain relevant active dawg positions.
|
|
|
|
* Each entry in the active_dawgs vector contains an index
|
2010-07-27 21:23:23 +08:00
|
|
|
* into the dawgs_ vector and an EDGE_REF that indicates the last edge
|
2013-09-23 23:26:50 +08:00
|
|
|
* followed in the dawg. It also may contain a position in the punctuation
|
|
|
|
* dawg which describes surrounding punctuation (see struct DawgPosition).
|
2010-07-27 21:23:23 +08:00
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* At word_index 0 dawg_args->active_dawgs should contain an entry for each
|
2013-09-23 23:26:50 +08:00
|
|
|
* dawg that may start at the beginning of a word, with punc_ref and edge_ref
|
|
|
|
* initialized to NO_EDGE. Since the punctuation dawg includes the empty
|
|
|
|
* pattern " " (meaning anything without surrounding punctuation), having a
|
|
|
|
* single entry for the punctuation dawg will cover all dawgs reachable
|
|
|
|
* therefrom -- that includes all number and word dawgs. The only dawg
|
|
|
|
* non-reachable from the punctuation_dawg is the pattern dawg.
|
|
|
|
* If hyphen state needs to be applied, initial dawg_args->active_dawgs can
|
|
|
|
* be copied from the saved hyphen state (maintained by Dict).
|
|
|
|
* For word_index > 0 the corresponding state (active_dawgs and punc position)
|
|
|
|
* can be obtained from dawg_args->updated_dawgs passed to
|
|
|
|
* def_letter_is_okay for word_index-1.
|
|
|
|
* Note: the function assumes that active_dawgs, nd updated_dawgs
|
2010-07-27 21:23:23 +08:00
|
|
|
* member variables of dawg_args are not NULL.
|
|
|
|
*
|
|
|
|
* Output:
|
2013-09-23 23:26:50 +08:00
|
|
|
* The function fills in dawg_args->updated_dawgs vector with the
|
2010-07-27 21:23:23 +08:00
|
|
|
* entries for dawgs that contain the word up to the letter at word_index.
|
|
|
|
*
|
|
|
|
*/
|
2009-07-11 10:20:33 +08:00
|
|
|
|
2010-11-24 02:34:14 +08:00
|
|
|
//
|
|
|
|
int def_letter_is_okay(void* void_dawg_args,
|
2012-02-02 10:56:18 +08:00
|
|
|
UNICHAR_ID unichar_id, bool word_end) const;
|
2010-11-24 02:34:14 +08:00
|
|
|
|
|
|
|
int (Dict::*letter_is_okay_)(void* void_dawg_args,
|
2012-02-02 10:56:18 +08:00
|
|
|
UNICHAR_ID unichar_id, bool word_end) const;
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Calls letter_is_okay_ member function.
|
|
|
|
int LetterIsOkay(void* void_dawg_args,
|
2012-02-02 10:56:18 +08:00
|
|
|
UNICHAR_ID unichar_id, bool word_end) const {
|
2010-11-24 02:34:14 +08:00
|
|
|
return (this->*letter_is_okay_)(void_dawg_args, unichar_id, word_end);
|
|
|
|
}
|
|
|
|
|
2009-07-11 10:20:33 +08:00
|
|
|
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Probability in context function used by the ngram permuter.
|
2011-03-19 05:53:35 +08:00
|
|
|
double (Dict::*probability_in_context_)(const char* lang,
|
|
|
|
const char* context,
|
2010-11-24 02:34:14 +08:00
|
|
|
int context_bytes,
|
|
|
|
const char* character,
|
|
|
|
int character_bytes);
|
|
|
|
/// Calls probability_in_context_ member function.
|
|
|
|
double ProbabilityInContext(const char* context,
|
|
|
|
int context_bytes,
|
|
|
|
const char* character,
|
|
|
|
int character_bytes) {
|
2011-03-19 05:53:35 +08:00
|
|
|
return (this->*probability_in_context_)(
|
2014-01-10 01:31:29 +08:00
|
|
|
getCCUtil()->lang.string(),
|
2011-03-19 05:53:35 +08:00
|
|
|
context, context_bytes,
|
|
|
|
character, character_bytes);
|
2010-11-24 02:34:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Default (no-op) implementation of probability in context function.
|
|
|
|
double def_probability_in_context(
|
2011-03-19 05:53:35 +08:00
|
|
|
const char* lang, const char* context, int context_bytes,
|
2010-11-24 02:34:14 +08:00
|
|
|
const char* character, int character_bytes) {
|
2016-11-13 00:43:22 +08:00
|
|
|
(void) lang;
|
2010-11-24 02:34:14 +08:00
|
|
|
(void) context;
|
|
|
|
(void) context_bytes;
|
|
|
|
(void) character;
|
|
|
|
(void) character_bytes;
|
|
|
|
return 0.0;
|
|
|
|
}
|
2011-03-19 05:53:35 +08:00
|
|
|
double ngram_probability_in_context(const char* lang,
|
|
|
|
const char* context,
|
2010-11-24 02:34:14 +08:00
|
|
|
int context_bytes,
|
|
|
|
const char* character,
|
|
|
|
int character_bytes);
|
|
|
|
|
2013-09-23 23:26:50 +08:00
|
|
|
// Interface with params model.
|
|
|
|
float (Dict::*params_model_classify_)(const char *lang, void *path);
|
|
|
|
float ParamsModelClassify(const char *lang, void *path);
|
|
|
|
// Call params_model_classify_ member function.
|
|
|
|
float CallParamsModelClassify(void *path) {
|
|
|
|
ASSERT_HOST(params_model_classify_ != NULL); // ASSERT_HOST -> assert
|
|
|
|
return (this->*params_model_classify_)(
|
2014-01-10 01:31:29 +08:00
|
|
|
getCCUtil()->lang.string(), path);
|
2013-09-23 23:26:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void SetWildcardID(UNICHAR_ID id) { wildcard_unichar_id_ = id; }
|
2016-11-08 07:38:07 +08:00
|
|
|
inline UNICHAR_ID WildcardID() const { return wildcard_unichar_id_; }
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Return the number of dawgs in the dawgs_ vector.
|
2015-11-05 05:34:22 +08:00
|
|
|
inline int NumDawgs() const { return dawgs_.size(); }
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Return i-th dawg pointer recorded in the dawgs_ vector.
|
2009-07-11 10:20:33 +08:00
|
|
|
inline const Dawg *GetDawg(int index) const { return dawgs_[index]; }
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Return the points to the punctuation dawg.
|
|
|
|
inline const Dawg *GetPuncDawg() const { return punc_dawg_; }
|
2012-02-02 10:56:18 +08:00
|
|
|
/// Return the points to the unambiguous words dawg.
|
|
|
|
inline const Dawg *GetUnambigDawg() const { return unambig_dawg_; }
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Returns the appropriate next node given the EDGE_REF.
|
|
|
|
static inline NODE_REF GetStartingNode(const Dawg *dawg, EDGE_REF edge_ref) {
|
|
|
|
if (edge_ref == NO_EDGE) return 0; // beginning to explore the dawg
|
|
|
|
NODE_REF node = dawg->next_node(edge_ref);
|
|
|
|
if (node == 0) node = NO_EDGE; // end of word
|
|
|
|
return node;
|
|
|
|
}
|
2013-09-23 23:26:50 +08:00
|
|
|
|
|
|
|
// Given a unichar from a string and a given dawg, return the unichar
|
|
|
|
// we should use to match in that dawg type. (for example, in the number
|
|
|
|
// dawg, all numbers are transformed to kPatternUnicharId).
|
|
|
|
inline UNICHAR_ID char_for_dawg(UNICHAR_ID ch, const Dawg *dawg) const {
|
|
|
|
if (!dawg) return ch;
|
|
|
|
switch (dawg->type()) {
|
|
|
|
case DAWG_TYPE_NUMBER:
|
|
|
|
return getUnicharset().get_isdigit(ch) ? Dawg::kPatternUnicharID : ch;
|
|
|
|
default:
|
|
|
|
return ch;
|
2009-07-11 10:20:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-24 02:34:14 +08:00
|
|
|
/// For each of the character classes of the given unichar_id (and the
|
|
|
|
/// unichar_id itself) finds the corresponding outgoing node or self-loop
|
|
|
|
/// in the given dawg and (after checking that it is valid) records it in
|
|
|
|
/// dawg_args->updated_ative_dawgs. Updates current_permuter if any valid
|
|
|
|
/// edges were found.
|
2013-09-23 23:26:50 +08:00
|
|
|
void ProcessPatternEdges(const Dawg *dawg, const DawgPosition &info,
|
2010-11-24 02:34:14 +08:00
|
|
|
UNICHAR_ID unichar_id, bool word_end,
|
2016-11-08 07:38:07 +08:00
|
|
|
DawgArgs *dawg_args,
|
2012-02-02 10:56:18 +08:00
|
|
|
PermuterType *current_permuter) const;
|
2010-11-24 02:34:14 +08:00
|
|
|
|
|
|
|
/// Read/Write/Access special purpose dawgs which contain words
|
|
|
|
/// only of a certain length (used for phrase search for
|
|
|
|
/// non-space-delimited languages).
|
|
|
|
|
|
|
|
/// Check all the DAWGs to see if this word is in any of them.
|
2012-02-02 10:56:18 +08:00
|
|
|
inline static bool valid_word_permuter(uinT8 perm, bool numbers_ok) {
|
2010-11-24 02:34:14 +08:00
|
|
|
return (perm == SYSTEM_DAWG_PERM || perm == FREQ_DAWG_PERM ||
|
|
|
|
perm == DOC_DAWG_PERM || perm == USER_DAWG_PERM ||
|
2013-09-23 23:26:50 +08:00
|
|
|
perm == USER_PATTERN_PERM || perm == COMPOUND_PERM ||
|
|
|
|
(numbers_ok && perm == NUMBER_PERM));
|
2010-11-24 02:34:14 +08:00
|
|
|
}
|
2012-02-02 10:56:18 +08:00
|
|
|
int valid_word(const WERD_CHOICE &word, bool numbers_ok) const;
|
|
|
|
int valid_word(const WERD_CHOICE &word) const {
|
2010-11-24 02:34:14 +08:00
|
|
|
return valid_word(word, false); // return NO_PERM for words with digits
|
|
|
|
}
|
2012-02-02 10:56:18 +08:00
|
|
|
int valid_word_or_number(const WERD_CHOICE &word) const {
|
2010-11-24 02:34:14 +08:00
|
|
|
return valid_word(word, true); // return NUMBER_PERM for valid numbers
|
|
|
|
}
|
|
|
|
/// This function is used by api/tesseract_cube_combiner.cpp
|
2012-02-02 10:56:18 +08:00
|
|
|
int valid_word(const char *string) const {
|
2010-11-24 02:34:14 +08:00
|
|
|
WERD_CHOICE word(string, getUnicharset());
|
|
|
|
return valid_word(word);
|
|
|
|
}
|
2012-02-02 10:56:18 +08:00
|
|
|
// Do the two WERD_CHOICEs form a meaningful bigram?
|
|
|
|
bool valid_bigram(const WERD_CHOICE &word1, const WERD_CHOICE &word2) const;
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Returns true if the word contains a valid punctuation pattern.
|
|
|
|
/// Note: Since the domains of punctuation symbols and symblos
|
|
|
|
/// used in numbers are not disjoint, a valid number might contain
|
|
|
|
/// an invalid punctuation pattern (e.g. .99).
|
|
|
|
bool valid_punctuation(const WERD_CHOICE &word);
|
|
|
|
/// Returns true if a good answer is found for the unknown blob rating.
|
|
|
|
int good_choice(const WERD_CHOICE &choice);
|
|
|
|
/// Adds a word found on this document to the document specific dictionary.
|
|
|
|
void add_document_word(const WERD_CHOICE &best_choice);
|
|
|
|
/// Adjusts the rating of the given word.
|
2013-09-23 23:26:50 +08:00
|
|
|
void adjust_word(WERD_CHOICE *word,
|
|
|
|
bool nonword, XHeightConsistencyEnum xheight_consistency,
|
|
|
|
float additional_adjust,
|
|
|
|
bool modify_rating,
|
|
|
|
bool debug);
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Set wordseg_rating_adjust_factor_ to the given value.
|
|
|
|
inline void SetWordsegRatingAdjustFactor(float f) {
|
|
|
|
wordseg_rating_adjust_factor_ = f;
|
|
|
|
}
|
2016-11-08 07:38:07 +08:00
|
|
|
/// Returns true if the language is space-delimited (not CJ, or T).
|
|
|
|
bool IsSpaceDelimitedLang() const;
|
2009-07-11 10:20:33 +08:00
|
|
|
|
|
|
|
private:
|
2010-07-27 21:23:23 +08:00
|
|
|
/** Private member variables. */
|
2014-01-10 01:31:29 +08:00
|
|
|
CCUtil* ccutil_;
|
2010-07-27 21:23:23 +08:00
|
|
|
/**
|
|
|
|
* Table that stores ambiguities computed during training
|
|
|
|
* (loaded when NoDangerousAmbigs() is called for the first time).
|
|
|
|
* Each entry i in the table stores a set of amibiguities whose
|
|
|
|
* wrong ngram starts with unichar id i.
|
|
|
|
*/
|
2009-07-11 10:20:33 +08:00
|
|
|
UnicharAmbigs *dang_ambigs_table_;
|
2010-07-27 21:23:23 +08:00
|
|
|
/** Same as above, but for ambiguities with replace flag set. */
|
2009-07-11 10:20:33 +08:00
|
|
|
UnicharAmbigs *replace_ambigs_table_;
|
2010-07-27 21:23:23 +08:00
|
|
|
/** Additional certainty padding allowed before a word is rejected. */
|
2009-07-11 10:20:33 +08:00
|
|
|
FLOAT32 reject_offset_;
|
2013-09-23 23:26:50 +08:00
|
|
|
// Cached UNICHAR_IDs:
|
|
|
|
UNICHAR_ID wildcard_unichar_id_; // kDictWildcard.
|
|
|
|
UNICHAR_ID apostrophe_unichar_id_; // kApostropheSymbol.
|
|
|
|
UNICHAR_ID question_unichar_id_; // kQuestionSymbol.
|
|
|
|
UNICHAR_ID slash_unichar_id_; // kSlashSymbol.
|
|
|
|
UNICHAR_ID hyphen_unichar_id_; // kHyphenSymbol.
|
2009-07-11 10:20:33 +08:00
|
|
|
// Hyphen-related variables.
|
|
|
|
WERD_CHOICE *hyphen_word_;
|
2013-09-23 23:26:50 +08:00
|
|
|
DawgPositionVector hyphen_active_dawgs_;
|
2009-07-11 10:20:33 +08:00
|
|
|
bool last_word_on_line_;
|
2012-02-02 10:56:18 +08:00
|
|
|
// List of lists of "equivalent" UNICHAR_IDs for the purposes of dictionary
|
|
|
|
// matching. The first member of each list is taken as canonical. For
|
|
|
|
// example, the first list contains hyphens and dashes with the first symbol
|
|
|
|
// being the ASCII hyphen minus.
|
|
|
|
GenericVector<GenericVectorEqEq<UNICHAR_ID> > equivalent_symbols_;
|
2013-09-23 23:26:50 +08:00
|
|
|
// Dawg Cache reference - this is who we ask to allocate/deallocate dawgs.
|
|
|
|
DawgCache *dawg_cache_;
|
|
|
|
bool dawg_cache_is_ours_; // we should delete our own dawg_cache_
|
2009-07-11 10:20:33 +08:00
|
|
|
// Dawgs.
|
|
|
|
DawgVector dawgs_;
|
|
|
|
SuccessorListsVector successors_;
|
|
|
|
Trie *pending_words_;
|
2012-02-02 10:56:18 +08:00
|
|
|
// bigram_dawg_ points to a dawg of two-word bigrams which always supercede if
|
|
|
|
// any of them are present on the best choices list for a word pair.
|
|
|
|
// the bigrams are stored as space-separated words where:
|
|
|
|
// (1) leading and trailing punctuation has been removed from each word and
|
|
|
|
// (2) any digits have been replaced with '?' marks.
|
|
|
|
Dawg *bigram_dawg_;
|
2010-07-27 21:23:23 +08:00
|
|
|
/// The following pointers are only cached for convenience.
|
|
|
|
/// The dawgs will be deleted when dawgs_ vector is destroyed.
|
2009-07-11 10:20:33 +08:00
|
|
|
// TODO(daria): need to support multiple languages in the future,
|
|
|
|
// so maybe will need to maintain a list of dawgs of each kind.
|
2010-11-24 02:34:14 +08:00
|
|
|
Dawg *freq_dawg_;
|
2012-02-02 10:56:18 +08:00
|
|
|
Dawg *unambig_dawg_;
|
2010-11-24 02:34:14 +08:00
|
|
|
Dawg *punc_dawg_;
|
2009-07-11 10:20:33 +08:00
|
|
|
Trie *document_words_;
|
2010-11-24 02:34:14 +08:00
|
|
|
/// Current segmentation cost adjust factor for word rating.
|
|
|
|
/// See comments in incorporate_segcost.
|
|
|
|
float wordseg_rating_adjust_factor_;
|
2012-02-02 10:56:18 +08:00
|
|
|
// File for recording ambiguities discovered during dictionary search.
|
|
|
|
FILE *output_ambig_words_file_;
|
2010-11-24 02:34:14 +08:00
|
|
|
|
|
|
|
public:
|
|
|
|
/// Variable members.
|
|
|
|
/// These have to be declared and initialized after image_ptr_, which contains
|
|
|
|
/// the pointer to the params vector - the member of its base CCUtil class.
|
2014-05-05 05:11:00 +08:00
|
|
|
STRING_VAR_H(user_words_file, "", "A filename of user-provided words.");
|
|
|
|
STRING_VAR_H(user_words_suffix, "",
|
|
|
|
"A suffix of user-provided words located in tessdata.");
|
|
|
|
STRING_VAR_H(user_patterns_file, "",
|
|
|
|
"A filename of user-provided patterns.");
|
2010-11-24 02:34:14 +08:00
|
|
|
STRING_VAR_H(user_patterns_suffix, "",
|
2014-05-05 05:11:00 +08:00
|
|
|
"A suffix of user-provided patterns located in tessdata.");
|
2010-11-24 02:34:14 +08:00
|
|
|
BOOL_VAR_H(load_system_dawg, true, "Load system word dawg.");
|
|
|
|
BOOL_VAR_H(load_freq_dawg, true, "Load frequent word dawg.");
|
2012-02-02 10:56:18 +08:00
|
|
|
BOOL_VAR_H(load_unambig_dawg, true, "Load unambiguous word dawg.");
|
2010-11-24 02:34:14 +08:00
|
|
|
BOOL_VAR_H(load_punc_dawg, true,
|
|
|
|
"Load dawg with punctuation patterns.");
|
|
|
|
BOOL_VAR_H(load_number_dawg, true, "Load dawg with number patterns.");
|
2013-09-23 23:26:50 +08:00
|
|
|
BOOL_VAR_H(load_bigram_dawg, true,
|
2012-02-02 10:56:18 +08:00
|
|
|
"Load dawg with special word bigrams.");
|
2013-09-23 23:26:50 +08:00
|
|
|
double_VAR_H(xheight_penalty_subscripts, 0.125,
|
|
|
|
"Score penalty (0.1 = 10%) added if there are subscripts "
|
|
|
|
"or superscripts in a word, but it is otherwise OK.");
|
|
|
|
double_VAR_H(xheight_penalty_inconsistent, 0.25,
|
|
|
|
"Score penalty (0.1 = 10%) added if an xheight is "
|
|
|
|
"inconsistent.");
|
2010-11-24 02:34:14 +08:00
|
|
|
double_VAR_H(segment_penalty_dict_frequent_word, 1.0,
|
|
|
|
"Score multiplier for word matches which have good case and"
|
|
|
|
"are frequent in the given language (lower is better).");
|
|
|
|
|
|
|
|
double_VAR_H(segment_penalty_dict_case_ok, 1.1,
|
|
|
|
"Score multiplier for word matches that have good case "
|
|
|
|
"(lower is better).");
|
|
|
|
|
|
|
|
double_VAR_H(segment_penalty_dict_case_bad, 1.3125,
|
|
|
|
"Default score multiplier for word matches, which may have "
|
|
|
|
"case issues (lower is better).");
|
|
|
|
|
|
|
|
// TODO(daria): remove this param when ngram permuter is deprecated.
|
|
|
|
double_VAR_H(segment_penalty_ngram_best_choice, 1.24,
|
|
|
|
"Multipler to for the best choice from the ngram model.");
|
|
|
|
|
|
|
|
double_VAR_H(segment_penalty_dict_nonword, 1.25,
|
|
|
|
"Score multiplier for glyph fragment segmentations which "
|
|
|
|
"do not match a dictionary word (lower is better).");
|
|
|
|
|
|
|
|
double_VAR_H(segment_penalty_garbage, 1.50,
|
|
|
|
"Score multiplier for poorly cased strings that are not in"
|
|
|
|
" the dictionary and generally look like garbage (lower is"
|
|
|
|
" better).");
|
2012-02-02 10:56:18 +08:00
|
|
|
STRING_VAR_H(output_ambig_words_file, "",
|
|
|
|
"Output file for ambiguities found in the dictionary");
|
2010-11-24 02:34:14 +08:00
|
|
|
INT_VAR_H(dawg_debug_level, 0, "Set to 1 for general debug info"
|
|
|
|
", to 2 for more details, to 3 to see all the debug messages");
|
|
|
|
INT_VAR_H(hyphen_debug_level, 0, "Debug level for hyphenated words.");
|
|
|
|
INT_VAR_H(max_viterbi_list_size, 10, "Maximum size of viterbi list.");
|
|
|
|
BOOL_VAR_H(use_only_first_uft8_step, false,
|
|
|
|
"Use only the first UTF8 step of the given string"
|
|
|
|
" when computing log probabilities.");
|
|
|
|
double_VAR_H(certainty_scale, 20.0, "Certainty scaling factor");
|
|
|
|
double_VAR_H(stopper_nondict_certainty_base, -2.50,
|
|
|
|
"Certainty threshold for non-dict words");
|
|
|
|
double_VAR_H(stopper_phase2_certainty_rejection_offset, 1.0,
|
|
|
|
"Reject certainty offset");
|
|
|
|
INT_VAR_H(stopper_smallword_size, 2,
|
|
|
|
"Size of dict word to be treated as non-dict word");
|
|
|
|
double_VAR_H(stopper_certainty_per_char, -0.50,
|
|
|
|
"Certainty to add for each dict char above small word size.");
|
|
|
|
double_VAR_H(stopper_allowable_character_badness, 3.0,
|
|
|
|
"Max certaintly variation allowed in a word (in sigma)");
|
|
|
|
INT_VAR_H(stopper_debug_level, 0, "Stopper debug level");
|
|
|
|
BOOL_VAR_H(stopper_no_acceptable_choices, false,
|
|
|
|
"Make AcceptableChoice() always return false. Useful"
|
|
|
|
" when there is a need to explore all segmentations");
|
2013-11-09 04:30:56 +08:00
|
|
|
BOOL_VAR_H(save_raw_choices, false,
|
2015-09-15 04:16:42 +08:00
|
|
|
"Deprecated- backward compatibility only");
|
2010-11-24 02:34:14 +08:00
|
|
|
INT_VAR_H(tessedit_truncate_wordchoice_log, 10, "Max words to keep in list");
|
|
|
|
STRING_VAR_H(word_to_debug, "", "Word for which stopper debug information"
|
|
|
|
" should be printed to stdout");
|
|
|
|
STRING_VAR_H(word_to_debug_lengths, "",
|
|
|
|
"Lengths of unichars in word_to_debug");
|
|
|
|
INT_VAR_H(fragments_debug, 0, "Debug character fragments");
|
2012-02-02 10:56:18 +08:00
|
|
|
BOOL_VAR_H(segment_nonalphabetic_script, false,
|
|
|
|
"Don't use any alphabetic-specific tricks."
|
|
|
|
"Set to true in the traineddata config file for"
|
|
|
|
" scripts that are cursive or inherently fixed-pitch");
|
2010-11-24 02:34:14 +08:00
|
|
|
BOOL_VAR_H(save_doc_words, 0, "Save Document Words");
|
|
|
|
double_VAR_H(doc_dict_pending_threshold, 0.0,
|
|
|
|
"Worst certainty for using pending dictionary");
|
|
|
|
double_VAR_H(doc_dict_certainty_threshold, -2.25, "Worst certainty"
|
|
|
|
" for words that can be inserted into the document dictionary");
|
|
|
|
INT_VAR_H(max_permuter_attempts, 10000, "Maximum number of different"
|
2013-09-23 23:26:50 +08:00
|
|
|
" character choices to consider during permutation."
|
|
|
|
" This limit is especially useful when user patterns"
|
|
|
|
" are specified, since overly generic patterns can result in"
|
|
|
|
" dawg search exploring an overly large number of options.");
|
2009-07-11 10:20:33 +08:00
|
|
|
};
|
|
|
|
} // namespace tesseract
|
|
|
|
|
|
|
|
#endif // THIRD_PARTY_TESSERACT_DICT_DICT_H_
|