tesseract  4.00.00dev
ambigs.cpp
Go to the documentation of this file.
1 // File: ambigs.cc
3 // Description: Functions for dealing with ambiguities
4 // (training and recognition).
5 // Author: Daria Antonova
6 // Created: Mon Feb 5 11:26:43 PDT 2009
7 //
8 // (C) Copyright 2008, 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 //
20 
21 #include "ambigs.h"
22 
23 #include <stdio.h>
24 #include "helpers.h"
25 #include "universalambigs.h"
26 
27 #if defined _WIN32
28 #ifndef __GNUC__
29 #define strtok_r strtok_s
30 #else
31 #include "strtok_r.h"
32 #endif /* __GNUC__ */
33 #endif /* _WIN32 */
34 
35 namespace tesseract {
36 
37 // Maximum line size:
38 // 10 for sizes of ambigs, tabs, abmig type and newline
39 // UNICHAR_LEN * (MAX_AMBIG_SIZE + 1) for each part of the ambig
41 
43  wrong_ngram[0] = INVALID_UNICHAR_ID;
44  correct_fragments[0] = INVALID_UNICHAR_ID;
45  correct_ngram_id = INVALID_UNICHAR_ID;
46  type = NOT_AMBIG;
47  wrong_ngram_size = 0;
48 }
49 
51 
52 // Initializes the ambigs by adding a NULL pointer to each table.
54  bool use_ambigs_for_adaption) {
55  for (int i = 0; i < unicharset.size(); ++i) {
56  replace_ambigs_.push_back(NULL);
57  dang_ambigs_.push_back(NULL);
58  one_to_one_definite_ambigs_.push_back(NULL);
59  if (use_ambigs_for_adaption) {
60  ambigs_for_adaption_.push_back(NULL);
61  reverse_ambigs_for_adaption_.push_back(NULL);
62  }
63  }
64 }
65 
66 // Loads the universal ambigs that are useful for any language.
67 void UnicharAmbigs::LoadUniversal(const UNICHARSET& encoder_set,
68  UNICHARSET* unicharset) {
69  TFile file;
71  LoadUnicharAmbigs(encoder_set, &file, 0, false, unicharset);
72 }
73 
75  TFile *ambig_file,
76  int debug_level,
77  bool use_ambigs_for_adaption,
78  UNICHARSET *unicharset) {
79  int i, j;
80  UnicharIdVector *adaption_ambigs_entry;
81  if (debug_level) tprintf("Reading ambiguities\n");
82 
83  int test_ambig_part_size;
84  int replacement_ambig_part_size;
85  // The space for buffer is allocated on the heap to avoid
86  // GCC frame size warning.
87  const int kBufferSize = 10 + 2 * kMaxAmbigStringSize;
88  char *buffer = new char[kBufferSize];
89  char replacement_string[kMaxAmbigStringSize];
90  UNICHAR_ID test_unichar_ids[MAX_AMBIG_SIZE + 1];
91  int line_num = 0;
92  int type = NOT_AMBIG;
93 
94  // Determine the version of the ambigs file.
95  int version = 0;
96  ASSERT_HOST(ambig_file->FGets(buffer, kBufferSize) != NULL &&
97  strlen(buffer) > 0);
98  if (*buffer == 'v') {
99  version = static_cast<int>(strtol(buffer+1, NULL, 10));
100  ++line_num;
101  } else {
102  ambig_file->Rewind();
103  }
104  while (ambig_file->FGets(buffer, kBufferSize) != NULL) {
105  chomp_string(buffer);
106  if (debug_level > 2) tprintf("read line %s\n", buffer);
107  ++line_num;
108  if (!ParseAmbiguityLine(line_num, version, debug_level, encoder_set,
109  buffer, &test_ambig_part_size, test_unichar_ids,
110  &replacement_ambig_part_size,
111  replacement_string, &type)) continue;
112  // Construct AmbigSpec and add it to the appropriate AmbigSpec_LIST.
113  AmbigSpec *ambig_spec = new AmbigSpec();
114  if (!InsertIntoTable((type == REPLACE_AMBIG) ? replace_ambigs_
115  : dang_ambigs_,
116  test_ambig_part_size, test_unichar_ids,
117  replacement_ambig_part_size, replacement_string, type,
118  ambig_spec, unicharset))
119  continue;
120 
121  // Update one_to_one_definite_ambigs_.
122  if (test_ambig_part_size == 1 &&
123  replacement_ambig_part_size == 1 && type == DEFINITE_AMBIG) {
124  if (one_to_one_definite_ambigs_[test_unichar_ids[0]] == NULL) {
125  one_to_one_definite_ambigs_[test_unichar_ids[0]] = new UnicharIdVector();
126  }
127  one_to_one_definite_ambigs_[test_unichar_ids[0]]->push_back(
128  ambig_spec->correct_ngram_id);
129  }
130  // Update ambigs_for_adaption_.
131  if (use_ambigs_for_adaption) {
132  GenericVector<UNICHAR_ID> encoding;
133  // Silently ignore invalid strings, as before, so it is safe to use a
134  // universal ambigs file.
135  if (unicharset->encode_string(replacement_string, true, &encoding,
136  NULL, NULL)) {
137  for (i = 0; i < test_ambig_part_size; ++i) {
138  if (ambigs_for_adaption_[test_unichar_ids[i]] == NULL) {
139  ambigs_for_adaption_[test_unichar_ids[i]] = new UnicharIdVector();
140  }
141  adaption_ambigs_entry = ambigs_for_adaption_[test_unichar_ids[i]];
142  for (int r = 0; r < encoding.size(); ++r) {
143  UNICHAR_ID id_to_insert = encoding[r];
144  ASSERT_HOST(id_to_insert != INVALID_UNICHAR_ID);
145  // Add the new unichar id to adaption_ambigs_entry (only if the
146  // vector does not already contain it) keeping it in sorted order.
147  for (j = 0; j < adaption_ambigs_entry->size() &&
148  (*adaption_ambigs_entry)[j] > id_to_insert; ++j);
149  if (j < adaption_ambigs_entry->size()) {
150  if ((*adaption_ambigs_entry)[j] != id_to_insert) {
151  adaption_ambigs_entry->insert(id_to_insert, j);
152  }
153  } else {
154  adaption_ambigs_entry->push_back(id_to_insert);
155  }
156  }
157  }
158  }
159  }
160  }
161  delete[] buffer;
162 
163  // Fill in reverse_ambigs_for_adaption from ambigs_for_adaption vector.
164  if (use_ambigs_for_adaption) {
165  for (i = 0; i < ambigs_for_adaption_.size(); ++i) {
166  adaption_ambigs_entry = ambigs_for_adaption_[i];
167  if (adaption_ambigs_entry == NULL) continue;
168  for (j = 0; j < adaption_ambigs_entry->size(); ++j) {
169  UNICHAR_ID ambig_id = (*adaption_ambigs_entry)[j];
170  if (reverse_ambigs_for_adaption_[ambig_id] == NULL) {
171  reverse_ambigs_for_adaption_[ambig_id] = new UnicharIdVector();
172  }
173  reverse_ambigs_for_adaption_[ambig_id]->push_back(i);
174  }
175  }
176  }
177 
178  // Print what was read from the input file.
179  if (debug_level > 1) {
180  for (int tbl = 0; tbl < 2; ++tbl) {
181  const UnicharAmbigsVector &print_table =
182  (tbl == 0) ? replace_ambigs_ : dang_ambigs_;
183  for (i = 0; i < print_table.size(); ++i) {
184  AmbigSpec_LIST *lst = print_table[i];
185  if (lst == NULL) continue;
186  if (!lst->empty()) {
187  tprintf("%s Ambiguities for %s:\n",
188  (tbl == 0) ? "Replaceable" : "Dangerous",
189  unicharset->debug_str(i).string());
190  }
191  AmbigSpec_IT lst_it(lst);
192  for (lst_it.mark_cycle_pt(); !lst_it.cycled_list(); lst_it.forward()) {
193  AmbigSpec *ambig_spec = lst_it.data();
194  tprintf("wrong_ngram:");
195  UnicharIdArrayUtils::print(ambig_spec->wrong_ngram, *unicharset);
196  tprintf("correct_fragments:");
197  UnicharIdArrayUtils::print(ambig_spec->correct_fragments, *unicharset);
198  }
199  }
200  }
201  if (use_ambigs_for_adaption) {
202  for (int vec_id = 0; vec_id < 2; ++vec_id) {
203  const GenericVector<UnicharIdVector *> &vec = (vec_id == 0) ?
204  ambigs_for_adaption_ : reverse_ambigs_for_adaption_;
205  for (i = 0; i < vec.size(); ++i) {
206  adaption_ambigs_entry = vec[i];
207  if (adaption_ambigs_entry != NULL) {
208  tprintf("%sAmbigs for adaption for %s:\n",
209  (vec_id == 0) ? "" : "Reverse ",
210  unicharset->debug_str(i).string());
211  for (j = 0; j < adaption_ambigs_entry->size(); ++j) {
212  tprintf("%s ", unicharset->debug_str(
213  (*adaption_ambigs_entry)[j]).string());
214  }
215  tprintf("\n");
216  }
217  }
218  }
219  }
220  }
221 }
222 
223 bool UnicharAmbigs::ParseAmbiguityLine(
224  int line_num, int version, int debug_level, const UNICHARSET &unicharset,
225  char *buffer, int *test_ambig_part_size, UNICHAR_ID *test_unichar_ids,
226  int *replacement_ambig_part_size, char *replacement_string, int *type) {
227  if (version > 1) {
228  // Simpler format is just wrong-string correct-string type\n.
229  STRING input(buffer);
230  GenericVector<STRING> fields;
231  input.split(' ', &fields);
232  if (fields.size() != 3) {
233  if (debug_level) tprintf(kIllegalMsg, line_num);
234  return false;
235  }
236  // Encode wrong-string.
237  GenericVector<UNICHAR_ID> unichars;
238  if (!unicharset.encode_string(fields[0].string(), true, &unichars, NULL,
239  NULL)) {
240  return false;
241  }
242  *test_ambig_part_size = unichars.size();
243  if (*test_ambig_part_size > MAX_AMBIG_SIZE) {
244  if (debug_level)
245  tprintf("Too many unichars in ambiguity on line %d\n", line_num);
246  return false;
247  }
248  // Copy encoded string to output.
249  for (int i = 0; i < unichars.size(); ++i)
250  test_unichar_ids[i] = unichars[i];
251  test_unichar_ids[unichars.size()] = INVALID_UNICHAR_ID;
252  // Encode replacement-string to check validity.
253  if (!unicharset.encode_string(fields[1].string(), true, &unichars, NULL,
254  NULL)) {
255  return false;
256  }
257  *replacement_ambig_part_size = unichars.size();
258  if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) {
259  if (debug_level)
260  tprintf("Too many unichars in ambiguity on line %d\n", line_num);
261  return false;
262  }
263  if (sscanf(fields[2].string(), "%d", type) != 1) {
264  if (debug_level) tprintf(kIllegalMsg, line_num);
265  return false;
266  }
267  snprintf(replacement_string, kMaxAmbigStringSize, "%s", fields[1].string());
268  return true;
269  }
270  int i;
271  char *token;
272  char *next_token;
273  if (!(token = strtok_r(buffer, kAmbigDelimiters, &next_token)) ||
274  !sscanf(token, "%d", test_ambig_part_size) ||
275  *test_ambig_part_size <= 0) {
276  if (debug_level) tprintf(kIllegalMsg, line_num);
277  return false;
278  }
279  if (*test_ambig_part_size > MAX_AMBIG_SIZE) {
280  if (debug_level)
281  tprintf("Too many unichars in ambiguity on line %d\n", line_num);
282  return false;
283  }
284  for (i = 0; i < *test_ambig_part_size; ++i) {
285  if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break;
286  if (!unicharset.contains_unichar(token)) {
287  if (debug_level) tprintf(kIllegalUnicharMsg, token);
288  break;
289  }
290  test_unichar_ids[i] = unicharset.unichar_to_id(token);
291  }
292  test_unichar_ids[i] = INVALID_UNICHAR_ID;
293 
294  if (i != *test_ambig_part_size ||
295  !(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) ||
296  !sscanf(token, "%d", replacement_ambig_part_size) ||
297  *replacement_ambig_part_size <= 0) {
298  if (debug_level) tprintf(kIllegalMsg, line_num);
299  return false;
300  }
301  if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) {
302  if (debug_level)
303  tprintf("Too many unichars in ambiguity on line %d\n", line_num);
304  return false;
305  }
306  replacement_string[0] = '\0';
307  for (i = 0; i < *replacement_ambig_part_size; ++i) {
308  if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break;
309  strcat(replacement_string, token);
310  if (!unicharset.contains_unichar(token)) {
311  if (debug_level) tprintf(kIllegalUnicharMsg, token);
312  break;
313  }
314  }
315  if (i != *replacement_ambig_part_size) {
316  if (debug_level) tprintf(kIllegalMsg, line_num);
317  return false;
318  }
319  if (version > 0) {
320  // The next field being true indicates that the abiguity should
321  // always be substituted (e.g. '' should always be changed to ").
322  // For such "certain" n -> m ambigs tesseract will insert character
323  // fragments for the n pieces in the unicharset. AmbigsFound()
324  // will then replace the incorrect ngram with the character
325  // fragments of the correct character (or ngram if m > 1).
326  // Note that if m > 1, an ngram will be inserted into the
327  // modified word, not the individual unigrams. Tesseract
328  // has limited support for ngram unichar (e.g. dawg permuter).
329  if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) ||
330  !sscanf(token, "%d", type)) {
331  if (debug_level) tprintf(kIllegalMsg, line_num);
332  return false;
333  }
334  }
335  return true;
336 }
337 
338 bool UnicharAmbigs::InsertIntoTable(
339  UnicharAmbigsVector &table, int test_ambig_part_size,
340  UNICHAR_ID *test_unichar_ids, int replacement_ambig_part_size,
341  const char *replacement_string, int type,
342  AmbigSpec *ambig_spec, UNICHARSET *unicharset) {
343  ambig_spec->type = static_cast<AmbigType>(type);
344  if (test_ambig_part_size == 1 && replacement_ambig_part_size == 1 &&
345  unicharset->to_lower(test_unichar_ids[0]) ==
346  unicharset->to_lower(unicharset->unichar_to_id(replacement_string))) {
347  ambig_spec->type = CASE_AMBIG;
348  }
349 
350  ambig_spec->wrong_ngram_size =
351  UnicharIdArrayUtils::copy(test_unichar_ids, ambig_spec->wrong_ngram);
352 
353  // Since we need to maintain a constant number of unichar positions in
354  // order to construct ambig_blob_choices vector in NoDangerousAmbig(), for
355  // each n->m ambiguity we will have to place n character fragments of the
356  // correct ngram into the corresponding positions in the vector (e.g. given
357  // "vvvvw" and vvvv->ww we will place v and |ww|0|4 into position 0, v and
358  // |ww|1|4 into position 1 and so on. The correct ngram is reconstructed
359  // from fragments by dawg_permute_and_select().
360 
361  // Insert the corresponding correct ngram into the unicharset.
362  // Unicharset code assumes that the "base" ngram is inserted into
363  // the unicharset before fragments of this ngram are inserted.
364  unicharset->unichar_insert(replacement_string);
365  ambig_spec->correct_ngram_id =
366  unicharset->unichar_to_id(replacement_string);
367  if (replacement_ambig_part_size > 1) {
368  unicharset->set_isngram(ambig_spec->correct_ngram_id, true);
369  }
370  // Add the corresponding fragments of the wrong ngram to unicharset.
371  int i;
372  for (i = 0; i < test_ambig_part_size; ++i) {
373  UNICHAR_ID unichar_id;
374  if (test_ambig_part_size == 1) {
375  unichar_id = ambig_spec->correct_ngram_id;
376  } else {
377  STRING frag_str = CHAR_FRAGMENT::to_string(
378  replacement_string, i, test_ambig_part_size, false);
379  unicharset->unichar_insert(frag_str.string());
380  unichar_id = unicharset->unichar_to_id(frag_str.string());
381  }
382  ambig_spec->correct_fragments[i] = unichar_id;
383  }
384  ambig_spec->correct_fragments[i] = INVALID_UNICHAR_ID;
385 
386  // Add AmbigSpec for this ambiguity to the corresponding AmbigSpec_LIST.
387  // Keep AmbigSpec_LISTs sorted by AmbigSpec.wrong_ngram.
388  if (table[test_unichar_ids[0]] == NULL) {
389  table[test_unichar_ids[0]] = new AmbigSpec_LIST();
390  }
391  if (table[test_unichar_ids[0]]->add_sorted(
392  AmbigSpec::compare_ambig_specs, true, ambig_spec))
393  return true;
394  delete ambig_spec;
395  return false;
396 }
397 
398 } // namespace tesseract
AmbigType
Definition: ambigs.h:44
STRING to_string() const
Definition: unicharset.h:73
void LoadUniversal(const UNICHARSET &encoder_set, UNICHARSET *unicharset)
Definition: ambigs.cpp:67
bool contains_unichar(const char *const unichar_repr) const
Definition: unicharset.cpp:644
int UNICHAR_ID
Definition: unichar.h:33
static int compare_ambig_specs(const void *spec1, const void *spec2)
Definition: ambigs.h:122
#define MAX_AMBIG_SIZE
Definition: ambigs.h:30
voidpf void uLong size
Definition: ioapi.h:39
UNICHAR_ID correct_fragments[MAX_AMBIG_SIZE+1]
Definition: ambigs.h:134
int push_back(T object)
#define tprintf(...)
Definition: tprintf.h:31
char * strtok_r(char *s1, const char *s2, char **lasts)
Definition: strtok_r.cpp:38
const char * string() const
Definition: strngs.cpp:198
AmbigType type
Definition: ambigs.h:136
int size() const
Definition: genericvector.h:72
#define UNICHAR_LEN
Definition: unichar.h:30
#define ASSERT_HOST(x)
Definition: errcode.h:84
const int ksizeofUniversalAmbigsFile
void insert(T t, int index)
void chomp_string(char *str)
Definition: helpers.h:82
UNICHAR_ID to_lower(UNICHAR_ID unichar_id) const
Definition: unicharset.h:664
Definition: strngs.h:45
UNICHAR_ID wrong_ngram[MAX_AMBIG_SIZE+1]
Definition: ambigs.h:133
static int copy(const UNICHAR_ID src[], UNICHAR_ID dst[])
Definition: ambigs.h:88
const char kUniversalAmbigsFile[]
void set_isngram(UNICHAR_ID unichar_id, bool value)
Definition: unicharset.h:416
bool encode_string(const char *str, bool give_up_on_failure, GenericVector< UNICHAR_ID > *encoding, GenericVector< char > *lengths, int *encoded_length) const
Definition: unicharset.cpp:234
bool Open(const STRING &filename, FileReader reader)
Definition: serialis.cpp:38
int size() const
Definition: unicharset.h:299
UNICHAR_ID correct_ngram_id
Definition: ambigs.h:135
GenericVector< UNICHAR_ID > UnicharIdVector
Definition: ambigs.h:34
const int kMaxAmbigStringSize
Definition: ambigs.cpp:40
ELISTIZE(AmbigSpec)
char * FGets(char *buffer, int buffer_size)
Definition: serialis.cpp:86
UNICHAR_ID unichar_to_id(const char *const unichar_repr) const
Definition: unicharset.cpp:194
void InitUnicharAmbigs(const UNICHARSET &unicharset, bool use_ambigs_for_adaption)
Definition: ambigs.cpp:53
static void print(const UNICHAR_ID array[], const UNICHARSET &unicharset)
Definition: ambigs.h:98
void split(const char c, GenericVector< STRING > *splited)
Definition: strngs.cpp:286
STRING debug_str(UNICHAR_ID id) const
Definition: unicharset.cpp:318
void unichar_insert(const char *const unichar_repr)
Definition: unicharset.cpp:612
void LoadUnicharAmbigs(const UNICHARSET &encoder_set, TFile *ambigs_file, int debug_level, bool use_ambigs_for_adaption, UNICHARSET *unicharset)
Definition: ambigs.cpp:74