67 const char* truth_str,
const TBOX& word_box) {
69 truth_has_char_boxes_ =
false;
73 unicharset.
encode_string(truth_str,
false, &encoding, &lengths, NULL);
75 for (
int i = 0; i < encoding.
size(); total_length += lengths[i++]) {
76 STRING uch(truth_str + total_length);
87 const char* char_str,
const TBOX& char_box) {
88 STRING symbol_str(char_str);
90 if (
id != INVALID_UNICHAR_ID) {
92 if (normed_uch.length() > 0) symbol_str = normed_uch;
94 int length = truth_word_.
length();
98 truth_has_char_boxes_ =
true;
99 else if (truth_word_.
BlobBox(length - 1) == char_box)
100 truth_has_char_boxes_ =
false;
107 truth_has_char_boxes_ =
false;
112 if (word_choice == NULL)
return false;
115 for (
int i = 0; i < word_choice->
length(); ++i) {
120 return truth_str == normed_choice_str;
126 (*debug) +=
"Truth ";
127 for (
int i = 0; i < this->truth_text_.
length(); ++i) {
128 (*debug) += this->truth_text_[i];
130 if (!this->truth_has_char_boxes_) (*debug) +=
" (no char boxes)";
131 if (choice != NULL) {
132 (*debug) +=
" Choice ";
135 (*debug) += choice_str;
147 norm_box_tolerance_ = kBlamerBoxTolerance * denorm.
x_scale();
152 for (
int b = 0; b < truth_word_.
length(); ++b) {
154 topleft.
x = box.
left();
155 topleft.
y = box.
top();
160 TBOX norm_box(norm_topleft.
x, norm_botright.
y,
161 norm_botright.
x, norm_topleft.
y);
175 int begin2_truth_index = -1;
177 truth_has_char_boxes_) {
178 debug_str =
"Looking for truth split at";
181 debug_str +=
"\nnorm_truth_word boxes:\n";
182 if (norm_truth_word_.
length() > 1) {
184 for (b = 1; b < norm_truth_word_.
length(); ++b) {
186 if ((abs(word1_right - norm_truth_word_.
BlobBox(b - 1).
right()) <
187 norm_box_tolerance_) &&
188 (abs(word2_left - norm_truth_word_.
BlobBox(b).
left()) <
189 norm_box_tolerance_)) {
190 begin2_truth_index = b;
191 debug_str +=
"Split found";
200 if (begin2_truth_index > 0) {
201 bundle1->truth_has_char_boxes_ =
true;
202 bundle1->norm_box_tolerance_ = norm_box_tolerance_;
203 bundle2->truth_has_char_boxes_ =
true;
204 bundle2->norm_box_tolerance_ = norm_box_tolerance_;
206 for (b = 0; b < norm_truth_word_.
length(); ++b) {
207 if (b == begin2_truth_index) curr_bb = bundle2;
210 curr_bb->truth_text_.
push_back(truth_text_[b]);
216 debug_str +=
"Truth split not found";
217 debug_str += truth_has_char_boxes_ ?
218 "\n" :
" (no truth char boxes)\n";
230 if (bundle1.incorrect_result_reason_ !=
IRR_CORRECT &&
233 debug_str +=
"Blame from part 1: ";
234 debug_str += bundle1.debug_;
235 irr = bundle1.incorrect_result_reason_;
237 if (bundle2.incorrect_result_reason_ !=
IRR_CORRECT &&
240 debug_str +=
"Blame from part 2: ";
241 debug_str += bundle2.debug_;
243 irr = bundle2.incorrect_result_reason_;
244 }
else if (irr != bundle2.incorrect_result_reason_) {
248 incorrect_result_reason_ = irr;
250 SetBlame(irr, debug_str, NULL,
debug);
258 const TBOX& blob_box,
259 const BLOB_CHOICE_LIST& choices,
261 if (!truth_has_char_boxes_ ||
265 for (
int b = 0; b < norm_truth_word_.
length(); ++b) {
266 const TBOX &truth_box = norm_truth_word_.
BlobBox(b);
272 bool incorrect_adapted =
false;
273 UNICHAR_ID incorrect_adapted_id = INVALID_UNICHAR_ID;
274 const char *truth_str = truth_text_[b].string();
277 BLOB_CHOICE_IT choices_it(const_cast<BLOB_CHOICE_LIST*>(&choices));
278 for (choices_it.mark_cycle_pt(); !choices_it.cycled_list();
279 choices_it.forward()) {
286 incorrect_adapted =
true;
291 STRING debug_str =
"unichar ";
292 debug_str += truth_str;
293 debug_str +=
" not found in classification list";
295 }
else if (incorrect_adapted) {
296 STRING debug_str =
"better rating for adapted ";
298 debug_str +=
" than for correct ";
299 debug_str += truth_str;
311 if (
NoTruth() || !truth_has_char_boxes_ ||
316 bool missing_chop =
false;
321 while (box_index < truth_word_.
length() && blob_index < num_blobs) {
328 truth_x + norm_box_tolerance_) {
335 if (missing_chop || box_index < norm_truth_word_.
length()) {
338 debug_str.
add_str_int(
"Detected missing chop (tolerance=",
339 norm_box_tolerance_);
340 debug_str +=
") at Bounding Box=";
343 debug_str.
add_str_int(
"\nNo chop for truth at x=", truth_x);
346 norm_truth_word_.
length() - box_index);
347 debug_str +=
" truth box(es)";
349 debug_str +=
"\nMaximally chopped word boxes:\n";
350 for (blob_index = 0; blob_index < num_blobs; ++blob_index) {
355 debug_str +=
"Truth bounding boxes:\n";
356 for (box_index = 0; box_index < norm_truth_word_.
length(); ++box_index) {
371 const UNICHARSET& unicharset,
bool valid_permuter,
bool debug) {
372 if (valid_permuter) {
374 best_choice_is_dict_and_top_choice_ =
true;
379 for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list();
380 blob_choice_it.forward()) {
381 if (!(unicharset.
get_fragment(blob_choice_it.data()->unichar_id()))) {
382 first_choice = blob_choice_it.data();
388 best_choice_is_dict_and_top_choice_ =
false;
394 if (best_choice_is_dict_and_top_choice_) {
395 debug_str =
"Best choice is: incorrect, top choice, dictionary word";
396 debug_str +=
" with permuter ";
399 debug_str =
"Classifier/Old LM tradeoff is to blame";
409 if (incorrect_result_reason_ !=
IRR_CORRECT || !truth_has_char_boxes_)
413 debug_str +=
"Blamer computing correct_segmentation_cols\n";
414 int curr_box_col = 0;
415 int next_box_col = 0;
417 if (num_blobs == 0)
return;
419 inT16 next_box_x = word->
blobs[blob_index]->bounding_box().right();
420 for (
int truth_idx = 0; blob_index < num_blobs &&
421 truth_idx < norm_truth_word_.
length();
424 inT16 curr_box_x = next_box_x;
425 if (blob_index + 1 < num_blobs)
426 next_box_x = word->
blobs[blob_index + 1]->bounding_box().right();
428 debug_str.
add_str_int(
"Box x coord vs. truth: ", curr_box_x);
431 if (curr_box_x > (truth_x + norm_box_tolerance_)) {
433 }
else if (curr_box_x >= truth_x - norm_box_tolerance_ &&
434 (blob_index + 1 >= num_blobs ||
435 next_box_x > truth_x + norm_box_tolerance_)) {
436 correct_segmentation_cols_.
push_back(curr_box_col);
437 correct_segmentation_rows_.
push_back(next_box_col-1);
442 curr_box_col = next_box_col;
445 if (blob_index < num_blobs ||
446 correct_segmentation_cols_.
length() != norm_truth_word_.
length()) {
447 debug_str.
add_str_int(
"Blamer failed to find correct segmentation" 448 " (tolerance=", norm_box_tolerance_);
449 if (blob_index >= num_blobs) debug_str +=
" blob == NULL";
455 correct_segmentation_cols_.
clear();
456 correct_segmentation_rows_.
clear();
463 !segsearch_is_looking_for_blame_ &&
464 truth_has_char_boxes_ &&
475 bool debug,
STRING *debug_str,
477 segsearch_is_looking_for_blame_ =
true;
479 tprintf(
"segsearch starting to look for blame\n");
483 *debug_str +=
"Correct segmentation:\n";
484 for (
int idx = 0; idx < correct_segmentation_cols_.
length(); ++idx) {
485 debug_str->
add_str_int(
"col=", correct_segmentation_cols_[idx]);
486 debug_str->
add_str_int(
" row=", correct_segmentation_rows_[idx]);
488 if (!ratings->
Classified(correct_segmentation_cols_[idx],
489 correct_segmentation_rows_[idx],
491 !cb->
Run(correct_segmentation_cols_[idx],
492 correct_segmentation_rows_[idx])) {
493 segsearch_is_looking_for_blame_ =
false;
494 *debug_str +=
"\nFailed to insert pain point\n";
502 return segsearch_is_looking_for_blame_;
507 bool debug,
STRING *debug_str) {
519 if (segsearch_is_looking_for_blame_) {
520 segsearch_is_looking_for_blame_ =
false;
521 if (best_choice_is_dict_and_top_choice_) {
522 *debug_str =
"Best choice is: incorrect, top choice, dictionary word";
523 *debug_str +=
" with permuter ";
526 }
else if (best_correctly_segmented_rating_ <
528 *debug_str +=
"Correct segmentation state was not explored";
531 if (best_correctly_segmented_rating_ >=
533 *debug_str +=
"Correct segmentation paths were pruned by LM\n";
536 best_correctly_segmented_rating_);
559 STRING debug_str =
"Choice is incorrect after recognition";
578 misadaption_debug_ =
"misadapt to word (";
580 misadaption_debug_ +=
"): ";
void string_and_lengths(STRING *word_str, STRING *word_lengths_str) const
const char *const kIncorrectResultReasonNames[]
void SetWordTruth(const UNICHARSET &unicharset, const char *truth_str, const TBOX &word_box)
void SetupCorrectSegmentation(const TWERD *word, bool debug)
const char kBlameNoTruth[]
GenericVector< TBLOB * > blobs
const char kBlameAdaption[]
void InsertBox(int index, const TBOX &box)
void SetupNormTruthWord(const DENORM &denorm)
UNICHAR_ID unichar_id(int index) const
const char kBlameNoTruthSplit[]
STRING TruthString() const
void FinishSegSearch(const WERD_CHOICE *best_choice, bool debug, STRING *debug_str)
void print_to_str(STRING *str) const
const char * get_normed_unichar(UNICHAR_ID unichar_id) const
void NormTransform(const DENORM *first_norm, const TPOINT &pt, TPOINT *transformed) const
const STRING & debug() const
const char kBlameClassLMTradeoff[]
void BlameClassifierOrLangModel(const WERD_RES *word, const UNICHARSET &unicharset, bool valid_permuter, bool debug)
bool GuidedSegsearchStillGoing() const
const char kBlameClassOldLMTradeoff[]
bool Classified(int col, int row, int wildcard_id) const
WERD_CHOICE * best_choice
TBOX bounding_box() const
void add_str_double(const char *str, double number)
void SplitBundle(int word1_right, int word2_left, bool debug, BlamerBundle *bundle1, BlamerBundle *bundle2) const
const UNICHARSET * unicharset() const
UNICHAR_ID unichar_id() const
bool encode_string(const char *str, bool give_up_on_failure, GenericVector< UNICHAR_ID > *encoding, GenericVector< char > *lengths, int *encoded_length) const
void add_str_int(const char *str, int number)
bool ChoiceIsCorrect(const WERD_CHOICE *word_choice) const
static const char * permuter_name(uinT8 permuter)
void truncate_at(inT32 index)
const char * string() const
const char kBlameSegsearchHeur[]
static const char * IncorrectReasonName(IncorrectResultReason irr)
void JoinBlames(const BlamerBundle &bundle1, const BlamerBundle &bundle2, bool debug)
BlamerBundle * blamer_bundle
bool x_almost_equal(const TBOX &box, int tolerance) const
void FillDebugString(const STRING &msg, const WERD_CHOICE *choice, STRING *debug)
const char kBlameClassifier[]
const char kBlameCorrect[]
BLOB_CHOICE_LIST * GetBlobChoices(int index) const
void SetSymbolTruth(const UNICHARSET &unicharset, const char *char_str, const TBOX &char_box)
void SetMisAdaptionDebug(const WERD_CHOICE *best_choice, bool debug)
UNICHAR_ID TESS_API unichar_to_id(const char *const unichar_repr) const
const char kBlamePageLayout[]
const char kBlameSegsearchPP[]
const TBOX & BlobBox(int index) const
const char * IncorrectReason() const
void BlameClassifier(const UNICHARSET &unicharset, const TBOX &blob_box, const BLOB_CHOICE_LIST &choices, bool debug)
const char kBlameChopper[]
static const float kBadRating
static void LastChanceBlame(bool debug, WERD_RES *word)
void InitForSegSearch(const WERD_CHOICE *best_choice, MATRIX *ratings, UNICHAR_ID wildcard_id, bool debug, STRING *debug_str, TessResultCallback2< bool, int, int > *pp_cb)
void SetChopperBlame(const WERD_RES *word, bool debug)
bool GuidedSegsearchNeeded(const WERD_CHOICE *best_choice) const
const char * id_to_unichar(UNICHAR_ID id) const
const char kBlameUnknown[]
const CHAR_FRAGMENT * get_fragment(UNICHAR_ID unichar_id) const
void StartHypothesisList()