All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
reject.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: reject.cpp (Formerly reject.c)
3  * Description: Rejection functions used in tessedit
4  * Author: Phil Cheatle
5  * Created: Wed Sep 23 16:50:21 BST 1992
6  *
7  * (C) Copyright 1992, Hewlett-Packard Ltd.
8  ** Licensed under the Apache License, Version 2.0 (the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http://www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #ifdef _MSC_VER
21 #pragma warning(disable:4244) // Conversion warnings
22 #pragma warning(disable:4305) // int/float warnings
23 #endif
24 
25 #include "tessvars.h"
26 #ifdef __UNIX__
27 #include <assert.h>
28 #include <errno.h>
29 #endif
30 #include "scanutils.h"
31 #include <ctype.h>
32 #include <string.h>
33 #include "genericvector.h"
34 #include "reject.h"
35 #include "control.h"
36 #include "docqual.h"
37 #include "globaloc.h" // For err_exit.
38 #include "globals.h"
39 #include "helpers.h"
40 
41 #include "tesseractclass.h"
42 
43 // Include automatically generated configuration file if running autoconf.
44 #ifdef HAVE_CONFIG_H
45 #include "config_auto.h"
46 #endif
47 
49 
50 /*************************************************************************
51  * set_done()
52  *
53  * Set the done flag based on the word acceptability criteria
54  *************************************************************************/
55 
56 namespace tesseract {
57 void Tesseract::set_done(WERD_RES *word, inT16 pass) {
58  word->done = word->tess_accepted &&
59  (strchr(word->best_choice->unichar_string().string(), ' ') == NULL);
60  bool word_is_ambig = word->best_choice->dangerous_ambig_found();
61  bool word_from_dict = word->best_choice->permuter() == SYSTEM_DAWG_PERM ||
62  word->best_choice->permuter() == FREQ_DAWG_PERM ||
64  if (word->done && (pass == 1) && (!word_from_dict || word_is_ambig) &&
65  one_ell_conflict(word, FALSE)) {
66  if (tessedit_rejection_debug) tprintf("one_ell_conflict detected\n");
67  word->done = FALSE;
68  }
69  if (word->done && ((!word_from_dict &&
70  word->best_choice->permuter() != NUMBER_PERM) || word_is_ambig)) {
71  if (tessedit_rejection_debug) tprintf("non-dict or ambig word detected\n");
72  word->done = FALSE;
73  }
74  if (tessedit_rejection_debug) {
75  tprintf("set_done(): done=%d\n", word->done);
76  word->best_choice->print("");
77  }
78 }
79 
80 
81 /*************************************************************************
82  * make_reject_map()
83  *
84  * Sets the done flag to indicate whether the resylt is acceptable.
85  *
86  * Sets a reject map for the word.
87  *************************************************************************/
88 void Tesseract::make_reject_map(WERD_RES *word, ROW *row, inT16 pass) {
89  int i;
90  int offset;
91 
92  flip_0O(word);
93  check_debug_pt(word, -1); // For trap only
94  set_done(word, pass); // Set acceptance
96  reject_blanks(word);
97  /*
98  0: Rays original heuristic - the baseline
99  */
100  if (tessedit_reject_mode == 0) {
101  if (!word->done)
102  reject_poor_matches(word);
103  } else if (tessedit_reject_mode == 5) {
104  /*
105  5: Reject I/1/l from words where there is no strong contextual confirmation;
106  the whole of any unacceptable words (incl PERM rej of dubious 1/I/ls);
107  and the whole of any words which are very small
108  */
109  if (kBlnXHeight / word->denorm.y_scale() <= min_sane_x_ht_pixels) {
111  } else {
112  one_ell_conflict(word, TRUE);
113  /*
114  Originally the code here just used the done flag. Now I have duplicated
115  and unpacked the conditions for setting the done flag so that each
116  mechanism can be turned on or off independently. This works WITHOUT
117  affecting the done flag setting.
118  */
119  if (rej_use_tess_accepted && !word->tess_accepted)
121 
122  if (rej_use_tess_blanks &&
123  (strchr (word->best_choice->unichar_string().string (), ' ') != NULL))
125 
126  WERD_CHOICE* best_choice = word->best_choice;
127  if (rej_use_good_perm) {
128  if ((best_choice->permuter() == SYSTEM_DAWG_PERM ||
129  best_choice->permuter() == FREQ_DAWG_PERM ||
130  best_choice->permuter() == USER_DAWG_PERM) &&
131  (!rej_use_sensible_wd ||
132  acceptable_word_string(*word->uch_set,
133  best_choice->unichar_string().string(),
134  best_choice->unichar_lengths().string()) !=
135  AC_UNACCEPTABLE)) {
136  // PASSED TEST
137  } else if (best_choice->permuter() == NUMBER_PERM) {
138  if (rej_alphas_in_number_perm) {
139  for (i = 0, offset = 0;
140  best_choice->unichar_string()[offset] != '\0';
141  offset += best_choice->unichar_lengths()[i++]) {
142  if (word->reject_map[i].accepted() &&
143  word->uch_set->get_isalpha(
144  best_choice->unichar_string().string() + offset,
145  best_choice->unichar_lengths()[i]))
146  word->reject_map[i].setrej_bad_permuter();
147  // rej alpha
148  }
149  }
150  } else {
152  }
153  }
154  /* Ambig word rejection was here once !!*/
155  }
156  } else {
157  tprintf("BAD tessedit_reject_mode\n");
158  err_exit();
159  }
160 
161  if (tessedit_image_border > -1)
162  reject_edge_blobs(word);
163 
164  check_debug_pt (word, 10);
165  if (tessedit_rejection_debug) {
166  tprintf("Permuter Type = %d\n", word->best_choice->permuter ());
167  tprintf("Certainty: %f Rating: %f\n",
168  word->best_choice->certainty (), word->best_choice->rating ());
169  tprintf("Dict word: %d\n", dict_word(*(word->best_choice)));
170  }
171 
172  flip_hyphens(word);
173  check_debug_pt(word, 20);
174 }
175 } // namespace tesseract
176 
177 
178 void reject_blanks(WERD_RES *word) {
179  inT16 i;
180  inT16 offset;
181 
182  for (i = 0, offset = 0; word->best_choice->unichar_string()[offset] != '\0';
183  offset += word->best_choice->unichar_lengths()[i], i += 1) {
184  if (word->best_choice->unichar_string()[offset] == ' ')
185  //rej unrecognised blobs
186  word->reject_map[i].setrej_tess_failure ();
187  }
188 }
189 
190 namespace tesseract {
192  inT16 i;
193  inT16 offset;
194 
195  for (i = 0, offset = 0; word->best_choice->unichar_string()[offset] != '\0';
196  offset += word->best_choice->unichar_lengths()[i], i += 1) {
198  contains (word->best_choice->unichar_string()[offset])) {
199  //rej 1Il conflict
200  word->reject_map[i].setrej_1Il_conflict ();
201  }
202  }
203 }
204 } // namespace tesseract
205 
206 
208  float threshold = compute_reject_threshold(word->best_choice);
209  for (int i = 0; i < word->best_choice->length(); ++i) {
210  if (word->best_choice->unichar_id(i) == UNICHAR_SPACE)
211  word->reject_map[i].setrej_tess_failure();
212  else if (word->best_choice->certainty(i) < threshold)
213  word->reject_map[i].setrej_poor_match();
214  }
215 }
216 
217 
218 /**********************************************************************
219  * compute_reject_threshold
220  *
221  * Set a rejection threshold for this word.
222  * Initially this is a trivial function which looks for the largest
223  * gap in the certainty value.
224  **********************************************************************/
225 
227  float threshold; // rejection threshold
228  float bestgap = 0.0f; // biggest gap
229  float gapstart; // bottom of gap
230  // super iterator
231  BLOB_CHOICE_IT choice_it; // real iterator
232 
233  int blob_count = word->length();
234  GenericVector<float> ratings;
235  ratings.init_to_size(blob_count, 0.0f);
236  for (int i = 0; i < blob_count; ++i) {
237  ratings[i] = word->certainty(i);
238  }
239  ratings.sort();
240  gapstart = ratings[0] - 1; // all reject if none better
241  if (blob_count >= 3) {
242  for (int index = 0; index < blob_count - 1; index++) {
243  if (ratings[index + 1] - ratings[index] > bestgap) {
244  bestgap = ratings[index + 1] - ratings[index];
245  // find biggest
246  gapstart = ratings[index];
247  }
248  }
249  }
250  threshold = gapstart + bestgap / 2;
251 
252  return threshold;
253 }
254 
255 
256 /*************************************************************************
257  * reject_edge_blobs()
258  *
259  * If the word is perilously close to the edge of the image, reject those blobs
260  * in the word which are too close to the edge as they could be clipped.
261  *************************************************************************/
262 namespace tesseract {
264  TBOX word_box = word->word->bounding_box();
265  // Use the box_word as it is already denormed back to image coordinates.
266  int blobcount = word->box_word->length();
267 
268  if (word_box.left() < tessedit_image_border ||
269  word_box.bottom() < tessedit_image_border ||
270  word_box.right() + tessedit_image_border > ImageWidth() - 1 ||
271  word_box.top() + tessedit_image_border > ImageHeight() - 1) {
272  ASSERT_HOST(word->reject_map.length() == blobcount);
273  for (int blobindex = 0; blobindex < blobcount; blobindex++) {
274  TBOX blob_box = word->box_word->BlobBox(blobindex);
275  if (blob_box.left() < tessedit_image_border ||
276  blob_box.bottom() < tessedit_image_border ||
277  blob_box.right() + tessedit_image_border > ImageWidth() - 1 ||
278  blob_box.top() + tessedit_image_border > ImageHeight() - 1) {
279  word->reject_map[blobindex].setrej_edge_char();
280  // Close to edge
281  }
282  }
283  }
284 }
285 
286 /**********************************************************************
287  * one_ell_conflict()
288  *
289  * Identify words where there is a potential I/l/1 error.
290  * - A bundle of contextual heuristics!
291  **********************************************************************/
293  const char *word;
294  const char *lengths;
295  inT16 word_len; //its length
296  inT16 first_alphanum_index_;
297  inT16 first_alphanum_offset_;
298  inT16 i;
299  inT16 offset;
300  BOOL8 non_conflict_set_char; //non conf set a/n?
301  BOOL8 conflict = FALSE;
302  BOOL8 allow_1s;
303  ACCEPTABLE_WERD_TYPE word_type;
304  BOOL8 dict_perm_type;
305  BOOL8 dict_word_ok;
306  int dict_word_type;
307 
308  word = word_res->best_choice->unichar_string().string ();
309  lengths = word_res->best_choice->unichar_lengths().string();
310  word_len = strlen (lengths);
311  /*
312  If there are no occurrences of the conflict set characters then the word
313  is OK.
314  */
315  if (strpbrk (word, conflict_set_I_l_1.string ()) == NULL)
316  return FALSE;
317 
318  /*
319  There is a conflict if there are NO other (confirmed) alphanumerics apart
320  from those in the conflict set.
321  */
322 
323  for (i = 0, offset = 0, non_conflict_set_char = FALSE;
324  (i < word_len) && !non_conflict_set_char; offset += lengths[i++])
325  non_conflict_set_char =
326  (word_res->uch_set->get_isalpha(word + offset, lengths[i]) ||
327  word_res->uch_set->get_isdigit(word + offset, lengths[i])) &&
328  !STRING (conflict_set_I_l_1).contains (word[offset]);
329  if (!non_conflict_set_char) {
330  if (update_map)
331  reject_I_1_L(word_res);
332  return TRUE;
333  }
334 
335  /*
336  If the word is accepted by a dawg permuter, and the first alpha character
337  is "I" or "l", check to see if the alternative is also a dawg word. If it
338  is, then there is a potential error otherwise the word is ok.
339  */
340 
341  dict_perm_type = (word_res->best_choice->permuter () == SYSTEM_DAWG_PERM) ||
342  (word_res->best_choice->permuter () == USER_DAWG_PERM) ||
344  (word_res->best_choice->permuter () == DOC_DAWG_PERM)) ||
345  (word_res->best_choice->permuter () == FREQ_DAWG_PERM);
346  dict_word_type = dict_word(*(word_res->best_choice));
347  dict_word_ok = (dict_word_type > 0) &&
348  (rej_trust_doc_dawg || (dict_word_type != DOC_DAWG_PERM));
349 
350  if ((rej_1Il_use_dict_word && dict_word_ok) ||
351  (rej_1Il_trust_permuter_type && dict_perm_type) ||
352  (dict_perm_type && dict_word_ok)) {
353  first_alphanum_index_ = first_alphanum_index (word, lengths);
354  first_alphanum_offset_ = first_alphanum_offset (word, lengths);
355  if (lengths[first_alphanum_index_] == 1 &&
356  word[first_alphanum_offset_] == 'I') {
357  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l';
358  if (safe_dict_word(word_res) > 0) {
359  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I';
360  if (update_map)
361  word_res->reject_map[first_alphanum_index_].
362  setrej_1Il_conflict();
363  return TRUE;
364  }
365  else {
366  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I';
367  return FALSE;
368  }
369  }
370 
371  if (lengths[first_alphanum_index_] == 1 &&
372  word[first_alphanum_offset_] == 'l') {
373  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I';
374  if (safe_dict_word(word_res) > 0) {
375  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l';
376  if (update_map)
377  word_res->reject_map[first_alphanum_index_].
378  setrej_1Il_conflict();
379  return TRUE;
380  }
381  else {
382  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l';
383  return FALSE;
384  }
385  }
386  return FALSE;
387  }
388 
389  /*
390  NEW 1Il code. The old code relied on permuter types too much. In fact,
391  tess will use TOP_CHOICE permute for good things like "palette".
392  In this code the string is examined independently to see if it looks like
393  a well formed word.
394  */
395 
396  /*
397  REGARDLESS OF PERMUTER, see if flipping a leading I/l generates a
398  dictionary word.
399  */
400  first_alphanum_index_ = first_alphanum_index (word, lengths);
401  first_alphanum_offset_ = first_alphanum_offset (word, lengths);
402  if (lengths[first_alphanum_index_] == 1 &&
403  word[first_alphanum_offset_] == 'l') {
404  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I';
405  if (safe_dict_word(word_res) > 0)
406  return FALSE;
407  else
408  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l';
409  }
410  else if (lengths[first_alphanum_index_] == 1 &&
411  word[first_alphanum_offset_] == 'I') {
412  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l';
413  if (safe_dict_word(word_res) > 0)
414  return FALSE;
415  else
416  word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I';
417  }
418  /*
419  For strings containing digits:
420  If there are no alphas OR the numeric permuter liked the word,
421  reject any non 1 conflict chs
422  Else reject all conflict chs
423  */
424  if (word_contains_non_1_digit (word, lengths)) {
425  allow_1s = (alpha_count (word, lengths) == 0) ||
426  (word_res->best_choice->permuter () == NUMBER_PERM);
427 
428  inT16 offset;
429  conflict = FALSE;
430  for (i = 0, offset = 0; word[offset] != '\0';
431  offset += word_res->best_choice->unichar_lengths()[i++]) {
432  if ((!allow_1s || (word[offset] != '1')) &&
433  STRING (conflict_set_I_l_1).contains (word[offset])) {
434  if (update_map)
435  word_res->reject_map[i].setrej_1Il_conflict ();
436  conflict = TRUE;
437  }
438  }
439  return conflict;
440  }
441  /*
442  For anything else. See if it conforms to an acceptable word type. If so,
443  treat accordingly.
444  */
445  word_type = acceptable_word_string(*word_res->uch_set, word, lengths);
446  if ((word_type == AC_LOWER_CASE) || (word_type == AC_INITIAL_CAP)) {
447  first_alphanum_index_ = first_alphanum_index (word, lengths);
448  first_alphanum_offset_ = first_alphanum_offset (word, lengths);
449  if (STRING (conflict_set_I_l_1).contains (word[first_alphanum_offset_])) {
450  if (update_map)
451  word_res->reject_map[first_alphanum_index_].
452  setrej_1Il_conflict ();
453  return TRUE;
454  }
455  else
456  return FALSE;
457  }
458  else if (word_type == AC_UPPER_CASE) {
459  return FALSE;
460  }
461  else {
462  if (update_map)
463  reject_I_1_L(word_res);
464  return TRUE;
465  }
466 }
467 
468 
470  const char *word_lengths) {
471  inT16 i;
472  inT16 offset;
473 
474  for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) {
475  if (unicharset.get_isalpha(word + offset, word_lengths[i]) ||
476  unicharset.get_isdigit(word + offset, word_lengths[i]))
477  return i;
478  }
479  return -1;
480 }
481 
483  const char *word_lengths) {
484  inT16 i;
485  inT16 offset;
486 
487  for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) {
488  if (unicharset.get_isalpha(word + offset, word_lengths[i]) ||
489  unicharset.get_isdigit(word + offset, word_lengths[i]))
490  return offset;
491  }
492  return -1;
493 }
494 
495 inT16 Tesseract::alpha_count(const char *word,
496  const char *word_lengths) {
497  inT16 i;
498  inT16 offset;
499  inT16 count = 0;
500 
501  for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) {
502  if (unicharset.get_isalpha (word + offset, word_lengths[i]))
503  count++;
504  }
505  return count;
506 }
507 
508 
510  const char *word_lengths) {
511  inT16 i;
512  inT16 offset;
513 
514  for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) {
515  if (unicharset.get_isdigit (word + offset, word_lengths[i]) &&
516  (word_lengths[i] != 1 || word[offset] != '1'))
517  return TRUE;
518  }
519  return FALSE;
520 }
521 
522 /*************************************************************************
523  * dont_allow_1Il()
524  * Dont unreject LONE accepted 1Il conflict set chars
525  *************************************************************************/
527  int i = 0;
528  int offset;
529  int word_len = word->reject_map.length();
530  const char *s = word->best_choice->unichar_string().string();
531  const char *lengths = word->best_choice->unichar_lengths().string();
532  BOOL8 accepted_1Il = FALSE;
533 
534  for (i = 0, offset = 0; i < word_len;
535  offset += word->best_choice->unichar_lengths()[i++]) {
536  if (word->reject_map[i].accepted()) {
537  if (STRING(conflict_set_I_l_1).contains(s[offset])) {
538  accepted_1Il = TRUE;
539  } else {
540  if (word->uch_set->get_isalpha(s + offset, lengths[i]) ||
541  word->uch_set->get_isdigit(s + offset, lengths[i]))
542  return; // >=1 non 1Il ch accepted
543  }
544  }
545  }
546  if (!accepted_1Il)
547  return; //Nothing to worry about
548 
549  for (i = 0, offset = 0; i < word_len;
550  offset += word->best_choice->unichar_lengths()[i++]) {
551  if (STRING(conflict_set_I_l_1).contains(s[offset]) &&
552  word->reject_map[i].accepted())
553  word->reject_map[i].setrej_postNN_1Il();
554  }
555 }
556 
557 
559  int count = 0;
560  const WERD_CHOICE *best_choice = word_res->best_choice;
561  for (int i = 0; i < word_res->reject_map.length(); ++i) {
562  if ((word_res->reject_map[i].accepted()) &&
563  (word_res->uch_set->get_isalpha(best_choice->unichar_id(i)) ||
564  word_res->uch_set->get_isdigit(best_choice->unichar_id(i)))) {
565  count++;
566  }
567  }
568  return count;
569 }
570 
571 
572 // reject all if most rejected.
574  /* Reject the whole of the word if the fraction of rejects exceeds a limit */
575 
576  if ((float) word->reject_map.reject_count() / word->reject_map.length() >=
579 }
580 
581 
583  inT16 char_quality;
584  inT16 accepted_char_quality;
585 
586  if (word->best_choice->unichar_lengths().length() <= 1)
587  return FALSE;
588 
590  contains(word->best_choice->unichar_string()[0]))
591  return FALSE;
592 
593  UNICHAR_ID uch_id = word->best_choice->unichar_id(0);
594  for (int i = 1; i < word->best_choice->length(); ++i) {
595  if (word->best_choice->unichar_id(i) != uch_id) return FALSE;
596  }
597 
598  word_char_quality(word, row, &char_quality, &accepted_char_quality);
599 
600  if ((word->best_choice->unichar_lengths().length () == char_quality) &&
601  (char_quality == accepted_char_quality))
602  return TRUE;
603  else
604  return FALSE;
605 }
606 
608  const WERD_CHOICE &word = *werd_res->best_choice;
609  int dict_word_type = werd_res->tesseract->dict_word(word);
610  return dict_word_type == DOC_DAWG_PERM ? 0 : dict_word_type;
611 }
612 
613 // Note: After running this function word_res->ratings
614 // might not contain the right BLOB_CHOICE corresponding to each character
615 // in word_res->best_choice.
617  WERD_CHOICE *best_choice = word_res->best_choice;
618  int i;
619  int prev_right = -9999;
620  int next_left;
621  TBOX out_box;
622  float aspect_ratio;
623 
625  return;
626 
627  int num_blobs = word_res->rebuild_word->NumBlobs();
628  UNICHAR_ID unichar_dash = word_res->uch_set->unichar_to_id("-");
629  for (i = 0; i < best_choice->length() && i < num_blobs; ++i) {
630  TBLOB* blob = word_res->rebuild_word->blobs[i];
631  out_box = blob->bounding_box();
632  if (i + 1 == num_blobs)
633  next_left = 9999;
634  else
635  next_left = word_res->rebuild_word->blobs[i + 1]->bounding_box().left();
636  // Dont touch small or touching blobs - it is too dangerous.
637  if ((out_box.width() > 8 * word_res->denorm.x_scale()) &&
638  (out_box.left() > prev_right) && (out_box.right() < next_left)) {
639  aspect_ratio = out_box.width() / (float) out_box.height();
640  if (word_res->uch_set->eq(best_choice->unichar_id(i), ".")) {
641  if (aspect_ratio >= tessedit_upper_flip_hyphen &&
642  word_res->uch_set->contains_unichar_id(unichar_dash) &&
643  word_res->uch_set->get_enabled(unichar_dash)) {
644  /* Certain HYPHEN */
645  best_choice->set_unichar_id(unichar_dash, i);
646  if (word_res->reject_map[i].rejected())
647  word_res->reject_map[i].setrej_hyphen_accept();
648  }
649  if ((aspect_ratio > tessedit_lower_flip_hyphen) &&
650  word_res->reject_map[i].accepted())
651  //Suspected HYPHEN
652  word_res->reject_map[i].setrej_hyphen ();
653  }
654  else if (best_choice->unichar_id(i) == unichar_dash) {
655  if ((aspect_ratio >= tessedit_upper_flip_hyphen) &&
656  (word_res->reject_map[i].rejected()))
657  word_res->reject_map[i].setrej_hyphen_accept();
658  //Certain HYPHEN
659 
660  if ((aspect_ratio <= tessedit_lower_flip_hyphen) &&
661  (word_res->reject_map[i].accepted()))
662  //Suspected HYPHEN
663  word_res->reject_map[i].setrej_hyphen();
664  }
665  }
666  prev_right = out_box.right();
667  }
668 }
669 
670 // Note: After running this function word_res->ratings
671 // might not contain the right BLOB_CHOICE corresponding to each character
672 // in word_res->best_choice.
673 void Tesseract::flip_0O(WERD_RES *word_res) {
674  WERD_CHOICE *best_choice = word_res->best_choice;
675  int i;
676  TBOX out_box;
677 
678  if (!tessedit_flip_0O)
679  return;
680 
681  int num_blobs = word_res->rebuild_word->NumBlobs();
682  for (i = 0; i < best_choice->length() && i < num_blobs; ++i) {
683  TBLOB* blob = word_res->rebuild_word->blobs[i];
684  if (word_res->uch_set->get_isupper(best_choice->unichar_id(i)) ||
685  word_res->uch_set->get_isdigit(best_choice->unichar_id(i))) {
686  out_box = blob->bounding_box();
687  if ((out_box.top() < kBlnBaselineOffset + kBlnXHeight) ||
688  (out_box.bottom() > kBlnBaselineOffset + kBlnXHeight / 4))
689  return; //Beware words with sub/superscripts
690  }
691  }
692  UNICHAR_ID unichar_0 = word_res->uch_set->unichar_to_id("0");
693  UNICHAR_ID unichar_O = word_res->uch_set->unichar_to_id("O");
694  if (unichar_0 == INVALID_UNICHAR_ID ||
695  !word_res->uch_set->get_enabled(unichar_0) ||
696  unichar_O == INVALID_UNICHAR_ID ||
697  !word_res->uch_set->get_enabled(unichar_O)) {
698  return; // 0 or O are not present/enabled in unicharset
699  }
700  for (i = 1; i < best_choice->length(); ++i) {
701  if (best_choice->unichar_id(i) == unichar_0 ||
702  best_choice->unichar_id(i) == unichar_O) {
703  /* A0A */
704  if ((i+1) < best_choice->length() &&
705  non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-1)) &&
706  non_O_upper(*word_res->uch_set, best_choice->unichar_id(i+1))) {
707  best_choice->set_unichar_id(unichar_O, i);
708  }
709  /* A00A */
710  if (non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-1)) &&
711  (i+1) < best_choice->length() &&
712  (best_choice->unichar_id(i+1) == unichar_0 ||
713  best_choice->unichar_id(i+1) == unichar_O) &&
714  (i+2) < best_choice->length() &&
715  non_O_upper(*word_res->uch_set, best_choice->unichar_id(i+2))) {
716  best_choice->set_unichar_id(unichar_O, i);
717  i++;
718  }
719  /* AA0<non digit or end of word> */
720  if ((i > 1) &&
721  non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-2)) &&
722  non_O_upper(*word_res->uch_set, best_choice->unichar_id(i-1)) &&
723  (((i+1) < best_choice->length() &&
724  !word_res->uch_set->get_isdigit(best_choice->unichar_id(i+1)) &&
725  !word_res->uch_set->eq(best_choice->unichar_id(i+1), "l") &&
726  !word_res->uch_set->eq(best_choice->unichar_id(i+1), "I")) ||
727  (i == best_choice->length() - 1))) {
728  best_choice->set_unichar_id(unichar_O, i);
729  }
730  /* 9O9 */
731  if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) &&
732  (i+1) < best_choice->length() &&
733  non_0_digit(*word_res->uch_set, best_choice->unichar_id(i+1))) {
734  best_choice->set_unichar_id(unichar_0, i);
735  }
736  /* 9OOO */
737  if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) &&
738  (i+2) < best_choice->length() &&
739  (best_choice->unichar_id(i+1) == unichar_0 ||
740  best_choice->unichar_id(i+1) == unichar_O) &&
741  (best_choice->unichar_id(i+2) == unichar_0 ||
742  best_choice->unichar_id(i+2) == unichar_O)) {
743  best_choice->set_unichar_id(unichar_0, i);
744  best_choice->set_unichar_id(unichar_0, i+1);
745  best_choice->set_unichar_id(unichar_0, i+2);
746  i += 2;
747  }
748  /* 9OO<non upper> */
749  if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) &&
750  (i+2) < best_choice->length() &&
751  (best_choice->unichar_id(i+1) == unichar_0 ||
752  best_choice->unichar_id(i+1) == unichar_O) &&
753  !word_res->uch_set->get_isupper(best_choice->unichar_id(i+2))) {
754  best_choice->set_unichar_id(unichar_0, i);
755  best_choice->set_unichar_id(unichar_0, i+1);
756  i++;
757  }
758  /* 9O<non upper> */
759  if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i-1)) &&
760  (i+1) < best_choice->length() &&
761  !word_res->uch_set->get_isupper(best_choice->unichar_id(i+1))) {
762  best_choice->set_unichar_id(unichar_0, i);
763  }
764  /* 9[.,]OOO.. */
765  if ((i > 1) &&
766  (word_res->uch_set->eq(best_choice->unichar_id(i-1), ".") ||
767  word_res->uch_set->eq(best_choice->unichar_id(i-1), ",")) &&
768  (word_res->uch_set->get_isdigit(best_choice->unichar_id(i-2)) ||
769  best_choice->unichar_id(i-2) == unichar_O)) {
770  if (best_choice->unichar_id(i-2) == unichar_O) {
771  best_choice->set_unichar_id(unichar_0, i-2);
772  }
773  while (i < best_choice->length() &&
774  (best_choice->unichar_id(i) == unichar_O ||
775  best_choice->unichar_id(i) == unichar_0)) {
776  best_choice->set_unichar_id(unichar_0, i);
777  i++;
778  }
779  i--;
780  }
781  }
782  }
783 }
784 
785 BOOL8 Tesseract::non_O_upper(const UNICHARSET& ch_set, UNICHAR_ID unichar_id) {
786  return ch_set.get_isupper(unichar_id) && !ch_set.eq(unichar_id, "O");
787 }
788 
789 BOOL8 Tesseract::non_0_digit(const UNICHARSET& ch_set, UNICHAR_ID unichar_id) {
790  return ch_set.get_isdigit(unichar_id) && !ch_set.eq(unichar_id, "0");
791 }
792 } // namespace tesseract
const int kBlnXHeight
Definition: normalis.h:28
BOOL8 tess_accepted
Definition: pageres.h:280
Definition: blobs.h:261
void set_unichar_id(UNICHAR_ID unichar_id, int index)
Definition: ratngs.h:356
inT16 first_alphanum_offset(const char *word, const char *word_lengths)
Definition: reject.cpp:482
tesseract::BoxWord * box_word
Definition: pageres.h:250
void reject_mostly_rejects(WERD_RES *word)
Definition: reject.cpp:573
float rating() const
Definition: ratngs.h:324
const UNICHAR_ID unichar_to_id(const char *const unichar_repr) const
Definition: unicharset.cpp:194
void err_exit()
Definition: globaloc.cpp:74
inT32 length() const
Definition: rejctmap.h:237
CLISTIZEH(STRING) CLISTIZE(STRING) namespace tesseract
Definition: reject.cpp:48
BOOL8 one_ell_conflict(WERD_RES *word_res, BOOL8 update_map)
Definition: reject.cpp:292
bool eq(UNICHAR_ID unichar_id, const char *const unichar_repr) const
Definition: unicharset.cpp:656
int length() const
Definition: ratngs.h:300
WERD_CHOICE * best_choice
Definition: pageres.h:219
void dont_allow_1Il(WERD_RES *word)
Definition: reject.cpp:526
REJMAP reject_map
Definition: pageres.h:271
#define tprintf(...)
Definition: tprintf.h:31
int ImageHeight() const
bool get_isupper(UNICHAR_ID unichar_id) const
Definition: unicharset.h:463
const TBOX & BlobBox(int index) const
Definition: boxword.h:88
char * ok_repeated_ch_non_alphanum_wds
void flip_0O(WERD_RES *word)
UNICHARSET unicharset
Definition: ccutil.h:72
void reject_blanks(WERD_RES *word)
Definition: reject.cpp:178
void flip_hyphens(WERD_RES *word)
const STRING & unichar_lengths() const
Definition: ratngs.h:531
unsigned char BOOL8
Definition: host.h:113
TBOX bounding_box() const
Definition: werd.cpp:160
inT32 length() const
Definition: strngs.cpp:188
inT16 right() const
Definition: rect.h:75
int dict_word(const WERD_CHOICE &word)
Definition: tface.cpp:124
inT16 safe_dict_word(const WERD_RES *werd_res)
Definition: reject.cpp:607
bool dangerous_ambig_found() const
Definition: ratngs.h:360
#define ASSERT_HOST(x)
Definition: errcode.h:84
Definition: ocrrow.h:32
inT16 count_alphanums(const WERD_CHOICE &word)
Definition: output.cpp:410
const STRING & unichar_string() const
Definition: ratngs.h:524
ALL upper case.
Definition: control.h:38
int ImageWidth() const
int NumBlobs() const
Definition: blobs.h:425
bool get_isdigit(UNICHAR_ID unichar_id) const
Definition: unicharset.h:470
void rej_word_small_xht()
Definition: rejctmap.cpp:416
inT16 alpha_count(const char *word, const char *word_lengths)
Definition: reject.cpp:495
inT16 first_alphanum_index(const char *word, const char *word_lengths)
Definition: reject.cpp:469
#define CLISTIZE(CLASSNAME)
Definition: clst.h:958
void reject_edge_blobs(WERD_RES *word)
Definition: reject.cpp:263
void word_char_quality(WERD_RES *word, ROW *row, inT16 *match_count, inT16 *accepted_match_count)
Definition: docqual.cpp:97
inT16 left() const
Definition: rect.h:68
float certainty() const
Definition: ratngs.h:327
TWERD * rebuild_word
Definition: pageres.h:244
const UNICHAR_ID unichar_id(int index) const
Definition: ratngs.h:312
const UNICHARSET * uch_set
Definition: pageres.h:192
ACCEPTABLE_WERD_TYPE
Definition: control.h:34
void init_to_size(int size, T t)
DENORM denorm
Definition: pageres.h:190
uinT8 permuter() const
Definition: ratngs.h:343
BOOL8 word_contains_non_1_digit(const char *word, const char *word_lengths)
Definition: reject.cpp:509
tesseract::Tesseract * tesseract
Definition: pageres.h:266
const int kBlnBaselineOffset
Definition: normalis.h:29
ACCEPTABLE_WERD_TYPE acceptable_word_string(const UNICHARSET &char_set, const char *s, const char *lengths)
Definition: control.cpp:1663
int UNICHAR_ID
Definition: unichar.h:33
inT16 bottom() const
Definition: rect.h:61
BOOL8 done
Definition: pageres.h:282
void rej_word_mostly_rej()
Definition: rejctmap.cpp:479
double rej_whole_of_mostly_reject_word_fract
WERD * word
Definition: pageres.h:175
inT16 height() const
Definition: rect.h:104
ALL lower case.
Definition: control.h:37
Unacceptable word.
Definition: control.h:36
inT16 width() const
Definition: rect.h:111
GenericVector< TBLOB * > blobs
Definition: blobs.h:436
const int length() const
Definition: boxword.h:85
#define FALSE
Definition: capi.h:29
BOOL8 non_O_upper(const UNICHARSET &ch_set, UNICHAR_ID unichar_id)
Definition: reject.cpp:785
int count(LIST var_list)
Definition: oldlist.cpp:108
bool get_enabled(UNICHAR_ID unichar_id) const
Definition: unicharset.h:826
bool get_isalpha(UNICHAR_ID unichar_id) const
Definition: unicharset.h:449
void flip_hyphens(WERD_RES *word)
Definition: reject.cpp:616
inT16 reject_count()
Definition: rejctmap.h:243
float x_scale() const
Definition: normalis.h:269
Definition: rect.h:30
#define TRUE
Definition: capi.h:28
float y_scale() const
Definition: normalis.h:272
void print() const
Definition: ratngs.h:563
ALL but initial lc.
Definition: control.h:39
Definition: strngs.h:44
void initialise(inT16 length)
Definition: rejctmap.cpp:318
#define NULL
Definition: host.h:144
float compute_reject_threshold(WERD_CHOICE *word)
Definition: reject.cpp:226
TBOX bounding_box() const
Definition: blobs.cpp:482
void reject_I_1_L(WERD_RES *word)
Definition: reject.cpp:191
const char * string() const
Definition: strngs.cpp:193
BOOL8 repeated_nonalphanum_wd(WERD_RES *word, ROW *row)
Definition: reject.cpp:582
inT16 top() const
Definition: rect.h:54
void rej_word_contains_blanks()
Definition: rejctmap.cpp:443
void rej_word_bad_permuter()
Definition: rejctmap.cpp:452
void reject_poor_matches(WERD_RES *word)
Definition: reject.cpp:207
void flip_0O(WERD_RES *word)
Definition: reject.cpp:673
BOOL8 contains(const char c) const
Definition: strngs.cpp:184
void rej_word_not_tess_accepted()
Definition: rejctmap.cpp:434
BOOL8 non_0_digit(const UNICHARSET &ch_set, UNICHAR_ID unichar_id)
Definition: reject.cpp:789
short inT16
Definition: host.h:100
bool contains_unichar_id(UNICHAR_ID unichar_id) const
Definition: unicharset.h:242