diff options
Diffstat (limited to 'tesseract/src/ccstruct/ratngs.h')
-rw-r--r-- | tesseract/src/ccstruct/ratngs.h | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/tesseract/src/ccstruct/ratngs.h b/tesseract/src/ccstruct/ratngs.h new file mode 100644 index 00000000..c2bc962a --- /dev/null +++ b/tesseract/src/ccstruct/ratngs.h @@ -0,0 +1,661 @@ +/********************************************************************** + * File: ratngs.h (Formerly ratings.h) + * Description: Definition of the WERD_CHOICE and BLOB_CHOICE classes. + * Author: Ray Smith + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef RATNGS_H +#define RATNGS_H + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" // DISABLED_LEGACY_ENGINE +#endif + +#include "clst.h" +#include "elst.h" +#ifndef DISABLED_LEGACY_ENGINE +#include "fontinfo.h" +#endif // undef DISABLED_LEGACY_ENGINE +#include "matrix.h" +#include "unicharset.h" +#include "werd.h" + +#include "genericvector.h" +#include <tesseract/unichar.h> + +#include <cassert> +#include <cfloat> // for FLT_MAX + +namespace tesseract { + +class MATRIX; +struct TBLOB; +struct TWERD; + +// Enum to describe the source of a BLOB_CHOICE to make it possible to determine +// whether a blob has been classified by inspecting the BLOB_CHOICEs. +enum BlobChoiceClassifier { + BCC_STATIC_CLASSIFIER, // From the char_norm classifier. + BCC_ADAPTED_CLASSIFIER, // From the adaptive classifier. + BCC_SPECKLE_CLASSIFIER, // Backup for failed classification. + BCC_AMBIG, // Generated by ambiguity detection. + BCC_FAKE, // From some other process. +}; + +class BLOB_CHOICE: public ELIST_LINK +{ + public: + BLOB_CHOICE() { + unichar_id_ = UNICHAR_SPACE; + fontinfo_id_ = -1; + fontinfo_id2_ = -1; + rating_ = 10.0; + certainty_ = -1.0; + script_id_ = -1; + min_xheight_ = 0.0f; + max_xheight_ = 0.0f; + yshift_ = 0.0f; + classifier_ = BCC_FAKE; + } + BLOB_CHOICE(UNICHAR_ID src_unichar_id, // character id + float src_rating, // rating + float src_cert, // certainty + int script_id, // script + float min_xheight, // min xheight in image pixel units + float max_xheight, // max xheight allowed by this char + float yshift, // the larger of y shift (top or bottom) + BlobChoiceClassifier c); // adapted match or other + BLOB_CHOICE(const BLOB_CHOICE &other); + ~BLOB_CHOICE() = default; + + UNICHAR_ID unichar_id() const { + return unichar_id_; + } + float rating() const { + return rating_; + } + float certainty() const { + return certainty_; + } + int16_t fontinfo_id() const { + return fontinfo_id_; + } + int16_t fontinfo_id2() const { + return fontinfo_id2_; + } + #ifndef DISABLED_LEGACY_ENGINE + const std::vector<ScoredFont>& fonts() const { + return fonts_; + } + void set_fonts(const std::vector<ScoredFont>& fonts) { + fonts_ = fonts; + int score1 = 0, score2 = 0; + fontinfo_id_ = -1; + fontinfo_id2_ = -1; + for (int f = 0; f < fonts_.size(); ++f) { + if (fonts_[f].score > score1) { + score2 = score1; + fontinfo_id2_ = fontinfo_id_; + score1 = fonts_[f].score; + fontinfo_id_ = fonts_[f].fontinfo_id; + } else if (fonts_[f].score > score2) { + score2 = fonts_[f].score; + fontinfo_id2_ = fonts_[f].fontinfo_id; + } + } + } + #endif // ndef DISABLED_LEGACY_ENGINE + int script_id() const { + return script_id_; + } + const MATRIX_COORD& matrix_cell() { + return matrix_cell_; + } + float min_xheight() const { + return min_xheight_; + } + float max_xheight() const { + return max_xheight_; + } + float yshift() const { + return yshift_; + } + BlobChoiceClassifier classifier() const { + return classifier_; + } + bool IsAdapted() const { + return classifier_ == BCC_ADAPTED_CLASSIFIER; + } + bool IsClassified() const { + return classifier_ == BCC_STATIC_CLASSIFIER || + classifier_ == BCC_ADAPTED_CLASSIFIER || + classifier_ == BCC_SPECKLE_CLASSIFIER; + } + + void set_unichar_id(UNICHAR_ID newunichar_id) { + unichar_id_ = newunichar_id; + } + void set_rating(float newrat) { + rating_ = newrat; + } + void set_certainty(float newrat) { + certainty_ = newrat; + } + void set_script(int newscript_id) { + script_id_ = newscript_id; + } + void set_matrix_cell(int col, int row) { + matrix_cell_.col = col; + matrix_cell_.row = row; + } + void set_classifier(BlobChoiceClassifier classifier) { + classifier_ = classifier; + } + static BLOB_CHOICE* deep_copy(const BLOB_CHOICE* src) { + auto* choice = new BLOB_CHOICE; + *choice = *src; + return choice; + } + // Returns true if *this and other agree on the baseline and x-height + // to within some tolerance based on a given estimate of the x-height. + bool PosAndSizeAgree(const BLOB_CHOICE& other, float x_height, + bool debug) const; + + void print(const UNICHARSET *unicharset) const { + tprintf("r%.2f c%.2f x[%g,%g]: %d %s", + rating_, certainty_, + min_xheight_, max_xheight_, unichar_id_, + (unicharset == nullptr) ? "" : + unicharset->debug_str(unichar_id_).c_str()); + } + void print_full() const { + print(nullptr); + tprintf(" script=%d, font1=%d, font2=%d, yshift=%g, classifier=%d\n", + script_id_, fontinfo_id_, fontinfo_id2_, yshift_, classifier_); + } + // Sort function for sorting BLOB_CHOICEs in increasing order of rating. + static int SortByRating(const void *p1, const void *p2) { + const BLOB_CHOICE *bc1 = *static_cast<const BLOB_CHOICE *const *>(p1); + const BLOB_CHOICE *bc2 = *static_cast<const BLOB_CHOICE *const *>(p2); + return (bc1->rating_ < bc2->rating_) ? -1 : 1; + } + + private: + // Copy assignment operator. + BLOB_CHOICE& operator=(const BLOB_CHOICE& other); + + UNICHAR_ID unichar_id_; // unichar id +#ifndef DISABLED_LEGACY_ENGINE + // Fonts and scores. Allowed to be empty. + std::vector<ScoredFont> fonts_; +#endif // ndef DISABLED_LEGACY_ENGINE + int16_t fontinfo_id_; // char font information + int16_t fontinfo_id2_; // 2nd choice font information + // Rating is the classifier distance weighted by the length of the outline + // in the blob. In terms of probability, classifier distance is -klog p such + // that the resulting distance is in the range [0, 1] and then + // rating = w (-k log p) where w is the weight for the length of the outline. + // Sums of ratings may be compared meaningfully for words of different + // segmentation. + float rating_; // size related + // Certainty is a number in [-20, 0] indicating the classifier certainty + // of the choice. In terms of probability, certainty = 20 (k log p) where + // k is defined as above to normalize -klog p to the range [0, 1]. + float certainty_; // absolute + int script_id_; + // Holds the position of this choice in the ratings matrix. + // Used to location position in the matrix during path backtracking. + MATRIX_COORD matrix_cell_; + // X-height range (in image pixels) that this classification supports. + float min_xheight_; + float max_xheight_; + // yshift_ - The vertical distance (in image pixels) the character is + // shifted (up or down) from an acceptable y position. + float yshift_; + BlobChoiceClassifier classifier_; // What generated *this. +}; + +// Make BLOB_CHOICE listable. +ELISTIZEH(BLOB_CHOICE) + +// Return the BLOB_CHOICE in bc_list matching a given unichar_id, +// or nullptr if there is no match. +BLOB_CHOICE *FindMatchingChoice(UNICHAR_ID char_id, BLOB_CHOICE_LIST *bc_list); + +// Permuter codes used in WERD_CHOICEs. +enum PermuterType { + NO_PERM, // 0 + PUNC_PERM, // 1 + TOP_CHOICE_PERM, // 2 + LOWER_CASE_PERM, // 3 + UPPER_CASE_PERM, // 4 + NGRAM_PERM, // 5 + NUMBER_PERM, // 6 + USER_PATTERN_PERM, // 7 + SYSTEM_DAWG_PERM, // 8 + DOC_DAWG_PERM, // 9 + USER_DAWG_PERM, // 10 + FREQ_DAWG_PERM, // 11 + COMPOUND_PERM, // 12 + + NUM_PERMUTER_TYPES +}; + +// ScriptPos tells whether a character is subscript, superscript or normal. +enum ScriptPos { + SP_NORMAL, + SP_SUBSCRIPT, + SP_SUPERSCRIPT, + SP_DROPCAP +}; + +const char *ScriptPosToString(ScriptPos script_pos); + +class TESS_API WERD_CHOICE : public ELIST_LINK { + public: + static const float kBadRating; + static const char *permuter_name(uint8_t permuter); + + WERD_CHOICE(const UNICHARSET *unicharset) + : unicharset_(unicharset) { this->init(8); } + WERD_CHOICE(const UNICHARSET *unicharset, int reserved) + : unicharset_(unicharset) { this->init(reserved); } + WERD_CHOICE(const char *src_string, + const char *src_lengths, + float src_rating, + float src_certainty, + uint8_t src_permuter, + const UNICHARSET &unicharset) + : unicharset_(&unicharset) { + this->init(src_string, src_lengths, src_rating, + src_certainty, src_permuter); + } + WERD_CHOICE(const char *src_string, const UNICHARSET &unicharset); + WERD_CHOICE(const WERD_CHOICE &word) + : ELIST_LINK(word), unicharset_(word.unicharset_) { + this->init(word.length()); + this->operator=(word); + } + ~WERD_CHOICE(); + + const UNICHARSET *unicharset() const { + return unicharset_; + } + inline int length() const { + return length_; + } + float adjust_factor() const { + return adjust_factor_; + } + void set_adjust_factor(float factor) { + adjust_factor_ = factor; + } + inline const UNICHAR_ID *unichar_ids() const { + return unichar_ids_; + } + inline UNICHAR_ID unichar_id(int index) const { + assert(index < length_); + return unichar_ids_[index]; + } + inline int state(int index) const { + return state_[index]; + } + ScriptPos BlobPosition(int index) const { + if (index < 0 || index >= length_) + return SP_NORMAL; + return script_pos_[index]; + } + inline float rating() const { + return rating_; + } + inline float certainty() const { + return certainty_; + } + inline float certainty(int index) const { + return certainties_[index]; + } + inline float min_x_height() const { + return min_x_height_; + } + inline float max_x_height() const { + return max_x_height_; + } + inline void set_x_heights(float min_height, float max_height) { + min_x_height_ = min_height; + max_x_height_ = max_height; + } + inline uint8_t permuter() const { + return permuter_; + } + const char *permuter_name() const; + // Returns the BLOB_CHOICE_LIST corresponding to the given index in the word, + // taken from the appropriate cell in the ratings MATRIX. + // Borrowed pointer, so do not delete. + BLOB_CHOICE_LIST* blob_choices(int index, MATRIX* ratings) const; + + // Returns the MATRIX_COORD corresponding to the location in the ratings + // MATRIX for the given index into the word. + MATRIX_COORD MatrixCoord(int index) const; + + inline void set_unichar_id(UNICHAR_ID unichar_id, int index) { + assert(index < length_); + unichar_ids_[index] = unichar_id; + } + bool dangerous_ambig_found() const { + return dangerous_ambig_found_; + } + void set_dangerous_ambig_found_(bool value) { + dangerous_ambig_found_ = value; + } + inline void set_rating(float new_val) { + rating_ = new_val; + } + inline void set_certainty(float new_val) { + certainty_ = new_val; + } + inline void set_permuter(uint8_t perm) { + permuter_ = perm; + } + // Note: this function should only be used if all the fields + // are populated manually with set_* functions (rather than + // (copy)constructors and append_* functions). + inline void set_length(int len) { + ASSERT_HOST(reserved_ >= len); + length_ = len; + } + + /// Make more space in unichar_id_ and fragment_lengths_ arrays. + inline void double_the_size() { + if (reserved_ > 0) { + unichar_ids_ = GenericVector<UNICHAR_ID>::double_the_size_memcpy( + reserved_, unichar_ids_); + script_pos_ = GenericVector<ScriptPos>::double_the_size_memcpy( + reserved_, script_pos_); + state_ = GenericVector<int>::double_the_size_memcpy( + reserved_, state_); + certainties_ = GenericVector<float>::double_the_size_memcpy( + reserved_, certainties_); + reserved_ *= 2; + } else { + unichar_ids_ = new UNICHAR_ID[1]; + script_pos_ = new ScriptPos[1]; + state_ = new int[1]; + certainties_ = new float[1]; + reserved_ = 1; + } + } + + /// Initializes WERD_CHOICE - reserves length slots in unichar_ids_ and + /// fragment_length_ arrays. Sets other values to default (blank) values. + inline void init(int reserved) { + reserved_ = reserved; + if (reserved > 0) { + unichar_ids_ = new UNICHAR_ID[reserved]; + script_pos_ = new ScriptPos[reserved]; + state_ = new int[reserved]; + certainties_ = new float[reserved]; + } else { + unichar_ids_ = nullptr; + script_pos_ = nullptr; + state_ = nullptr; + certainties_ = nullptr; + } + length_ = 0; + adjust_factor_ = 1.0f; + rating_ = 0.0; + certainty_ = FLT_MAX; + min_x_height_ = 0.0f; + max_x_height_ = FLT_MAX; + permuter_ = NO_PERM; + unichars_in_script_order_ = false; // Tesseract is strict left-to-right. + dangerous_ambig_found_ = false; + } + + /// Helper function to build a WERD_CHOICE from the given string, + /// fragment lengths, rating, certainty and permuter. + /// The function assumes that src_string is not nullptr. + /// src_lengths argument could be nullptr, in which case the unichars + /// in src_string are assumed to all be of length 1. + void init(const char *src_string, const char *src_lengths, + float src_rating, float src_certainty, + uint8_t src_permuter); + + /// Set the fields in this choice to be default (bad) values. + inline void make_bad() { + length_ = 0; + rating_ = kBadRating; + certainty_ = -FLT_MAX; + } + + /// This function assumes that there is enough space reserved + /// in the WERD_CHOICE for adding another unichar. + /// This is an efficient alternative to append_unichar_id(). + inline void append_unichar_id_space_allocated( + UNICHAR_ID unichar_id, int blob_count, + float rating, float certainty) { + assert(reserved_ > length_); + length_++; + this->set_unichar_id(unichar_id, blob_count, + rating, certainty, length_-1); + } + + void append_unichar_id(UNICHAR_ID unichar_id, int blob_count, + float rating, float certainty); + + inline void set_unichar_id(UNICHAR_ID unichar_id, int blob_count, + float rating, float certainty, int index) { + assert(index < length_); + unichar_ids_[index] = unichar_id; + state_[index] = blob_count; + certainties_[index] = certainty; + script_pos_[index] = SP_NORMAL; + rating_ += rating; + if (certainty < certainty_) { + certainty_ = certainty; + } + } + // Sets the entries for the given index from the BLOB_CHOICE, assuming + // unit fragment lengths, but setting the state for this index to blob_count. + void set_blob_choice(int index, int blob_count, + const BLOB_CHOICE* blob_choice); + + bool contains_unichar_id(UNICHAR_ID unichar_id) const; + void remove_unichar_ids(int index, int num); + inline void remove_last_unichar_id() { --length_; } + inline void remove_unichar_id(int index) { + this->remove_unichar_ids(index, 1); + } + bool has_rtl_unichar_id() const; + void reverse_and_mirror_unichar_ids(); + + // Returns the half-open interval of unichar_id indices [start, end) which + // enclose the core portion of this word -- the part after stripping + // punctuation from the left and right. + void punct_stripped(int *start_core, int *end_core) const; + + // Returns the indices [start, end) containing the core of the word, stripped + // of any superscript digits on either side. (i.e., the non-footnote part + // of the word). There is no guarantee that the output range is non-empty. + void GetNonSuperscriptSpan(int *start, int *end) const; + + // Return a copy of this WERD_CHOICE with the choices [start, end). + // The result is useful only for checking against a dictionary. + WERD_CHOICE shallow_copy(int start, int end) const; + + void string_and_lengths(STRING *word_str, STRING *word_lengths_str) const; + const STRING debug_string() const { + STRING word_str; + for (int i = 0; i < length_; ++i) { + word_str += unicharset_->debug_str(unichar_ids_[i]); + word_str += " "; + } + return word_str; + } + // Returns true if any unichar_id in the word is a non-space-delimited char. + bool ContainsAnyNonSpaceDelimited() const { + for (int i = 0; i < length_; ++i) { + if (!unicharset_->IsSpaceDelimited(unichar_ids_[i])) return true; + } + return false; + } + // Returns true if the word is all spaces. + bool IsAllSpaces() const { + for (int i = 0; i < length_; ++i) { + if (unichar_ids_[i] != UNICHAR_SPACE) return false; + } + return true; + } + + // Call this to override the default (strict left to right graphemes) + // with the fact that some engine produces a "reading order" set of + // Graphemes for each word. + bool set_unichars_in_script_order(bool in_script_order) { + return unichars_in_script_order_ = in_script_order; + } + + bool unichars_in_script_order() const { + return unichars_in_script_order_; + } + + // Returns a UTF-8 string equivalent to the current choice + // of UNICHAR IDs. + STRING &unichar_string() { + this->string_and_lengths(&unichar_string_, &unichar_lengths_); + return unichar_string_; + } + + // Returns a UTF-8 string equivalent to the current choice + // of UNICHAR IDs. + const STRING &unichar_string() const { + this->string_and_lengths(&unichar_string_, &unichar_lengths_); + return unichar_string_; + } + + // Returns the lengths, one byte each, representing the number of bytes + // required in the unichar_string for each UNICHAR_ID. + const STRING &unichar_lengths() const { + this->string_and_lengths(&unichar_string_, &unichar_lengths_); + return unichar_lengths_; + } + + // Sets up the script_pos_ member using the blobs_list to get the bln + // bounding boxes, *this to get the unichars, and this->unicharset + // to get the target positions. If small_caps is true, sub/super are not + // considered, but dropcaps are. + // NOTE: blobs_list should be the chopped_word blobs. (Fully segemented.) + void SetScriptPositions(bool small_caps, TWERD* word, int debug = 0); + // Sets the script_pos_ member from some source positions with a given length. + void SetScriptPositions(const ScriptPos* positions, int length); + // Sets all the script_pos_ positions to the given position. + void SetAllScriptPositions(ScriptPos position); + + static ScriptPos ScriptPositionOf(bool print_debug, + const UNICHARSET& unicharset, + const TBOX& blob_box, + UNICHAR_ID unichar_id); + + // Returns the "dominant" script ID for the word. By "dominant", the script + // must account for at least half the characters. Otherwise, it returns 0. + // Note that for Japanese, Hiragana and Katakana are simply treated as Han. + int GetTopScriptID() const; + + // Fixes the state_ for a chop at the given blob_posiiton. + void UpdateStateForSplit(int blob_position); + + // Returns the sum of all the state elements, being the total number of blobs. + int TotalOfStates() const; + + void print() const { this->print(""); } + void print(const char *msg) const; + // Prints the segmentation state with an introductory message. + void print_state(const char *msg) const; + + // Displays the segmentation state of *this (if not the same as the last + // one displayed) and waits for a click in the window. + void DisplaySegmentation(TWERD* word); + + WERD_CHOICE& operator+= ( // concatanate + const WERD_CHOICE & second);// second on first + + WERD_CHOICE& operator= (const WERD_CHOICE& source); + + private: + const UNICHARSET *unicharset_; + // TODO(rays) Perhaps replace the multiple arrays with an array of structs? + // unichar_ids_ is an array of classifier "results" that make up a word. + // For each unichar_ids_[i], script_pos_[i] has the sub/super/normal position + // of each unichar_id. + // state_[i] indicates the number of blobs in WERD_RES::chopped_word that + // were put together to make the classification results in the ith position + // in unichar_ids_, and certainties_[i] is the certainty of the choice that + // was used in this word. + // == Change from before == + // Previously there was fragment_lengths_ that allowed a word to be + // artificially composed of multiple fragment results. Since the new + // segmentation search doesn't do fragments, treatment of fragments has + // been moved to a lower level, augmenting the ratings matrix with the + // combined fragments, and allowing the language-model/segmentation-search + // to deal with only the combined unichar_ids. + UNICHAR_ID *unichar_ids_; // unichar ids that represent the text of the word + ScriptPos* script_pos_; // Normal/Sub/Superscript of each unichar. + int* state_; // Number of blobs in each unichar. + float* certainties_; // Certainty of each unichar. + int reserved_; // size of the above arrays + int length_; // word length + // Factor that was used to adjust the rating. + float adjust_factor_; + // Rating is the sum of the ratings of the individual blobs in the word. + float rating_; // size related + // certainty is the min (worst) certainty of the individual blobs in the word. + float certainty_; // absolute + // xheight computed from the result, or 0 if inconsistent. + float min_x_height_; + float max_x_height_; + uint8_t permuter_; // permuter code + + // Normally, the ratings_ matrix represents the recognition results in order + // from left-to-right. However, some engines (say Cube) may return + // recognition results in the order of the script's major reading direction + // (for Arabic, that is right-to-left). + bool unichars_in_script_order_; + // True if NoDangerousAmbig found an ambiguity. + bool dangerous_ambig_found_; + + // The following variables are populated and passed by reference any + // time unichar_string() or unichar_lengths() are called. + mutable STRING unichar_string_; + mutable STRING unichar_lengths_; +}; + +// Make WERD_CHOICE listable. +ELISTIZEH(WERD_CHOICE) +using BLOB_CHOICE_LIST_VECTOR = GenericVector<BLOB_CHOICE_LIST *>; + +// Utilities for comparing WERD_CHOICEs + +bool EqualIgnoringCaseAndTerminalPunct(const WERD_CHOICE &word1, + const WERD_CHOICE &word2); + +// Utilities for debug printing. +void print_ratings_list( + const char *msg, // intro message + BLOB_CHOICE_LIST *ratings, // list of results + const UNICHARSET ¤t_unicharset // unicharset that can be used + // for id-to-unichar conversion + ); + +} // namespace tesseract + +#endif |