tesseract 4.1.1
Loading...
Searching...
No Matches
tesseract::TableRecognizer Class Reference

#include <tablerecog.h>

Public Member Functions

 TableRecognizer ()
 
 ~TableRecognizer ()
 
void Init ()
 
void set_text_grid (ColPartitionGrid *text)
 
void set_line_grid (ColPartitionGrid *lines)
 
void set_min_height (int height)
 
void set_min_width (int width)
 
void set_max_text_height (int height)
 
StructuredTableRecognizeTable (const TBOX &guess_box)
 

Protected Member Functions

bool RecognizeLinedTable (const TBOX &guess_box, StructuredTable *table)
 
bool HasSignificantLines (const TBOX &guess)
 
bool FindLinesBoundingBox (TBOX *bounding_box)
 
bool FindLinesBoundingBoxIteration (TBOX *bounding_box)
 
bool RecognizeWhitespacedTable (const TBOX &guess_box, StructuredTable *table)
 
int NextHorizontalSplit (int left, int right, int y, bool top_to_bottom)
 

Static Protected Member Functions

static bool IsWeakTableRow (StructuredTable *table, int row)
 

Protected Attributes

ColPartitionGridtext_grid_
 
ColPartitionGridline_grid_
 
int min_height_
 
int min_width_
 
int max_text_height_
 

Detailed Description

Definition at line 257 of file tablerecog.h.

Constructor & Destructor Documentation

◆ TableRecognizer()

tesseract::TableRecognizer::TableRecognizer ( )

Definition at line 705 of file tablerecog.cpp.

706 : text_grid_(nullptr),
707 line_grid_(nullptr),
708 min_height_(0),
709 min_width_(0),
710 max_text_height_(INT32_MAX) {
711}
ColPartitionGrid * text_grid_
Definition: tablerecog.h:367
ColPartitionGrid * line_grid_
Definition: tablerecog.h:368

◆ ~TableRecognizer()

tesseract::TableRecognizer::~TableRecognizer ( )

Definition at line 713 of file tablerecog.cpp.

713 {
714}

Member Function Documentation

◆ FindLinesBoundingBox()

bool tesseract::TableRecognizer::FindLinesBoundingBox ( TBOX bounding_box)
protected

Definition at line 814 of file tablerecog.cpp.

814 {
815 // The first iteration will tell us if there are lines
816 // present and shrink the box to a minimal iterative size.
817 if (!FindLinesBoundingBoxIteration(bounding_box))
818 return false;
819
820 // Keep growing until the area of the table stabilizes.
821 // The box can only get bigger, increasing area.
822 bool changed = true;
823 while (changed) {
824 changed = false;
825 int old_area = bounding_box->area();
826 bool check = FindLinesBoundingBoxIteration(bounding_box);
827 // At this point, the function will return true.
828 ASSERT_HOST(check);
829 ASSERT_HOST(bounding_box->area() >= old_area);
830 changed = (bounding_box->area() > old_area);
831 }
832
833 return true;
834}
#define ASSERT_HOST(x)
Definition: errcode.h:88
int32_t area() const
Definition: rect.h:122
bool FindLinesBoundingBoxIteration(TBOX *bounding_box)
Definition: tablerecog.cpp:836

◆ FindLinesBoundingBoxIteration()

bool tesseract::TableRecognizer::FindLinesBoundingBoxIteration ( TBOX bounding_box)
protected

Definition at line 836 of file tablerecog.cpp.

836 {
837 // Search for all of the lines in the current box, keeping track of extents.
839 box_search.SetUniqueMode(true);
840 box_search.StartRectSearch(*bounding_box);
841 ColPartition* line = nullptr;
842 bool first_line = true;
843
844 while ((line = box_search.NextRectSearch()) != nullptr) {
845 if (line->IsLineType()) {
846 if (first_line) {
847 // The first iteration can shrink the box.
848 *bounding_box = line->bounding_box();
849 first_line = false;
850 } else {
851 *bounding_box += line->bounding_box();
852 }
853 }
854 }
855 return !first_line;
856}
GridSearch< ColPartition, ColPartition_CLIST, ColPartition_C_IT > ColPartitionGridSearch
Definition: colpartition.h:936

◆ HasSignificantLines()

bool tesseract::TableRecognizer::HasSignificantLines ( const TBOX guess)
protected

Definition at line 775 of file tablerecog.cpp.

775 {
777 box_search.SetUniqueMode(true);
778 box_search.StartRectSearch(guess);
779 ColPartition* line = nullptr;
780 int vertical_count = 0;
781 int horizontal_count = 0;
782
783 while ((line = box_search.NextRectSearch()) != nullptr) {
784 if (line->IsHorizontalLine())
785 ++horizontal_count;
786 if (line->IsVerticalLine())
787 ++vertical_count;
788 }
789
790 return vertical_count >= kLinedTableMinVerticalLines &&
791 horizontal_count >= kLinedTableMinHorizontalLines;
792}
const int kLinedTableMinHorizontalLines
Definition: tablerecog.cpp:45
const int kLinedTableMinVerticalLines
Definition: tablerecog.cpp:44

◆ Init()

void tesseract::TableRecognizer::Init ( )

Definition at line 716 of file tablerecog.cpp.

716 {
717}

◆ IsWeakTableRow()

bool tesseract::TableRecognizer::IsWeakTableRow ( StructuredTable table,
int  row 
)
staticprotected

Definition at line 1052 of file tablerecog.cpp.

1052 {
1053 if (!table->VerifyRowFilled(row))
1054 return false;
1055
1056 double threshold = 0.0;
1057 if (table->column_count() > kGoodRowNumberOfColumnsSmallSize)
1058 threshold = table->column_count() * kGoodRowNumberOfColumnsLarge;
1059 else
1060 threshold = kGoodRowNumberOfColumnsSmall[table->column_count()];
1061
1062 return table->CountFilledCellsInRow(row) < threshold;
1063}
const double kGoodRowNumberOfColumnsLarge
Definition: tablerecog.cpp:60
const double kGoodRowNumberOfColumnsSmall[]
Definition: tablerecog.cpp:56
const int kGoodRowNumberOfColumnsSmallSize
Definition: tablerecog.cpp:57

◆ NextHorizontalSplit()

int tesseract::TableRecognizer::NextHorizontalSplit ( int  left,
int  right,
int  y,
bool  top_to_bottom 
)
protected

Definition at line 1018 of file tablerecog.cpp.

1019 {
1021 gsearch.SetUniqueMode(true);
1022 gsearch.StartVerticalSearch(left, right, y);
1023 ColPartition* text = nullptr;
1024 int last_y = y;
1025 while ((text = gsearch.NextVerticalSearch(top_to_bottom)) != nullptr) {
1026 if (!text->IsTextType() || !text->IsHorizontalType())
1027 continue;
1028 if (text->bounding_box().height() > max_text_height_)
1029 continue;
1030
1031 const TBOX& text_box = text->bounding_box();
1032 if (top_to_bottom && (last_y >= y || last_y <= text_box.top())) {
1033 last_y = std::min(last_y, static_cast<int>(text_box.bottom()));
1034 continue;
1035 }
1036 if (!top_to_bottom && (last_y <= y || last_y >= text_box.bottom())) {
1037 last_y = std::max(last_y, static_cast<int>(text_box.top()));
1038 continue;
1039 }
1040
1041 return last_y;
1042 }
1043 // If none is found, we at least want to preserve the min/max,
1044 // which defines the overlap of y with the last partition in the grid.
1045 return last_y;
1046}
Definition: rect.h:34
int16_t top() const
Definition: rect.h:58
int16_t bottom() const
Definition: rect.h:65

◆ RecognizeLinedTable()

bool tesseract::TableRecognizer::RecognizeLinedTable ( const TBOX guess_box,
StructuredTable table 
)
protected

Definition at line 758 of file tablerecog.cpp.

759 {
760 if (!HasSignificantLines(guess_box))
761 return false;
762 TBOX line_bound = guess_box;
763 if (!FindLinesBoundingBox(&line_bound))
764 return false;
765 table->set_bounding_box(line_bound);
766 return table->FindLinedStructure();
767}
bool FindLinesBoundingBox(TBOX *bounding_box)
Definition: tablerecog.cpp:814
bool HasSignificantLines(const TBOX &guess)
Definition: tablerecog.cpp:775

◆ RecognizeTable()

StructuredTable * tesseract::TableRecognizer::RecognizeTable ( const TBOX guess_box)

Definition at line 735 of file tablerecog.cpp.

735 {
736 auto* table = new StructuredTable();
737 table->Init();
738 table->set_text_grid(text_grid_);
739 table->set_line_grid(line_grid_);
740 table->set_max_text_height(max_text_height_);
741
742 // Try to solve this simple case, a table with *both*
743 // vertical and horizontal lines.
744 if (RecognizeLinedTable(guess, table))
745 return table;
746
747 // Fallback to whitespace if that failed.
748 // TODO(nbeato): Break this apart to take advantage of horizontal
749 // lines or vertical lines when present.
750 if (RecognizeWhitespacedTable(guess, table))
751 return table;
752
753 // No table found...
754 delete table;
755 return nullptr;
756}
bool RecognizeLinedTable(const TBOX &guess_box, StructuredTable *table)
Definition: tablerecog.cpp:758
bool RecognizeWhitespacedTable(const TBOX &guess_box, StructuredTable *table)
Definition: tablerecog.cpp:874

◆ RecognizeWhitespacedTable()

bool tesseract::TableRecognizer::RecognizeWhitespacedTable ( const TBOX guess_box,
StructuredTable table 
)
protected

Definition at line 874 of file tablerecog.cpp.

875 {
876 TBOX best_box = guess_box; // Best borders known.
877 int best_below = 0; // Margin size above best table.
878 int best_above = 0; // Margin size below best table.
879 TBOX adjusted = guess_box; // The search box.
880
881 // We assume that the guess box is somewhat accurate, so we don't allow
882 // the adjusted border to pass half of the guessed area. This prevents
883 // "negative" tables from forming.
884 const int kMidGuessY = (guess_box.bottom() + guess_box.top()) / 2;
885 // Keeps track of the most columns in an accepted table. The resulting table
886 // may be less than the max, but we don't want to stray too far.
887 int best_cols = 0;
888 // Make sure we find a good border.
889 bool found_good_border = false;
890
891 // Find the bottom of the table by trying a few different locations. For
892 // each location, the top, left, and right are fixed. We start the search
893 // in a smaller table to favor best_cols getting a good estimate sooner.
894 int last_bottom = INT32_MAX;
895 int bottom = NextHorizontalSplit(guess_box.left(), guess_box.right(),
896 kMidGuessY - min_height_ / 2, true);
897 int top = NextHorizontalSplit(guess_box.left(), guess_box.right(),
898 kMidGuessY + min_height_ / 2, false);
899 adjusted.set_top(top);
900
901 // Headers/footers can be spaced far from everything.
902 // Make sure that the space below is greater than the space above
903 // the lowest row.
904 int previous_below = 0;
905 const int kMaxChances = 10;
906 int chances = kMaxChances;
907 while (bottom != last_bottom) {
908 adjusted.set_bottom(bottom);
909
910 if (adjusted.height() >= min_height_) {
911 // Try to fit the grid on the current box. We give it a chance
912 // if the number of columns didn't significantly drop.
913 table->set_bounding_box(adjusted);
914 if (table->FindWhitespacedStructure() &&
915 table->column_count() >= best_cols * kRequiredColumns) {
916 if (false && IsWeakTableRow(table, 0)) {
917 // Currently buggy, but was looking promising so disabled.
918 --chances;
919 } else {
920 // We favor 2 things,
921 // 1- Adding rows that have partitioned data.
922 // 2- Better margins (to find header/footer).
923 // For better tables, we just look for multiple cells in the
924 // bottom row with data in them.
925 // For margins, the space below the last row should
926 // be better than a table with the last row removed.
927 chances = kMaxChances;
928 double max_row_height = kMaxRowSize * table->median_cell_height();
929 if ((table->space_below() * kMarginFactor >= best_below &&
930 table->space_below() >= previous_below) ||
931 (table->CountFilledCellsInRow(0) > 1 &&
932 table->row_height(0) < max_row_height)) {
933 best_box.set_bottom(bottom);
934 best_below = table->space_below();
935 best_cols = std::max(table->column_count(), best_cols);
936 found_good_border = true;
937 }
938 }
939 previous_below = table->space_below();
940 } else {
941 --chances;
942 }
943 }
944 if (chances <= 0)
945 break;
946
947 last_bottom = bottom;
948 bottom = NextHorizontalSplit(guess_box.left(), guess_box.right(),
949 last_bottom, true);
950 }
951 if (!found_good_border)
952 return false;
953
954 // TODO(nbeato) comments: follow modified code above... put it in a function!
955 found_good_border = false;
956 int last_top = INT32_MIN;
957 top = NextHorizontalSplit(guess_box.left(), guess_box.right(),
958 kMidGuessY + min_height_ / 2, false);
959 int previous_above = 0;
960 chances = kMaxChances;
961
962 adjusted.set_bottom(best_box.bottom());
963 while (last_top != top) {
964 adjusted.set_top(top);
965 if (adjusted.height() >= min_height_) {
966 table->set_bounding_box(adjusted);
967 if (table->FindWhitespacedStructure() &&
968 table->column_count() >= best_cols * kRequiredColumns) {
969 int last_row = table->row_count() - 1;
970 if (false && IsWeakTableRow(table, last_row)) {
971 // Currently buggy, but was looking promising so disabled.
972 --chances;
973 } else {
974 chances = kMaxChances;
975 double max_row_height = kMaxRowSize * table->median_cell_height();
976 if ((table->space_above() * kMarginFactor >= best_above &&
977 table->space_above() >= previous_above) ||
978 (table->CountFilledCellsInRow(last_row) > 1 &&
979 table->row_height(last_row) < max_row_height)) {
980 best_box.set_top(top);
981 best_above = table->space_above();
982 best_cols = std::max(table->column_count(), best_cols);
983 found_good_border = true;
984 }
985 }
986 previous_above = table->space_above();
987 } else {
988 --chances;
989 }
990 }
991 if (chances <= 0)
992 break;
993
994 last_top = top;
995 top = NextHorizontalSplit(guess_box.left(), guess_box.right(),
996 last_top, false);
997 }
998
999 if (!found_good_border)
1000 return false;
1001
1002 // If we get here, this shouldn't happen. It can be an assert, but
1003 // I haven't tested it enough to make it crash things.
1004 if (best_box.null_box())
1005 return false;
1006
1007 // Given the best locations, fit the box to those locations.
1008 table->set_bounding_box(best_box);
1009 return table->FindWhitespacedStructure();
1010}
const double kRequiredColumns
Definition: tablerecog.cpp:48
const double kMaxRowSize
Definition: tablerecog.cpp:53
const double kMarginFactor
Definition: tablerecog.cpp:50
void set_bottom(int y)
Definition: rect.h:68
int16_t height() const
Definition: rect.h:108
void set_top(int y)
Definition: rect.h:61
int16_t left() const
Definition: rect.h:72
bool null_box() const
Definition: rect.h:50
int16_t right() const
Definition: rect.h:79
static bool IsWeakTableRow(StructuredTable *table, int row)
int NextHorizontalSplit(int left, int right, int y, bool top_to_bottom)

◆ set_line_grid()

void tesseract::TableRecognizer::set_line_grid ( ColPartitionGrid lines)

Definition at line 722 of file tablerecog.cpp.

722 {
723 line_grid_ = line_grid;
724}

◆ set_max_text_height()

void tesseract::TableRecognizer::set_max_text_height ( int  height)

Definition at line 731 of file tablerecog.cpp.

731 {
732 max_text_height_ = height;
733}

◆ set_min_height()

void tesseract::TableRecognizer::set_min_height ( int  height)

Definition at line 725 of file tablerecog.cpp.

725 {
726 min_height_ = height;
727}

◆ set_min_width()

void tesseract::TableRecognizer::set_min_width ( int  width)

Definition at line 728 of file tablerecog.cpp.

728 {
729 min_width_ = width;
730}

◆ set_text_grid()

void tesseract::TableRecognizer::set_text_grid ( ColPartitionGrid text)

Definition at line 719 of file tablerecog.cpp.

719 {
720 text_grid_ = text_grid;
721}

Member Data Documentation

◆ line_grid_

ColPartitionGrid* tesseract::TableRecognizer::line_grid_
protected

Definition at line 368 of file tablerecog.h.

◆ max_text_height_

int tesseract::TableRecognizer::max_text_height_
protected

Definition at line 373 of file tablerecog.h.

◆ min_height_

int tesseract::TableRecognizer::min_height_
protected

Definition at line 370 of file tablerecog.h.

◆ min_width_

int tesseract::TableRecognizer::min_width_
protected

Definition at line 371 of file tablerecog.h.

◆ text_grid_

ColPartitionGrid* tesseract::TableRecognizer::text_grid_
protected

Definition at line 367 of file tablerecog.h.


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