tesseract v5.3.3.20231005
ltrresultiterator.cpp
Go to the documentation of this file.
1
2// 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
27namespace 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) {
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.
86void 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.
91void 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.)
158const 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.
298bool 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.
341const 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.
445const 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
479std::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
487void 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:54
@ SP_SUBSCRIPT
Definition: ratngs.h:254
@ SP_DROPCAP
Definition: ratngs.h:254
@ SP_SUPERSCRIPT
Definition: ratngs.h:254
StrongScriptDirection
Definition: unichar.h:41
@ DIR_MIX
Definition: unichar.h:45
@ DIR_LEFT_TO_RIGHT
Definition: unichar.h:43
@ DIR_RIGHT_TO_LEFT
Definition: unichar.h:44
@ DIR_NEUTRAL
Definition: unichar.h:42
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
Definition: helpers.h:105
int UNICHAR_ID
Definition: unichar.h:34
@ SYSTEM_DAWG_PERM
Definition: ratngs.h:244
@ NUMBER_PERM
Definition: ratngs.h:242
@ USER_DAWG_PERM
Definition: ratngs.h:246
@ FREQ_DAWG_PERM
Definition: ratngs.h:247
constexpr int kPointsPerInch
Definition: publictypes.h:31
def next(obj)
Definition: ast.py:56
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
std::string TruthString() const
Definition: blamer.h:124
const std::string & misadaption_debug() const
Definition: blamer.h:143
int lattice_size() const
Definition: blamer.h:166
const tesseract::ParamsTrainingBundle & params_training_bundle() const
Definition: blamer.h:176
bool HasDebugInfo() const
Definition: blamer.h:137
bool NoTruth() const
Definition: blamer.h:134
const std::string & debug() const
Definition: blamer.h:140
bool ChoiceIsCorrect(const WERD_CHOICE *word_choice) const
Definition: blamer.cpp:116
const char * lattice_data() const
Definition: blamer.h:163
int32_t universal_id
Definition: fontinfo.h:140
PARA * para() const
Definition: ocrrow.h:120
float x_height() const
Definition: ocrrow.h:66
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
BlamerBundle * blamer_bundle
Definition: pageres.h:250
const UNICHARSET * uch_set
Definition: pageres.h:201
const char * BestUTF8(unsigned blob_index, bool in_rtl_context) const
Definition: pageres.h:361
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 * block() const
Definition: pageres.h:769
WERD_RES * forward()
Definition: pageres.h:743
BLOCK_RES * prev_block() const
Definition: pageres.h:760
WERD_RES * word() const
Definition: pageres.h:763
ROW_RES * prev_row() const
Definition: pageres.h:757
ROW_RES * row() const
Definition: pageres.h:766
float certainty() const
Definition: ratngs.h:315
UNICHAR_ID unichar_id(unsigned index) const
Definition: ratngs.h:299
uint8_t permuter() const
Definition: ratngs.h:331
unsigned length() const
Definition: ratngs.h:287
std::string & unichar_string()
Definition: ratngs.h:519
ScriptPos BlobPosition(unsigned index) const
Definition: ratngs.h:306
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:859
const char * id_to_unichar_ext(UNICHAR_ID id) const
Definition: unicharset.cpp:287