tesseract v5.3.3.20231005
tesseract::FPRow Class Reference

Public Member Functions

 FPRow ()
 
 ~FPRow ()=default
 
void Init (TO_ROW *row)
 
void EstimatePitch (bool pass1)
 
void Pass1Analyze ()
 
bool Pass2Analyze ()
 
void MergeFragments ()
 
void FinalizeLargeChars ()
 
void OutputEstimations ()
 
void DebugOutputResult (int row_index)
 
int good_pitches ()
 
float pitch ()
 
float estimated_pitch ()
 
void set_estimated_pitch (float v)
 
float height ()
 
float height_pitch_ratio ()
 
float gap ()
 
size_t num_chars ()
 
FPCharcharacter (int i)
 
const TBOXbox (int i)
 
const TBOXreal_body (int i)
 
bool is_box_modified (int i)
 
float center_x (int i)
 
bool is_final (int i)
 
void finalize (int i)
 
bool is_good (int i)
 
void mark_good (int i)
 
void mark_bad (int i)
 
void clear_alignment (int i)
 

Detailed Description

Definition at line 288 of file cjkpitch.cpp.

Constructor & Destructor Documentation

◆ FPRow()

tesseract::FPRow::FPRow ( )
inline

Definition at line 290 of file cjkpitch.cpp.

290: all_pitches_(), all_gaps_(), good_pitches_(), good_gaps_(), heights_(), characters_() {}

◆ ~FPRow()

tesseract::FPRow::~FPRow ( )
default

Member Function Documentation

◆ box()

const TBOX & tesseract::FPRow::box ( int  i)
inline

Definition at line 366 of file cjkpitch.cpp.

366 {
367 return characters_[i].box();
368 }

◆ center_x()

float tesseract::FPRow::center_x ( int  i)
inline

Definition at line 378 of file cjkpitch.cpp.

378 {
379 return (characters_[i].box().left() + characters_[i].box().right()) / 2.0;
380 }
const TBOX & box(int i)
Definition: cjkpitch.cpp:366

◆ character()

FPChar * tesseract::FPRow::character ( int  i)
inline

Definition at line 362 of file cjkpitch.cpp.

362 {
363 return &characters_[i];
364 }

◆ clear_alignment()

void tesseract::FPRow::clear_alignment ( int  i)
inline

Definition at line 402 of file cjkpitch.cpp.

402 {
403 characters_[i].set_alignment(FPChar::ALIGN_UNKNOWN);
404 }

◆ DebugOutputResult()

void tesseract::FPRow::DebugOutputResult ( int  row_index)

Definition at line 686 of file cjkpitch.cpp.

686 {
687 if (num_chars() > 0) {
688 tprintf(
689 "Row %d: pitch_decision=%d, fixed_pitch=%f, max_nonspace=%d, "
690 "space_size=%f, space_threshold=%d, xheight=%f\n",
691 row_index, static_cast<int>(real_row_->pitch_decision), real_row_->fixed_pitch,
692 real_row_->max_nonspace, real_row_->space_size, real_row_->space_threshold,
693 real_row_->xheight);
694
695 for (unsigned i = 0; i < num_chars(); i++) {
696 tprintf("Char %u: is_final=%d is_good=%d num_blobs=%d: ", i, is_final(i), is_good(i),
697 character(i)->num_blobs());
698 box(i).print();
699 }
700 }
701}
void tprintf(const char *format,...)
Definition: tprintf.cpp:41
int32_t max_nonspace
Definition: blobbox.h:670
float space_size
Definition: blobbox.h:673
float fixed_pitch
Definition: blobbox.h:657
int32_t space_threshold
Definition: blobbox.h:671
PITCH_TYPE pitch_decision
Definition: blobbox.h:656
void print() const
Definition: rect.h:289
bool is_good(int i)
Definition: cjkpitch.cpp:390
bool is_final(int i)
Definition: cjkpitch.cpp:382
FPChar * character(int i)
Definition: cjkpitch.cpp:362
size_t num_chars()
Definition: cjkpitch.cpp:359

◆ estimated_pitch()

float tesseract::FPRow::estimated_pitch ( )
inline

Definition at line 336 of file cjkpitch.cpp.

336 {
337 return estimated_pitch_;
338 }

◆ EstimatePitch()

void tesseract::FPRow::EstimatePitch ( bool  pass1)

Definition at line 615 of file cjkpitch.cpp.

615 {
616 good_pitches_.Clear();
617 all_pitches_.Clear();
618 good_gaps_.Clear();
619 all_gaps_.Clear();
620 heights_.Clear();
621 if (num_chars() == 0) {
622 return;
623 }
624
625 int32_t cx0, cx1;
626 bool prev_was_good = is_good(0);
627 cx0 = center_x(0);
628
629 heights_.Add(box(0).height());
630 for (size_t i = 1; i < num_chars(); i++) {
631 cx1 = center_x(i);
632 int32_t pitch = cx1 - cx0;
633 int32_t gap = std::max(0, real_body(i - 1).x_gap(real_body(i)));
634
635 heights_.Add(box(i).height());
636 // Ignore if the pitch is too close. But don't ignore wide pitch
637 // may be the result of large tracking.
638 if (pitch > height_ * 0.5) {
639 all_pitches_.Add(pitch);
640 all_gaps_.Add(gap);
641 if (is_good(i)) {
642 // In pass1 (after Pass1Analyze()), all characters marked as
643 // "good" have a good consistent pitch with their previous
644 // characters. However, it's not true in pass2 and a good
645 // character may have a good pitch only between its successor.
646 // So we collect only pitch values between two good
647 // characters. and within tolerance in pass2.
648 if (pass1 ||
649 (prev_was_good && std::fabs(estimated_pitch_ - pitch) < kFPTolerance * estimated_pitch_)) {
650 good_pitches_.Add(pitch);
651 if (!is_box_modified(i - 1) && !is_box_modified(i)) {
652 good_gaps_.Add(gap);
653 }
654 }
655 prev_was_good = true;
656 } else {
657 prev_was_good = false;
658 }
659 }
660 cx0 = cx1;
661 }
662
663 good_pitches_.Finish();
664 all_pitches_.Finish();
665 good_gaps_.Finish();
666 all_gaps_.Finish();
667 heights_.Finish();
668
669 height_ = heights_.ile(0.875);
670 if (all_pitches_.empty()) {
671 pitch_ = 0.0f;
672 gap_ = 0.0f;
673 } else if (good_pitches_.size() < 2) {
674 // We don't have enough data to estimate the pitch of this row yet.
675 // Use median of all pitches as the initial guess.
676 pitch_ = all_pitches_.median();
677 ASSERT_HOST(pitch_ > 0.0f);
678 gap_ = all_gaps_.ile(0.125);
679 } else {
680 pitch_ = good_pitches_.median();
681 ASSERT_HOST(pitch_ > 0.0f);
682 gap_ = good_gaps_.ile(0.125);
683 }
684}
#define ASSERT_HOST(x)
Definition: errcode.h:54
float ile(double frac)
Definition: cjkpitch.cpp:62
bool empty() const
Definition: cjkpitch.cpp:95
void Add(float value)
Definition: cjkpitch.cpp:52
bool is_box_modified(int i)
Definition: cjkpitch.cpp:374
const TBOX & real_body(int i)
Definition: cjkpitch.cpp:370
float center_x(int i)
Definition: cjkpitch.cpp:378

◆ finalize()

void tesseract::FPRow::finalize ( int  i)
inline

Definition at line 386 of file cjkpitch.cpp.

386 {
387 characters_[i].set_final(true);
388 }

◆ FinalizeLargeChars()

void tesseract::FPRow::FinalizeLargeChars ( )

Definition at line 870 of file cjkpitch.cpp.

870 {
871 float row_pitch = estimated_pitch();
872 for (size_t i = 0; i < num_chars(); i++) {
873 if (is_final(i)) {
874 continue;
875 }
876
877 // Finalize if both neighbors are finalized. We have no other choice.
878 if (i > 0 && is_final(i - 1) && i < num_chars() - 1 && is_final(i + 1)) {
879 finalize(i);
880 continue;
881 }
882
883 float cx = center_x(i);
884 TBOX ibody(cx - 0.5 * row_pitch, 0, cx + 0.5 * row_pitch, 1);
885 if (i > 0) {
886 // The preceding character significantly intersects with the
887 // imaginary body of this character. Let Pass2Analyze() handle
888 // this case.
889 if (x_overlap_fraction(ibody, box(i - 1)) > 0.1) {
890 continue;
891 }
892 if (!is_final(i - 1)) {
893 TBOX merged = box(i);
894 merged += box(i - 1);
895 if (merged.width() < row_pitch) {
896 continue;
897 }
898 // This character cannot be finalized yet because it can be
899 // merged with the previous one. Again, let Pass2Analyze()
900 // handle this case.
901 }
902 }
903 if (i < num_chars() - 1) {
904 if (x_overlap_fraction(ibody, box(i + 1)) > 0.1) {
905 continue;
906 }
907 if (!is_final(i + 1)) {
908 TBOX merged = box(i);
909 merged += box(i + 1);
910 if (merged.width() < row_pitch) {
911 continue;
912 }
913 }
914 }
915 finalize(i);
916 }
917
918 // Update alignment decision. We only consider finalized characters
919 // in pass2. E.g. if a finalized character C has another finalized
920 // character L on its left and a not-finalized character R on its
921 // right, we mark C as good if the pitch between C and L is good,
922 // regardless of the pitch between C and R.
923 for (size_t i = 0; i < num_chars(); i++) {
924 if (!is_final(i)) {
925 continue;
926 }
927 bool good_pitch = false;
928 bool bad_pitch = false;
929 if (i > 0 && is_final(i - 1)) {
930 if (is_good_pitch(row_pitch, box(i - 1), box(i))) {
931 good_pitch = true;
932 } else {
933 bad_pitch = true;
934 }
935 }
936 if (i < num_chars() - 1 && is_final(i + 1)) {
937 if (is_good_pitch(row_pitch, box(i), box(i + 1))) {
938 good_pitch = true;
939 } else {
940 bad_pitch = true;
941 }
942 }
943 if (good_pitch && !bad_pitch) {
944 mark_good(i);
945 } else if (!good_pitch && bad_pitch) {
946 mark_bad(i);
947 }
948 }
949}
@ TBOX
void mark_bad(int i)
Definition: cjkpitch.cpp:398
void mark_good(int i)
Definition: cjkpitch.cpp:394
void finalize(int i)
Definition: cjkpitch.cpp:386
float estimated_pitch()
Definition: cjkpitch.cpp:336

◆ gap()

float tesseract::FPRow::gap ( )
inline

Definition at line 355 of file cjkpitch.cpp.

355 {
356 return gap_;
357 }

◆ good_pitches()

int tesseract::FPRow::good_pitches ( )
inline

Definition at line 328 of file cjkpitch.cpp.

328 {
329 return good_pitches_.size();
330 }

◆ height()

float tesseract::FPRow::height ( )
inline

Definition at line 344 of file cjkpitch.cpp.

344 {
345 return height_;
346 }

◆ height_pitch_ratio()

float tesseract::FPRow::height_pitch_ratio ( )
inline

Definition at line 348 of file cjkpitch.cpp.

348 {
349 if (good_pitches_.size() < 2) {
350 return -1.0;
351 }
352 return height_ / good_pitches_.median();
353 }

◆ Init()

void tesseract::FPRow::Init ( TO_ROW row)

Definition at line 496 of file cjkpitch.cpp.

496 {
497 ASSERT_HOST(row != nullptr);
498 ASSERT_HOST(row->xheight > 0);
499 real_row_ = row;
500 real_row_->pitch_decision = PITCH_CORR_PROP; // Default decision.
501
502 BLOBNBOX_IT blob_it = row->blob_list();
503 // Initialize characters_ and compute the initial estimation of
504 // character height.
505 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
506 if (is_interesting_blob(blob_it.data())) {
507 FPChar fp_char;
508 fp_char.Init(blob_it.data());
509 // Merge unconditionally if two blobs overlap.
510 if (!characters_.empty() && significant_overlap(fp_char.box(), characters_.back().box())) {
511 characters_.back().Merge(fp_char);
512 } else {
513 characters_.push_back(fp_char);
514 }
515 TBOX bound = blob_it.data()->bounding_box();
516 if (bound.height() * 3.0 > bound.width()) {
517 heights_.Add(bound.height());
518 }
519 }
520 }
521 heights_.Finish();
522 height_ = heights_.ile(0.875);
523}
@ PITCH_CORR_PROP
Definition: blobbox.h:54

◆ is_box_modified()

bool tesseract::FPRow::is_box_modified ( int  i)
inline

Definition at line 374 of file cjkpitch.cpp.

374 {
375 return !(characters_[i].box() == characters_[i].real_body());
376 }

◆ is_final()

bool tesseract::FPRow::is_final ( int  i)
inline

Definition at line 382 of file cjkpitch.cpp.

382 {
383 return characters_[i].is_final();
384 }

◆ is_good()

bool tesseract::FPRow::is_good ( int  i)
inline

Definition at line 390 of file cjkpitch.cpp.

390 {
391 return characters_[i].alignment() == FPChar::ALIGN_GOOD;
392 }

◆ mark_bad()

void tesseract::FPRow::mark_bad ( int  i)
inline

Definition at line 398 of file cjkpitch.cpp.

398 {
399 characters_[i].set_alignment(FPChar::ALIGN_BAD);
400 }

◆ mark_good()

void tesseract::FPRow::mark_good ( int  i)
inline

Definition at line 394 of file cjkpitch.cpp.

394 {
395 characters_[i].set_alignment(FPChar::ALIGN_GOOD);
396 }

◆ MergeFragments()

void tesseract::FPRow::MergeFragments ( )

Definition at line 854 of file cjkpitch.cpp.

854 {
855 int last_char = 0;
856
857 for (size_t j = 0; j < num_chars(); ++j) {
858 if (character(j)->merge_to_prev()) {
859 character(last_char)->Merge(*character(j));
860 character(j)->set_delete_flag(true);
861 clear_alignment(last_char);
862 character(j - 1)->set_merge_to_prev(false);
863 } else {
864 last_char = j;
865 }
866 }
867 DeleteChars();
868}
bool merge_to_prev() const
Definition: cjkpitch.cpp:243
void Merge(const FPChar &next)
Definition: cjkpitch.cpp:206
void set_delete_flag(bool flag)
Definition: cjkpitch.cpp:253
void set_merge_to_prev(bool flag)
Definition: cjkpitch.cpp:246
void clear_alignment(int i)
Definition: cjkpitch.cpp:402

◆ num_chars()

size_t tesseract::FPRow::num_chars ( )
inline

Definition at line 359 of file cjkpitch.cpp.

359 {
360 return characters_.size();
361 }

◆ OutputEstimations()

void tesseract::FPRow::OutputEstimations ( )

Definition at line 525 of file cjkpitch.cpp.

525 {
526 if (good_pitches_.empty()) {
527 pitch_ = 0.0f;
528 real_row_->pitch_decision = PITCH_CORR_PROP;
529 return;
530 }
531
532 pitch_ = good_pitches_.median();
533 real_row_->fixed_pitch = pitch_;
534 // good_gaps_.ile(0.125) can be large if most characters on the row
535 // are skinny. Use pitch_ - height_ instead if it's smaller, but
536 // positive.
537 real_row_->kern_size = real_row_->pr_nonsp =
538 std::min(good_gaps_.ile(0.125), std::max(pitch_ - height_, 0.0f));
539 real_row_->body_size = pitch_ - real_row_->kern_size;
540
541 if (good_pitches_.size() < all_pitches_.size() * kFixedPitchThreshold) {
542 // If more than half of the characters of a line don't fit to the
543 // fixed pitch model, consider the line to be proportional. 50%
544 // seems to be a good threshold in practice as well.
545 // Anyway we store estimated values (fixed_pitch, kern_size, etc.) in
546 // real_row_ as a partial estimation result and try to use them in the
547 // normalization process.
548 real_row_->pitch_decision = PITCH_CORR_PROP;
549 return;
550 } else if (good_pitches_.size() > all_pitches_.size() * 0.75) {
551 real_row_->pitch_decision = PITCH_DEF_FIXED;
552 } else {
553 real_row_->pitch_decision = PITCH_CORR_FIXED;
554 }
555
556 real_row_->space_size = real_row_->pr_space = pitch_;
557 // Set min_space to 50% of character pitch so that we can break CJK
558 // text at a half-width space after punctuation.
559 real_row_->min_space = (pitch_ + good_gaps_.minimum()) * 0.5;
560
561 // Don't consider a quarter space as a real space, because it's used
562 // for line justification in traditional Japanese books.
563 real_row_->max_nonspace =
564 std::max(pitch_ * 0.25 + good_gaps_.minimum(), static_cast<double>(good_gaps_.ile(0.875)));
565
566 int space_threshold = std::min((real_row_->max_nonspace + real_row_->min_space) / 2,
567 static_cast<int>(real_row_->xheight));
568
569 // Make max_nonspace larger than any intra-character gap so that
570 // make_prop_words() won't break a row at the middle of a character.
571 for (size_t i = 0; i < num_chars(); ++i) {
572 if (characters_[i].max_gap() > real_row_->max_nonspace) {
573 real_row_->max_nonspace = characters_[i].max_gap();
574 }
575 }
576 real_row_->space_threshold = std::min((real_row_->max_nonspace + real_row_->min_space) / 2,
577 static_cast<int>(real_row_->xheight));
578 real_row_->used_dm_model = false;
579
580 // Setup char_cells.
581 ICOORDELT_IT cell_it = &real_row_->char_cells;
582 auto *cell = new ICOORDELT(real_body(0).left(), 0);
583 cell_it.add_after_then_move(cell);
584
585 int right = real_body(0).right();
586 for (size_t i = 1; i < num_chars(); ++i) {
587 // Put a word break if gap between two characters is bigger than
588 // space_threshold. Don't break if none of two characters
589 // couldn't be "finalized", because maybe they need to be merged
590 // to one character.
591 if ((is_final(i - 1) || is_final(i)) &&
592 real_body(i - 1).x_gap(real_body(i)) > space_threshold) {
593 cell = new ICOORDELT(right + 1, 0);
594 cell_it.add_after_then_move(cell);
595 while (right + pitch_ < box(i).left()) {
596 right += pitch_;
597 cell = new ICOORDELT(right + 1, 0);
598 cell_it.add_after_then_move(cell);
599 }
600 right = box(i).left();
601 }
602 cell = new ICOORDELT((right + real_body(i).left()) / 2, 0);
603 cell_it.add_after_then_move(cell);
604 right = real_body(i).right();
605 }
606
607 cell = new ICOORDELT(right + 1, 0);
608 cell_it.add_after_then_move(cell);
609
610 // TODO(takenaka): add code to store alignment/fragmentation
611 // information to blobs so that it can be reused later, e.g. in
612 // recognition phase.
613}
@ PITCH_DEF_FIXED
Definition: blobbox.h:49
@ PITCH_CORR_FIXED
Definition: blobbox.h:53
int32_t min_space
Definition: blobbox.h:669
ICOORDELT_LIST char_cells
Definition: blobbox.h:675
bool used_dm_model
Definition: blobbox.h:653
TDimension left() const
Definition: rect.h:82
int x_gap(const TBOX &box) const
Definition: rect.h:238
TDimension right() const
Definition: rect.h:89

◆ Pass1Analyze()

void tesseract::FPRow::Pass1Analyze ( )

Definition at line 703 of file cjkpitch.cpp.

703 {
704 if (num_chars() < 2) {
705 return;
706 }
707
708 if (estimated_pitch_ > 0.0f) {
709 for (size_t i = 2; i < num_chars(); i++) {
710 if (is_good_pitch(estimated_pitch_, box(i - 2), box(i - 1)) &&
711 is_good_pitch(estimated_pitch_, box(i - 1), box(i))) {
712 mark_good(i - 1);
713 }
714 }
715 } else {
716 for (size_t i = 2; i < num_chars(); i++) {
717 if (is_good_pitch(box_pitch(box(i - 2), box(i - 1)), box(i - 1), box(i))) {
718 mark_good(i - 1);
719 }
720 }
721 }
722 character(0)->set_alignment(character(1)->alignment());
723 character(num_chars() - 1)->set_alignment(character(num_chars() - 2)->alignment());
724}
void set_alignment(Alignment alignment)
Definition: cjkpitch.cpp:239

◆ Pass2Analyze()

bool tesseract::FPRow::Pass2Analyze ( )

Definition at line 726 of file cjkpitch.cpp.

726 {
727 bool changed = false;
728 if (num_chars() <= 1 || estimated_pitch_ == 0.0f) {
729 return false;
730 }
731 for (size_t i = 0; i < num_chars(); i++) {
732 if (is_final(i)) {
733 continue;
734 }
735
736 FPChar::Alignment alignment = character(i)->alignment();
737 bool intersecting = false;
738 bool not_intersecting = false;
739
740 if (i < num_chars() - 1 && is_final(i + 1)) {
741 // Next character is already finalized. Estimate the imaginary
742 // body including this character based on the character. Skip
743 // whitespace if necessary.
744 bool skipped_whitespaces = false;
745 float c1 = center_x(i + 1) - 1.5 * estimated_pitch_;
746 while (c1 > box(i).right()) {
747 skipped_whitespaces = true;
748 c1 -= estimated_pitch_;
749 }
750 TBOX ibody(c1, box(i).bottom(), c1 + estimated_pitch_, box(i).top());
751
752 // Collect all characters that mostly fit in the region.
753 // Also, their union height shouldn't be too big.
754 int j = i;
755 TBOX merged;
756 while (j >= 0 && !is_final(j) && mostly_overlap(ibody, box(j)) &&
757 merged.bounding_union(box(j)).height() < estimated_pitch_ * (1 + kFPTolerance)) {
758 merged += box(j);
759 j--;
760 }
761
762 if (j >= 0 && significant_overlap(ibody, box(j))) {
763 // character(j) lies on the character boundary and doesn't fit
764 // well into the imaginary body.
765 if (!is_final(j)) {
766 intersecting = true;
767 }
768 } else {
769 not_intersecting = true;
770 if (i - j > 0) {
771 // Merge character(j+1) ... character(i) because they fit
772 // into the body nicely.
773 if (i - j == 1) {
774 // Only one char in the imaginary body.
775 if (!skipped_whitespaces) {
776 mark_good(i);
777 }
778 // set ibody as bounding box of this character to get
779 // better pitch analysis result for halfwidth glyphs
780 // followed by a halfwidth space.
781 if (box(i).width() <= estimated_pitch_ * 0.5) {
782 ibody += box(i);
783 character(i)->set_box(ibody);
784 }
786 finalize(i);
787 } else {
788 for (int k = i; k > j + 1; k--) {
790 }
791 }
792 }
793 }
794 }
795 if (i > 0 && is_final(i - 1)) {
796 // Now we repeat everything from the opposite side. Previous
797 // character is already finalized. Estimate the imaginary body
798 // including this character based on the character.
799 bool skipped_whitespaces = false;
800 float c1 = center_x(i - 1) + 1.5 * estimated_pitch_;
801 while (c1 < box(i).left()) {
802 skipped_whitespaces = true;
803 c1 += estimated_pitch_;
804 }
805 TBOX ibody(c1 - estimated_pitch_, box(i).bottom(), c1, box(i).top());
806
807 size_t j = i;
808 TBOX merged;
809 while (j < num_chars() && !is_final(j) && mostly_overlap(ibody, box(j)) &&
810 merged.bounding_union(box(j)).height() < estimated_pitch_ * (1 + kFPTolerance)) {
811 merged += box(j);
812 j++;
813 }
814
815 if (j < num_chars() && significant_overlap(ibody, box(j))) {
816 if (!is_final(j)) {
817 intersecting = true;
818 }
819 } else {
820 not_intersecting = true;
821 if (j - i > 0) {
822 if (j - i == 1) {
823 if (!skipped_whitespaces) {
824 mark_good(i);
825 }
826 if (box(i).width() <= estimated_pitch_ * 0.5) {
827 ibody += box(i);
828 character(i)->set_box(ibody);
829 }
831 finalize(i);
832 } else {
833 for (size_t k = i + 1; k < j; k++) {
835 }
836 }
837 }
838 }
839 }
840
841 // This character doesn't fit well into the estimated imaginary
842 // bodies. Mark it as bad.
843 if (intersecting && !not_intersecting) {
844 mark_bad(i);
845 }
846 if (character(i)->alignment() != alignment || character(i)->merge_to_prev()) {
847 changed = true;
848 }
849 }
850
851 return changed;
852}
const Alignment & alignment() const
Definition: cjkpitch.cpp:236
void set_box(const TBOX &box)
Definition: cjkpitch.cpp:222

◆ pitch()

float tesseract::FPRow::pitch ( )
inline

Definition at line 332 of file cjkpitch.cpp.

332 {
333 return pitch_;
334 }

◆ real_body()

const TBOX & tesseract::FPRow::real_body ( int  i)
inline

Definition at line 370 of file cjkpitch.cpp.

370 {
371 return characters_[i].real_body();
372 }

◆ set_estimated_pitch()

void tesseract::FPRow::set_estimated_pitch ( float  v)
inline

Definition at line 340 of file cjkpitch.cpp.

340 {
341 estimated_pitch_ = v;
342 }

The documentation for this class was generated from the following file: