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