tesseract  5.0.0
ltrresultiterator.cpp
Go to the documentation of this file.
1 // File: ltrresultiterator.cpp
3 // Description: Iterator for tesseract results in strict left-to-right
4 // order that avoids using tesseract internal data structures.
5 // Author: Ray Smith
6 //
7 // (C) Copyright 2010, Google Inc.
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 //
19 
21 
22 #include "pageres.h"
23 #include "tesseractclass.h"
24 
25 #include <allheaders.h>
26 
27 namespace tesseract {
28 
30  int scaled_yres, int rect_left, int rect_top, int rect_width,
31  int rect_height)
32  : PageIterator(page_res, tesseract, scale, scaled_yres, rect_left, rect_top, rect_width,
33  rect_height)
34  , line_separator_("\n")
35  , paragraph_separator_("\n") {}
36 
37 // Destructor.
38 // It is defined here, so the compiler can create a single vtable
39 // instead of weak vtables in every compilation unit.
41 
42 // Returns the null terminated UTF-8 encoded text string for the current
43 // object at the given level. Use delete [] to free after use.
45  if (it_->word() == nullptr) {
46  return nullptr; // Already at the end!
47  }
48  std::string text;
49  PAGE_RES_IT res_it(*it_);
50  WERD_CHOICE *best_choice = res_it.word()->best_choice;
51  ASSERT_HOST(best_choice != nullptr);
52  if (level == RIL_SYMBOL) {
53  text = res_it.word()->BestUTF8(blob_index_, false);
54  } else if (level == RIL_WORD) {
55  text = best_choice->unichar_string();
56  } else {
57  bool eol = false; // end of line?
58  bool eop = false; // end of paragraph?
59  do { // for each paragraph in a block
60  do { // for each text line in a paragraph
61  do { // for each word in a text line
62  best_choice = res_it.word()->best_choice;
63  ASSERT_HOST(best_choice != nullptr);
64  text += best_choice->unichar_string();
65  text += " ";
66  res_it.forward();
67  eol = res_it.row() != res_it.prev_row();
68  } while (!eol);
69  text.resize(text.length() - 1);
70  text += line_separator_;
71  eop = res_it.block() != res_it.prev_block() ||
72  res_it.row()->row->para() != res_it.prev_row()->row->para();
73  } while (level != RIL_TEXTLINE && !eop);
74  if (eop) {
75  text += paragraph_separator_;
76  }
77  } while (level == RIL_BLOCK && res_it.block() == res_it.prev_block());
78  }
79  int length = text.length() + 1;
80  char *result = new char[length];
81  strncpy(result, text.c_str(), length);
82  return result;
83 }
84 
85 // Set the string inserted at the end of each text line. "\n" by default.
86 void LTRResultIterator::SetLineSeparator(const char *new_line) {
87  line_separator_ = new_line;
88 }
89 
90 // Set the string inserted at the end of each paragraph. "\n" by default.
91 void LTRResultIterator::SetParagraphSeparator(const char *new_para) {
92  paragraph_separator_ = new_para;
93 }
94 
95 // Returns the mean confidence of the current object at the given level.
96 // The number should be interpreted as a percent probability. (0.0f-100.0f)
98  if (it_->word() == nullptr) {
99  return 0.0f; // Already at the end!
100  }
101  float mean_certainty = 0.0f;
102  int certainty_count = 0;
103  PAGE_RES_IT res_it(*it_);
104  WERD_CHOICE *best_choice = res_it.word()->best_choice;
105  ASSERT_HOST(best_choice != nullptr);
106  switch (level) {
107  case RIL_BLOCK:
108  do {
109  best_choice = res_it.word()->best_choice;
110  ASSERT_HOST(best_choice != nullptr);
111  mean_certainty += best_choice->certainty();
112  ++certainty_count;
113  res_it.forward();
114  } while (res_it.block() == res_it.prev_block());
115  break;
116  case RIL_PARA:
117  do {
118  best_choice = res_it.word()->best_choice;
119  ASSERT_HOST(best_choice != nullptr);
120  mean_certainty += best_choice->certainty();
121  ++certainty_count;
122  res_it.forward();
123  } while (res_it.block() == res_it.prev_block() &&
124  res_it.row()->row->para() == res_it.prev_row()->row->para());
125  break;
126  case RIL_TEXTLINE:
127  do {
128  best_choice = res_it.word()->best_choice;
129  ASSERT_HOST(best_choice != nullptr);
130  mean_certainty += best_choice->certainty();
131  ++certainty_count;
132  res_it.forward();
133  } while (res_it.row() == res_it.prev_row());
134  break;
135  case RIL_WORD:
136  mean_certainty += best_choice->certainty();
137  ++certainty_count;
138  break;
139  case RIL_SYMBOL:
140  mean_certainty += best_choice->certainty(blob_index_);
141  ++certainty_count;
142  }
143  if (certainty_count > 0) {
144  mean_certainty /= certainty_count;
145  return ClipToRange(100 + 5 * mean_certainty, 0.0f, 100.0f);
146  }
147  return 0.0f;
148 }
149 
150 // Returns the font attributes of the current word. If iterating at a higher
151 // level object than words, eg textlines, then this will return the
152 // attributes of the first word in that textline.
153 // The actual return value is a string representing a font name. It points
154 // to an internal table and SHOULD NOT BE DELETED. Lifespan is the same as
155 // the iterator itself, ie rendered invalid by various members of
156 // TessBaseAPI, including Init, SetImage, End or deleting the TessBaseAPI.
157 // Pointsize is returned in printers points (1/72 inch.)
158 const char *LTRResultIterator::WordFontAttributes(bool *is_bold, bool *is_italic,
159  bool *is_underlined, bool *is_monospace,
160  bool *is_serif, bool *is_smallcaps,
161  int *pointsize, int *font_id) const {
162  const char *result = nullptr;
163 
164  if (it_->word() == nullptr) {
165  // Already at the end!
166  *pointsize = 0;
167  } else {
168  float row_height =
169  it_->row()->row->x_height() + it_->row()->row->ascenders() - it_->row()->row->descenders();
170  // Convert from pixels to printers points.
171  *pointsize =
172  scaled_yres_ > 0 ? static_cast<int>(row_height * kPointsPerInch / scaled_yres_ + 0.5) : 0;
173 
174 #ifndef DISABLED_LEGACY_ENGINE
175  const FontInfo *font_info = it_->word()->fontinfo;
176  if (font_info) {
177  // Font information available.
178  *font_id = font_info->universal_id;
179  *is_bold = font_info->is_bold();
180  *is_italic = font_info->is_italic();
181  *is_underlined = false; // TODO(rays) fix this!
182  *is_monospace = font_info->is_fixed_pitch();
183  *is_serif = font_info->is_serif();
184  result = font_info->name;
185  }
186 #endif // ndef DISABLED_LEGACY_ENGINE
187 
188  *is_smallcaps = it_->word()->small_caps;
189  }
190 
191  if (!result) {
192  *is_bold = false;
193  *is_italic = false;
194  *is_underlined = false;
195  *is_monospace = false;
196  *is_serif = false;
197  *is_smallcaps = false;
198  *font_id = -1;
199  }
200 
201  return result;
202 }
203 
204 // Returns the name of the language used to recognize this word.
206  if (it_->word() == nullptr || it_->word()->tesseract == nullptr) {
207  return nullptr;
208  }
209  return it_->word()->tesseract->lang.c_str();
210 }
211 
212 // Return the overall directionality of this word.
214  if (it_->word() == nullptr) {
215  return DIR_NEUTRAL;
216  }
217  bool has_rtl = it_->word()->AnyRtlCharsInWord();
218  bool has_ltr = it_->word()->AnyLtrCharsInWord();
219  if (has_rtl && !has_ltr) {
220  return DIR_RIGHT_TO_LEFT;
221  }
222  if (has_ltr && !has_rtl) {
223  return DIR_LEFT_TO_RIGHT;
224  }
225  if (!has_ltr && !has_rtl) {
226  return DIR_NEUTRAL;
227  }
228  return DIR_MIX;
229 }
230 
231 // Returns true if the current word was found in a dictionary.
233  if (it_->word() == nullptr) {
234  return false; // Already at the end!
235  }
236  int permuter = it_->word()->best_choice->permuter();
237  return permuter == SYSTEM_DAWG_PERM || permuter == FREQ_DAWG_PERM || permuter == USER_DAWG_PERM;
238 }
239 
240 // Returns the number of blanks before the current word.
242  if (it_->word() == nullptr) {
243  return 1;
244  }
245  return it_->word()->word->space();
246 }
247 
248 // Returns true if the current word is numeric.
250  if (it_->word() == nullptr) {
251  return false; // Already at the end!
252  }
253  int permuter = it_->word()->best_choice->permuter();
254  return permuter == NUMBER_PERM;
255 }
256 
257 // Returns true if the word contains blamer information.
259  return it_->word() != nullptr && it_->word()->blamer_bundle != nullptr &&
261 }
262 
263 #ifndef DISABLED_LEGACY_ENGINE
264 // Returns the pointer to ParamsTrainingBundle stored in the BlamerBundle
265 // of the current word.
267  return (it_->word() != nullptr && it_->word()->blamer_bundle != nullptr)
269  : nullptr;
270 }
271 #endif // ndef DISABLED_LEGACY_ENGINE
272 
273 // Returns the pointer to the string with blamer information for this word.
274 // Assumes that the word's blamer_bundle is not nullptr.
276  return it_->word()->blamer_bundle->debug().c_str();
277 }
278 
279 // Returns the pointer to the string with misadaption information for this word.
280 // Assumes that the word's blamer_bundle is not nullptr.
282  return it_->word()->blamer_bundle->misadaption_debug().c_str();
283 }
284 
285 // Returns true if a truth string was recorded for the current word.
287  if (it_->word() == nullptr) {
288  return false; // Already at the end!
289  }
290  if (it_->word()->blamer_bundle == nullptr || it_->word()->blamer_bundle->NoTruth()) {
291  return false; // no truth information for this word
292  }
293  return true;
294 }
295 
296 // Returns true if the given string is equivalent to the truth string for
297 // the current word.
298 bool LTRResultIterator::EquivalentToTruth(const char *str) const {
299  if (!HasTruthString()) {
300  return false;
301  }
302  ASSERT_HOST(it_->word()->uch_set != nullptr);
303  WERD_CHOICE str_wd(str, *(it_->word()->uch_set));
304  return it_->word()->blamer_bundle->ChoiceIsCorrect(&str_wd);
305 }
306 
307 // Returns the null terminated UTF-8 encoded truth string for the current word.
308 // Use delete [] to free after use.
310  if (!HasTruthString()) {
311  return nullptr;
312  }
313  std::string truth_text = it_->word()->blamer_bundle->TruthString();
314  int length = truth_text.length() + 1;
315  char *result = new char[length];
316  strncpy(result, truth_text.c_str(), length);
317  return result;
318 }
319 
320 // Returns the null terminated UTF-8 encoded normalized OCR string for the
321 // current word. Use delete [] to free after use.
323  if (it_->word() == nullptr) {
324  return nullptr; // Already at the end!
325  }
326  std::string ocr_text;
327  WERD_CHOICE *best_choice = it_->word()->best_choice;
328  const UNICHARSET *unicharset = it_->word()->uch_set;
329  ASSERT_HOST(best_choice != nullptr);
330  for (unsigned i = 0; i < best_choice->length(); ++i) {
331  ocr_text += unicharset->get_normed_unichar(best_choice->unichar_id(i));
332  }
333  auto length = ocr_text.length() + 1;
334  char *result = new char[length];
335  strncpy(result, ocr_text.c_str(), length);
336  return result;
337 }
338 
339 // Returns a pointer to serialized choice lattice.
340 // Fills lattice_size with the number of bytes in lattice data.
341 const char *LTRResultIterator::WordLattice(int *lattice_size) const {
342  if (it_->word() == nullptr) {
343  return nullptr; // Already at the end!
344  }
345  if (it_->word()->blamer_bundle == nullptr) {
346  return nullptr;
347  }
348  *lattice_size = it_->word()->blamer_bundle->lattice_size();
349  return it_->word()->blamer_bundle->lattice_data();
350 }
351 
352 // Returns true if the current symbol is a superscript.
353 // If iterating at a higher level object than symbols, eg words, then
354 // this will return the attributes of the first symbol in that word.
356  if (cblob_it_ == nullptr && it_->word() != nullptr) {
358  }
359  return false;
360 }
361 
362 // Returns true if the current symbol is a subscript.
363 // If iterating at a higher level object than symbols, eg words, then
364 // this will return the attributes of the first symbol in that word.
366  if (cblob_it_ == nullptr && it_->word() != nullptr) {
368  }
369  return false;
370 }
371 
372 // Returns true if the current symbol is a dropcap.
373 // If iterating at a higher level object than symbols, eg words, then
374 // this will return the attributes of the first symbol in that word.
376  if (cblob_it_ == nullptr && it_->word() != nullptr) {
378  }
379  return false;
380 }
381 
383  ASSERT_HOST(result_it.it_->word() != nullptr);
384  word_res_ = result_it.it_->word();
385  oemLSTM_ = word_res_->tesseract->AnyLSTMLang();
386  // Is there legacy engine related trained data?
387  bool oemLegacy = word_res_->tesseract->AnyTessLang();
388  // Is lstm_choice_mode activated?
389  bool lstm_choice_mode = word_res_->tesseract->lstm_choice_mode;
390  rating_coefficient_ = word_res_->tesseract->lstm_rating_coefficient;
391  blanks_before_word_ = result_it.BlanksBeforeWord();
392  BLOB_CHOICE_LIST *choices = nullptr;
393  tstep_index_ = &result_it.blob_index_;
394  if (oemLSTM_ && !word_res_->CTC_symbol_choices.empty()) {
395  if (!word_res_->CTC_symbol_choices[0].empty() &&
396  strcmp(word_res_->CTC_symbol_choices[0][0].first, " ")) {
397  blanks_before_word_ = 0;
398  }
399  unsigned index = *tstep_index_;
400  index += blanks_before_word_;
401  if (index < word_res_->CTC_symbol_choices.size()) {
402  LSTM_choices_ = &word_res_->CTC_symbol_choices[index];
403  filterSpaces();
404  }
405  }
406  if ((oemLegacy || !lstm_choice_mode) && word_res_->ratings != nullptr) {
407  choices = word_res_->GetBlobChoices(result_it.blob_index_);
408  }
409  if (choices != nullptr && !choices->empty()) {
410  choice_it_ = new BLOB_CHOICE_IT(choices);
411  choice_it_->mark_cycle_pt();
412  } else {
413  choice_it_ = nullptr;
414  }
415  if (LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
416  LSTM_choice_it_ = LSTM_choices_->begin();
417  }
418 }
420  delete choice_it_;
421 }
422 
423 // Moves to the next choice for the symbol and returns false if there
424 // are none left.
426  if (oemLSTM_ && LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
427  if (LSTM_choice_it_ == LSTM_choices_->end() ||
428  next(LSTM_choice_it_) == LSTM_choices_->end()) {
429  return false;
430  } else {
431  ++LSTM_choice_it_;
432  return true;
433  }
434  } else {
435  if (choice_it_ == nullptr) {
436  return false;
437  }
438  choice_it_->forward();
439  return !choice_it_->cycled_list();
440  }
441 }
442 
443 // Returns the null terminated UTF-8 encoded text string for the current
444 // choice. Do NOT use delete [] to free after use.
445 const char *ChoiceIterator::GetUTF8Text() const {
446  if (oemLSTM_ && LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
447  std::pair<const char *, float> choice = *LSTM_choice_it_;
448  return choice.first;
449  } else {
450  if (choice_it_ == nullptr) {
451  return nullptr;
452  }
453  UNICHAR_ID id = choice_it_->data()->unichar_id();
454  return word_res_->uch_set->id_to_unichar_ext(id);
455  }
456 }
457 
458 // Returns the confidence of the current choice depending on the used language
459 // data. If only LSTM traineddata is used the value range is 0.0f - 1.0f. All
460 // choices for one symbol should roughly add up to 1.0f.
461 // If only traineddata of the legacy engine is used, the number should be
462 // interpreted as a percent probability. (0.0f-100.0f) In this case
463 // probabilities won't add up to 100. Each one stands on its own.
465  float confidence;
466  if (oemLSTM_ && LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
467  std::pair<const char *, float> choice = *LSTM_choice_it_;
468  confidence = 100 - rating_coefficient_ * choice.second;
469  } else {
470  if (choice_it_ == nullptr) {
471  return 0.0f;
472  }
473  confidence = 100 + 5 * choice_it_->data()->certainty();
474  }
475  return ClipToRange(confidence, 0.0f, 100.0f);
476 }
477 
478 // Returns the set of timesteps which belong to the current symbol
479 std::vector<std::vector<std::pair<const char *, float>>> *ChoiceIterator::Timesteps() const {
480  unsigned offset = *tstep_index_ + blanks_before_word_;
481  if (offset >= word_res_->segmented_timesteps.size() || !oemLSTM_) {
482  return nullptr;
483  }
484  return &word_res_->segmented_timesteps[offset];
485 }
486 
487 void ChoiceIterator::filterSpaces() {
488  if (LSTM_choices_->empty()) {
489  return;
490  }
491  std::vector<std::pair<const char *, float>>::iterator it;
492  for (it = LSTM_choices_->begin(); it != LSTM_choices_->end();) {
493  if (!strcmp(it->first, " ")) {
494  it = LSTM_choices_->erase(it);
495  } else {
496  ++it;
497  }
498  }
499 }
500 } // namespace tesseract.
#define ASSERT_HOST(x)
Definition: errcode.h:59
@ SP_SUBSCRIPT
Definition: ratngs.h:250
@ SP_DROPCAP
Definition: ratngs.h:250
@ SP_SUPERSCRIPT
Definition: ratngs.h:250
StrongScriptDirection
Definition: unichar.h:43
@ DIR_MIX
Definition: unichar.h:47
@ DIR_LEFT_TO_RIGHT
Definition: unichar.h:45
@ DIR_RIGHT_TO_LEFT
Definition: unichar.h:46
@ DIR_NEUTRAL
Definition: unichar.h:44
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
Definition: helpers.h:110
int UNICHAR_ID
Definition: unichar.h:36
@ SYSTEM_DAWG_PERM
Definition: ratngs.h:240
@ NUMBER_PERM
Definition: ratngs.h:238
@ USER_DAWG_PERM
Definition: ratngs.h:242
@ FREQ_DAWG_PERM
Definition: ratngs.h:243
constexpr int kPointsPerInch
Definition: publictypes.h:33
const char * GetBlamerDebug() const
char * GetUTF8Text(PageIteratorLevel level) const
const void * GetParamsTrainingBundle() const
StrongScriptDirection WordDirection() const
const char * WordFontAttributes(bool *is_bold, bool *is_italic, bool *is_underlined, bool *is_monospace, bool *is_serif, bool *is_smallcaps, int *pointsize, int *font_id) const
const char * GetBlamerMisadaptionDebug() const
const char * WordRecognitionLanguage() const
void SetLineSeparator(const char *new_line)
const char * WordLattice(int *lattice_size) const
LTRResultIterator(PAGE_RES *page_res, Tesseract *tesseract, int scale, int scaled_yres, int rect_left, int rect_top, int rect_width, int rect_height)
void SetParagraphSeparator(const char *new_para)
float Confidence(PageIteratorLevel level) const
bool EquivalentToTruth(const char *str) const
const char * GetUTF8Text() const
ChoiceIterator(const LTRResultIterator &result_it)
std::vector< std::vector< std::pair< const char *, float > > > * Timesteps() const
bool AnyTessLang() const
bool AnyLSTMLang() const
const std::string & debug() const
Definition: blamer.h:140
std::string TruthString() const
Definition: blamer.h:124
const tesseract::ParamsTrainingBundle & params_training_bundle() const
Definition: blamer.h:176
int lattice_size() const
Definition: blamer.h:166
bool HasDebugInfo() const
Definition: blamer.h:137
bool NoTruth() const
Definition: blamer.h:134
bool ChoiceIsCorrect(const WERD_CHOICE *word_choice) const
Definition: blamer.cpp:116
const std::string & misadaption_debug() const
Definition: blamer.h:143
const char * lattice_data() const
Definition: blamer.h:163
int32_t universal_id
Definition: fontinfo.h:140
float x_height() const
Definition: ocrrow.h:66
PARA * para() const
Definition: ocrrow.h:120
float ascenders() const
Definition: ocrrow.h:84
float descenders() const
Definition: ocrrow.h:87
tesseract::Tesseract * tesseract
Definition: pageres.h:278
WERD_CHOICE * best_choice
Definition: pageres.h:239
bool AnyRtlCharsInWord() const
Definition: pageres.h:394
const char * BestUTF8(unsigned blob_index, bool in_rtl_context) const
Definition: pageres.h:361
BlamerBundle * blamer_bundle
Definition: pageres.h:250
const UNICHARSET * uch_set
Definition: pageres.h:201
const FontInfo * fontinfo
Definition: pageres.h:307
BLOB_CHOICE_LIST * GetBlobChoices(int index) const
Definition: pageres.cpp:779
MATRIX * ratings
Definition: pageres.h:235
std::vector< std::vector< std::pair< const char *, float > > > CTC_symbol_choices
Definition: pageres.h:224
bool AnyLtrCharsInWord() const
Definition: pageres.h:413
std::vector< std::vector< std::vector< std::pair< const char *, float > > > > segmented_timesteps
Definition: pageres.h:222
BLOCK_RES * prev_block() const
Definition: pageres.h:760
ROW_RES * prev_row() const
Definition: pageres.h:757
WERD_RES * forward()
Definition: pageres.h:743
ROW_RES * row() const
Definition: pageres.h:766
WERD_RES * word() const
Definition: pageres.h:763
BLOCK_RES * block() const
Definition: pageres.h:769
float certainty() const
Definition: ratngs.h:311
UNICHAR_ID unichar_id(unsigned index) const
Definition: ratngs.h:295
uint8_t permuter() const
Definition: ratngs.h:327
unsigned length() const
Definition: ratngs.h:283
std::string & unichar_string()
Definition: ratngs.h:515
ScriptPos BlobPosition(unsigned index) const
Definition: ratngs.h:302
uint8_t space() const
Definition: werd.h:100
std::string lang
Definition: ccutil.h:59
const char * get_normed_unichar(UNICHAR_ID unichar_id) const
Definition: unicharset.h:860
const char * id_to_unichar_ext(UNICHAR_ID id) const
Definition: unicharset.cpp:287