tesseract  5.0.0-alpha-619-ge9db
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 }

◆ ~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 }

◆ 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 }

◆ 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 }

◆ 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 }

◆ 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 }

◆ 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 }

◆ 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 }

◆ 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 }

◆ 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:
tesseract::TableRecognizer::text_grid_
ColPartitionGrid * text_grid_
Definition: tablerecog.h:367
ASSERT_HOST
#define ASSERT_HOST(x)
Definition: errcode.h:87
TBOX::top
int16_t top() const
Definition: rect.h:57
TBOX::area
int32_t area() const
Definition: rect.h:121
TBOX::set_top
void set_top(int y)
Definition: rect.h:60
tesseract::TableRecognizer::min_height_
int min_height_
Definition: tablerecog.h:370
tesseract::TableRecognizer::FindLinesBoundingBoxIteration
bool FindLinesBoundingBoxIteration(TBOX *bounding_box)
Definition: tablerecog.cpp:836
tesseract::ColPartitionGridSearch
GridSearch< ColPartition, ColPartition_CLIST, ColPartition_C_IT > ColPartitionGridSearch
Definition: colpartition.h:935
tesseract::TableRecognizer::IsWeakTableRow
static bool IsWeakTableRow(StructuredTable *table, int row)
Definition: tablerecog.cpp:1052
TBOX::height
int16_t height() const
Definition: rect.h:107
tesseract::kGoodRowNumberOfColumnsSmallSize
const int kGoodRowNumberOfColumnsSmallSize
Definition: tablerecog.cpp:57
tesseract::TableRecognizer::NextHorizontalSplit
int NextHorizontalSplit(int left, int right, int y, bool top_to_bottom)
Definition: tablerecog.cpp:1018
tesseract::kLinedTableMinHorizontalLines
const int kLinedTableMinHorizontalLines
Definition: tablerecog.cpp:45
TBOX::null_box
bool null_box() const
Definition: rect.h:49
tesseract::TableRecognizer::FindLinesBoundingBox
bool FindLinesBoundingBox(TBOX *bounding_box)
Definition: tablerecog.cpp:814
TBOX::bottom
int16_t bottom() const
Definition: rect.h:64
tesseract::kMaxRowSize
const double kMaxRowSize
Definition: tablerecog.cpp:53
tesseract::kRequiredColumns
const double kRequiredColumns
Definition: tablerecog.cpp:48
tesseract::kGoodRowNumberOfColumnsLarge
const double kGoodRowNumberOfColumnsLarge
Definition: tablerecog.cpp:60
tesseract::TableRecognizer::HasSignificantLines
bool HasSignificantLines(const TBOX &guess)
Definition: tablerecog.cpp:775
TBOX::left
int16_t left() const
Definition: rect.h:71
tesseract::kMarginFactor
const double kMarginFactor
Definition: tablerecog.cpp:50
TBOX::right
int16_t right() const
Definition: rect.h:78
tesseract::TableRecognizer::line_grid_
ColPartitionGrid * line_grid_
Definition: tablerecog.h:368
tesseract::TableRecognizer::RecognizeWhitespacedTable
bool RecognizeWhitespacedTable(const TBOX &guess_box, StructuredTable *table)
Definition: tablerecog.cpp:874
tesseract::kLinedTableMinVerticalLines
const int kLinedTableMinVerticalLines
Definition: tablerecog.cpp:44
tesseract::kGoodRowNumberOfColumnsSmall
const double kGoodRowNumberOfColumnsSmall[]
Definition: tablerecog.cpp:56
TBOX::set_bottom
void set_bottom(int y)
Definition: rect.h:67
tesseract::TableRecognizer::max_text_height_
int max_text_height_
Definition: tablerecog.h:373
tesseract::TableRecognizer::min_width_
int min_width_
Definition: tablerecog.h:371
TBOX
Definition: rect.h:33
tesseract::TableRecognizer::RecognizeLinedTable
bool RecognizeLinedTable(const TBOX &guess_box, StructuredTable *table)
Definition: tablerecog.cpp:758