42 5, 10, 16, 16, 16, 16, 16, 16, 16, 16,
45 static const char* kNodeContNames[] = {
"Anything",
"OnlyDup",
"NoDup"};
50 if (
code == null_char) {
59 if (depth > 0 &&
prev !=
nullptr) {
61 prev->
Print(null_char, unicharset, depth - 1);
69 int null_char,
bool simple_text,
Dict* dict)
75 space_delimited_(true),
76 is_simple_text_(simple_text),
77 null_char_(null_char) {
83 double cert_offset,
double worst_dict_cert,
84 const UNICHARSET* charset,
int lstm_choice_mode) {
86 int width = output.
Width();
89 for (
int t = 0; t < width; ++t) {
90 ComputeTopN(output.
f(t), output.
NumFeatures(), kBeamWidths[0]);
91 DecodeStep(output.
f(t), t, dict_ratio, cert_offset, worst_dict_cert,
93 if (lstm_choice_mode) {
94 SaveMostCertainChoices(output.
f(t), output.
NumFeatures(), charset, t);
99 double dict_ratio,
double cert_offset,
100 double worst_dict_cert,
103 int width = output.
dim1();
104 for (
int t = 0; t < width; ++t) {
105 ComputeTopN(output[t], output.
dim2(), kBeamWidths[0]);
106 DecodeStep(output[t], t, dict_ratio, cert_offset, worst_dict_cert, charset);
110 void RecodeBeamSearch::SaveMostCertainChoices(
const float* outputs,
114 std::vector<std::pair<const char*, float>> choices;
115 for (
int i = 0; i < num_outputs; ++i) {
116 if (outputs[i] >= 0.01f) {
118 if (i + 2 >= num_outputs) {
128 while (choices.size() > pos && choices[pos].second > outputs[i]) {
131 choices.insert(choices.begin() + pos,
132 std::pair<const char*, float>(
character, outputs[i]));
144 ExtractBestPaths(&best_nodes,
nullptr);
147 int width = best_nodes.
size();
149 int label = best_nodes[t]->code;
150 if (label != null_char_) {
154 while (++t < width && !is_simple_text_ && best_nodes[t]->code == label) {
167 ExtractBestPaths(&best_nodes,
nullptr);
168 ExtractPathAsUnicharIds(best_nodes, unichar_ids, certs, ratings, xcoords);
170 DebugPath(unicharset, best_nodes);
171 DebugUnicharPath(unicharset, best_nodes, *unichar_ids, *certs, *ratings,
178 float scale_factor,
bool debug,
181 int lstm_choice_mode) {
189 std::deque<std::tuple<int, int>> best_choices;
190 ExtractBestPaths(&best_nodes, &second_nodes);
192 DebugPath(unicharset, best_nodes);
193 ExtractPathAsUnicharIds(second_nodes, &unichar_ids, &certs, &ratings,
195 tprintf(
"\nSecond choice path:\n");
196 DebugUnicharPath(unicharset, second_nodes, unichar_ids, certs, ratings,
202 if (lstm_choice_mode == 2) {
203 ExtractPathAsUnicharIds(best_nodes, &unichar_ids, &certs, &ratings,
204 &xcoords, &best_choices);
205 if (best_choices.size() > 0) {
206 timestepEnd = std::get<1>(best_choices.front());
207 best_choices.pop_front();
210 ExtractPathAsUnicharIds(best_nodes, &unichar_ids, &certs, &ratings,
213 int num_ids = unichar_ids.
size();
215 DebugUnicharPath(unicharset, best_nodes, unichar_ids, certs, ratings,
220 float prev_space_cert = 0.0f;
221 for (
int word_start = 0; word_start < num_ids; word_start = word_end) {
222 for (word_end = word_start + 1; word_end < num_ids; ++word_end) {
227 int index = xcoords[word_end];
228 if (best_nodes[index]->start_of_word)
break;
234 float space_cert = 0.0f;
235 if (word_end < num_ids && unichar_ids[word_end] ==
UNICHAR_SPACE)
236 space_cert = certs[word_end];
238 word_start > 0 && unichar_ids[word_start - 1] ==
UNICHAR_SPACE;
240 WERD_RES* word_res = InitializeWord(
241 leading_space, line_box, word_start, word_end,
242 std::min(space_cert, prev_space_cert), unicharset, xcoords, scale_factor);
243 if (lstm_choice_mode == 1) {
244 for (
size_t i = timestepEnd; i < xcoords[word_end]; i++) {
247 timestepEnd = xcoords[word_end];
248 }
else if (lstm_choice_mode == 2){
251 std::vector<std::pair<const char*, float>> choice_pairs;
252 for (
size_t i = timestepEnd; i < xcoords[word_end]; i++) {
253 for (std::pair<const char*, float> choice :
timesteps[i]) {
254 if (std::strcmp(choice.first,
"")) {
255 sum += choice.second;
256 choice_pairs.push_back(choice);
259 if ((best_choices.size() > 0 && i == std::get<1>(best_choices.front()) - 1)
260 || i == xcoords[word_end]-1) {
261 std::map<const char*, float> summed_propabilities;
262 for (
auto & choice_pair : choice_pairs) {
263 summed_propabilities[choice_pair.first] += choice_pair.second;
265 std::vector<std::pair<const char*, float>> accumulated_timestep;
266 for (
auto& summed_propability : summed_propabilities) {
268 summed_propability.second/=sum;
270 while (accumulated_timestep.size() > pos
271 && accumulated_timestep[pos].second > summed_propability.second) {
274 accumulated_timestep.insert(accumulated_timestep.begin() + pos,
275 std::pair<const char*,float>(summed_propability.first,
276 summed_propability.second));
278 if (best_choices.size() > 0) {
279 best_choices.pop_front();
281 choice_pairs.clear();
282 word_res->
timesteps.push_back(accumulated_timestep);
286 timestepEnd = xcoords[word_end];
288 for (
int i = word_start; i < word_end; ++i) {
289 auto* choices =
new BLOB_CHOICE_LIST;
290 BLOB_CHOICE_IT bc_it(choices);
292 unichar_ids[i], ratings[i], certs[i], -1, 1.0f,
294 int col = i - word_start;
295 choice->set_matrix_cell(col, col);
296 bc_it.add_after_then_move(choice);
299 int index = xcoords[word_end - 1];
302 prev_space_cert = space_cert;
303 if (word_end < num_ids && unichar_ids[word_end] ==
UNICHAR_SPACE)
310 for (
int p = 0; p < beam_size_; ++p) {
311 for (
int d = 0; d < 2; ++d) {
312 for (
int c = 0; c <
NC_COUNT; ++c) {
315 if (beam_[p]->beams_[index].empty())
continue;
317 tprintf(
"Position %d: %s+%s beam\n", p, d ?
"Dict" :
"Non-Dict",
319 DebugBeamPos(unicharset, beam_[p]->beams_[index]);
326 void RecodeBeamSearch::DebugBeamPos(
const UNICHARSET& unicharset,
331 int heap_size = heap.
size();
332 for (
int i = 0; i < heap_size; ++i) {
335 if (null_best ==
nullptr || null_best->
score < node->
score) null_best = node;
337 if (unichar_bests[node->
unichar_id] ==
nullptr ||
343 for (
int u = 0; u < unichar_bests.
size(); ++u) {
344 if (unichar_bests[u] !=
nullptr) {
346 node.
Print(null_char_, unicharset, 1);
349 if (null_best !=
nullptr) {
350 null_best->
Print(null_char_, unicharset, 1);
357 void RecodeBeamSearch::ExtractPathAsUnicharIds(
361 std::deque<std::tuple<int, int>>* best_choices) {
368 int width = best_nodes.
size();
372 double certainty = 0.0;
374 while (t < width && best_nodes[t]->unichar_id == INVALID_UNICHAR_ID) {
375 double cert = best_nodes[t++]->certainty;
376 if (cert < certainty) certainty = cert;
380 int unichar_id = best_nodes[t]->unichar_id;
382 best_nodes[t]->permuter !=
NO_PERM) {
385 if (certainty < certs->back()) certs->
back() = certainty;
386 ratings->
back() += rating;
392 if (best_choices !=
nullptr) {
397 double cert = best_nodes[t++]->certainty;
401 best_nodes[t - 1]->permuter ==
NO_PERM)) {
405 }
while (t < width && best_nodes[t]->duplicate);
408 }
else if (!certs->
empty()) {
409 if (certainty < certs->back()) certs->
back() = certainty;
410 ratings->
back() += rating;
412 if (best_choices !=
nullptr) {
413 best_choices->push_back(
414 std::tuple<int, int>(
id, tposition));
422 WERD_RES* RecodeBeamSearch::InitializeWord(
bool leading_space,
423 const TBOX& line_box,
int word_start,
424 int word_end,
float space_certainty,
427 float scale_factor) {
430 C_BLOB_IT b_it(&blobs);
431 for (
int i = word_start; i < word_end; ++i) {
432 int min_half_width = xcoords[i + 1] - xcoords[i];
433 if (i > 0 && xcoords[i] - xcoords[i - 1] < min_half_width)
434 min_half_width = xcoords[i] - xcoords[i - 1];
435 if (min_half_width < 1) min_half_width = 1;
437 TBOX box(xcoords[i] - min_half_width, 0, xcoords[i] + min_half_width,
439 box.
scale(scale_factor);
441 box.set_top(line_box.
top());
445 WERD* word =
new WERD(&blobs, leading_space,
nullptr);
447 auto* word_res =
new WERD_RES(word);
448 word_res->uch_set = unicharset;
449 word_res->combination =
true;
450 word_res->space_certainty = space_certainty;
451 word_res->ratings =
new MATRIX(word_end - word_start, 1);
457 void RecodeBeamSearch::ComputeTopN(
const float* outputs,
int num_outputs,
463 for (
int i = 0; i < num_outputs; ++i) {
464 if (top_heap_.size() < top_n || outputs[i] > top_heap_.PeekTop().key) {
466 top_heap_.Push(&entry);
467 if (top_heap_.size() > top_n) top_heap_.Pop(&entry);
470 while (!top_heap_.empty()) {
472 top_heap_.Pop(&entry);
473 if (top_heap_.size() > 1) {
477 if (top_heap_.empty())
478 top_code_ = entry.
data;
480 second_code_ = entry.
data;
483 top_n_flags_[null_char_] =
TN_TOP2;
489 void RecodeBeamSearch::DecodeStep(
const float* outputs,
int t,
490 double dict_ratio,
double cert_offset,
491 double worst_dict_cert,
494 RecodeBeam* step = beam_[t];
500 charset, dict_ratio, cert_offset, worst_dict_cert, step);
501 if (dict_ !=
nullptr) {
503 charset, dict_ratio, cert_offset, worst_dict_cert, step);
506 RecodeBeam* prev = beam_[t - 1];
509 for (
int i = prev->beams_[beam_index].size() - 1; i >= 0; --i) {
511 ExtractPath(&prev->beams_[beam_index].get(i).data, &path);
512 tprintf(
"Step %d: Dawg beam %d:\n", t, i);
513 DebugPath(charset, path);
516 for (
int i = prev->beams_[beam_index].size() - 1; i >= 0; --i) {
518 ExtractPath(&prev->beams_[beam_index].get(i).data, &path);
519 tprintf(
"Step %d: Non-Dawg beam %d:\n", t, i);
520 DebugPath(charset, path);
528 for (
int tn = 0; tn <
TN_COUNT && total_beam == 0; ++tn) {
530 for (
int index = 0; index <
kNumBeams; ++index) {
534 for (
int i = prev->beams_[index].size() - 1; i >= 0; --i) {
535 ContinueContext(&prev->beams_[index].get(i).data, index, outputs, top_n,
536 charset, dict_ratio, cert_offset, worst_dict_cert, step);
539 for (
int index = 0; index <
kNumBeams; ++index) {
541 total_beam += step->beams_[index].size();
546 for (
int c = 0; c <
NC_COUNT; ++c) {
547 if (step->best_initial_dawgs_[c].code >= 0) {
548 int index =
BeamIndex(
true, static_cast<NodeContinuation>(c), 0);
550 PushHeapIfBetter(kBeamWidths[0], &step->best_initial_dawgs_[c],
561 void RecodeBeamSearch::ContinueContext(
const RecodeNode* prev,
int index,
562 const float* outputs,
567 double worst_dict_cert,
575 for (
int p = length - 1; p >= 0; --p, previous = previous->
prev) {
576 while (previous !=
nullptr &&
577 (previous->duplicate || previous->code == null_char_)) {
578 previous = previous->prev;
580 if (previous !=
nullptr) {
581 prefix.
Set(p, previous->code);
582 full_code.
Set(p, previous->code);
585 if (prev !=
nullptr && !is_simple_text_) {
586 if (top_n_flags_[prev->
code] == top_n_flag) {
590 PushDupOrNoDawgIfBetter(length,
true, prev->
code, prev->
unichar_id,
591 cert, worst_dict_cert, dict_ratio, use_dawgs,
595 prev->
code != null_char_) {
597 outputs[null_char_]) +
599 PushDupOrNoDawgIfBetter(length,
true, prev->
code, prev->
unichar_id,
600 cert, worst_dict_cert, dict_ratio, use_dawgs,
605 if (prev->
code != null_char_ && length > 0 &&
606 top_n_flags_[null_char_] == top_n_flag) {
611 PushDupOrNoDawgIfBetter(length,
false, null_char_, INVALID_UNICHAR_ID,
612 cert, worst_dict_cert, dict_ratio, use_dawgs,
617 if (final_codes !=
nullptr) {
618 for (
int i = 0; i < final_codes->
size(); ++i) {
619 int code = (*final_codes)[i];
620 if (top_n_flags_[code] != top_n_flag)
continue;
621 if (prev !=
nullptr && prev->
code == code && !is_simple_text_)
continue;
624 full_code.
Set(length, code);
627 if (length == 0 && code == null_char_) unichar_id = INVALID_UNICHAR_ID;
628 if (unichar_id != INVALID_UNICHAR_ID &&
629 charset !=
nullptr &&
632 ContinueUnichar(code, unichar_id, cert, worst_dict_cert, dict_ratio,
634 if (top_n_flag ==
TN_TOP2 && code != null_char_) {
635 float prob = outputs[code] + outputs[null_char_];
637 prev->
code != null_char_ &&
638 ((prev->
code == top_code_ && code == second_code_) ||
639 (code == top_code_ && prev->
code == second_code_))) {
640 prob += outputs[prev->
code];
643 ContinueUnichar(code, unichar_id, cert, worst_dict_cert, dict_ratio,
649 if (next_codes !=
nullptr) {
650 for (
int i = 0; i < next_codes->
size(); ++i) {
651 int code = (*next_codes)[i];
652 if (top_n_flags_[code] != top_n_flag)
continue;
653 if (prev !=
nullptr && prev->
code == code && !is_simple_text_)
continue;
655 PushDupOrNoDawgIfBetter(length + 1,
false, code, INVALID_UNICHAR_ID, cert,
656 worst_dict_cert, dict_ratio, use_dawgs,
658 if (top_n_flag ==
TN_TOP2 && code != null_char_) {
659 float prob = outputs[code] + outputs[null_char_];
661 prev->
code != null_char_ &&
662 ((prev->
code == top_code_ && code == second_code_) ||
663 (code == top_code_ && prev->
code == second_code_))) {
664 prob += outputs[prev->
code];
667 PushDupOrNoDawgIfBetter(length + 1,
false, code, INVALID_UNICHAR_ID,
668 cert, worst_dict_cert, dict_ratio, use_dawgs,
676 void RecodeBeamSearch::ContinueUnichar(
int code,
int unichar_id,
float cert,
677 float worst_dict_cert,
float dict_ratio,
682 if (cert > worst_dict_cert) {
683 ContinueDawg(code, unichar_id, cert, cont, prev, step);
687 PushHeapIfBetter(kBeamWidths[0], code, unichar_id,
TOP_CHOICE_PERM,
false,
688 false,
false,
false, cert * dict_ratio, prev,
nullptr,
690 if (dict_ !=
nullptr &&
696 float dawg_cert = cert;
710 dawg_cert *= dict_ratio;
711 PushInitialDawgIfBetter(code, unichar_id, permuter,
false,
false,
712 dawg_cert, cont, prev, step);
720 void RecodeBeamSearch::ContinueDawg(
int code,
int unichar_id,
float cert,
725 if (unichar_id == INVALID_UNICHAR_ID) {
726 PushHeapIfBetter(kBeamWidths[0], code, unichar_id,
NO_PERM,
false,
false,
727 false,
false, cert, prev,
nullptr, dawg_heap);
732 if (prev !=
nullptr) score += prev->
score;
733 if (dawg_heap->
size() >= kBeamWidths[0] &&
735 nodawg_heap->
size() >= kBeamWidths[0] &&
742 while (uni_prev !=
nullptr &&
744 uni_prev = uni_prev->
prev;
746 if (uni_prev !=
nullptr && uni_prev->
end_of_word) {
749 PushInitialDawgIfBetter(code, unichar_id, uni_prev->
permuter,
false,
750 false, cert, cont, prev, step);
751 PushHeapIfBetter(kBeamWidths[0], code, unichar_id, uni_prev->
permuter,
752 false,
false,
false,
false, cert, prev,
nullptr,
765 bool word_start =
false;
766 if (uni_prev ==
nullptr) {
770 }
else if (uni_prev->
dawgs !=
nullptr) {
781 PushHeapIfBetter(kBeamWidths[0], code, unichar_id, permuter,
false,
782 word_start, dawg_args.
valid_end,
false, cert, prev,
784 if (dawg_args.
valid_end && !space_delimited_) {
788 PushInitialDawgIfBetter(code, unichar_id, permuter, word_start,
true,
789 cert, cont, prev, step);
790 PushHeapIfBetter(kBeamWidths[0], code, unichar_id, permuter,
false,
791 word_start,
true,
false, cert, prev,
nullptr, nodawg_heap);
794 delete updated_dawgs;
801 void RecodeBeamSearch::PushInitialDawgIfBetter(
int code,
int unichar_id,
803 bool start,
bool end,
float cert,
807 RecodeNode* best_initial_dawg = &step->best_initial_dawgs_[cont];
809 if (prev !=
nullptr) score += prev->
score;
810 if (best_initial_dawg->
code < 0 || score > best_initial_dawg->
score) {
813 RecodeNode node(code, unichar_id, permuter,
true, start, end,
false, cert,
814 score, prev, initial_dawgs,
815 ComputeCodeHash(code,
false, prev));
816 *best_initial_dawg = node;
824 void RecodeBeamSearch::PushDupOrNoDawgIfBetter(
825 int length,
bool dup,
int code,
int unichar_id,
float cert,
826 float worst_dict_cert,
float dict_ratio,
bool use_dawgs,
828 int index =
BeamIndex(use_dawgs, cont, length);
830 if (cert > worst_dict_cert) {
831 PushHeapIfBetter(kBeamWidths[length], code, unichar_id,
833 dup, cert, prev,
nullptr, &step->beams_[index]);
838 PushHeapIfBetter(kBeamWidths[length], code, unichar_id,
840 false, dup, cert, prev,
nullptr, &step->beams_[index]);
848 void RecodeBeamSearch::PushHeapIfBetter(
int max_size,
int code,
int unichar_id,
850 bool word_start,
bool end,
bool dup,
855 if (prev !=
nullptr) score += prev->
score;
857 uint64_t hash = ComputeCodeHash(code, dup, prev);
858 RecodeNode node(code, unichar_id, permuter, dawg_start, word_start, end,
859 dup, cert, score, prev, d, hash);
860 if (UpdateHeapIfMatched(&node, heap))
return;
864 if (heap->
size() > max_size) heap->
Pop(&entry);
872 void RecodeBeamSearch::PushHeapIfBetter(
int max_size,
RecodeNode* node,
875 if (UpdateHeapIfMatched(node, heap)) {
881 if (heap->
size() > max_size) heap->
Pop(&entry);
887 bool RecodeBeamSearch::UpdateHeapIfMatched(
RecodeNode* new_node,
893 for (
int i = 0; i < nodes->
size(); ++i) {
902 (*nodes)[i].key = node.
score;
912 uint64_t RecodeBeamSearch::ComputeCodeHash(
int code,
bool dup,
914 uint64_t hash = prev ==
nullptr ? 0 : prev->
code_hash;
915 if (!dup && code != null_char_) {
917 uint64_t carry = (((hash >> 32) * num_classes) >> 32);
929 void RecodeBeamSearch::ExtractBestPaths(
935 const RecodeBeam* last_beam = beam_[beam_size_ - 1];
936 for (
int c = 0; c <
NC_COUNT; ++c) {
939 for (
int is_dawg = 0; is_dawg < 2; ++is_dawg) {
940 int beam_index =
BeamIndex(is_dawg, cont, 0);
941 int heap_size = last_beam->beams_[beam_index].size();
942 for (
int h = 0; h < heap_size; ++h) {
943 const RecodeNode* node = &last_beam->beams_[beam_index].get(h).data;
948 while (dawg_node !=
nullptr &&
949 (dawg_node->
unichar_id == INVALID_UNICHAR_ID ||
951 dawg_node = dawg_node->
prev;
952 if (dawg_node ==
nullptr || (!dawg_node->
end_of_word &&
958 if (best_node ==
nullptr || node->
score > best_node->
score) {
959 second_best_node = best_node;
961 }
else if (second_best_node ==
nullptr ||
963 second_best_node = node;
968 if (second_nodes !=
nullptr) ExtractPath(second_best_node, second_nodes);
969 ExtractPath(best_node, best_nodes);
974 void RecodeBeamSearch::ExtractPath(
977 while (node !=
nullptr) {
985 void RecodeBeamSearch::DebugPath(
988 for (
int c = 0; c < path.
size(); ++c) {
991 node.
Print(null_char_, *unicharset, 1);
996 void RecodeBeamSearch::DebugUnicharPath(
1001 int num_ids = unichar_ids.
size();
1002 double total_rating = 0.0;
1003 for (
int c = 0; c < num_ids; ++c) {
1004 int coord = xcoords[c];
1005 tprintf(
"%d %d=%s r=%g, c=%g, s=%d, e=%d, perm=%d\n", coord, unichar_ids[c],
1007 certs[c], path[coord]->start_of_word, path[coord]->end_of_word,
1008 path[coord]->permuter);
1009 total_rating += ratings[c];
1011 tprintf(
"Path total rating = %g\n", total_rating);
void Decode(const NetworkIO &output, double dict_ratio, double cert_offset, double worst_dict_cert, const UNICHARSET *charset, int lstm_choice_mode=0)
RecodeBeamSearch(const UnicharCompress &recoder, int null_char, bool simple_text, Dict *dict)
bool get_enabled(UNICHAR_ID unichar_id) const
std::vector< std::vector< std::pair< const char *, float > > > timesteps
static NodeContinuation ContinuationFromBeamsIndex(int index)
DawgPositionVector * updated_dawgs
const GenericVector< int > * GetNextCodes(const RecodedCharID &code) const
static const int kNumBeams
GenericVector< Pair > * heap()
void ExtractBestPathAsUnicharIds(bool debug, const UNICHARSET *unicharset, GenericVector< int > *unichar_ids, GenericVector< float > *certs, GenericVector< float > *ratings, GenericVector< int > *xcoords) const
int def_letter_is_okay(void *void_dawg_args, const UNICHARSET &unicharset, UNICHAR_ID unichar_id, bool word_end) const
DawgPositionVector * active_dawgs
void Set(int index, int value)
static int LengthFromBeamsIndex(int index)
void FakeWordFromRatings(PermuterType permuter)
static const int kMaxCodeLen
bool IsSpaceDelimited(UNICHAR_ID unichar_id) const
void init_to_size(int size, const T &t)
static const float kMinCertainty
int DecodeUnichar(const RecodedCharID &code) const
const char * id_to_unichar_ext(UNICHAR_ID id) const
std::vector< std::vector< std::pair< const char *, float > > > timesteps
static float ProbToCertainty(float prob)
DawgPositionVector * dawgs
const Pair & PeekTop() const
void ExtractBestPathAsLabels(GenericVector< int > *labels, GenericVector< int > *xcoords) const
void put(ICOORD pos, const T &thing)
const char * string() const
DLLSYM void tprintf(const char *format,...)
const GenericVector< int > * GetFinalCodes(const RecodedCharID &code) const
static int BeamIndex(bool is_dawg, NodeContinuation cont, int length)
STRING debug_str(UNICHAR_ID id) const
bool IsSpaceDelimitedLang() const
Returns true if the language is space-delimited (not CJ, or T).
void DebugBeams(const UNICHARSET &unicharset) const
const UNICHARSET & getUnicharset() const
void default_dawgs(DawgPositionVector *anylength_dawgs, bool suppress_patterns) const
void ExtractBestPathAsWords(const TBOX &line_box, float scale_factor, bool debug, const UNICHARSET *unicharset, PointerVector< WERD_RES > *words, int lstm_choice_mode=0)
void Reshuffle(Pair *pair)
void Print(int null_char, const UNICHARSET &unicharset, int depth) const
static bool IsDawgFromBeamsIndex(int index)
const Pair & get(int index) const
void scale(const float f)
static C_BLOB * FakeBlob(const TBOX &box)