/src/tesseract/src/wordrec/language_model.h
Line | Count | Source |
1 | | /////////////////////////////////////////////////////////////////////// |
2 | | // File: language_model.h |
3 | | // Description: Functions that utilize the knowledge about the properties, |
4 | | // structure and statistics of the language to help segmentation |
5 | | // search. |
6 | | // Author: Daria Antonova |
7 | | // |
8 | | // (C) Copyright 2009, Google Inc. |
9 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
10 | | // you may not use this file except in compliance with the License. |
11 | | // You may obtain a copy of the License at |
12 | | // http://www.apache.org/licenses/LICENSE-2.0 |
13 | | // Unless required by applicable law or agreed to in writing, software |
14 | | // distributed under the License is distributed on an "AS IS" BASIS, |
15 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | // See the License for the specific language governing permissions and |
17 | | // limitations under the License. |
18 | | // |
19 | | /////////////////////////////////////////////////////////////////////// |
20 | | |
21 | | #ifndef TESSERACT_WORDREC_LANGUAGE_MODEL_H_ |
22 | | #define TESSERACT_WORDREC_LANGUAGE_MODEL_H_ |
23 | | |
24 | | #include "associate.h" // for AssociateStats (ptr only), AssociateUtils |
25 | | #include "dawg.h" // for DawgPositionVector |
26 | | #include "dict.h" // for DawgArgs, Dict |
27 | | #include "lm_consistency.h" // for LMConsistencyInfo |
28 | | #include "lm_state.h" // for ViterbiStateEntry, LanguageModelFlagsType |
29 | | #include "params.h" // for DoubleParam, double_VAR_H, IntParam, Boo... |
30 | | #include "params_model.h" // for ParamsModel |
31 | | #include "ratngs.h" // for BLOB_CHOICE (ptr only), BLOB_CHOICE_LIST... |
32 | | #include "stopper.h" // for DANGERR |
33 | | |
34 | | #include <cmath> // for exp |
35 | | |
36 | | namespace tesseract { |
37 | | |
38 | | class UNICHARSET; |
39 | | class WERD_RES; |
40 | | |
41 | | struct BlamerBundle; |
42 | | |
43 | | template <typename T> |
44 | | class UnicityTable; |
45 | | |
46 | | class LMPainPoints; |
47 | | struct FontInfo; |
48 | | |
49 | | // This class that contains the data structures and functions necessary |
50 | | // to represent and use the knowledge about the language. |
51 | | class LanguageModel { |
52 | | public: |
53 | | // Masks for keeping track of top choices that should not be pruned out. |
54 | | static const LanguageModelFlagsType kSmallestRatingFlag = 0x1; |
55 | | static const LanguageModelFlagsType kLowerCaseFlag = 0x2; |
56 | | static const LanguageModelFlagsType kUpperCaseFlag = 0x4; |
57 | | static const LanguageModelFlagsType kDigitFlag = 0x8; |
58 | | static const LanguageModelFlagsType kXhtConsistentFlag = 0x10; |
59 | | |
60 | | // Denominator for normalizing per-letter ngram cost when deriving |
61 | | // penalty adjustments. |
62 | | static const float kMaxAvgNgramCost; |
63 | | |
64 | | LanguageModel(const UnicityTable<FontInfo> *fontinfo_table, Dict *dict); |
65 | | ~LanguageModel(); |
66 | | |
67 | | // Fills the given floats array with features extracted from path represented |
68 | | // by the given ViterbiStateEntry. See ccstruct/params_training_featdef.h |
69 | | // for feature information. |
70 | | // Note: the function assumes that features points to an array of size |
71 | | // PTRAIN_NUM_FEATURE_TYPES. |
72 | | static void ExtractFeaturesFromPath(const ViterbiStateEntry &vse, float features[]); |
73 | | |
74 | | // Updates data structures that are used for the duration of the segmentation |
75 | | // search on the current word; |
76 | | void InitForWord(const WERD_CHOICE *prev_word, bool fixed_pitch, float max_char_wh_ratio, |
77 | | float rating_cert_scale); |
78 | | |
79 | | // Updates language model state of the given BLOB_CHOICE_LIST (from |
80 | | // the ratings matrix) and its parent. |
81 | | // |
82 | | // At most language_model_viterbi_list_size are kept in each |
83 | | // LanguageModelState.viterbi_state_entries list. |
84 | | // At most language_model_viterbi_list_max_num_prunable of those are prunable |
85 | | // (non-dictionary) paths. |
86 | | // The entries that represent dictionary word paths are kept at the front |
87 | | // of the list. |
88 | | // The list ordered by cost that is computed collectively by several |
89 | | // language model components (currently dawg and ngram components). |
90 | | bool UpdateState(bool just_classified, int curr_col, int curr_row, BLOB_CHOICE_LIST *curr_list, |
91 | | LanguageModelState *parent_node, WERD_RES *word_res, |
92 | | BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle); |
93 | | |
94 | | // Returns true if an acceptable best choice was discovered. |
95 | 2.49M | inline bool AcceptableChoiceFound() { |
96 | 2.49M | return acceptable_choice_found_; |
97 | 2.49M | } |
98 | 0 | inline void SetAcceptableChoiceFound(bool val) { |
99 | 0 | acceptable_choice_found_ = val; |
100 | 0 | } |
101 | | // Returns the reference to ParamsModel. |
102 | 112k | inline ParamsModel &getParamsModel() { |
103 | 112k | return params_model_; |
104 | 112k | } |
105 | | |
106 | | protected: |
107 | 0 | inline float CertaintyScore(float cert) { |
108 | 0 | if (language_model_use_sigmoidal_certainty) { |
109 | | // cert is assumed to be between 0 and -dict_->certainty_scale. |
110 | | // If you enable language_model_use_sigmoidal_certainty, you |
111 | | // need to adjust language_model_ngram_nonmatch_score as well. |
112 | 0 | cert = -cert / dict_->certainty_scale; |
113 | 0 | return 1.0f / (1.0f + exp(10.0f * cert)); |
114 | 0 | } else { |
115 | 0 | return (-1.0f / cert); |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | 95.4M | inline float ComputeAdjustment(int num_problems, float penalty) { |
120 | 95.4M | if (num_problems == 0) { |
121 | 72.0M | return 0.0f; |
122 | 72.0M | } |
123 | 23.4M | if (num_problems == 1) { |
124 | 12.0M | return penalty; |
125 | 12.0M | } |
126 | 11.3M | return (penalty + (language_model_penalty_increment * static_cast<float>(num_problems - 1))); |
127 | 23.4M | } |
128 | | |
129 | | // Computes the adjustment to the ratings sum based on the given |
130 | | // consistency_info. The paths with invalid punctuation, inconsistent |
131 | | // case and character type are penalized proportionally to the number |
132 | | // of inconsistencies on the path. |
133 | | inline float ComputeConsistencyAdjustment(const LanguageModelDawgInfo *dawg_info, |
134 | 23.8M | const LMConsistencyInfo &consistency_info) { |
135 | 23.8M | if (dawg_info != nullptr) { |
136 | 0 | return ComputeAdjustment(consistency_info.NumInconsistentCase(), |
137 | 0 | language_model_penalty_case) + |
138 | 0 | (consistency_info.inconsistent_script ? language_model_penalty_script : 0.0f); |
139 | 0 | } |
140 | 23.8M | return (ComputeAdjustment(consistency_info.NumInconsistentPunc(), language_model_penalty_punc) + |
141 | 23.8M | ComputeAdjustment(consistency_info.NumInconsistentCase(), language_model_penalty_case) + |
142 | 23.8M | ComputeAdjustment(consistency_info.NumInconsistentChartype(), |
143 | 23.8M | language_model_penalty_chartype) + |
144 | 23.8M | ComputeAdjustment(consistency_info.NumInconsistentSpaces(), |
145 | 23.8M | language_model_penalty_spacing) + |
146 | 23.8M | (consistency_info.inconsistent_script ? language_model_penalty_script : 0.0f) + |
147 | 23.8M | (consistency_info.inconsistent_font ? language_model_penalty_font : 0.0f)); |
148 | 23.8M | } |
149 | | |
150 | | // Returns an adjusted ratings sum that includes inconsistency penalties, |
151 | | // penalties for non-dictionary paths and paths with dips in ngram |
152 | | // probability. |
153 | | float ComputeAdjustedPathCost(ViterbiStateEntry *vse); |
154 | | |
155 | | // Finds the first lower and upper case letter and first digit in curr_list. |
156 | | // Uses the first character in the list in place of empty results. |
157 | | // Returns true if both alpha and digits are found. |
158 | | bool GetTopLowerUpperDigit(BLOB_CHOICE_LIST *curr_list, BLOB_CHOICE **first_lower, |
159 | | BLOB_CHOICE **first_upper, BLOB_CHOICE **first_digit) const; |
160 | | // Forces there to be at least one entry in the overall set of the |
161 | | // viterbi_state_entries of each element of parent_node that has the |
162 | | // top_choice_flag set for lower, upper and digit using the same rules as |
163 | | // GetTopLowerUpperDigit, setting the flag on the first found suitable |
164 | | // candidate, whether or not the flag is set on some other parent. |
165 | | // Returns 1 if both alpha and digits are found among the parents, -1 if no |
166 | | // parents are found at all (a legitimate case), and 0 otherwise. |
167 | | int SetTopParentLowerUpperDigit(LanguageModelState *parent_node) const; |
168 | | |
169 | | // Finds the next ViterbiStateEntry with which the given unichar_id can |
170 | | // combine sensibly, taking into account any mixed alnum/mixed case |
171 | | // situation, and whether this combination has been inspected before. |
172 | | ViterbiStateEntry *GetNextParentVSE(bool just_classified, bool mixed_alnum, const BLOB_CHOICE *bc, |
173 | | LanguageModelFlagsType blob_choice_flags, |
174 | | const UNICHARSET &unicharset, WERD_RES *word_res, |
175 | | ViterbiStateEntry_IT *vse_it, |
176 | | LanguageModelFlagsType *top_choice_flags) const; |
177 | | // Helper function that computes the cost of the path composed of the |
178 | | // path in the given parent ViterbiStateEntry and the given BLOB_CHOICE. |
179 | | // If the new path looks good enough, adds a new ViterbiStateEntry to the |
180 | | // list of viterbi entries in the given BLOB_CHOICE and returns true. |
181 | | bool AddViterbiStateEntry(LanguageModelFlagsType top_choice_flags, float denom, bool word_end, |
182 | | int curr_col, int curr_row, BLOB_CHOICE *b, |
183 | | LanguageModelState *curr_state, ViterbiStateEntry *parent_vse, |
184 | | WERD_RES *word_res, |
185 | | BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle); |
186 | | |
187 | | // Determines whether a potential entry is a true top choice and |
188 | | // updates changed accordingly. |
189 | | // |
190 | | // Note: The function assumes that b, top_choice_flags and changed |
191 | | // are not nullptr. |
192 | | void GenerateTopChoiceInfo(ViterbiStateEntry *new_vse, |
193 | | LanguageModelState *lms); |
194 | | |
195 | | // Calls dict_->LetterIsOk() with DawgArgs initialized from parent_vse and |
196 | | // unichar from b.unichar_id(). Constructs and returns LanguageModelDawgInfo |
197 | | // with updated active dawgs, constraints and permuter. |
198 | | // |
199 | | // Note: the caller is responsible for deleting the returned pointer. |
200 | | LanguageModelDawgInfo *GenerateDawgInfo(bool word_end, int curr_col, const BLOB_CHOICE &b, |
201 | | const ViterbiStateEntry *parent_vse); |
202 | | |
203 | | // Computes p(unichar | parent context) and records it in ngram_cost. |
204 | | // If b.unichar_id() is an unlikely continuation of the parent context |
205 | | // sets found_small_prob to true and returns nullptr. |
206 | | // Otherwise creates a new LanguageModelNgramInfo entry containing the |
207 | | // updated context (that includes b.unichar_id() at the end) and returns it. |
208 | | // |
209 | | // Note: the caller is responsible for deleting the returned pointer. |
210 | | LanguageModelNgramInfo *GenerateNgramInfo(const char *unichar, float certainty, float denom, |
211 | | float outline_length, |
212 | | const ViterbiStateEntry *parent_vse); |
213 | | |
214 | | // Computes -(log(prob(classifier)) + log(prob(ngram model))) |
215 | | // for the given unichar in the given context. If there are multiple |
216 | | // unichars at one position - takes the average of their probabilities. |
217 | | // UNICHAR::utf8_step() is used to separate out individual UTF8 characters, |
218 | | // since probability_in_context() can only handle one at a time (while |
219 | | // unicharset might contain ngrams and glyphs composed from multiple UTF8 |
220 | | // characters). |
221 | | float ComputeNgramCost(const char *unichar, float certainty, float denom, const char *context, |
222 | | int *unichar_step_len, bool *found_small_prob, float *ngram_prob); |
223 | | |
224 | | // Computes the normalization factors for the classifier confidences |
225 | | // (used by ComputeNgramCost()). |
226 | | float ComputeDenom(BLOB_CHOICE_LIST *curr_list); |
227 | | |
228 | | // Fills the given consistency_info based on parent_vse.consistency_info |
229 | | // and on the consistency of the given unichar_id with parent_vse. |
230 | | void FillConsistencyInfo(int curr_col, bool word_end, BLOB_CHOICE *b, |
231 | | ViterbiStateEntry *parent_vse, WERD_RES *word_res, |
232 | | LMConsistencyInfo *consistency_info); |
233 | | |
234 | | // Constructs WERD_CHOICE by recording unichar_ids of the BLOB_CHOICEs |
235 | | // on the path represented by the given BLOB_CHOICE and language model |
236 | | // state entries (lmse, dse). The path is re-constructed by following |
237 | | // the parent pointers in the lang model state entries). If the |
238 | | // constructed WERD_CHOICE is better than the best/raw choice recorded |
239 | | // in the best_choice_bundle, this function updates the corresponding |
240 | | // fields and sets best_choice_bunldle->updated to true. |
241 | | void UpdateBestChoice(ViterbiStateEntry *vse, WERD_RES *word_res, BestChoiceBundle *best_choice_bundle, |
242 | | BlamerBundle *blamer_bundle); |
243 | | |
244 | | // Constructs a WERD_CHOICE by tracing parent pointers starting with |
245 | | // the given LanguageModelStateEntry. Returns the constructed word. |
246 | | // Updates best_char_choices, certainties and state if they are not |
247 | | // nullptr (best_char_choices and certainties are assumed to have the |
248 | | // length equal to lmse->length). |
249 | | // The caller is responsible for freeing memory associated with the |
250 | | // returned WERD_CHOICE. |
251 | | WERD_CHOICE *ConstructWord(ViterbiStateEntry *vse, WERD_RES *word_res, DANGERR *fixpt, |
252 | | BlamerBundle *blamer_bundle, bool *truth_path); |
253 | | |
254 | | // Wrapper around AssociateUtils::ComputeStats(). |
255 | | inline void ComputeAssociateStats(int col, int row, float max_char_wh_ratio, |
256 | | ViterbiStateEntry *parent_vse, WERD_RES *word_res, |
257 | 23.8M | AssociateStats *associate_stats) { |
258 | 23.8M | AssociateUtils::ComputeStats( |
259 | 23.8M | col, row, (parent_vse != nullptr) ? &(parent_vse->associate_stats) : nullptr, |
260 | 23.8M | (parent_vse != nullptr) ? parent_vse->length : 0, fixed_pitch_, max_char_wh_ratio, word_res, |
261 | 23.8M | language_model_debug_level > 2, associate_stats); |
262 | 23.8M | } |
263 | | |
264 | | // Returns true if the path with such top_choice_flags and dawg_info |
265 | | // could be pruned out (i.e. is neither a system/user/frequent dictionary |
266 | | // nor a top choice path). |
267 | | // In non-space delimited languages all paths can be "somewhat" dictionary |
268 | | // words. In such languages we cannot do dictionary-driven path pruning, |
269 | | // so paths with non-empty dawg_info are considered prunable. |
270 | 115M | inline bool PrunablePath(const ViterbiStateEntry &vse) { |
271 | 115M | if (vse.top_choice_flags) { |
272 | 76.3M | return false; |
273 | 76.3M | } |
274 | 39.4M | if (vse.dawg_info != nullptr && |
275 | 0 | (vse.dawg_info->permuter == SYSTEM_DAWG_PERM || vse.dawg_info->permuter == USER_DAWG_PERM || |
276 | 0 | vse.dawg_info->permuter == FREQ_DAWG_PERM)) { |
277 | 0 | return false; |
278 | 0 | } |
279 | 39.4M | return true; |
280 | 39.4M | } |
281 | | |
282 | | // Returns true if the given ViterbiStateEntry represents an acceptable path. |
283 | 0 | inline bool AcceptablePath(const ViterbiStateEntry &vse) { |
284 | 0 | return (vse.dawg_info != nullptr || vse.Consistent() || |
285 | 0 | (vse.ngram_info != nullptr && !vse.ngram_info->pruned)); |
286 | 0 | } |
287 | | |
288 | | public: |
289 | | // Parameters. |
290 | | INT_VAR_H(language_model_debug_level); |
291 | | BOOL_VAR_H(language_model_ngram_on); |
292 | | INT_VAR_H(language_model_ngram_order); |
293 | | INT_VAR_H(language_model_viterbi_list_max_num_prunable); |
294 | | INT_VAR_H(language_model_viterbi_list_max_size); |
295 | | double_VAR_H(language_model_ngram_small_prob); |
296 | | double_VAR_H(language_model_ngram_nonmatch_score); |
297 | | BOOL_VAR_H(language_model_ngram_use_only_first_uft8_step); |
298 | | double_VAR_H(language_model_ngram_scale_factor); |
299 | | double_VAR_H(language_model_ngram_rating_factor); |
300 | | BOOL_VAR_H(language_model_ngram_space_delimited_language); |
301 | | INT_VAR_H(language_model_min_compound_length); |
302 | | // Penalties used for adjusting path costs and final word rating. |
303 | | double_VAR_H(language_model_penalty_non_freq_dict_word); |
304 | | double_VAR_H(language_model_penalty_non_dict_word); |
305 | | double_VAR_H(language_model_penalty_punc); |
306 | | double_VAR_H(language_model_penalty_case); |
307 | | double_VAR_H(language_model_penalty_script); |
308 | | double_VAR_H(language_model_penalty_chartype); |
309 | | double_VAR_H(language_model_penalty_font); |
310 | | double_VAR_H(language_model_penalty_spacing); |
311 | | double_VAR_H(language_model_penalty_increment); |
312 | | INT_VAR_H(wordrec_display_segmentations); |
313 | | BOOL_VAR_H(language_model_use_sigmoidal_certainty); |
314 | | |
315 | | protected: |
316 | | // Member Variables. |
317 | | |
318 | | // Temporary DawgArgs struct that is re-used across different words to |
319 | | // avoid dynamic memory re-allocation (should be cleared before each use). |
320 | | DawgArgs dawg_args_; |
321 | | // Scaling for recovering blob outline length from rating and certainty. |
322 | | float rating_cert_scale_ = 0.0f; |
323 | | |
324 | | // The following variables are set at construction time. |
325 | | |
326 | | // Pointer to fontinfo table (not owned by LanguageModel). |
327 | | const UnicityTable<FontInfo> *fontinfo_table_ = nullptr; |
328 | | |
329 | | // Pointer to Dict class, that is used for querying the dictionaries |
330 | | // (the pointer is not owned by LanguageModel). |
331 | | Dict *dict_ = nullptr; |
332 | | |
333 | | // TODO(daria): the following variables should become LanguageModel params |
334 | | // when the old code in bestfirst.cpp and heuristic.cpp is deprecated. |
335 | | // |
336 | | // Set to true if we are dealing with fixed pitch text |
337 | | // (set to assume_fixed_pitch_char_segment). |
338 | | bool fixed_pitch_ = false; |
339 | | // Max char width-to-height ratio allowed |
340 | | // (set to segsearch_max_char_wh_ratio). |
341 | | float max_char_wh_ratio_ = 0.0f; |
342 | | |
343 | | // The following variables are initialized with InitForWord(). |
344 | | |
345 | | // String representation of the classification of the previous word |
346 | | // (since this is only used by the character ngram model component, |
347 | | // only the last language_model_ngram_order of the word are stored). |
348 | | std::string prev_word_str_; |
349 | | int prev_word_unichar_step_len_ = 0; |
350 | | // Active dawg vector. |
351 | | DawgPositionVector very_beginning_active_dawgs_; // includes continuation |
352 | | DawgPositionVector beginning_active_dawgs_; |
353 | | // Set to true if acceptable choice was discovered. |
354 | | // Note: it would be nice to use this to terminate the search once an |
355 | | // acceptable choices is found. However we do not do that and once an |
356 | | // acceptable choice is found we finish looking for alternative choices |
357 | | // in the current segmentation graph and then exit the search (no more |
358 | | // classifications are done after an acceptable choice is found). |
359 | | // This is needed in order to let the search find the words very close to |
360 | | // the best choice in rating (e.g. what/What, Cat/cat, etc) and log these |
361 | | // choices. This way the stopper will know that the best choice is not |
362 | | // ambiguous (i.e. there are best choices in the best choice list that have |
363 | | // ratings close to the very best one) and will be less likely to mis-adapt. |
364 | | bool acceptable_choice_found_ = false; |
365 | | // Set to true if a choice representing correct segmentation was explored. |
366 | | bool correct_segmentation_explored_ = false; |
367 | | |
368 | | // Params models containing weights for computing ViterbiStateEntry costs. |
369 | | ParamsModel params_model_; |
370 | | }; |
371 | | |
372 | | } // namespace tesseract |
373 | | |
374 | | #endif // TESSERACT_WORDREC_LANGUAGE_MODEL_H_ |