tesseract  4.0.0-1-g2a2b
blamer.cpp
Go to the documentation of this file.
1 // File: blamer.cpp
3 // Description: Module allowing precise error causes to be allocated.
4 // Author: Rike Antonova
5 // Refactored: Ray Smith
6 // Created: Mon Feb 04 14:37:01 PST 2013
7 //
8 // (C) Copyright 2013, Google Inc.
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
20 
21 #include "blamer.h"
22 #include <cmath> // for abs
23 #include <cstdlib> // for abs
24 #include "blobs.h" // for TPOINT, TWERD, TBLOB
25 #include "errcode.h" // for ASSERT_HOST
26 #include "matrix.h" // for MATRIX
27 #include "normalis.h" // for DENORM
28 #include "pageres.h" // for WERD_RES
29 #include "tesscallback.h" // for TessResultCallback2
30 #include "unicharset.h" // for UNICHARSET
31 
32 // Names for each value of IncorrectResultReason enum. Keep in sync.
33 const char kBlameCorrect[] = "corr";
34 const char kBlameClassifier[] = "cl";
35 const char kBlameChopper[] = "chop";
36 const char kBlameClassLMTradeoff[] = "cl/LM";
37 const char kBlamePageLayout[] = "pglt";
38 const char kBlameSegsearchHeur[] = "ss_heur";
39 const char kBlameSegsearchPP[] = "ss_pp";
40 const char kBlameClassOldLMTradeoff[] = "cl/old_LM";
41 const char kBlameAdaption[] = "adapt";
42 const char kBlameNoTruthSplit[] = "no_tr_spl";
43 const char kBlameNoTruth[] = "no_tr";
44 const char kBlameUnknown[] = "unkn";
45 
46 const char * const kIncorrectResultReasonNames[] = {
59 };
60 
62  return kIncorrectResultReasonNames[irr];
63 }
64 
65 const char *BlamerBundle::IncorrectReason() const {
66  return kIncorrectResultReasonNames[incorrect_result_reason_];
67 }
68 
69 // Functions to setup the blamer.
70 // Whole word string, whole word bounding box.
71 void BlamerBundle::SetWordTruth(const UNICHARSET& unicharset,
72  const char* truth_str, const TBOX& word_box) {
73  truth_word_.InsertBox(0, word_box);
74  truth_has_char_boxes_ = false;
75  // Encode the string as UNICHAR_IDs.
77  GenericVector<char> lengths;
78  unicharset.encode_string(truth_str, false, &encoding, &lengths, nullptr);
79  int total_length = 0;
80  for (int i = 0; i < encoding.size(); total_length += lengths[i++]) {
81  STRING uch(truth_str + total_length);
82  uch.truncate_at(lengths[i] - total_length);
83  UNICHAR_ID id = encoding[i];
84  if (id != INVALID_UNICHAR_ID) uch = unicharset.get_normed_unichar(id);
85  truth_text_.push_back(uch);
86  }
87 }
88 
89 // Single "character" string, "character" bounding box.
90 // May be called multiple times to indicate the characters in a word.
92  const char* char_str, const TBOX& char_box) {
93  STRING symbol_str(char_str);
94  UNICHAR_ID id = unicharset.unichar_to_id(char_str);
95  if (id != INVALID_UNICHAR_ID) {
96  STRING normed_uch(unicharset.get_normed_unichar(id));
97  if (normed_uch.length() > 0) symbol_str = normed_uch;
98  }
99  int length = truth_word_.length();
100  truth_text_.push_back(symbol_str);
101  truth_word_.InsertBox(length, char_box);
102  if (length == 0)
103  truth_has_char_boxes_ = true;
104  else if (truth_word_.BlobBox(length - 1) == char_box)
105  truth_has_char_boxes_ = false;
106 }
107 
108 // Marks that there is something wrong with the truth text, like it contains
109 // reject characters.
111  incorrect_result_reason_ = IRR_NO_TRUTH;
112  truth_has_char_boxes_ = false;
113 }
114 
115 // Returns true if the provided word_choice is correct.
116 bool BlamerBundle::ChoiceIsCorrect(const WERD_CHOICE* word_choice) const {
117  if (word_choice == nullptr) return false;
118  const UNICHARSET* uni_set = word_choice->unicharset();
119  STRING normed_choice_str;
120  for (int i = 0; i < word_choice->length(); ++i) {
121  normed_choice_str +=
122  uni_set->get_normed_unichar(word_choice->unichar_id(i));
123  }
124  STRING truth_str = TruthString();
125  return truth_str == normed_choice_str;
126 }
127 
129  const WERD_CHOICE *choice,
130  STRING *debug) {
131  (*debug) += "Truth ";
132  for (int i = 0; i < this->truth_text_.length(); ++i) {
133  (*debug) += this->truth_text_[i];
134  }
135  if (!this->truth_has_char_boxes_) (*debug) += " (no char boxes)";
136  if (choice != nullptr) {
137  (*debug) += " Choice ";
138  STRING choice_str;
139  choice->string_and_lengths(&choice_str, nullptr);
140  (*debug) += choice_str;
141  }
142  if (msg.length() > 0) {
143  (*debug) += "\n";
144  (*debug) += msg;
145  }
146  (*debug) += "\n";
147 }
148 
149 // Sets up the norm_truth_word from truth_word using the given DENORM.
151  // TODO(rays) Is this the last use of denorm in WERD_RES and can it go?
152  norm_box_tolerance_ = kBlamerBoxTolerance * denorm.x_scale();
153  TPOINT topleft;
154  TPOINT botright;
155  TPOINT norm_topleft;
156  TPOINT norm_botright;
157  for (int b = 0; b < truth_word_.length(); ++b) {
158  const TBOX &box = truth_word_.BlobBox(b);
159  topleft.x = box.left();
160  topleft.y = box.top();
161  botright.x = box.right();
162  botright.y = box.bottom();
163  denorm.NormTransform(nullptr, topleft, &norm_topleft);
164  denorm.NormTransform(nullptr, botright, &norm_botright);
165  TBOX norm_box(norm_topleft.x, norm_botright.y,
166  norm_botright.x, norm_topleft.y);
167  norm_truth_word_.InsertBox(b, norm_box);
168  }
169 }
170 
171 // Splits *this into two pieces in bundle1 and bundle2 (preallocated, empty
172 // bundles) where the right edge/ of the left-hand word is word1_right,
173 // and the left edge of the right-hand word is word2_left.
174 void BlamerBundle::SplitBundle(int word1_right, int word2_left, bool debug,
175  BlamerBundle* bundle1,
176  BlamerBundle* bundle2) const {
177  STRING debug_str;
178  // Find truth boxes that correspond to the split in the blobs.
179  int b;
180  int begin2_truth_index = -1;
181  if (incorrect_result_reason_ != IRR_NO_TRUTH &&
182  truth_has_char_boxes_) {
183  debug_str = "Looking for truth split at";
184  debug_str.add_str_int(" end1_x ", word1_right);
185  debug_str.add_str_int(" begin2_x ", word2_left);
186  debug_str += "\nnorm_truth_word boxes:\n";
187  if (norm_truth_word_.length() > 1) {
188  norm_truth_word_.BlobBox(0).print_to_str(&debug_str);
189  for (b = 1; b < norm_truth_word_.length(); ++b) {
190  norm_truth_word_.BlobBox(b).print_to_str(&debug_str);
191  if ((abs(word1_right - norm_truth_word_.BlobBox(b - 1).right()) <
192  norm_box_tolerance_) &&
193  (abs(word2_left - norm_truth_word_.BlobBox(b).left()) <
194  norm_box_tolerance_)) {
195  begin2_truth_index = b;
196  debug_str += "Split found";
197  break;
198  }
199  }
200  debug_str += '\n';
201  }
202  }
203  // Populate truth information in word and word2 with the first and second
204  // part of the original truth.
205  if (begin2_truth_index > 0) {
206  bundle1->truth_has_char_boxes_ = true;
207  bundle1->norm_box_tolerance_ = norm_box_tolerance_;
208  bundle2->truth_has_char_boxes_ = true;
209  bundle2->norm_box_tolerance_ = norm_box_tolerance_;
210  BlamerBundle *curr_bb = bundle1;
211  for (b = 0; b < norm_truth_word_.length(); ++b) {
212  if (b == begin2_truth_index) curr_bb = bundle2;
213  curr_bb->norm_truth_word_.InsertBox(b, norm_truth_word_.BlobBox(b));
214  curr_bb->truth_word_.InsertBox(b, truth_word_.BlobBox(b));
215  curr_bb->truth_text_.push_back(truth_text_[b]);
216  }
217  } else if (incorrect_result_reason_ == IRR_NO_TRUTH) {
218  bundle1->incorrect_result_reason_ = IRR_NO_TRUTH;
219  bundle2->incorrect_result_reason_ = IRR_NO_TRUTH;
220  } else {
221  debug_str += "Truth split not found";
222  debug_str += truth_has_char_boxes_ ?
223  "\n" : " (no truth char boxes)\n";
224  bundle1->SetBlame(IRR_NO_TRUTH_SPLIT, debug_str, nullptr, debug);
225  bundle2->SetBlame(IRR_NO_TRUTH_SPLIT, debug_str, nullptr, debug);
226  }
227 }
228 
229 // "Joins" the blames from bundle1 and bundle2 into *this.
231  const BlamerBundle& bundle2, bool debug) {
232  STRING debug_str;
233  IncorrectResultReason irr = incorrect_result_reason_;
234  if (irr != IRR_NO_TRUTH_SPLIT) debug_str = "";
235  if (bundle1.incorrect_result_reason_ != IRR_CORRECT &&
236  bundle1.incorrect_result_reason_ != IRR_NO_TRUTH &&
237  bundle1.incorrect_result_reason_ != IRR_NO_TRUTH_SPLIT) {
238  debug_str += "Blame from part 1: ";
239  debug_str += bundle1.debug_;
240  irr = bundle1.incorrect_result_reason_;
241  }
242  if (bundle2.incorrect_result_reason_ != IRR_CORRECT &&
243  bundle2.incorrect_result_reason_ != IRR_NO_TRUTH &&
244  bundle2.incorrect_result_reason_ != IRR_NO_TRUTH_SPLIT) {
245  debug_str += "Blame from part 2: ";
246  debug_str += bundle2.debug_;
247  if (irr == IRR_CORRECT) {
248  irr = bundle2.incorrect_result_reason_;
249  } else if (irr != bundle2.incorrect_result_reason_) {
250  irr = IRR_UNKNOWN;
251  }
252  }
253  incorrect_result_reason_ = irr;
254  if (irr != IRR_CORRECT && irr != IRR_NO_TRUTH) {
255  SetBlame(irr, debug_str, nullptr, debug);
256  }
257 }
258 
259 // If a blob with the same bounding box as one of the truth character
260 // bounding boxes is not classified as the corresponding truth character
261 // blames character classifier for incorrect answer.
263  const TBOX& blob_box,
264  const BLOB_CHOICE_LIST& choices,
265  bool debug) {
266  if (!truth_has_char_boxes_ ||
267  incorrect_result_reason_ != IRR_CORRECT)
268  return; // Nothing to do here.
269 
270  for (int b = 0; b < norm_truth_word_.length(); ++b) {
271  const TBOX &truth_box = norm_truth_word_.BlobBox(b);
272  // Note that we are more strict on the bounding box boundaries here
273  // than in other places (chopper, segmentation search), since we do
274  // not have the ability to check the previous and next bounding box.
275  if (blob_box.x_almost_equal(truth_box, norm_box_tolerance_/2)) {
276  bool found = false;
277  bool incorrect_adapted = false;
278  UNICHAR_ID incorrect_adapted_id = INVALID_UNICHAR_ID;
279  const char *truth_str = truth_text_[b].string();
280  // We promise not to modify the list or its contents, using a
281  // const BLOB_CHOICE* below.
282  BLOB_CHOICE_IT choices_it(const_cast<BLOB_CHOICE_LIST*>(&choices));
283  for (choices_it.mark_cycle_pt(); !choices_it.cycled_list();
284  choices_it.forward()) {
285  const BLOB_CHOICE* choice = choices_it.data();
286  if (strcmp(truth_str, unicharset.get_normed_unichar(
287  choice->unichar_id())) == 0) {
288  found = true;
289  break;
290  } else if (choice->IsAdapted()) {
291  incorrect_adapted = true;
292  incorrect_adapted_id = choice->unichar_id();
293  }
294  } // end choices_it for loop
295  if (!found) {
296  STRING debug_str = "unichar ";
297  debug_str += truth_str;
298  debug_str += " not found in classification list";
299  SetBlame(IRR_CLASSIFIER, debug_str, nullptr, debug);
300  } else if (incorrect_adapted) {
301  STRING debug_str = "better rating for adapted ";
302  debug_str += unicharset.id_to_unichar(incorrect_adapted_id);
303  debug_str += " than for correct ";
304  debug_str += truth_str;
305  SetBlame(IRR_ADAPTION, debug_str, nullptr, debug);
306  }
307  break;
308  }
309  } // end iterating over blamer_bundle->norm_truth_word
310 }
311 
312 // Checks whether chops were made at all the character bounding box
313 // boundaries in word->truth_word. If not - blames the chopper for an
314 // incorrect answer.
315 void BlamerBundle::SetChopperBlame(const WERD_RES* word, bool debug) {
316  if (NoTruth() || !truth_has_char_boxes_ ||
318  return;
319  }
320  STRING debug_str;
321  bool missing_chop = false;
322  int num_blobs = word->chopped_word->blobs.size();
323  int box_index = 0;
324  int blob_index = 0;
325  int16_t truth_x = -1;
326  while (box_index < truth_word_.length() && blob_index < num_blobs) {
327  truth_x = norm_truth_word_.BlobBox(box_index).right();
328  TBLOB * curr_blob = word->chopped_word->blobs[blob_index];
329  if (curr_blob->bounding_box().right() < truth_x - norm_box_tolerance_) {
330  ++blob_index;
331  continue; // encountered an extra chop, keep looking
332  } else if (curr_blob->bounding_box().right() >
333  truth_x + norm_box_tolerance_) {
334  missing_chop = true;
335  break;
336  } else {
337  ++blob_index;
338  }
339  }
340  if (missing_chop || box_index < norm_truth_word_.length()) {
341  STRING debug_str;
342  if (missing_chop) {
343  debug_str.add_str_int("Detected missing chop (tolerance=",
344  norm_box_tolerance_);
345  debug_str += ") at Bounding Box=";
346  TBLOB * curr_blob = word->chopped_word->blobs[blob_index];
347  curr_blob->bounding_box().print_to_str(&debug_str);
348  debug_str.add_str_int("\nNo chop for truth at x=", truth_x);
349  } else {
350  debug_str.add_str_int("Missing chops for last ",
351  norm_truth_word_.length() - box_index);
352  debug_str += " truth box(es)";
353  }
354  debug_str += "\nMaximally chopped word boxes:\n";
355  for (blob_index = 0; blob_index < num_blobs; ++blob_index) {
356  TBLOB * curr_blob = word->chopped_word->blobs[blob_index];
357  curr_blob->bounding_box().print_to_str(&debug_str);
358  debug_str += '\n';
359  }
360  debug_str += "Truth bounding boxes:\n";
361  for (box_index = 0; box_index < norm_truth_word_.length(); ++box_index) {
362  norm_truth_word_.BlobBox(box_index).print_to_str(&debug_str);
363  debug_str += '\n';
364  }
365  SetBlame(IRR_CHOPPER, debug_str, word->best_choice, debug);
366  }
367 }
368 
369 // Blames the classifier or the language model if, after running only the
370 // chopper, best_choice is incorrect and no blame has been yet set.
371 // Blames the classifier if best_choice is classifier's top choice and is a
372 // dictionary word (i.e. language model could not have helped).
373 // Otherwise, blames the language model (formerly permuter word adjustment).
375  const WERD_RES* word,
376  const UNICHARSET& unicharset, bool valid_permuter, bool debug) {
377  if (valid_permuter) {
378  // Find out whether best choice is a top choice.
379  best_choice_is_dict_and_top_choice_ = true;
380  for (int i = 0; i < word->best_choice->length(); ++i) {
381  BLOB_CHOICE_IT blob_choice_it(word->GetBlobChoices(i));
382  ASSERT_HOST(!blob_choice_it.empty());
383  BLOB_CHOICE *first_choice = nullptr;
384  for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list();
385  blob_choice_it.forward()) { // find first non-fragment choice
386  if (!(unicharset.get_fragment(blob_choice_it.data()->unichar_id()))) {
387  first_choice = blob_choice_it.data();
388  break;
389  }
390  }
391  ASSERT_HOST(first_choice != nullptr);
392  if (first_choice->unichar_id() != word->best_choice->unichar_id(i)) {
393  best_choice_is_dict_and_top_choice_ = false;
394  break;
395  }
396  }
397  }
398  STRING debug_str;
399  if (best_choice_is_dict_and_top_choice_) {
400  debug_str = "Best choice is: incorrect, top choice, dictionary word";
401  debug_str += " with permuter ";
402  debug_str += word->best_choice->permuter_name();
403  } else {
404  debug_str = "Classifier/Old LM tradeoff is to blame";
405  }
406  SetBlame(best_choice_is_dict_and_top_choice_ ? IRR_CLASSIFIER
408  debug_str, word->best_choice, debug);
409 }
410 
411 // Sets up the correct_segmentation_* to mark the correct bounding boxes.
413  params_training_bundle_.StartHypothesisList();
414  if (incorrect_result_reason_ != IRR_CORRECT || !truth_has_char_boxes_)
415  return; // Nothing to do here.
416 
417  STRING debug_str;
418  debug_str += "Blamer computing correct_segmentation_cols\n";
419  int curr_box_col = 0;
420  int next_box_col = 0;
421  int num_blobs = word->NumBlobs();
422  if (num_blobs == 0) return; // No blobs to play with.
423  int blob_index = 0;
424  int16_t next_box_x = word->blobs[blob_index]->bounding_box().right();
425  for (int truth_idx = 0; blob_index < num_blobs &&
426  truth_idx < norm_truth_word_.length();
427  ++blob_index) {
428  ++next_box_col;
429  int16_t curr_box_x = next_box_x;
430  if (blob_index + 1 < num_blobs)
431  next_box_x = word->blobs[blob_index + 1]->bounding_box().right();
432  int16_t truth_x = norm_truth_word_.BlobBox(truth_idx).right();
433  debug_str.add_str_int("Box x coord vs. truth: ", curr_box_x);
434  debug_str.add_str_int(" ", truth_x);
435  debug_str += "\n";
436  if (curr_box_x > (truth_x + norm_box_tolerance_)) {
437  break; // failed to find a matching box
438  } else if (curr_box_x >= truth_x - norm_box_tolerance_ && // matched
439  (blob_index + 1 >= num_blobs || // next box can't be included
440  next_box_x > truth_x + norm_box_tolerance_)) {
441  correct_segmentation_cols_.push_back(curr_box_col);
442  correct_segmentation_rows_.push_back(next_box_col-1);
443  ++truth_idx;
444  debug_str.add_str_int("col=", curr_box_col);
445  debug_str.add_str_int(" row=", next_box_col-1);
446  debug_str += "\n";
447  curr_box_col = next_box_col;
448  }
449  }
450  if (blob_index < num_blobs || // trailing blobs
451  correct_segmentation_cols_.length() != norm_truth_word_.length()) {
452  debug_str.add_str_int("Blamer failed to find correct segmentation"
453  " (tolerance=", norm_box_tolerance_);
454  if (blob_index >= num_blobs) debug_str += " blob == nullptr";
455  debug_str += ")\n";
456  debug_str.add_str_int(" path length ", correct_segmentation_cols_.length());
457  debug_str.add_str_int(" vs. truth ", norm_truth_word_.length());
458  debug_str += "\n";
459  SetBlame(IRR_UNKNOWN, debug_str, nullptr, debug);
460  correct_segmentation_cols_.clear();
461  correct_segmentation_rows_.clear();
462  }
463 }
464 
465 // Returns true if a guided segmentation search is needed.
466 bool BlamerBundle::GuidedSegsearchNeeded(const WERD_CHOICE *best_choice) const {
467  return incorrect_result_reason_ == IRR_CORRECT &&
468  !segsearch_is_looking_for_blame_ &&
469  truth_has_char_boxes_ &&
470  !ChoiceIsCorrect(best_choice);
471 }
472 
473 // Setup ready to guide the segmentation search to the correct segmentation.
474 // The callback pp_cb is used to avoid a cyclic dependency.
475 // It calls into LMPainPoints::GenerateForBlamer by pre-binding the
476 // WERD_RES, and the LMPainPoints itself.
477 // pp_cb must be a permanent callback, and should be deleted by the caller.
479  MATRIX* ratings, UNICHAR_ID wildcard_id,
480  bool debug, STRING *debug_str,
482  segsearch_is_looking_for_blame_ = true;
483  if (debug) {
484  tprintf("segsearch starting to look for blame\n");
485  }
486  // Fill pain points for any unclassifed blob corresponding to the
487  // correct segmentation state.
488  *debug_str += "Correct segmentation:\n";
489  for (int idx = 0; idx < correct_segmentation_cols_.length(); ++idx) {
490  debug_str->add_str_int("col=", correct_segmentation_cols_[idx]);
491  debug_str->add_str_int(" row=", correct_segmentation_rows_[idx]);
492  *debug_str += "\n";
493  if (!ratings->Classified(correct_segmentation_cols_[idx],
494  correct_segmentation_rows_[idx],
495  wildcard_id) &&
496  !cb->Run(correct_segmentation_cols_[idx],
497  correct_segmentation_rows_[idx])) {
498  segsearch_is_looking_for_blame_ = false;
499  *debug_str += "\nFailed to insert pain point\n";
500  SetBlame(IRR_SEGSEARCH_HEUR, *debug_str, best_choice, debug);
501  break;
502  }
503  } // end for blamer_bundle->correct_segmentation_cols/rows
504 }
505 // Returns true if the guided segsearch is in progress.
507  return segsearch_is_looking_for_blame_;
508 }
509 
510 // The segmentation search has ended. Sets the blame appropriately.
512  bool debug, STRING *debug_str) {
513  // If we are still looking for blame (i.e. best_choice is incorrect, but a
514  // path representing the correct segmentation could be constructed), we can
515  // blame segmentation search pain point prioritization if the rating of the
516  // path corresponding to the correct segmentation is better than that of
517  // best_choice (i.e. language model would have done the correct thing, but
518  // because of poor pain point prioritization the correct segmentation was
519  // never explored). Otherwise we blame the tradeoff between the language model
520  // and the classifier, since even after exploring the path corresponding to
521  // the correct segmentation incorrect best_choice would have been chosen.
522  // One special case when we blame the classifier instead is when best choice
523  // is incorrect, but it is a dictionary word and it classifier's top choice.
524  if (segsearch_is_looking_for_blame_) {
525  segsearch_is_looking_for_blame_ = false;
526  if (best_choice_is_dict_and_top_choice_) {
527  *debug_str = "Best choice is: incorrect, top choice, dictionary word";
528  *debug_str += " with permuter ";
529  *debug_str += best_choice->permuter_name();
530  SetBlame(IRR_CLASSIFIER, *debug_str, best_choice, debug);
531  } else if (best_correctly_segmented_rating_ <
532  best_choice->rating()) {
533  *debug_str += "Correct segmentation state was not explored";
534  SetBlame(IRR_SEGSEARCH_PP, *debug_str, best_choice, debug);
535  } else {
536  if (best_correctly_segmented_rating_ >=
538  *debug_str += "Correct segmentation paths were pruned by LM\n";
539  } else {
540  debug_str->add_str_double("Best correct segmentation rating ",
541  best_correctly_segmented_rating_);
542  debug_str->add_str_double(" vs. best choice rating ",
543  best_choice->rating());
544  }
545  SetBlame(IRR_CLASS_LM_TRADEOFF, *debug_str, best_choice, debug);
546  }
547  }
548 }
549 
550 // If the bundle is null or still does not indicate the correct result,
551 // fix it and use some backup reason for the blame.
553  if (word->blamer_bundle == nullptr) {
555  word->blamer_bundle->SetBlame(IRR_PAGE_LAYOUT, "LastChanceBlame",
557  } else if (word->blamer_bundle->incorrect_result_reason_ == IRR_NO_TRUTH) {
558  word->blamer_bundle->SetBlame(IRR_NO_TRUTH, "Rejected truth",
560  } else {
562  IncorrectResultReason irr = word->blamer_bundle->incorrect_result_reason_;
563  if (irr == IRR_CORRECT && !correct) {
564  STRING debug_str = "Choice is incorrect after recognition";
565  word->blamer_bundle->SetBlame(IRR_UNKNOWN, debug_str, word->best_choice,
566  debug);
567  } else if (irr != IRR_CORRECT && correct) {
568  if (debug) {
569  tprintf("Corrected %s\n", word->blamer_bundle->debug_.string());
570  }
571  word->blamer_bundle->incorrect_result_reason_ = IRR_CORRECT;
572  word->blamer_bundle->debug_ = "";
573  }
574  }
575 }
576 
577 // Sets the misadaption debug if this word is incorrect, as this word is
578 // being adapted to.
580  bool debug) {
581  if (incorrect_result_reason_ != IRR_NO_TRUTH &&
582  !ChoiceIsCorrect(best_choice)) {
583  misadaption_debug_ ="misadapt to word (";
584  misadaption_debug_ += best_choice->permuter_name();
585  misadaption_debug_ += "): ";
586  FillDebugString("", best_choice, &misadaption_debug_);
587  if (debug) {
588  tprintf("%s\n", misadaption_debug_.string());
589  }
590  }
591 }
bool GuidedSegsearchStillGoing() const
Definition: blamer.cpp:506
const char kBlameNoTruth[]
Definition: blamer.cpp:43
const STRING & debug() const
Definition: blamer.h:128
void SetRejectedTruth()
Definition: blamer.cpp:110
void NormTransform(const DENORM *first_norm, const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:335
static const float kBadRating
Definition: ratngs.h:275
int UNICHAR_ID
Definition: unichar.h:35
int size() const
Definition: genericvector.h:71
bool encode_string(const char *str, bool give_up_on_failure, GenericVector< UNICHAR_ID > *encoding, GenericVector< char > *lengths, int *encoded_length) const
Definition: unicharset.cpp:258
Definition: blobs.h:402
void SplitBundle(int word1_right, int word2_left, bool debug, BlamerBundle *bundle1, BlamerBundle *bundle2) const
Definition: blamer.cpp:174
const char * get_normed_unichar(UNICHAR_ID unichar_id) const
Definition: unicharset.h:823
const char kBlameClassifier[]
Definition: blamer.cpp:34
void BlameClassifier(const UNICHARSET &unicharset, const TBOX &blob_box, const BLOB_CHOICE_LIST &choices, bool debug)
Definition: blamer.cpp:262
IncorrectResultReason
Definition: blamer.h:49
const char * string() const
Definition: strngs.cpp:196
BLOB_CHOICE_LIST * GetBlobChoices(int index) const
Definition: pageres.cpp:765
const char kBlameNoTruthSplit[]
Definition: blamer.cpp:42
const UNICHARSET * unicharset() const
Definition: ratngs.h:300
static const char * permuter_name(uint8_t permuter)
Definition: ratngs.cpp:194
Definition: rect.h:34
void SetChopperBlame(const WERD_RES *word, bool debug)
Definition: blamer.cpp:315
UNICHAR_ID unichar_to_id(const char *const unichar_repr) const
Definition: unicharset.cpp:209
float rating() const
Definition: ratngs.h:327
const char kBlameClassLMTradeoff[]
Definition: blamer.cpp:36
static const char * IncorrectReasonName(IncorrectResultReason irr)
Definition: blamer.cpp:61
void SetMisAdaptionDebug(const WERD_CHOICE *best_choice, bool debug)
Definition: blamer.cpp:579
const TBOX & BlobBox(int index) const
Definition: boxword.h:84
const char kBlameSegsearchHeur[]
Definition: blamer.cpp:38
bool ChoiceIsCorrect(const WERD_CHOICE *word_choice) const
Definition: blamer.cpp:116
void InitForSegSearch(const WERD_CHOICE *best_choice, MATRIX *ratings, UNICHAR_ID wildcard_id, bool debug, STRING *debug_str, TessResultCallback2< bool, int, int > *pp_cb)
Definition: blamer.cpp:478
const char kBlameClassOldLMTradeoff[]
Definition: blamer.cpp:40
bool GuidedSegsearchNeeded(const WERD_CHOICE *best_choice) const
Definition: blamer.cpp:466
const char kBlameCorrect[]
Definition: blamer.cpp:33
void BlameClassifierOrLangModel(const WERD_RES *word, const UNICHARSET &unicharset, bool valid_permuter, bool debug)
Definition: blamer.cpp:374
int16_t left() const
Definition: rect.h:72
int16_t top() const
Definition: rect.h:58
void SetSymbolTruth(const UNICHARSET &unicharset, const char *char_str, const TBOX &char_box)
Definition: blamer.cpp:91
void JoinBlames(const BlamerBundle &bundle1, const BlamerBundle &bundle2, bool debug)
Definition: blamer.cpp:230
float x_scale() const
Definition: normalis.h:267
const char kBlamePageLayout[]
Definition: blamer.cpp:37
void add_str_double(const char *str, double number)
Definition: strngs.cpp:389
virtual R Run(A1, A2)=0
STRING TruthString() const
Definition: blamer.h:112
int length() const
Definition: genericvector.h:85
WERD_RES * word() const
Definition: pageres.h:751
bool NoTruth() const
Definition: blamer.h:121
void SetWordTruth(const UNICHARSET &unicharset, const char *truth_str, const TBOX &word_box)
Definition: blamer.cpp:71
UNICHAR_ID unichar_id(int index) const
Definition: ratngs.h:315
const char kBlameChopper[]
Definition: blamer.cpp:35
bool IsAdapted() const
Definition: ratngs.h:136
bool empty() const
Definition: genericvector.h:90
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:37
TBOX bounding_box() const
Definition: blobs.cpp:478
int length() const
Definition: ratngs.h:303
void truncate_at(int32_t index)
Definition: strngs.cpp:267
void FillDebugString(const STRING &msg, const WERD_CHOICE *choice, STRING *debug)
Definition: blamer.cpp:128
bool x_almost_equal(const TBOX &box, int tolerance) const
Definition: rect.cpp:253
int push_back(T object)
GenericVector< TBLOB * > blobs
Definition: blobs.h:443
int16_t x
Definition: blobs.h:78
void string_and_lengths(STRING *word_str, STRING *word_lengths_str) const
Definition: ratngs.cpp:449
void add_str_int(const char *str, int number)
Definition: strngs.cpp:379
const CHAR_FRAGMENT * get_fragment(UNICHAR_ID unichar_id) const
Definition: unicharset.h:729
Definition: strngs.h:45
void SetupCorrectSegmentation(const TWERD *word, bool debug)
Definition: blamer.cpp:412
bool Classified(int col, int row, int wildcard_id) const
Definition: matrix.cpp:41
const char kBlameSegsearchPP[]
Definition: blamer.cpp:39
const char * id_to_unichar(UNICHAR_ID id) const
Definition: unicharset.cpp:290
int length() const
Definition: boxword.h:83
BlamerBundle * blamer_bundle
Definition: pageres.h:246
int16_t right() const
Definition: rect.h:79
Definition: matrix.h:575
Definition: blobs.h:268
static void LastChanceBlame(bool debug, WERD_RES *word)
Definition: blamer.cpp:552
const char * IncorrectReason() const
Definition: blamer.cpp:65
void FinishSegSearch(const WERD_CHOICE *best_choice, bool debug, STRING *debug_str)
Definition: blamer.cpp:511
TWERD * chopped_word
Definition: pageres.h:215
Definition: blobs.h:57
int16_t y
Definition: blobs.h:79
int16_t bottom() const
Definition: rect.h:65
const char *const kIncorrectResultReasonNames[]
Definition: blamer.cpp:46
UNICHAR_ID unichar_id() const
Definition: ratngs.h:77
int32_t length() const
Definition: strngs.cpp:191
const char kBlameAdaption[]
Definition: blamer.cpp:41
WERD_CHOICE * best_choice
Definition: pageres.h:235
void SetupNormTruthWord(const DENORM &denorm)
Definition: blamer.cpp:150
void InsertBox(int index, const TBOX &box)
Definition: boxword.cpp:148
BlamerBundle()
Definition: blamer.h:102
const char kBlameUnknown[]
Definition: blamer.cpp:44
void print_to_str(STRING *str) const
Definition: rect.cpp:175
#define ASSERT_HOST(x)
Definition: errcode.h:84