/src/tesseract/src/dict/dict.h
Line | Count | Source |
1 | | /////////////////////////////////////////////////////////////////////// |
2 | | // File: dict.h |
3 | | // Description: dict class. |
4 | | // Author: Samuel Charron |
5 | | // |
6 | | // (C) Copyright 2006, Google Inc. |
7 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | // you may not use this file except in compliance with the License. |
9 | | // You may obtain a copy of the License at |
10 | | // http://www.apache.org/licenses/LICENSE-2.0 |
11 | | // Unless required by applicable law or agreed to in writing, software |
12 | | // distributed under the License is distributed on an "AS IS" BASIS, |
13 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | // See the License for the specific language governing permissions and |
15 | | // limitations under the License. |
16 | | // |
17 | | /////////////////////////////////////////////////////////////////////// |
18 | | |
19 | | #ifndef TESSERACT_DICT_DICT_H_ |
20 | | #define TESSERACT_DICT_DICT_H_ |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | # include "config_auto.h" // DISABLED_LEGACY_ENGINE |
24 | | #endif |
25 | | |
26 | | #ifndef DISABLED_LEGACY_ENGINE |
27 | | # include "ambigs.h" |
28 | | #endif |
29 | | #include "dawg.h" |
30 | | #include "dawg_cache.h" |
31 | | #include "ratngs.h" |
32 | | #include "stopper.h" |
33 | | #include "trie.h" |
34 | | #include "unicharset.h" |
35 | | #ifndef DISABLED_LEGACY_ENGINE |
36 | | # include "params_training_featdef.h" |
37 | | #endif // ndef DISABLED_LEGACY_ENGINE |
38 | | |
39 | | namespace tesseract { |
40 | | |
41 | | class MATRIX; |
42 | | class WERD_RES; |
43 | | |
44 | 0 | #define CHARS_PER_LINE 500 |
45 | 795k | #define MAX_WERD_LENGTH static_cast<int64_t>(128) |
46 | | #define NO_RATING -1 |
47 | | |
48 | | /** Struct used to hold temporary information about fragments. */ |
49 | | struct CHAR_FRAGMENT_INFO { |
50 | | UNICHAR_ID unichar_id; |
51 | | const CHAR_FRAGMENT *fragment; |
52 | | int num_fragments; |
53 | | float rating; |
54 | | float certainty; |
55 | | }; |
56 | | |
57 | | using DawgVector = std::vector<Dawg *>; |
58 | | |
59 | | // |
60 | | // Constants |
61 | | // |
62 | | static const int kRatingPad = 4; |
63 | | static const int kDictMaxWildcards = 2; // max wildcards for a word |
64 | | // TODO(daria): If hyphens are different in different languages and can be |
65 | | // inferred from training data we should load their values dynamically. |
66 | | static const char kHyphenSymbol[] = "-"; |
67 | | static const char kSlashSymbol[] = "/"; |
68 | | static const char kQuestionSymbol[] = "?"; |
69 | | static const char kApostropheSymbol[] = "'"; |
70 | | static const float kSimCertaintyScale = -10.0; // similarity matcher scaling |
71 | | static const float kSimCertaintyOffset = -10.0; // similarity matcher offset |
72 | | static const float kSimilarityFloor = 100.0; // worst E*L product to stop on |
73 | | static const int kDocDictMaxRepChars = 4; |
74 | | |
75 | | // Enum for describing whether the x-height for the word is consistent: |
76 | | // 0 - everything is good. |
77 | | // 1 - there are one or two secondary (but consistent) baselines |
78 | | // [think subscript and superscript], or there is an oversized |
79 | | // first character. |
80 | | // 2 - the word is inconsistent. |
81 | | enum XHeightConsistencyEnum { XH_GOOD, XH_SUBNORMAL, XH_INCONSISTENT }; |
82 | | |
83 | | struct DawgArgs { |
84 | | DawgArgs(DawgPositionVector *d, DawgPositionVector *up, PermuterType p) |
85 | 7.66M | : active_dawgs(d), updated_dawgs(up), permuter(p), valid_end(false) {} |
86 | | |
87 | | DawgPositionVector *active_dawgs; |
88 | | DawgPositionVector *updated_dawgs; |
89 | | PermuterType permuter; |
90 | | // True if the current position is a valid word end. |
91 | | bool valid_end; |
92 | | }; |
93 | | |
94 | | class TESS_API Dict { |
95 | | public: |
96 | | Dict(CCUtil *image_ptr); |
97 | | ~Dict(); |
98 | 1.62M | const CCUtil *getCCUtil() const { |
99 | 1.62M | return ccutil_; |
100 | 1.62M | } |
101 | 197M | CCUtil *getCCUtil() { |
102 | 197M | return ccutil_; |
103 | 197M | } |
104 | 2 | const UNICHARSET &getUnicharset() const { |
105 | 2 | return getCCUtil()->unicharset; |
106 | 2 | } |
107 | 197M | UNICHARSET &getUnicharset() { |
108 | 197M | return getCCUtil()->unicharset; |
109 | 197M | } |
110 | | #ifndef DISABLED_LEGACY_ENGINE |
111 | 1.62M | const UnicharAmbigs &getUnicharAmbigs() const { |
112 | 1.62M | return getCCUtil()->unichar_ambigs; |
113 | 1.62M | } |
114 | | #endif |
115 | | // Returns true if unichar_id is a word compounding character like - or /. |
116 | 21.9M | inline bool compound_marker(UNICHAR_ID unichar_id) { |
117 | 21.9M | const UNICHARSET &unicharset = getUnicharset(); |
118 | 21.9M | ASSERT_HOST(unicharset.contains_unichar_id(unichar_id)); |
119 | 21.9M | const auto &normed_ids = unicharset.normed_ids(unichar_id); |
120 | 21.9M | return normed_ids.size() == 1 && |
121 | 20.4M | (normed_ids[0] == hyphen_unichar_id_ || normed_ids[0] == slash_unichar_id_); |
122 | 21.9M | } |
123 | | // Returns true if unichar_id is an apostrophe-like character that may |
124 | | // separate prefix/suffix words from a main body word. |
125 | 0 | inline bool is_apostrophe(UNICHAR_ID unichar_id) { |
126 | 0 | const UNICHARSET &unicharset = getUnicharset(); |
127 | 0 | ASSERT_HOST(unicharset.contains_unichar_id(unichar_id)); |
128 | 0 | const auto &normed_ids = unicharset.normed_ids(unichar_id); |
129 | 0 | return normed_ids.size() == 1 && normed_ids[0] == apostrophe_unichar_id_; |
130 | 0 | } |
131 | | |
132 | | /* hyphen.cpp ************************************************************/ |
133 | | |
134 | | /// Returns true if we've recorded the beginning of a hyphenated word. |
135 | 2.29M | inline bool hyphenated() const { |
136 | 2.29M | return !last_word_on_line_ && hyphen_word_; |
137 | 2.29M | } |
138 | | /// Size of the base word (the part on the line before) of a hyphenated word. |
139 | 317k | inline int hyphen_base_size() const { |
140 | 317k | return this->hyphenated() ? hyphen_word_->length() : 0; |
141 | 317k | } |
142 | | /// If this word is hyphenated copy the base word (the part on |
143 | | /// the line before) of a hyphenated word into the given word. |
144 | | /// This function assumes that word is not nullptr. |
145 | 0 | inline void copy_hyphen_info(WERD_CHOICE *word) const { |
146 | 0 | if (this->hyphenated()) { |
147 | 0 | *word = *hyphen_word_; |
148 | 0 | if (hyphen_debug_level) { |
149 | 0 | word->print("copy_hyphen_info: "); |
150 | 0 | } |
151 | 0 | } |
152 | 0 | } |
153 | | /// Check whether the word has a hyphen at the end. |
154 | | inline bool has_hyphen_end(const UNICHARSET *unicharset, UNICHAR_ID unichar_id, |
155 | 490k | bool first_pos) const { |
156 | 490k | if (!last_word_on_line_ || first_pos) { |
157 | 444k | return false; |
158 | 444k | } |
159 | 45.5k | ASSERT_HOST(unicharset->contains_unichar_id(unichar_id)); |
160 | 45.5k | const auto &normed_ids = unicharset->normed_ids(unichar_id); |
161 | 45.5k | return normed_ids.size() == 1 && normed_ids[0] == hyphen_unichar_id_; |
162 | 490k | } |
163 | | /// Same as above, but check the unichar at the end of the word. |
164 | 109k | inline bool has_hyphen_end(const WERD_CHOICE &word) const { |
165 | 109k | int word_index = word.length() - 1; |
166 | 109k | return has_hyphen_end(word.unicharset(), word.unichar_id(word_index), word_index == 0); |
167 | 109k | } |
168 | | /// Unless the previous word was the last one on the line, and the current |
169 | | /// one is not (thus it is the first one on the line), erase hyphen_word_, |
170 | | /// clear hyphen_active_dawgs_, update last_word_on_line_. |
171 | | void reset_hyphen_vars(bool last_word_on_line); |
172 | | /// Update hyphen_word_, and copy the given DawgPositionVectors into |
173 | | /// hyphen_active_dawgs_ . |
174 | | void set_hyphen_word(const WERD_CHOICE &word, const DawgPositionVector &active_dawgs); |
175 | | |
176 | | /* permdawg.cpp ************************************************************/ |
177 | | // Note: Functions in permdawg.cpp are only used by NoDangerousAmbig(). |
178 | | // When this function is refactored, permdawg.cpp can be removed. |
179 | | |
180 | | /// Copies word into best_choice if its rating is smaller |
181 | | /// than that of best_choice. |
182 | 0 | inline void update_best_choice(const WERD_CHOICE &word, WERD_CHOICE *best_choice) { |
183 | 0 | if (word.rating() < best_choice->rating()) { |
184 | 0 | *best_choice = word; |
185 | 0 | } |
186 | 0 | } |
187 | | /// Fill the given active_dawgs vector with dawgs that could contain the |
188 | | /// beginning of the word. If hyphenated() returns true, copy the entries |
189 | | /// from hyphen_active_dawgs_ instead. |
190 | | void init_active_dawgs(DawgPositionVector *active_dawgs, bool ambigs_mode) const; |
191 | | // Fill the given vector with the default collection of any-length dawgs |
192 | | void default_dawgs(DawgPositionVector *anylength_dawgs, bool suppress_patterns) const; |
193 | | |
194 | | /// Recursively explore all the possible character combinations in |
195 | | /// the given char_choices. Use go_deeper_dawg_fxn() to explore all the |
196 | | /// dawgs in the dawgs_ vector in parallel and discard invalid words. |
197 | | /// |
198 | | /// Allocate and return a WERD_CHOICE with the best valid word found. |
199 | | WERD_CHOICE *dawg_permute_and_select(const BLOB_CHOICE_LIST_VECTOR &char_choices, |
200 | | float rating_limit); |
201 | | /// If the choice being composed so far could be a dictionary word |
202 | | /// and we have not reached the end of the word keep exploring the |
203 | | /// char_choices further. |
204 | | void go_deeper_dawg_fxn(const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, |
205 | | int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info, |
206 | | bool word_ending, WERD_CHOICE *word, float certainties[], float *limit, |
207 | | WERD_CHOICE *best_choice, int *attempts_left, void *void_more_args); |
208 | | |
209 | | /// Pointer to go_deeper function. |
210 | | void (Dict::*go_deeper_fxn_)(const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, |
211 | | int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info, |
212 | | bool word_ending, WERD_CHOICE *word, float certainties[], |
213 | | float *limit, WERD_CHOICE *best_choice, int *attempts_left, |
214 | | void *void_more_args); |
215 | | // |
216 | | // Helper functions for dawg_permute_and_select(). |
217 | | // |
218 | | void permute_choices(const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, |
219 | | int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info, |
220 | | WERD_CHOICE *word, float certainties[], float *limit, |
221 | | WERD_CHOICE *best_choice, int *attempts_left, void *more_args); |
222 | | |
223 | | void append_choices(const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, |
224 | | const BLOB_CHOICE &blob_choice, int char_choice_index, |
225 | | const CHAR_FRAGMENT_INFO *prev_char_frag_info, WERD_CHOICE *word, |
226 | | float certainties[], float *limit, WERD_CHOICE *best_choice, |
227 | | int *attempts_left, void *more_args); |
228 | | |
229 | | bool fragment_state_okay(UNICHAR_ID curr_unichar_id, float curr_rating, float curr_certainty, |
230 | | const CHAR_FRAGMENT_INFO *prev_char_frag_info, const char *debug, |
231 | | int word_ending, CHAR_FRAGMENT_INFO *char_frag_info); |
232 | | |
233 | | /* stopper.cpp *************************************************************/ |
234 | | #if !defined(DISABLED_LEGACY_ENGINE) |
235 | | bool NoDangerousAmbig(WERD_CHOICE *BestChoice, DANGERR *fixpt, bool fix_replaceable, |
236 | | MATRIX *ratings); |
237 | | #endif // !defined(DISABLED_LEGACY_ENGINE) |
238 | | // Replaces the corresponding wrong ngram in werd_choice with the correct |
239 | | // one. The whole correct n-gram is inserted into the ratings matrix and |
240 | | // the werd_choice: no more fragments!. Rating and certainty of new entries |
241 | | // in matrix and werd_choice are the sum and mean of the wrong ngram |
242 | | // respectively. |
243 | | // E.g. for werd_choice mystring'' and ambiguity ''->": werd_choice becomes |
244 | | // mystring", with a new entry in the ratings matrix for ". |
245 | | void ReplaceAmbig(int wrong_ngram_begin_index, int wrong_ngram_size, UNICHAR_ID correct_ngram_id, |
246 | | WERD_CHOICE *werd_choice, MATRIX *ratings); |
247 | | |
248 | | /// Returns the length of the shortest alpha run in WordChoice. |
249 | | int LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice) const; |
250 | | /// Returns true if the certainty of the BestChoice word is within a |
251 | | /// reasonable range of the average certainties for the best choices for |
252 | | /// each character in the segmentation. This test is used to catch words |
253 | | /// in which one character is much worse than the other characters in the |
254 | | /// word (i.e. false will be returned in that case). The algorithm computes |
255 | | /// the mean and std deviation of the certainties in the word with the worst |
256 | | /// certainty thrown out. |
257 | | int UniformCertainties(const WERD_CHOICE &word); |
258 | | /// Returns true if the given best_choice is good enough to stop. |
259 | | bool AcceptableChoice(const WERD_CHOICE &best_choice, XHeightConsistencyEnum xheight_consistency); |
260 | | /// Returns false if the best choice for the current word is questionable |
261 | | /// and should be tried again on the second pass or should be flagged to |
262 | | /// the user. |
263 | | bool AcceptableResult(WERD_RES *word) const; |
264 | | #if !defined(DISABLED_LEGACY_ENGINE) |
265 | | void EndDangerousAmbigs(); |
266 | | #endif // !defined(DISABLED_LEGACY_ENGINE) |
267 | | /// Prints the current choices for this word to stdout. |
268 | | void DebugWordChoices(); |
269 | | /// Sets up stopper variables in preparation for the first pass. |
270 | | void SetupStopperPass1(); |
271 | | /// Sets up stopper variables in preparation for the second pass. |
272 | | void SetupStopperPass2(); |
273 | | /* context.cpp *************************************************************/ |
274 | | /// Check a string to see if it matches a set of lexical rules. |
275 | | int case_ok(const WERD_CHOICE &word) const; |
276 | | /// Returns true if the word looks like an absolute garbage |
277 | | /// (e.g. image mistakenly recognized as text). |
278 | | bool absolute_garbage(const WERD_CHOICE &word, const UNICHARSET &unicharset); |
279 | | |
280 | | /* dict.cpp ****************************************************************/ |
281 | | |
282 | | /// Initialize Dict class - load dawgs from [lang].traineddata and |
283 | | /// user-specified wordlist and pattern list. |
284 | | static DawgCache *GlobalDawgCache(); |
285 | | // Sets up ready for a Load or LoadLSTM. |
286 | | void SetupForLoad(DawgCache *dawg_cache); |
287 | | // Loads the dawgs needed by Tesseract. Call FinishLoad() after. |
288 | | void Load(const std::string &lang, TessdataManager *data_file); |
289 | | // Loads the dawgs needed by the LSTM model. Call FinishLoad() after. |
290 | | void LoadLSTM(const std::string &lang, TessdataManager *data_file); |
291 | | // Completes the loading process after Load() and/or LoadLSTM(). |
292 | | // Returns false if no dictionaries were loaded. |
293 | | bool FinishLoad(); |
294 | | void End(); |
295 | | |
296 | | // Resets the document dictionary analogous to ResetAdaptiveClassifier. |
297 | 0 | void ResetDocumentDictionary() { |
298 | 0 | if (pending_words_ != nullptr) { |
299 | 0 | pending_words_->clear(); |
300 | 0 | } |
301 | 0 | if (document_words_ != nullptr) { |
302 | 0 | document_words_->clear(); |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | /** |
307 | | * Returns the maximal permuter code (from ccstruct/ratngs.h) if in light |
308 | | * of the current state the letter at word_index in the given word |
309 | | * is allowed according to at least one of the dawgs in dawgs_, |
310 | | * otherwise returns NO_PERM. |
311 | | * |
312 | | * The state is described by void_dawg_args, which are interpreted as |
313 | | * DawgArgs and contain relevant active dawg positions. |
314 | | * Each entry in the active_dawgs vector contains an index |
315 | | * into the dawgs_ vector and an EDGE_REF that indicates the last edge |
316 | | * followed in the dawg. It also may contain a position in the punctuation |
317 | | * dawg which describes surrounding punctuation (see struct DawgPosition). |
318 | | * |
319 | | * Input: |
320 | | * At word_index 0 dawg_args->active_dawgs should contain an entry for each |
321 | | * dawg that may start at the beginning of a word, with punc_ref and edge_ref |
322 | | * initialized to NO_EDGE. Since the punctuation dawg includes the empty |
323 | | * pattern " " (meaning anything without surrounding punctuation), having a |
324 | | * single entry for the punctuation dawg will cover all dawgs reachable |
325 | | * there from -- that includes all number and word dawgs. The only dawg |
326 | | * non-reachable from the punctuation_dawg is the pattern dawg. |
327 | | * If hyphen state needs to be applied, initial dawg_args->active_dawgs can |
328 | | * be copied from the saved hyphen state (maintained by Dict). |
329 | | * For word_index > 0 the corresponding state (active_dawgs and punc position) |
330 | | * can be obtained from dawg_args->updated_dawgs passed to |
331 | | * def_letter_is_okay for word_index-1. |
332 | | * Note: the function assumes that active_dawgs, and updated_dawgs |
333 | | * member variables of dawg_args are not nullptr. |
334 | | * |
335 | | * Output: |
336 | | * The function fills in dawg_args->updated_dawgs vector with the |
337 | | * entries for dawgs that contain the word up to the letter at word_index. |
338 | | * |
339 | | */ |
340 | | |
341 | | // |
342 | | int def_letter_is_okay(void *void_dawg_args, const UNICHARSET &unicharset, UNICHAR_ID unichar_id, |
343 | | bool word_end) const; |
344 | | |
345 | | int (Dict::*letter_is_okay_)(void *void_dawg_args, const UNICHARSET &unicharset, |
346 | | UNICHAR_ID unichar_id, bool word_end) const; |
347 | | /// Calls letter_is_okay_ member function. |
348 | | int LetterIsOkay(void *void_dawg_args, const UNICHARSET &unicharset, UNICHAR_ID unichar_id, |
349 | 1.49M | bool word_end) const { |
350 | 1.49M | return (this->*letter_is_okay_)(void_dawg_args, unicharset, unichar_id, word_end); |
351 | 1.49M | } |
352 | | |
353 | | /// Probability in context function used by the ngram permuter. |
354 | | double (Dict::*probability_in_context_)(const char *lang, const char *context, int context_bytes, |
355 | | const char *character, int character_bytes); |
356 | | /// Calls probability_in_context_ member function. |
357 | | double ProbabilityInContext(const char *context, int context_bytes, const char *character, |
358 | 0 | int character_bytes) { |
359 | 0 | return (this->*probability_in_context_)(getCCUtil()->lang.c_str(), context, context_bytes, |
360 | 0 | character, character_bytes); |
361 | 0 | } |
362 | | |
363 | | /// Default (no-op) implementation of probability in context function. |
364 | | double def_probability_in_context([[maybe_unused]] const char *lang, |
365 | | [[maybe_unused]] const char *context, |
366 | | [[maybe_unused]] int context_bytes, |
367 | | [[maybe_unused]] const char *character, |
368 | 0 | [[maybe_unused]] int character_bytes) { |
369 | 0 | return 0.0; |
370 | 0 | } |
371 | | |
372 | 0 | inline void SetWildcardID(UNICHAR_ID id) { |
373 | 0 | wildcard_unichar_id_ = id; |
374 | 0 | } |
375 | 0 | inline UNICHAR_ID WildcardID() const { |
376 | 0 | return wildcard_unichar_id_; |
377 | 0 | } |
378 | | /// Return the number of dawgs in the dawgs_ vector. |
379 | 10.3M | inline int NumDawgs() const { |
380 | 10.3M | return dawgs_.size(); |
381 | 10.3M | } |
382 | | /// Return i-th dawg pointer recorded in the dawgs_ vector. |
383 | 0 | inline const Dawg *GetDawg(int index) const { |
384 | 0 | return dawgs_[index]; |
385 | 0 | } |
386 | | /// Return the points to the punctuation dawg. |
387 | 20.7M | inline const Dawg *GetPuncDawg() const { |
388 | 20.7M | return punc_dawg_; |
389 | 20.7M | } |
390 | | /// Return the points to the unambiguous words dawg. |
391 | 0 | inline const Dawg *GetUnambigDawg() const { |
392 | 0 | return unambig_dawg_; |
393 | 0 | } |
394 | | /// Returns the appropriate next node given the EDGE_REF. |
395 | 13.1M | static inline NODE_REF GetStartingNode(const Dawg *dawg, EDGE_REF edge_ref) { |
396 | 13.1M | if (edge_ref == NO_EDGE) { |
397 | 2.68M | return 0; // beginning to explore the dawg |
398 | 2.68M | } |
399 | 10.4M | NODE_REF node = dawg->next_node(edge_ref); |
400 | 10.4M | if (node == 0) { |
401 | 307k | node = NO_EDGE; // end of word |
402 | 307k | } |
403 | 10.4M | return node; |
404 | 13.1M | } |
405 | | |
406 | | // Given a unichar from a string and a given dawg, return the unichar |
407 | | // we should use to match in that dawg type. (for example, in the number |
408 | | // dawg, all numbers are transformed to kPatternUnicharId). |
409 | 17.1M | UNICHAR_ID char_for_dawg(const UNICHARSET &unicharset, UNICHAR_ID ch, const Dawg *dawg) const { |
410 | 17.1M | if (!dawg) { |
411 | 0 | return ch; |
412 | 0 | } |
413 | 17.1M | switch (dawg->type()) { |
414 | 4.35M | case DAWG_TYPE_NUMBER: |
415 | 4.35M | return unicharset.get_isdigit(ch) ? Dawg::kPatternUnicharID : ch; |
416 | 12.8M | default: |
417 | 12.8M | return ch; |
418 | 17.1M | } |
419 | 17.1M | } |
420 | | |
421 | | /// For each of the character classes of the given unichar_id (and the |
422 | | /// unichar_id itself) finds the corresponding outgoing node or self-loop |
423 | | /// in the given dawg and (after checking that it is valid) records it in |
424 | | /// dawg_args->updated_active_dawgs. Updates current_permuter if any valid |
425 | | /// edges were found. |
426 | | void ProcessPatternEdges(const Dawg *dawg, const DawgPosition &info, UNICHAR_ID unichar_id, |
427 | | bool word_end, DawgArgs *dawg_args, |
428 | | PermuterType *current_permuter) const; |
429 | | |
430 | | /// Read/Write/Access special purpose dawgs which contain words |
431 | | /// only of a certain length (used for phrase search for |
432 | | /// non-space-delimited languages). |
433 | | |
434 | | /// Check all the DAWGs to see if this word is in any of them. |
435 | 918k | inline static bool valid_word_permuter(uint8_t perm, bool numbers_ok) { |
436 | 918k | return (perm == SYSTEM_DAWG_PERM || perm == FREQ_DAWG_PERM || perm == DOC_DAWG_PERM || |
437 | 584k | perm == USER_DAWG_PERM || perm == USER_PATTERN_PERM || perm == COMPOUND_PERM || |
438 | 584k | (numbers_ok && perm == NUMBER_PERM)); |
439 | 918k | } |
440 | | int valid_word(const WERD_CHOICE &word, bool numbers_ok) const; |
441 | 317k | int valid_word(const WERD_CHOICE &word) const { |
442 | 317k | return valid_word(word, false); // return NO_PERM for words with digits |
443 | 317k | } |
444 | 0 | int valid_word_or_number(const WERD_CHOICE &word) const { |
445 | 0 | return valid_word(word, true); // return NUMBER_PERM for valid numbers |
446 | 0 | } |
447 | | /// This function is used by api/tesseract_cube_combiner.cpp |
448 | 0 | int valid_word(const char *string) const { |
449 | 0 | WERD_CHOICE word(string, getUnicharset()); |
450 | 0 | return valid_word(word); |
451 | 0 | } |
452 | | // Do the two WERD_CHOICEs form a meaningful bigram? |
453 | | bool valid_bigram(const WERD_CHOICE &word1, const WERD_CHOICE &word2) const; |
454 | | /// Returns true if the word contains a valid punctuation pattern. |
455 | | /// Note: Since the domains of punctuation symbols and symblos |
456 | | /// used in numbers are not disjoint, a valid number might contain |
457 | | /// an invalid punctuation pattern (e.g. .99). |
458 | | bool valid_punctuation(const WERD_CHOICE &word); |
459 | | /// Returns true if a good answer is found for the unknown blob rating. |
460 | | int good_choice(const WERD_CHOICE &choice); |
461 | | /// Adds a word found on this document to the document specific dictionary. |
462 | | void add_document_word(const WERD_CHOICE &best_choice); |
463 | | /// Adjusts the rating of the given word. |
464 | | void adjust_word(WERD_CHOICE *word, bool nonword, XHeightConsistencyEnum xheight_consistency, |
465 | | float additional_adjust, bool modify_rating, bool debug); |
466 | | /// Set wordseg_rating_adjust_factor_ to the given value. |
467 | 0 | inline void SetWordsegRatingAdjustFactor(float f) { |
468 | 0 | wordseg_rating_adjust_factor_ = f; |
469 | 0 | } |
470 | | /// Returns true if the language is space-delimited (not CJ, or T). |
471 | | bool IsSpaceDelimitedLang() const; |
472 | | |
473 | | private: |
474 | | /** Private member variables. */ |
475 | | CCUtil *ccutil_; |
476 | | /** |
477 | | * Table that stores ambiguities computed during training |
478 | | * (loaded when NoDangerousAmbigs() is called for the first time). |
479 | | * Each entry i in the table stores a set of amibiguities whose |
480 | | * wrong ngram starts with unichar id i. |
481 | | */ |
482 | | #ifndef DISABLED_LEGACY_ENGINE |
483 | | UnicharAmbigs *dang_ambigs_table_ = nullptr; |
484 | | /** Same as above, but for ambiguities with replace flag set. */ |
485 | | UnicharAmbigs *replace_ambigs_table_ = nullptr; |
486 | | #endif |
487 | | /** Additional certainty padding allowed before a word is rejected. */ |
488 | | float reject_offset_; |
489 | | // Cached UNICHAR_IDs: |
490 | | UNICHAR_ID wildcard_unichar_id_; // kDictWildcard. |
491 | | UNICHAR_ID apostrophe_unichar_id_; // kApostropheSymbol. |
492 | | UNICHAR_ID question_unichar_id_; // kQuestionSymbol. |
493 | | UNICHAR_ID slash_unichar_id_; // kSlashSymbol. |
494 | | UNICHAR_ID hyphen_unichar_id_; // kHyphenSymbol. |
495 | | // Hyphen-related variables. |
496 | | WERD_CHOICE *hyphen_word_; |
497 | | DawgPositionVector hyphen_active_dawgs_; |
498 | | bool last_word_on_line_; |
499 | | // List of lists of "equivalent" UNICHAR_IDs for the purposes of dictionary |
500 | | // matching. The first member of each list is taken as canonical. For |
501 | | // example, the first list contains hyphens and dashes with the first symbol |
502 | | // being the ASCII hyphen minus. |
503 | | std::vector<std::vector<UNICHAR_ID>> equivalent_symbols_; |
504 | | // Dawg Cache reference - this is who we ask to allocate/deallocate dawgs. |
505 | | DawgCache *dawg_cache_; |
506 | | bool dawg_cache_is_ours_; // we should delete our own dawg_cache_ |
507 | | // Dawgs. |
508 | | DawgVector dawgs_; |
509 | | SuccessorListsVector successors_; |
510 | | Trie *pending_words_; |
511 | | /// The following pointers are only cached for convenience. |
512 | | /// The dawgs will be deleted when dawgs_ vector is destroyed. |
513 | | // bigram_dawg_ points to a dawg of two-word bigrams which always supersede if |
514 | | // any of them are present on the best choices list for a word pair. |
515 | | // the bigrams are stored as space-separated words where: |
516 | | // (1) leading and trailing punctuation has been removed from each word and |
517 | | // (2) any digits have been replaced with '?' marks. |
518 | | Dawg *bigram_dawg_; |
519 | | // TODO(daria): need to support multiple languages in the future, |
520 | | // so maybe will need to maintain a list of dawgs of each kind. |
521 | | Dawg *freq_dawg_; |
522 | | Dawg *unambig_dawg_; |
523 | | Dawg *punc_dawg_; |
524 | | Trie *document_words_; |
525 | | /// Current segmentation cost adjust factor for word rating. |
526 | | /// See comments in incorporate_segcost. |
527 | | float wordseg_rating_adjust_factor_; |
528 | | // File for recording ambiguities discovered during dictionary search. |
529 | | FILE *output_ambig_words_file_; |
530 | | |
531 | | public: |
532 | | /// Variable members. |
533 | | /// These have to be declared and initialized after image_ptr_, which contains |
534 | | /// the pointer to the params vector - the member of its base CCUtil class. |
535 | | STRING_VAR_H(user_words_file); |
536 | | STRING_VAR_H(user_words_suffix); |
537 | | STRING_VAR_H(user_patterns_file); |
538 | | STRING_VAR_H(user_patterns_suffix); |
539 | | BOOL_VAR_H(load_system_dawg); |
540 | | BOOL_VAR_H(load_freq_dawg); |
541 | | BOOL_VAR_H(load_unambig_dawg); |
542 | | BOOL_VAR_H(load_punc_dawg); |
543 | | BOOL_VAR_H(load_number_dawg); |
544 | | BOOL_VAR_H(load_bigram_dawg); |
545 | | double_VAR_H(xheight_penalty_subscripts); |
546 | | double_VAR_H(xheight_penalty_inconsistent); |
547 | | double_VAR_H(segment_penalty_dict_frequent_word); |
548 | | double_VAR_H(segment_penalty_dict_case_ok); |
549 | | double_VAR_H(segment_penalty_dict_case_bad); |
550 | | double_VAR_H(segment_penalty_dict_nonword); |
551 | | double_VAR_H(segment_penalty_garbage); |
552 | | STRING_VAR_H(output_ambig_words_file); |
553 | | INT_VAR_H(dawg_debug_level); |
554 | | INT_VAR_H(hyphen_debug_level); |
555 | | BOOL_VAR_H(use_only_first_uft8_step); |
556 | | double_VAR_H(certainty_scale); |
557 | | double_VAR_H(stopper_nondict_certainty_base); |
558 | | double_VAR_H(stopper_phase2_certainty_rejection_offset); |
559 | | INT_VAR_H(stopper_smallword_size); |
560 | | double_VAR_H(stopper_certainty_per_char); |
561 | | double_VAR_H(stopper_allowable_character_badness); |
562 | | INT_VAR_H(stopper_debug_level); |
563 | | BOOL_VAR_H(stopper_no_acceptable_choices); |
564 | | INT_VAR_H(tessedit_truncate_wordchoice_log); |
565 | | STRING_VAR_H(word_to_debug); |
566 | | BOOL_VAR_H(segment_nonalphabetic_script); |
567 | | BOOL_VAR_H(save_doc_words); |
568 | | double_VAR_H(doc_dict_pending_threshold); |
569 | | double_VAR_H(doc_dict_certainty_threshold); |
570 | | INT_VAR_H(max_permuter_attempts); |
571 | | }; |
572 | | |
573 | | } // namespace tesseract |
574 | | |
575 | | #endif // THIRD_PARTY_TESSERACT_DICT_DICT_H_ |