/////////////////////////////////////////////////////////////////////// // File: ambigs.cc // Description: Functions for dealing with ambiguities // (training and recognition). // Author: Daria Antonova // Created: Mon Feb 5 11:26:43 PDT 2009 // // (C) Copyright 2008, Google Inc. // 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. // /////////////////////////////////////////////////////////////////////// #include "ambigs.h" #include "helpers.h" INT_VAR(global_ambigs_debug_level, 0, "Debug level for unichar ambiguities"); BOOL_VAR(use_definite_ambigs_for_classifier, 0, "Use definite ambiguities when running character classifier"); namespace tesseract { AmbigSpec::AmbigSpec() { wrong_ngram[0] = INVALID_UNICHAR_ID; correct_fragments[0] = INVALID_UNICHAR_ID; correct_ngram_id = INVALID_UNICHAR_ID; type = NOT_AMBIG; wrong_ngram_size = 0; } ELISTIZE(AmbigSpec); void UnicharAmbigs::LoadUnicharAmbigs(FILE *AmbigFile, inT64 end_offset, UNICHARSET *unicharset) { int i; for (i = 0; i < unicharset->size(); ++i) { replace_ambigs_.push_back(NULL); dang_ambigs_.push_back(NULL); one_to_one_definite_ambigs_.push_back(NULL); } if (global_ambigs_debug_level) tprintf("Reading ambiguities\n"); int TestAmbigPartSize; int ReplacementAmbigPartSize; // Maximum line size: // 10 for sizes of ambigs, tabs, abmig type and newline // UNICHAR_LEN * (MAX_AMBIG_SIZE + 1) for each part of the ambig // The space for buffer is allocated on the heap to avoid // GCC frame size warning. const int kMaxAmbigStringSize = UNICHAR_LEN * (MAX_AMBIG_SIZE + 1); const int kBufferSize = 10 + 2 * kMaxAmbigStringSize; char *buffer = new char[kBufferSize]; char ReplacementString[kMaxAmbigStringSize]; UNICHAR_ID TestUnicharIds[MAX_AMBIG_SIZE + 1]; int line_num = 0; int type = NOT_AMBIG; // Determine the version of the ambigs file. int version = 0; ASSERT_HOST(fgets(buffer, kBufferSize, AmbigFile) != NULL && strlen(buffer) > 0); if (*buffer == 'v') { version = static_cast(strtol(buffer+1, NULL, 10)); ++line_num; } else { rewind(AmbigFile); } while ((end_offset < 0 || ftell(AmbigFile) < end_offset) && fgets(buffer, kBufferSize, AmbigFile) != NULL) { chomp_string(buffer); if (global_ambigs_debug_level > 2) tprintf("read line %s\n", buffer); ++line_num; if (!ParseAmbiguityLine(line_num, version, *unicharset, buffer, &TestAmbigPartSize, TestUnicharIds, &ReplacementAmbigPartSize, ReplacementString, &type)) continue; // Construct AmbigSpec and add it to the appropriate AmbigSpec_LIST. AmbigSpec *ambig_spec = new AmbigSpec(); InsertIntoTable((type == REPLACE_AMBIG) ? replace_ambigs_ : dang_ambigs_, TestAmbigPartSize, TestUnicharIds, ReplacementAmbigPartSize, ReplacementString, type, ambig_spec, unicharset); // Update one_to_one_definite_ambigs_. if (use_definite_ambigs_for_classifier && TestAmbigPartSize == 1 && ReplacementAmbigPartSize == 1 && type == DEFINITE_AMBIG) { if (one_to_one_definite_ambigs_[TestUnicharIds[0]] == NULL) { one_to_one_definite_ambigs_[TestUnicharIds[0]] = new UnicharIdVector(); } one_to_one_definite_ambigs_[TestUnicharIds[0]]->push_back( ambig_spec->correct_ngram_id); } } delete[] buffer; // Print what was read from the input file. if (global_ambigs_debug_level > 2) { for (int tbl = 0; tbl < 2; ++tbl) { const UnicharAmbigsVector &print_table = (tbl == 0) ? replace_ambigs_ : dang_ambigs_; for (i = 0; i < print_table.size(); ++i) { AmbigSpec_LIST *lst = print_table[i]; if (lst == NULL) continue; if (!lst->empty()) { tprintf("%s Ambiguities for %s:\n", (tbl == 0) ? "Replaceable" : "Dangerous", unicharset->debug_str(i).string()); } AmbigSpec_IT lst_it(lst); for (lst_it.mark_cycle_pt(); !lst_it.cycled_list(); lst_it.forward()) { AmbigSpec *ambig_spec = lst_it.data(); tprintf("wrong_ngram:"); UnicharIdArrayUtils::print(ambig_spec->wrong_ngram, *unicharset); tprintf("correct_fragments:"); UnicharIdArrayUtils::print(ambig_spec->correct_fragments, *unicharset); } } } } } bool UnicharAmbigs::ParseAmbiguityLine( int line_num, int version, const UNICHARSET &unicharset, char *buffer, int *TestAmbigPartSize, UNICHAR_ID *TestUnicharIds, int *ReplacementAmbigPartSize, char *ReplacementString, int *type) { int i; char *token; char *next_token; if (!(token = strtok_r(buffer, kAmbigDelimiters, &next_token)) || !sscanf(token, "%d", TestAmbigPartSize) || TestAmbigPartSize <= 0) { if (global_ambigs_debug_level) tprintf(kIllegalMsg, line_num); return false; } if (*TestAmbigPartSize > MAX_AMBIG_SIZE) { tprintf("Too many unichars in ambiguity on line %d\n"); return false; } for (i = 0; i < *TestAmbigPartSize; ++i) { if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break; if (!unicharset.contains_unichar(token)) { if (global_ambigs_debug_level) tprintf(kIllegalUnicharMsg, token); break; } TestUnicharIds[i] = unicharset.unichar_to_id(token); } TestUnicharIds[i] = INVALID_UNICHAR_ID; if (i != *TestAmbigPartSize || !(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) || !sscanf(token, "%d", ReplacementAmbigPartSize) || *ReplacementAmbigPartSize <= 0) { if (global_ambigs_debug_level) tprintf(kIllegalMsg, line_num); return false; } if (*ReplacementAmbigPartSize > MAX_AMBIG_SIZE) { tprintf("Too many unichars in ambiguity on line %d\n"); return false; } ReplacementString[0] = '\0'; for (i = 0; i < *ReplacementAmbigPartSize; ++i) { if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break; strcat(ReplacementString, token); if (!unicharset.contains_unichar(token)) { if (global_ambigs_debug_level) tprintf(kIllegalUnicharMsg, token); break; } } if (i != *ReplacementAmbigPartSize) { if (global_ambigs_debug_level) tprintf(kIllegalMsg, line_num); return false; } if (version > 0) { // The next field being true indicates that the abiguity should // always be substituted (e.g. '' should always be changed to "). // For such "certain" n -> m ambigs tesseract will insert character // fragments for the n pieces in the unicharset. AmbigsFound() // will then replace the incorrect ngram with the character // fragments of the correct character (or ngram if m > 1). // Note that if m > 1, an ngram will be inserted into the // modified word, not the individual unigrams. Tesseract // has limited support for ngram unichar (e.g. dawg permuter). if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) || !sscanf(token, "%d", type)) { if (global_ambigs_debug_level) tprintf(kIllegalMsg, line_num); return false; } } return true; } void UnicharAmbigs::InsertIntoTable( UnicharAmbigsVector &table, int TestAmbigPartSize, UNICHAR_ID *TestUnicharIds, int ReplacementAmbigPartSize, const char *ReplacementString, int type, AmbigSpec *ambig_spec, UNICHARSET *unicharset) { ambig_spec->type = static_cast(type); if (TestAmbigPartSize == 1 && ReplacementAmbigPartSize == 1 && unicharset->to_lower(TestUnicharIds[0]) == unicharset->to_lower(unicharset->unichar_to_id(ReplacementString))) { ambig_spec->type = CASE_AMBIG; } ambig_spec->wrong_ngram_size = UnicharIdArrayUtils::copy(TestUnicharIds, ambig_spec->wrong_ngram); // Since we need to maintain a constant number of unichar positions in // order to construct ambig_blob_choices vector in NoDangerousAmbig(), for // each n->m ambiguity we will have to place n character fragments of the // correct ngram into the corresponding positions in the vector (e.g. given // "vvvvw" and vvvv->ww we will place v and |ww|0|4 into position 0, v and // |ww|1|4 into position 1 and so on. The correct ngram is reconstructed // from fragments by dawg_permute_and_select(). // Insert the corresponding correct ngram into the unicharset. // Unicharset code assumes that the "base" ngram is inserted into // the unicharset before fragments of this ngram are inserted. unicharset->unichar_insert(ReplacementString); ambig_spec->correct_ngram_id = unicharset->unichar_to_id(ReplacementString); if (ReplacementAmbigPartSize > 1) { unicharset->set_isngram(ambig_spec->correct_ngram_id, true); } // Add the corresponding fragments of the correct ngram to unicharset. int i; for (i = 0; i < TestAmbigPartSize; ++i) { UNICHAR_ID unichar_id; if (TestAmbigPartSize == 1) { unichar_id = ambig_spec->correct_ngram_id; } else { STRING frag_str = CHAR_FRAGMENT::to_string( ReplacementString, i, TestAmbigPartSize); unicharset->unichar_insert(frag_str.string()); unichar_id = unicharset->unichar_to_id(frag_str.string()); } ambig_spec->correct_fragments[i] = unichar_id; } ambig_spec->correct_fragments[i] = INVALID_UNICHAR_ID; // Add AmbigSpec for this ambiguity to the corresponding AmbigSpec_LIST. // Keep AmbigSpec_LISTs sorted by AmbigSpec.wrong_ngram. if (table[TestUnicharIds[0]] == NULL) { table[TestUnicharIds[0]] = new AmbigSpec_LIST(); } table[TestUnicharIds[0]]->add_sorted( AmbigSpec::compare_ambig_specs, ambig_spec); } } // namespace tesseract