#include <tesseract/ltrresultiterator.h>
#include "helpers.h"
#include "pageres.h"
#include "tesseractclass.h"
#include <allheaders.h>
namespace tesseract {
LTRResultIterator::LTRResultIterator(PAGE_RES *page_res, Tesseract *tesseract, int scale,
int scaled_yres, int rect_left, int rect_top, int rect_width,
int rect_height)
: PageIterator(page_res, tesseract, scale, scaled_yres, rect_left, rect_top, rect_width,
rect_height)
, line_separator_("\n")
, paragraph_separator_("\n") {}
LTRResultIterator::~LTRResultIterator() = default;
char *LTRResultIterator::GetUTF8Text(PageIteratorLevel level) const {
if (it_->word() == nullptr) {
return nullptr;
}
std::string text;
PAGE_RES_IT res_it(*it_);
WERD_CHOICE *best_choice = res_it.word()->best_choice;
ASSERT_HOST(best_choice != nullptr);
if (level == RIL_SYMBOL) {
text = res_it.word()->BestUTF8(blob_index_, false);
} else if (level == RIL_WORD) {
text = best_choice->unichar_string();
} else {
bool eol = false;
bool eop = false;
do {
do {
do {
best_choice = res_it.word()->best_choice;
ASSERT_HOST(best_choice != nullptr);
text += best_choice->unichar_string();
text += " ";
res_it.forward();
eol = res_it.row() != res_it.prev_row();
} while (!eol);
text.resize(text.length() - 1);
text += line_separator_;
eop = res_it.block() != res_it.prev_block() ||
res_it.row()->row->para() != res_it.prev_row()->row->para();
} while (level != RIL_TEXTLINE && !eop);
if (eop) {
text += paragraph_separator_;
}
} while (level == RIL_BLOCK && res_it.block() == res_it.prev_block());
}
return copy_string(text);
}
void LTRResultIterator::SetLineSeparator(const char *new_line) {
line_separator_ = new_line;
}
void LTRResultIterator::SetParagraphSeparator(const char *new_para) {
paragraph_separator_ = new_para;
}
float LTRResultIterator::Confidence(PageIteratorLevel level) const {
if (it_->word() == nullptr) {
return 0.0f;
}
float mean_certainty = 0.0f;
int certainty_count = 0;
PAGE_RES_IT res_it(*it_);
WERD_CHOICE *best_choice;
switch (level) {
case RIL_BLOCK:
do {
best_choice = res_it.word()->best_choice;
mean_certainty += best_choice->certainty();
++certainty_count;
res_it.forward();
} while (res_it.block() == res_it.prev_block());
break;
case RIL_PARA:
do {
best_choice = res_it.word()->best_choice;
mean_certainty += best_choice->certainty();
++certainty_count;
res_it.forward();
} while (res_it.block() == res_it.prev_block() &&
res_it.row()->row->para() == res_it.prev_row()->row->para());
break;
case RIL_TEXTLINE:
do {
best_choice = res_it.word()->best_choice;
mean_certainty += best_choice->certainty();
++certainty_count;
res_it.forward();
} while (res_it.row() == res_it.prev_row());
break;
case RIL_WORD:
best_choice = res_it.word()->best_choice;
mean_certainty = best_choice->certainty();
certainty_count = 1;
break;
case RIL_SYMBOL:
best_choice = res_it.word()->best_choice;
mean_certainty = best_choice->certainty(blob_index_);
certainty_count = 1;
}
if (certainty_count > 0) {
mean_certainty /= certainty_count;
return ClipToRange(100 + 5 * mean_certainty, 0.0f, 100.0f);
}
return 0.0f;
}
const char *LTRResultIterator::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 *result = nullptr;
if (it_->word() == nullptr) {
*pointsize = 0;
} else {
float row_height =
it_->row()->row->x_height() + it_->row()->row->ascenders() - it_->row()->row->descenders();
*pointsize =
scaled_yres_ > 0 ? static_cast<int>(row_height * kPointsPerInch / scaled_yres_ + 0.5) : 0;
#ifndef DISABLED_LEGACY_ENGINE
const FontInfo *font_info = it_->word()->fontinfo;
if (font_info) {
*font_id = font_info->universal_id;
*is_bold = font_info->is_bold();
*is_italic = font_info->is_italic();
*is_underlined = false;
*is_monospace = font_info->is_fixed_pitch();
*is_serif = font_info->is_serif();
result = font_info->name;
}
#endif
*is_smallcaps = it_->word()->small_caps;
}
if (!result) {
*is_bold = false;
*is_italic = false;
*is_underlined = false;
*is_monospace = false;
*is_serif = false;
*is_smallcaps = false;
*font_id = -1;
}
return result;
}
const char *LTRResultIterator::WordRecognitionLanguage() const {
if (it_->word() == nullptr || it_->word()->tesseract == nullptr) {
return nullptr;
}
return it_->word()->tesseract->lang.c_str();
}
StrongScriptDirection LTRResultIterator::WordDirection() const {
if (it_->word() == nullptr) {
return DIR_NEUTRAL;
}
bool has_rtl = it_->word()->AnyRtlCharsInWord();
bool has_ltr = it_->word()->AnyLtrCharsInWord();
if (has_rtl && !has_ltr) {
return DIR_RIGHT_TO_LEFT;
}
if (has_ltr && !has_rtl) {
return DIR_LEFT_TO_RIGHT;
}
if (!has_ltr && !has_rtl) {
return DIR_NEUTRAL;
}
return DIR_MIX;
}
bool LTRResultIterator::WordIsFromDictionary() const {
if (it_->word() == nullptr) {
return false;
}
int permuter = it_->word()->best_choice->permuter();
return permuter == SYSTEM_DAWG_PERM || permuter == FREQ_DAWG_PERM || permuter == USER_DAWG_PERM;
}
int LTRResultIterator::BlanksBeforeWord() const {
if (it_->word() == nullptr) {
return 1;
}
return it_->word()->word->space();
}
bool LTRResultIterator::WordIsNumeric() const {
if (it_->word() == nullptr) {
return false;
}
int permuter = it_->word()->best_choice->permuter();
return permuter == NUMBER_PERM;
}
bool LTRResultIterator::HasBlamerInfo() const {
return it_->word() != nullptr && it_->word()->blamer_bundle != nullptr &&
it_->word()->blamer_bundle->HasDebugInfo();
}
#ifndef DISABLED_LEGACY_ENGINE
const void *LTRResultIterator::GetParamsTrainingBundle() const {
return (it_->word() != nullptr && it_->word()->blamer_bundle != nullptr)
? &(it_->word()->blamer_bundle->params_training_bundle())
: nullptr;
}
#endif
const char *LTRResultIterator::GetBlamerDebug() const {
return it_->word()->blamer_bundle->debug().c_str();
}
const char *LTRResultIterator::GetBlamerMisadaptionDebug() const {
return it_->word()->blamer_bundle->misadaption_debug().c_str();
}
bool LTRResultIterator::HasTruthString() const {
if (it_->word() == nullptr) {
return false;
}
if (it_->word()->blamer_bundle == nullptr || it_->word()->blamer_bundle->NoTruth()) {
return false;
}
return true;
}
bool LTRResultIterator::EquivalentToTruth(const char *str) const {
if (!HasTruthString()) {
return false;
}
ASSERT_HOST(it_->word()->uch_set != nullptr);
WERD_CHOICE str_wd(str, *(it_->word()->uch_set));
return it_->word()->blamer_bundle->ChoiceIsCorrect(&str_wd);
}
char *LTRResultIterator::WordTruthUTF8Text() const {
if (!HasTruthString()) {
return nullptr;
}
return copy_string(it_->word()->blamer_bundle->TruthString());
}
char *LTRResultIterator::WordNormedUTF8Text() const {
if (it_->word() == nullptr) {
return nullptr;
}
std::string ocr_text;
WERD_CHOICE *best_choice = it_->word()->best_choice;
const UNICHARSET *unicharset = it_->word()->uch_set;
for (unsigned i = 0; i < best_choice->length(); ++i) {
ocr_text += unicharset->get_normed_unichar(best_choice->unichar_id(i));
}
return copy_string(ocr_text);
}
const char *LTRResultIterator::WordLattice(int *lattice_size) const {
if (it_->word() == nullptr) {
return nullptr;
}
if (it_->word()->blamer_bundle == nullptr) {
return nullptr;
}
*lattice_size = it_->word()->blamer_bundle->lattice_size();
return it_->word()->blamer_bundle->lattice_data();
}
bool LTRResultIterator::SymbolIsSuperscript() const {
if (cblob_it_ == nullptr && it_->word() != nullptr) {
return it_->word()->best_choice->BlobPosition(blob_index_) == SP_SUPERSCRIPT;
}
return false;
}
bool LTRResultIterator::SymbolIsSubscript() const {
if (cblob_it_ == nullptr && it_->word() != nullptr) {
return it_->word()->best_choice->BlobPosition(blob_index_) == SP_SUBSCRIPT;
}
return false;
}
bool LTRResultIterator::SymbolIsDropcap() const {
if (cblob_it_ == nullptr && it_->word() != nullptr) {
return it_->word()->best_choice->BlobPosition(blob_index_) == SP_DROPCAP;
}
return false;
}
ChoiceIterator::ChoiceIterator(const LTRResultIterator &result_it) {
ASSERT_HOST(result_it.it_->word() != nullptr);
word_res_ = result_it.it_->word();
oemLSTM_ = word_res_->tesseract->AnyLSTMLang();
bool oemLegacy = word_res_->tesseract->AnyTessLang();
bool lstm_choice_mode = word_res_->tesseract->lstm_choice_mode;
rating_coefficient_ = word_res_->tesseract->lstm_rating_coefficient;
blanks_before_word_ = result_it.BlanksBeforeWord();
BLOB_CHOICE_LIST *choices = nullptr;
tstep_index_ = &result_it.blob_index_;
if (oemLSTM_ && !word_res_->CTC_symbol_choices.empty()) {
if (!word_res_->CTC_symbol_choices[0].empty() &&
strcmp(word_res_->CTC_symbol_choices[0][0].first, " ")) {
blanks_before_word_ = 0;
}
unsigned index = *tstep_index_;
index += blanks_before_word_;
if (index < word_res_->CTC_symbol_choices.size()) {
LSTM_choices_ = &word_res_->CTC_symbol_choices[index];
filterSpaces();
}
}
if ((oemLegacy || !lstm_choice_mode) && word_res_->ratings != nullptr) {
choices = word_res_->GetBlobChoices(result_it.blob_index_);
}
if (choices != nullptr && !choices->empty()) {
choice_it_ = new BLOB_CHOICE_IT(choices);
choice_it_->mark_cycle_pt();
} else {
choice_it_ = nullptr;
}
if (LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
LSTM_choice_it_ = LSTM_choices_->begin();
}
}
ChoiceIterator::~ChoiceIterator() {
delete choice_it_;
}
bool ChoiceIterator::Next() {
if (oemLSTM_ && LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
if (LSTM_choice_it_ == LSTM_choices_->end() ||
next(LSTM_choice_it_) == LSTM_choices_->end()) {
return false;
} else {
++LSTM_choice_it_;
return true;
}
} else {
if (choice_it_ == nullptr) {
return false;
}
choice_it_->forward();
return !choice_it_->cycled_list();
}
}
const char *ChoiceIterator::GetUTF8Text() const {
if (oemLSTM_ && LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
std::pair<const char *, float> choice = *LSTM_choice_it_;
return choice.first;
} else {
if (choice_it_ == nullptr) {
return nullptr;
}
UNICHAR_ID id = choice_it_->data()->unichar_id();
return word_res_->uch_set->id_to_unichar_ext(id);
}
}
float ChoiceIterator::Confidence() const {
float confidence;
if (oemLSTM_ && LSTM_choices_ != nullptr && !LSTM_choices_->empty()) {
std::pair<const char *, float> choice = *LSTM_choice_it_;
confidence = 100 - rating_coefficient_ * choice.second;
} else {
if (choice_it_ == nullptr) {
return 0.0f;
}
confidence = 100 + 5 * choice_it_->data()->certainty();
}
return ClipToRange(confidence, 0.0f, 100.0f);
}
std::vector<std::vector<std::pair<const char *, float>>> *ChoiceIterator::Timesteps() const {
unsigned offset = *tstep_index_ + blanks_before_word_;
if (offset >= word_res_->segmented_timesteps.size() || !oemLSTM_) {
return nullptr;
}
return &word_res_->segmented_timesteps[offset];
}
void ChoiceIterator::filterSpaces() {
if (LSTM_choices_->empty()) {
return;
}
std::vector<std::pair<const char *, float>>::iterator it;
for (it = LSTM_choices_->begin(); it != LSTM_choices_->end();) {
if (!strcmp(it->first, " ")) {
it = LSTM_choices_->erase(it);
} else {
++it;
}
}
}
}