66 #define ADAPT_TEMPLATE_SUFFIX ".a" 68 #define MAX_MATCHES 10 69 #define UNLIKELY_NUM_FEAT 200 71 #define MAX_ADAPTABLE_WERD_SIZE 40 73 #define ADAPTABLE_WERD_ADJUSTMENT (0.05) 75 #define Y_DIM_OFFSET (Y_SHIFT - BASELINE_Y_SHIFT) 77 #define WORST_POSSIBLE_RATING (0.0f) 95 HasNonfragment =
false;
100 best_unichar_id = INVALID_UNICHAR_ID;
101 best_match_index = -1;
103 for (
int i = 0; i < match.
size(); ++i) {
104 if (match[i].rating > best_rating) {
105 best_rating = match[i].rating;
106 best_unichar_id = match[i].unichar_id;
107 best_match_index = i;
122 inline bool MarginalMatch(
float confidence,
float matcher_great_threshold) {
123 return (1.0f - confidence) > matcher_great_threshold;
132 for (
int i = 0; i < results.
match.
size(); i++) {
133 if (results.
match[i].unichar_id ==
id)
142 int index = FindScoredUnichar(
id, results);
144 return results.
match[index].rating;
185 void Classify::AdaptiveClassifier(
TBLOB *Blob, BLOB_CHOICE_LIST *Choices) {
186 assert(Choices !=
NULL);
192 DoAdaptiveMatch(Blob, Results);
194 RemoveBadMatches(Results);
195 Results->
match.
sort(&UnicharRating::SortDescendingRating);
196 RemoveExtraPuncs(Results);
202 if (LargeSpeckle(*Blob) || Choices->length() == 0)
203 AddLargeSpeckleTo(Results->
BlobLength, Choices);
205 if (matcher_debug_level >= 1) {
207 PrintAdaptiveMatchResults(*Results);
210 #ifndef GRAPHICS_DISABLED 211 if (classify_enable_adaptive_debugger)
212 DebugAdaptiveClassifier(Blob, Results);
220 void Classify::RefreshDebugWindow(
ScrollView **win,
const char *msg,
221 int y_offset,
const TBOX &wbox) {
222 #ifndef GRAPHICS_DISABLED 223 const int kSampleSpaceWidth = 500;
225 *win =
new ScrollView(msg, 100, y_offset, kSampleSpaceWidth * 2, 200,
226 kSampleSpaceWidth * 2, 200,
true);
229 (*win)->Pen(64, 64, 64);
234 (*win)->ZoomToRectangle(wbox.
left(), wbox.
top(),
236 #endif // GRAPHICS_DISABLED 244 void Classify::LearnWord(
const char* fontname,
WERD_RES* word) {
246 if (word_len == 0)
return;
248 float* thresholds =
NULL;
249 if (fontname ==
NULL) {
254 if (classify_learning_debug_level >= 1)
255 tprintf(
"\n\nAdapting to word = %s\n",
257 thresholds =
new float[word_len];
259 matcher_perfect_threshold,
260 matcher_good_threshold,
261 matcher_rating_margin, thresholds);
265 #ifndef GRAPHICS_DISABLED 266 if (classify_debug_character_fragments) {
267 if (learn_fragmented_word_debug_win_ !=
NULL) {
270 RefreshDebugWindow(&learn_fragments_debug_win_,
"LearnPieces", 400,
272 RefreshDebugWindow(&learn_fragmented_word_debug_win_,
"LearnWord", 200,
277 #endif // GRAPHICS_DISABLED 279 for (
int ch = 0; ch < word_len; ++ch) {
280 if (classify_debug_character_fragments) {
284 float threshold = thresholds !=
NULL ? thresholds[ch] : 0.0f;
286 LearnPieces(fontname, start_blob, word->
best_state[ch], threshold,
293 bool garbage =
false;
295 for (frag = 0; frag < word->
best_state[ch]; ++frag) {
297 if (classify_character_fragments_garbage_certainty_threshold < 0) {
298 garbage |= LooksLikeGarbage(frag_blob);
305 if (pieces_all_natural || !prioritize_division) {
306 for (frag = 0; frag < word->
best_state[ch]; ++frag) {
311 tokens[0].
string(), frag, word->
best_state[ch],
315 for (
int i = 0; i < tokens.
size(); i++) {
316 full_string += tokens[i];
317 if (i != tokens.
size() - 1)
320 LearnPieces(fontname, start_blob + frag, 1, threshold,
356 delete [] thresholds;
368 void Classify::LearnPieces(
const char* fontname,
int start,
int length,
370 const char* correct_text,
WERD_RES* word) {
384 if (rotated_blob ==
NULL)
387 #ifndef GRAPHICS_DISABLED 389 if (strcmp(classify_learn_debug_str.string(), correct_text) == 0) {
390 RefreshDebugWindow(&learn_debug_win_,
"LearnPieces", 600,
393 learn_debug_win_->Update();
396 if (classify_debug_character_fragments && segmentation ==
CST_FRAGMENT) {
398 blob->
plot(learn_fragments_debug_win_,
400 learn_fragments_debug_win_->Update();
402 #endif // GRAPHICS_DISABLED 404 if (fontname !=
NULL) {
405 classify_norm_method.set_value(
character);
406 tess_bn_matching.set_value(
false);
407 tess_cn_matching.set_value(
false);
408 DENORM bl_denorm, cn_denorm;
410 SetupBLCNDenorms(*rotated_blob, classify_nonlinear_norm,
411 &bl_denorm, &cn_denorm, &fx_info);
412 LearnBlob(fontname, rotated_blob, cn_denorm, fx_info, correct_text);
413 }
else if (unicharset.contains_unichar(correct_text)) {
414 UNICHAR_ID class_id = unicharset.unichar_to_id(correct_text);
416 ? fontinfo_table_.get_id(*word->
fontinfo)
418 if (classify_learning_debug_level >= 1)
419 tprintf(
"Adapting to char = %s, thr= %g font_id= %d\n",
420 unicharset.id_to_unichar(class_id), threshold, font_id);
423 AdaptToChar(rotated_blob, class_id, font_id, threshold, AdaptedTemplates);
424 if (BackupAdaptedTemplates !=
NULL) {
427 AdaptToChar(rotated_blob, class_id, font_id, threshold,
428 BackupAdaptedTemplates);
430 }
else if (classify_debug_level >= 1) {
431 tprintf(
"Can't adapt to %s not in unicharset\n", correct_text);
433 if (rotated_blob != blob) {
456 void Classify::EndAdaptiveClassifier() {
460 if (AdaptedTemplates !=
NULL &&
461 classify_enable_adaptive_matcher && classify_save_adapted_templates) {
463 File = fopen (Filename.
string(),
"wb");
465 cprintf (
"Unable to save adapted templates to %s!\n", Filename.
string());
467 cprintf (
"\nSaving adapted templates to %s ...", Filename.
string());
469 WriteAdaptedTemplates(File, AdaptedTemplates);
475 if (AdaptedTemplates !=
NULL) {
477 AdaptedTemplates =
NULL;
479 if (BackupAdaptedTemplates !=
NULL) {
481 BackupAdaptedTemplates =
NULL;
484 if (PreTrainedTemplates !=
NULL) {
486 PreTrainedTemplates =
NULL;
488 getDict().EndDangerousAmbigs();
490 if (AllProtosOn !=
NULL) {
497 AllConfigsOff =
NULL;
498 TempProtoMask =
NULL;
502 if (static_classifier_ !=
NULL) {
503 delete static_classifier_;
504 static_classifier_ =
NULL;
527 void Classify::InitAdaptiveClassifier(
bool load_pre_trained_templates) {
528 if (!classify_enable_adaptive_matcher)
530 if (AllProtosOn !=
NULL)
531 EndAdaptiveClassifier();
535 if (language_data_path_prefix.length() > 0 &&
536 load_pre_trained_templates) {
538 PreTrainedTemplates =
539 ReadIntTemplates(tessdata_manager.GetDataFilePtr());
540 if (tessdata_manager.DebugLevel() > 0)
tprintf(
"Loaded inttemp\n");
544 if (!shape_table_->DeSerialize(tessdata_manager.swap(),
545 tessdata_manager.GetDataFilePtr())) {
546 tprintf(
"Error loading shape table!\n");
549 }
else if (tessdata_manager.DebugLevel() > 0) {
550 tprintf(
"Successfully loaded shape table!\n");
555 ReadNewCutoffs(tessdata_manager.GetDataFilePtr(),
556 tessdata_manager.swap(),
559 if (tessdata_manager.DebugLevel() > 0)
tprintf(
"Loaded pffmtable\n");
565 if (tessdata_manager.DebugLevel() > 0)
tprintf(
"Loaded normproto\n");
569 im_.Init(&classify_debug_level);
581 BaselineCutoffs[i] = 0;
584 if (classify_use_pre_adapted_templates) {
588 Filename = imagefile;
590 File = fopen(Filename.
string(),
"rb");
592 AdaptedTemplates = NewAdaptedTemplates(
true);
594 cprintf(
"\nReading pre-adapted templates from %s ...\n",
597 AdaptedTemplates = ReadAdaptedTemplates(File);
600 PrintAdaptedTemplates(stdout, AdaptedTemplates);
602 for (
int i = 0; i < AdaptedTemplates->Templates->NumClasses; i++) {
603 BaselineCutoffs[i] = CharNormCutoffs[i];
607 if (AdaptedTemplates !=
NULL)
609 AdaptedTemplates = NewAdaptedTemplates(
true);
613 void Classify::ResetAdaptiveClassifierInternal() {
614 if (classify_learning_debug_level > 0) {
615 tprintf(
"Resetting adaptive classifier (NumAdaptationsFailed=%d)\n",
616 NumAdaptationsFailed);
619 AdaptedTemplates = NewAdaptedTemplates(
true);
620 if (BackupAdaptedTemplates !=
NULL)
622 BackupAdaptedTemplates =
NULL;
623 NumAdaptationsFailed = 0;
628 void Classify::SwitchAdaptiveClassifier() {
629 if (BackupAdaptedTemplates ==
NULL) {
630 ResetAdaptiveClassifierInternal();
633 if (classify_learning_debug_level > 0) {
634 tprintf(
"Switch to backup adaptive classifier (NumAdaptationsFailed=%d)\n",
635 NumAdaptationsFailed);
638 AdaptedTemplates = BackupAdaptedTemplates;
639 BackupAdaptedTemplates =
NULL;
640 NumAdaptationsFailed = 0;
644 void Classify::StartBackupAdaptiveClassifier() {
645 if (BackupAdaptedTemplates !=
NULL)
647 BackupAdaptedTemplates = NewAdaptedTemplates(
true);
670 void Classify::SettupPass1() {
671 EnableLearning = classify_enable_learning;
673 getDict().SettupStopperPass1();
690 void Classify::SettupPass2() {
691 EnableLearning =
FALSE;
692 getDict().SettupStopperPass2();
717 void Classify::InitAdaptedClass(
TBLOB *Blob,
731 classify_norm_method.set_value(
baseline);
732 Features = ExtractOutlineFeatures(Blob);
743 if (Templates == AdaptedTemplates)
744 BaselineCutoffs[ClassId] = CharNormCutoffs[ClassId];
748 for (Fid = 0; Fid < Features->
NumFeatures; Fid++) {
754 Proto = &(TempProto->
Proto);
768 ConvertProto(Proto, Pid, IClass);
770 classify_learning_debug_level >= 2);
779 if (classify_learning_debug_level >= 1) {
780 tprintf(
"Added new class '%s' with class id %d and %d protos.\n",
781 unicharset.id_to_unichar(ClassId), ClassId, NumFeatures);
782 if (classify_learning_debug_level > 1)
783 DisplayAdaptedChar(Blob, IClass);
812 int Classify::GetAdaptiveFeatures(
TBLOB *Blob,
818 classify_norm_method.set_value(
baseline);
819 Features = ExtractPicoFeatures(Blob);
827 ComputeIntFeatures(Features, IntFeatures);
828 *FloatFeatures = Features;
854 float adaptable_score =
857 BestChoiceLength > 0 &&
903 Class = adaptive_templates->
Class[ClassId];
904 assert(Class !=
NULL);
906 InitAdaptedClass(Blob, ClassId, FontinfoId, Class, adaptive_templates);
910 NumFeatures = GetAdaptiveFeatures(Blob, IntFeatures, &FloatFeatures);
911 if (NumFeatures <= 0)
916 for (
int cfg = 0; cfg < IClass->
NumConfigs; ++cfg) {
917 if (GetFontinfoId(Class, cfg) == FontinfoId) {
918 SET_BIT(MatchingFontConfigs, cfg);
923 im_.Match(IClass, AllProtosOn, MatchingFontConfigs,
924 NumFeatures, IntFeatures,
925 &int_result, classify_adapt_feature_threshold,
926 NO_DEBUG, matcher_debug_separate_windows);
931 if (1.0f - int_result.
rating <= Threshold) {
933 if (classify_learning_debug_level >= 1)
934 tprintf(
"Found good match to perm config %d = %4.1f%%.\n",
945 if (classify_learning_debug_level >= 1)
946 tprintf(
"Increasing reliability of temp config %d to %d.\n",
949 if (TempConfigReliable(ClassId, TempConfig)) {
950 MakePermanent(adaptive_templates, ClassId, int_result.
config, Blob);
951 UpdateAmbigsGroup(ClassId, Blob);
954 if (classify_learning_debug_level >= 1) {
955 tprintf(
"Found poor match to temp config %d = %4.1f%%.\n",
957 if (classify_learning_debug_level > 2)
958 DisplayAdaptedChar(Blob, IClass);
961 MakeNewTemporaryConfig(adaptive_templates, ClassId, FontinfoId,
962 NumFeatures, IntFeatures, FloatFeatures);
963 if (NewTempConfigId >= 0 &&
964 TempConfigReliable(ClassId,
TempConfigFor(Class, NewTempConfigId))) {
965 MakePermanent(adaptive_templates, ClassId, NewTempConfigId, Blob);
966 UpdateAmbigsGroup(ClassId, Blob);
969 #ifndef GRAPHICS_DISABLED 970 if (classify_learning_debug_level > 1) {
971 DisplayAdaptedChar(Blob, IClass);
980 #ifndef GRAPHICS_DISABLED 986 if (sample ==
NULL)
return;
989 im_.Match(int_class, AllProtosOn, AllConfigsOn,
990 bl_features.
size(), &bl_features[0],
991 &int_result, classify_adapt_feature_threshold,
992 NO_DEBUG, matcher_debug_separate_windows);
993 tprintf(
"Best match to temp config %d = %4.1f%%.\n",
994 int_result.config, int_result.rating * 100.0);
995 if (classify_learning_debug_level >= 2) {
997 ConfigMask = 1 << int_result.config;
999 im_.Match(int_class, AllProtosOn, (
BIT_VECTOR)&ConfigMask,
1000 bl_features.
size(), &bl_features[0],
1001 &int_result, classify_adapt_feature_threshold,
1002 6 | 0x19, matcher_debug_separate_windows);
1039 int old_match = FindScoredUnichar(new_result.
unichar_id, *results);
1043 new_result.
rating <= results->
match[old_match].rating))
1046 if (!unicharset.get_fragment(new_result.
unichar_id))
1049 if (old_match < results->
match.
size()) {
1050 results->
match[old_match].rating = new_result.
rating;
1061 !unicharset.get_fragment(new_result.
unichar_id)) {
1089 void Classify::AmbigClassifier(
1097 if (int_features.
empty())
return;
1098 uinT8* CharNormArray =
new uinT8[unicharset.size()];
1101 results->
BlobLength = GetCharNormFeature(fx_info, templates,
NULL,
1103 bool debug = matcher_debug_level >= 2 || classify_debug_level > 1;
1109 while (*ambiguities >= 0) {
1114 AllProtosOn, AllConfigsOn,
1115 int_features.
size(), &int_features[0],
1117 classify_adapt_feature_threshold,
NO_DEBUG,
1118 matcher_debug_separate_windows);
1120 ExpandShapesAndApplyCorrections(
NULL, debug, class_id, bottom, top, 0,
1123 CharNormArray, &int_result, results);
1126 delete [] CharNormArray;
1135 const uinT8* norm_factors,
1138 int matcher_multiplier,
1139 const TBOX& blob_box,
1142 int top = blob_box.
top();
1143 int bottom = blob_box.
bottom();
1145 for (
int c = 0; c < results.
size(); c++) {
1146 CLASS_ID class_id = results[c].Class;
1155 num_features, features,
1156 &int_result, classify_adapt_feature_threshold, debug,
1157 matcher_debug_separate_windows);
1158 bool debug = matcher_debug_level >= 2 || classify_debug_level > 1;
1159 ExpandShapesAndApplyCorrections(classes, debug, class_id, bottom, top,
1162 matcher_multiplier, norm_factors,
1163 &int_result, final_results);
1172 void Classify::ExpandShapesAndApplyCorrections(
1173 ADAPT_CLASS* classes,
bool debug,
int class_id,
int bottom,
int top,
1174 float cp_rating,
int blob_length,
int matcher_multiplier,
1175 const uinT8* cn_factors,
1177 if (classes !=
NULL) {
1180 for (
int f = 0; f < int_result->
fonts.size(); ++f) {
1181 int_result->
fonts[f].fontinfo_id =
1182 GetFontinfoId(classes[class_id], int_result->
fonts[f].fontinfo_id);
1187 for (
int f = 0; f < int_result->
fonts.size(); ++f) {
1188 int_result->
fonts[f].fontinfo_id =
1189 ClassAndConfigIDToFontOrShapeID(class_id,
1190 int_result->
fonts[f].fontinfo_id);
1192 if (shape_table_ !=
NULL) {
1201 for (
int f = 0; f < int_result->
fonts.size(); ++f) {
1202 int shape_id = int_result->
fonts[f].fontinfo_id;
1203 const Shape& shape = shape_table_->GetShape(shape_id);
1204 for (
int c = 0; c < shape.
size(); ++c) {
1205 int unichar_id = shape[c].unichar_id;
1206 if (!unicharset.get_enabled(unichar_id))
continue;
1209 for (r = 0; r < mapped_results.
size() &&
1210 mapped_results[r].unichar_id != unichar_id; ++r) {}
1211 if (r == mapped_results.
size()) {
1213 mapped_results[r].unichar_id = unichar_id;
1214 mapped_results[r].fonts.
truncate(0);
1216 for (
int i = 0; i < shape[c].font_ids.
size(); ++i) {
1222 for (
int m = 0; m < mapped_results.
size(); ++m) {
1223 mapped_results[m].rating =
1224 ComputeCorrectedRating(debug, mapped_results[m].unichar_id,
1225 cp_rating, int_result->
rating,
1227 blob_length, matcher_multiplier, cn_factors);
1228 AddNewResult(mapped_results[m], final_results);
1233 if (unicharset.get_enabled(class_id)) {
1234 int_result->
rating = ComputeCorrectedRating(debug, class_id, cp_rating,
1237 bottom, top, blob_length,
1238 matcher_multiplier, cn_factors);
1239 AddNewResult(*int_result, final_results);
1246 double Classify::ComputeCorrectedRating(
bool debug,
int unichar_id,
1247 double cp_rating,
double im_rating,
1249 int bottom,
int top,
1250 int blob_length,
int matcher_multiplier,
1251 const uinT8* cn_factors) {
1253 double cn_corrected = im_.ApplyCNCorrection(1.0 - im_rating, blob_length,
1254 cn_factors[unichar_id],
1255 matcher_multiplier);
1256 double miss_penalty = tessedit_class_miss_scale * feature_misses;
1257 double vertical_penalty = 0.0;
1259 if (!unicharset.get_isalpha(unichar_id) &&
1260 !unicharset.get_isdigit(unichar_id) &&
1261 cn_factors[unichar_id] != 0 && classify_misfit_junk_penalty > 0.0) {
1262 int min_bottom, max_bottom, min_top, max_top;
1263 unicharset.get_top_bottom(unichar_id, &min_bottom, &max_bottom,
1264 &min_top, &max_top);
1266 tprintf(
"top=%d, vs [%d, %d], bottom=%d, vs [%d, %d]\n",
1267 top, min_top, max_top, bottom, min_bottom, max_bottom);
1269 if (top < min_top || top > max_top ||
1270 bottom < min_bottom || bottom > max_bottom) {
1271 vertical_penalty = classify_misfit_junk_penalty;
1274 double result = 1.0 - (cn_corrected + miss_penalty + vertical_penalty);
1278 tprintf(
"%s: %2.1f%%(CP%2.1f, IM%2.1f + CN%.2f(%d) + MP%2.1f + VP%2.1f)\n",
1279 unicharset.id_to_unichar(unichar_id),
1282 (1.0 - im_rating) * 100.0,
1283 (cn_corrected - (1.0 - im_rating)) * 100.0,
1284 cn_factors[unichar_id],
1285 miss_penalty * 100.0,
1286 vertical_penalty * 100.0);
1314 uinT8* CharNormArray =
new uinT8[unicharset.size()];
1315 ClearCharNormArray(CharNormArray);
1318 PruneClasses(Templates->
Templates, int_features.
size(), -1, &int_features[0],
1319 CharNormArray, BaselineCutoffs, &Results->
CPResults);
1321 if (matcher_debug_level >= 2 || classify_debug_level > 1)
1324 MasterMatcher(Templates->
Templates, int_features.
size(), &int_features[0],
1326 Templates->
Class, matcher_debug_flags, 0,
1329 delete [] CharNormArray;
1334 return Templates->
Class[ClassId]->
1358 int Classify::CharNormClassifier(
TBLOB *blob,
1365 static_classifier_->UnicharClassifySample(sample, blob->
denorm().
pix(), 0,
1366 -1, &unichar_results);
1368 for (
int r = 0; r < unichar_results.size(); ++r) {
1369 AddNewResult(unichar_results[r], adapt_results);
1376 int Classify::CharNormTrainingSample(
bool pruner_only,
1391 uinT8* char_norm_array =
new uinT8[unicharset.size()];
1392 int num_pruner_classes =
MAX(unicharset.size(),
1393 PreTrainedTemplates->NumClasses);
1394 uinT8* pruner_norm_array =
new uinT8[num_pruner_classes];
1397 ComputeCharNormArrays(norm_feature, PreTrainedTemplates, char_norm_array,
1400 PruneClasses(PreTrainedTemplates, num_features, keep_this, sample.
features(),
1402 shape_table_ !=
NULL ? &shapetable_cutoffs_[0] : CharNormCutoffs,
1404 delete [] pruner_norm_array;
1405 if (keep_this >= 0) {
1406 adapt_results->
CPResults[0].Class = keep_this;
1412 int class_id = adapt_results->
CPResults[i].Class;
1417 MasterMatcher(PreTrainedTemplates, num_features, sample.
features(),
1419 NULL, matcher_debug_flags,
1421 blob_box, adapt_results->
CPResults, adapt_results);
1423 for (
int i = 0; i < adapt_results->
match.
size(); i++) {
1426 results->
sort(&UnicharRating::SortDescendingRating);
1428 delete [] char_norm_array;
1429 delete adapt_results;
1430 return num_features;
1450 float rating = results->
BlobLength / matcher_avg_noise_size;
1452 rating /= 1.0 + rating;
1463 void Classify::ConvertMatchesToChoices(
const DENORM& denorm,
const TBOX& box,
1465 BLOB_CHOICE_LIST *Choices) {
1466 assert(Choices !=
NULL);
1469 BLOB_CHOICE_IT temp_it;
1470 bool contains_nonfrag =
false;
1471 temp_it.set_to_list(Choices);
1472 int choices_length = 0;
1479 if (shape_table_ !=
NULL) {
1480 max_matches = shape_table_->MaxNumUnichars() * 2;
1486 for (
int i = 0; i < Results->
match.
size(); i++) {
1488 bool adapted = result.
adapted;
1489 bool current_is_frag = (unicharset.get_fragment(result.
unichar_id) !=
NULL);
1490 if (temp_it.length()+1 == max_matches &&
1491 !contains_nonfrag && current_is_frag) {
1503 Rating = Certainty = (1.0f - result.
rating);
1504 Rating *= rating_scale * Results->
BlobLength;
1505 Certainty *= -(getDict().certainty_scale);
1512 if (Certainty > best_certainty) {
1513 best_certainty =
MIN(Certainty, classify_adapted_pruning_threshold);
1514 }
else if (adapted &&
1515 Certainty / classify_adapted_pruning_factor < best_certainty) {
1519 float min_xheight, max_xheight, yshift;
1521 &min_xheight, &max_xheight, &yshift);
1525 min_xheight, max_xheight, yshift,
1529 temp_it.add_to_end(choice);
1530 contains_nonfrag |= !current_is_frag;
1532 if (choices_length >= max_matches)
break;
1539 #ifndef GRAPHICS_DISABLED 1550 void Classify::DebugAdaptiveClassifier(
TBLOB *blob,
1552 if (static_classifier_ ==
NULL)
return;
1557 if (sample ==
NULL)
return;
1558 static_classifier_->DebugDisplay(*sample, blob->
denorm().
pix(),
1594 if (sample ==
NULL)
return;
1596 if (AdaptedTemplates->NumPermClasses < matcher_permanent_classes_min ||
1598 CharNormClassifier(Blob, *sample, Results);
1600 Ambiguities = BaselineClassifier(Blob, bl_features, fx_info,
1601 AdaptedTemplates, Results);
1604 matcher_reliable_adaptive_result) &&
1605 !tess_bn_matching) ||
1607 CharNormClassifier(Blob, *sample, Results);
1608 }
else if (Ambiguities && *Ambiguities >= 0 && !tess_bn_matching) {
1609 AmbigClassifier(bl_features, fx_info, Blob,
1610 PreTrainedTemplates,
1611 AdaptedTemplates->Class,
1622 ClassifyAsNoise(Results);
1655 if (sample ==
NULL) {
1660 CharNormClassifier(Blob, *sample, Results);
1662 RemoveBadMatches(Results);
1663 Results->
match.
sort(&UnicharRating::SortDescendingRating);
1670 Results->
match[0].unichar_id != CorrectClass)) {
1671 for (i = 0; i < Results->
match.
size(); i++)
1672 Ambiguities[i] = Results->
match[i].unichar_id;
1673 Ambiguities[i] = -1;
1675 Ambiguities[0] = -1;
1684 bool Classify::LooksLikeGarbage(
TBLOB *blob) {
1685 BLOB_CHOICE_LIST *ratings =
new BLOB_CHOICE_LIST();
1686 AdaptiveClassifier(blob, ratings);
1687 BLOB_CHOICE_IT ratings_it(ratings);
1688 const UNICHARSET &unicharset = getDict().getUnicharset();
1689 if (classify_debug_character_fragments) {
1691 ratings, unicharset);
1693 for (ratings_it.mark_cycle_pt(); !ratings_it.cycled_list();
1694 ratings_it.forward()) {
1698 float certainty = ratings_it.data()->certainty();
1701 classify_character_fragments_garbage_certainty_threshold;
1735 uinT8* pruner_norm_array,
1736 uinT8* char_norm_array) {
1746 ComputeCharNormArrays(norm_feature, templates, char_norm_array,
1755 uinT8* char_norm_array,
1756 uinT8* pruner_array) {
1757 ComputeIntCharNormArray(*norm_feature, char_norm_array);
1758 if (pruner_array !=
NULL) {
1759 if (shape_table_ ==
NULL) {
1760 ComputeIntCharNormArray(*norm_feature, pruner_array);
1763 templates->
NumClasses *
sizeof(pruner_array[0]));
1766 for (
int id = 0;
id < templates->
NumClasses; ++id) {
1768 const FontSet &fs = fontset_table_.get(font_set_id);
1769 for (
int config = 0; config < fs.
size; ++config) {
1770 const Shape& shape = shape_table_->GetShape(fs.
configs[config]);
1771 for (
int c = 0; c < shape.
size(); ++c) {
1772 if (char_norm_array[shape[c].unichar_id] < pruner_array[
id])
1773 pruner_array[id] = char_norm_array[shape[c].unichar_id];
1809 int MaxProtoId, OldMaxProtoId;
1817 if (classify_learning_debug_level >= 3)
1822 Class = Templates->
Class[ClassId];
1825 ++NumAdaptationsFailed;
1826 if (classify_learning_debug_level >= 1)
1827 cprintf(
"Cannot make new temporary config: maximum number exceeded.\n");
1833 NumOldProtos = im_.FindGoodProtos(IClass, AllProtosOn, AllConfigsOff,
1834 BlobLength, NumFeatures, Features,
1835 OldProtos, classify_adapt_proto_threshold,
1840 for (i = 0; i < NumOldProtos; i++)
1841 SET_BIT(TempProtoMask, OldProtos[i]);
1843 NumBadFeatures = im_.FindBadFeatures(IClass, TempProtoMask, AllConfigsOn,
1844 BlobLength, NumFeatures, Features,
1846 classify_adapt_feature_threshold,
1849 MaxProtoId = MakeNewTempProtos(FloatFeatures, NumBadFeatures, BadFeatures,
1850 IClass, Class, TempProtoMask);
1852 ++NumAdaptationsFailed;
1853 if (classify_learning_debug_level >= 1)
1854 cprintf(
"Cannot make new temp protos: maximum number exceeded.\n");
1864 if (classify_learning_debug_level >= 1)
1865 cprintf(
"Making new temp config %d fontinfo id %d" 1866 " using %d old and %d new protos.\n",
1868 NumOldProtos, MaxProtoId - OldMaxProtoId);
1911 for (ProtoStart = BadFeat, LastBad = ProtoStart + NumBadFeat;
1912 ProtoStart < LastBad; ProtoStart = ProtoEnd) {
1913 F1 = Features->
Features[*ProtoStart];
1918 for (ProtoEnd = ProtoStart + 1,
1922 F2 = Features->
Features[*ProtoEnd];
1927 AngleDelta = fabs(A1 - A2);
1928 if (AngleDelta > 0.5)
1929 AngleDelta = 1.0 - AngleDelta;
1931 if (AngleDelta > matcher_clustering_max_angle_delta ||
1932 fabs(X1 - X2) > SegmentLength ||
1933 fabs(Y1 - Y2) > SegmentLength)
1937 F2 = Features->
Features[*(ProtoEnd - 1)];
1947 Proto = &(TempProto->
Proto);
1952 Proto->
Length = SegmentLength;
1954 Proto->
X = (X1 + X2) / 2.0;
1961 ConvertProto(Proto, Pid, IClass);
1963 classify_learning_debug_level >= 2);
1992 Class = Templates->
Class[ClassId];
2001 Ambigs = GetAmbiguities(Blob, ClassId);
2003 "PERM_CONFIG_STRUCT");
2018 if (classify_learning_debug_level >= 1) {
2019 tprintf(
"Making config %d for %s (ClassId %d) permanent:" 2020 " fontinfo id %d, ambiguities '",
2021 ConfigId, getDict().getUnicharset().debug_str(ClassId).
string(),
2024 *AmbigsPointer >= 0; ++AmbigsPointer)
2025 tprintf(
"%s", unicharset.id_to_unichar(*AmbigsPointer));
2084 for (
int i = 0; i < results.
match.
size(); ++i) {
2085 tprintf(
"%s ", unicharset.debug_str(results.
match[i].unichar_id).string());
2086 results.
match[i].Print();
2109 static const char* romans =
"i v x I V X";
2110 BadMatchThreshold = Results->
best_rating - matcher_bad_match_pad;
2112 if (classify_bln_numeric_mode) {
2113 UNICHAR_ID unichar_id_one = unicharset.contains_unichar(
"1") ?
2114 unicharset.unichar_to_id(
"1") : -1;
2115 UNICHAR_ID unichar_id_zero = unicharset.contains_unichar(
"0") ?
2116 unicharset.unichar_to_id(
"0") : -1;
2117 float scored_one = ScoredUnichar(unichar_id_one, *Results);
2118 float scored_zero = ScoredUnichar(unichar_id_zero, *Results);
2120 for (Next = NextGood = 0; Next < Results->
match.
size(); Next++) {
2122 if (match.
rating >= BadMatchThreshold) {
2123 if (!unicharset.get_isalpha(match.
unichar_id) ||
2126 }
else if (unicharset.eq(match.
unichar_id,
"l") &&
2127 scored_one < BadMatchThreshold) {
2128 Results->
match[Next].unichar_id = unichar_id_one;
2129 }
else if (unicharset.eq(match.
unichar_id,
"O") &&
2130 scored_zero < BadMatchThreshold) {
2131 Results->
match[Next].unichar_id = unichar_id_zero;
2133 Results->
match[Next].unichar_id = INVALID_UNICHAR_ID;
2135 if (Results->
match[Next].unichar_id != INVALID_UNICHAR_ID) {
2136 if (NextGood == Next) {
2139 Results->
match[NextGood++] = Results->
match[Next];
2145 for (Next = NextGood = 0; Next < Results->
match.
size(); Next++) {
2146 if (Results->
match[Next].rating >= BadMatchThreshold) {
2147 if (NextGood == Next) {
2150 Results->
match[NextGood++] = Results->
match[Next];
2173 static char punc_chars[] =
". , ; : / ` ~ ' - = \\ | \" ! _ ^";
2174 static char digit_chars[] =
"0 1 2 3 4 5 6 7 8 9";
2178 for (Next = NextGood = 0; Next < Results->
match.
size(); Next++) {
2181 if (strstr(punc_chars,
2183 if (punc_count >= 2)
2187 if (strstr(digit_chars,
2189 if (digit_count >= 1)
2195 if (NextGood == Next) {
2220 Threshold = (Threshold == matcher_good_threshold) ? 0.9: (1.0 - Threshold);
2221 classify_adapt_proto_threshold.set_value(
2222 ClipToRange<int>(255 * Threshold, 0, 255));
2223 classify_adapt_feature_threshold.set_value(
2224 ClipToRange<int>(255 * Threshold, 0, 255));
2240 void Classify::ShowBestMatchFor(
int shape_id,
2243 #ifndef GRAPHICS_DISABLED 2246 tprintf(
"No built-in templates for class/shape %d\n", shape_id);
2249 if (num_features <= 0) {
2250 tprintf(
"Illegal blob (char norm features)!\n");
2254 classify_norm_method.set_value(
character);
2256 AllProtosOn, AllConfigsOn,
2257 num_features, features, &cn_result,
2258 classify_adapt_feature_threshold,
NO_DEBUG,
2259 matcher_debug_separate_windows);
2261 config_mask = 1 << cn_result.
config;
2263 tprintf(
"Static Shape ID: %d\n", shape_id);
2266 AllProtosOn, reinterpret_cast<BIT_VECTOR>(&config_mask),
2267 num_features, features, &cn_result,
2268 classify_adapt_feature_threshold,
2269 matcher_debug_flags,
2270 matcher_debug_separate_windows);
2272 #endif // GRAPHICS_DISABLED 2278 int class_id,
int config_id)
const {
2280 if (templates == PreTrainedTemplates && shape_table_ !=
NULL) {
2281 int shape_id = ClassAndConfigIDToFontOrShapeID(class_id, config_id);
2282 class_string = shape_table_->DebugStr(shape_id);
2284 class_string = unicharset.debug_str(class_id);
2286 return class_string;
2290 int Classify::ClassAndConfigIDToFontOrShapeID(
int class_id,
2291 int int_result_config)
const {
2292 int font_set_id = PreTrainedTemplates->Class[class_id]->font_set_id;
2294 if (font_set_id < 0)
2295 return kBlankFontinfoId;
2296 const FontSet &fs = fontset_table_.get(font_set_id);
2298 return fs.
configs[int_result_config];
2303 int Classify::ShapeIDToClassID(
int shape_id)
const {
2304 for (
int id = 0;
id < PreTrainedTemplates->NumClasses; ++id) {
2305 int font_set_id = PreTrainedTemplates->Class[id]->font_set_id;
2307 const FontSet &fs = fontset_table_.get(font_set_id);
2308 for (
int config = 0; config < fs.
size; ++config) {
2309 if (fs.
configs[config] == shape_id)
2313 tprintf(
"Shape %d not found\n", shape_id);
2321 if (classify_learning_debug_level >= 1) {
2322 tprintf(
"NumTimesSeen for config of %s is %d\n",
2323 getDict().getUnicharset().debug_str(class_id).
string(),
2326 if (config->
NumTimesSeen >= matcher_sufficient_examples_for_prototyping) {
2328 }
else if (config->
NumTimesSeen < matcher_min_examples_for_prototyping) {
2330 }
else if (use_ambigs_for_adaption) {
2334 getDict().getUnicharAmbigs().AmbigsForAdaption(class_id);
2335 int ambigs_size = (ambigs ==
NULL) ? 0 : ambigs->
size();
2336 for (
int ambig = 0; ambig < ambigs_size; ++ambig) {
2337 ADAPT_CLASS ambig_class = AdaptedTemplates->Class[(*ambigs)[ambig]];
2338 assert(ambig_class !=
NULL);
2341 matcher_min_examples_for_prototyping) {
2342 if (classify_learning_debug_level >= 1) {
2343 tprintf(
"Ambig %s has not been seen enough times," 2344 " not making config for %s permanent\n",
2345 getDict().getUnicharset().debug_str(
2346 (*ambigs)[ambig]).
string(),
2347 getDict().getUnicharset().debug_str(class_id).
string());
2358 getDict().getUnicharAmbigs().ReverseAmbigsForAdaption(class_id);
2359 int ambigs_size = (ambigs ==
NULL) ? 0 : ambigs->
size();
2360 if (classify_learning_debug_level >= 1) {
2361 tprintf(
"Running UpdateAmbigsGroup for %s class_id=%d\n",
2362 getDict().getUnicharset().debug_str(class_id).
string(), class_id);
2364 for (
int ambig = 0; ambig < ambigs_size; ++ambig) {
2365 CLASS_ID ambig_class_id = (*ambigs)[ambig];
2366 const ADAPT_CLASS ambigs_class = AdaptedTemplates->Class[ambig_class_id];
2370 TempConfigFor(AdaptedTemplates->Class[ambig_class_id], cfg);
2371 if (config !=
NULL && TempConfigReliable(ambig_class_id, config)) {
2372 if (classify_learning_debug_level >= 1) {
2373 tprintf(
"Making config %d of %s permanent\n", cfg,
2374 getDict().getUnicharset().debug_str(
2375 ambig_class_id).
string());
2377 MakePermanent(AdaptedTemplates, ambig_class_id, cfg, Blob);
PERM_CONFIG_STRUCT * PERM_CONFIG
GenericVector< CP_RESULT_STRUCT > CPResults
static void BreakPieces(const GenericVector< SEAM * > &seams, const GenericVector< TBLOB * > &blobs, int first, int last)
GenericVector< STRING > correct_text
#define IsEmptyAdaptedClass(Class)
#define SET_BIT(array, bit)
bool MarginalMatch(float confidence, float matcher_great_threshold)
#define set_all_bits(array, length)
int IntCastRounded(double x)
int classify_integer_matcher_multiplier
int MakeTempProtoPerm(void *item1, void *item2)
#define PRINT_PROTO_MATCHES
int AddIntConfig(INT_CLASS Class)
TBOX bounding_box() const
TEMP_PROTO_STRUCT * TEMP_PROTO
BIT_VECTOR NewBitVector(int NumBits)
static void JoinPieces(const GenericVector< SEAM * > &seams, const GenericVector< TBLOB * > &blobs, int first, int last)
void free_int_templates(INT_TEMPLATES templates)
TEMP_PROTO NewTempProto()
void FreeTempProto(void *arg)
UNICHAR_ID best_unichar_id
#define ADAPTABLE_WERD_ADJUSTMENT
const int kBlnBaselineOffset
ADAPT_CLASS Class[MAX_NUM_CLASSES]
void FreeBitVector(BIT_VECTOR BitVector)
int geo_feature(int index) const
int AddIntProto(INT_CLASS Class)
void UpdateMatchDisplay()
const double kStandardFeatureLength
void print_ratings_list(const char *msg, BLOB_CHOICE_LIST *ratings, const UNICHARSET ¤t_unicharset)
LIST push(LIST list, void *element)
void FillABC(PROTO Proto)
void SetAdaptiveThreshold(FLOAT32 Threshold)
TBOX bounding_box() const
NORM_PROTOS * ReadNormProtos(FILE *File)
const INT_FEATURE_STRUCT * features() const
void AddProtoToProtoPruner(PROTO Proto, int ProtoId, INT_CLASS Class, bool debug)
void FreeTempConfig(TEMP_CONFIG Config)
int outline_length() const
void set_fonts(const GenericVector< tesseract::ScoredFont > &fonts)
const FontInfo * fontinfo
INT_CLASS Class[MAX_NUM_CLASSES]
bool disable_character_fragments
#define MAX_ADAPTABLE_WERD_SIZE
GenericVector< ScoredFont > fonts
void ComputeAdaptionThresholds(float certainty_scale, float min_rating, float max_rating, float rating_margin, float *thresholds)
bool PiecesAllNatural(int start, int count) const
LIST delete_d(LIST list, void *key, int_compare is_equal)
#define UNLIKELY_NUM_FEAT
void * alloc_struct(inT32 count, const char *)
TBLOB * ClassifyNormalizeIfNeeded() const
#define LENGTH_COMPRESSION
const FEATURE_DESC_STRUCT CharNormDesc
#define ClassForClassId(T, c)
#define PRINT_FEATURE_MATCHES
const char * string() const
INT_FEATURE_STRUCT INT_FEATURE_ARRAY[MAX_NUM_INT_FEATURES]
#define ConfigIsPermanent(Class, ConfigId)
GenericVector< TBLOB * > blobs
const STRING debug_string() const
FEATURE_STRUCT * GetCNFeature() const
#define MakeConfigPermanent(Class, ConfigId)
#define MAX_NUM_INT_FEATURES
GenericVector< SEAM * > seam_array
#define IncreaseConfidence(TempConfig)
#define ADAPT_TEMPLATE_SUFFIX
#define WordsInVectorOfSize(NumBits)
void InitMatcherRatings(register FLOAT32 *Rating)
TEMP_CONFIG NewTempConfig(int MaxProtoId, int FontinfoId)
const CHAR_FRAGMENT * get_fragment(UNICHAR_ID unichar_id) const
#define GetPicoFeatureLength()
TrainingSample * BlobToTrainingSample(const TBLOB &blob, bool nonlinear_norm, INT_FX_RESULT_STRUCT *fx_info, GenericVector< INT_FEATURE_STRUCT > *bl_features)
void cprintf(const char *format,...)
#define PermConfigFor(Class, ConfigId)
void free_adapted_templates(ADAPT_TEMPLATES templates)
bool AlternativeChoiceAdjustmentsWorseThan(float threshold) const
#define MakeProtoPermanent(Class, ProtoId)
#define copy_all_bits(source, dest, length)
void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class)
#define test_bit(array, bit)
const DENORM & denorm() const
FLOAT32 ActualOutlineLength(FEATURE Feature)
#define WORST_POSSIBLE_RATING
float adjust_factor() const
#define zero_all_bits(array, length)
void FreeFeature(FEATURE Feature)
FEATURE NewFeature(const FEATURE_DESC_STRUCT *FeatureDesc)
char window_wait(ScrollView *win)
ADAPT_TEMPLATES Templates
void AddProtoToClassPruner(PROTO Proto, CLASS_ID ClassId, INT_TEMPLATES Templates)
#define UnusedClassIdIn(T, c)
#define PRINT_MATCH_SUMMARY
void XHeightRange(int unichar_id, const UNICHARSET &unicharset, const TBOX &bbox, float *min_xht, float *max_xht, float *yshift) const
GenericVector< int > best_state
WERD_CHOICE * best_choice
void plot(ScrollView *window)
void FreeFeatureSet(FEATURE_SET FeatureSet)
GenericVector< UnicharRating > match
#define TempConfigFor(Class, ConfigId)
void plot(ScrollView *window, ScrollView::Color color, ScrollView::Color child_color)
#define reset_bit(array, bit)