Tesseract
3.02
|
#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) |
StructuredTable * | RecognizeTable (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 | |
ColPartitionGrid * | text_grid_ |
ColPartitionGrid * | line_grid_ |
int | min_height_ |
int | min_width_ |
int | max_text_height_ |
Definition at line 257 of file tablerecog.h.
tesseract::TableRecognizer::TableRecognizer | ( | ) |
Definition at line 698 of file tablerecog.cpp.
: text_grid_(NULL), line_grid_(NULL), min_height_(0), min_width_(0), max_text_height_(MAX_INT32) { }
tesseract::TableRecognizer::~TableRecognizer | ( | ) |
Definition at line 706 of file tablerecog.cpp.
{ }
bool tesseract::TableRecognizer::FindLinesBoundingBox | ( | TBOX * | bounding_box | ) | [protected] |
Definition at line 807 of file tablerecog.cpp.
{ // The first iteration will tell us if there are lines // present and shrink the box to a minimal iterative size. if (!FindLinesBoundingBoxIteration(bounding_box)) return false; // Keep growing until the area of the table stabilizes. // The box can only get bigger, increasing area. bool changed = true; while (changed) { changed = false; int old_area = bounding_box->area(); bool check = FindLinesBoundingBoxIteration(bounding_box); // At this point, the function will return true. ASSERT_HOST(check); ASSERT_HOST(bounding_box->area() >= old_area); changed = (bounding_box->area() > old_area); } return true; }
bool tesseract::TableRecognizer::FindLinesBoundingBoxIteration | ( | TBOX * | bounding_box | ) | [protected] |
Definition at line 829 of file tablerecog.cpp.
{ // Search for all of the lines in the current box, keeping track of extents. ColPartitionGridSearch box_search(line_grid_); box_search.SetUniqueMode(true); box_search.StartRectSearch(*bounding_box); ColPartition* line = NULL; bool first_line = true; while ((line = box_search.NextRectSearch()) != NULL) { if (line->IsLineType()) { if (first_line) { // The first iteration can shrink the box. *bounding_box = line->bounding_box(); first_line = false; } else { *bounding_box += line->bounding_box(); } } } return !first_line; }
bool tesseract::TableRecognizer::HasSignificantLines | ( | const TBOX & | guess | ) | [protected] |
Definition at line 768 of file tablerecog.cpp.
{ ColPartitionGridSearch box_search(line_grid_); box_search.SetUniqueMode(true); box_search.StartRectSearch(guess); ColPartition* line = NULL; int vertical_count = 0; int horizontal_count = 0; while ((line = box_search.NextRectSearch()) != NULL) { if (line->IsHorizontalLine()) ++horizontal_count; if (line->IsVerticalLine()) ++vertical_count; } return vertical_count >= kLinedTableMinVerticalLines && horizontal_count >= kLinedTableMinHorizontalLines; }
void tesseract::TableRecognizer::Init | ( | ) |
Definition at line 709 of file tablerecog.cpp.
{ }
bool tesseract::TableRecognizer::IsWeakTableRow | ( | StructuredTable * | table, |
int | row | ||
) | [static, protected] |
Definition at line 1045 of file tablerecog.cpp.
{ if (!table->VerifyRowFilled(row)) return false; double threshold = 0.0; if (table->column_count() > kGoodRowNumberOfColumnsSmallSize) threshold = table->column_count() * kGoodRowNumberOfColumnsLarge; else threshold = kGoodRowNumberOfColumnsSmall[table->column_count()]; return table->CountFilledCellsInRow(row) < threshold; }
int tesseract::TableRecognizer::NextHorizontalSplit | ( | int | left, |
int | right, | ||
int | y, | ||
bool | top_to_bottom | ||
) | [protected] |
Definition at line 1011 of file tablerecog.cpp.
{ ColPartitionGridSearch gsearch(text_grid_); gsearch.SetUniqueMode(true); gsearch.StartVerticalSearch(left, right, y); ColPartition* text = NULL; int last_y = y; while ((text = gsearch.NextVerticalSearch(top_to_bottom)) != NULL) { if (!text->IsTextType() || !text->IsHorizontalType()) continue; if (text->bounding_box().height() > max_text_height_) continue; const TBOX& text_box = text->bounding_box(); if (top_to_bottom && (last_y >= y || last_y <= text_box.top())) { last_y = MIN(last_y, text_box.bottom()); continue; } if (!top_to_bottom && (last_y <= y || last_y >= text_box.bottom())) { last_y = MAX(last_y, text_box.top()); continue; } return last_y; } // If none is found, we at least want to preserve the min/max, // which defines the overlap of y with the last partition in the grid. return last_y; }
bool tesseract::TableRecognizer::RecognizeLinedTable | ( | const TBOX & | guess_box, |
StructuredTable * | table | ||
) | [protected] |
Definition at line 751 of file tablerecog.cpp.
{ if (!HasSignificantLines(guess_box)) return false; TBOX line_bound = guess_box; if (!FindLinesBoundingBox(&line_bound)) return false; table->set_bounding_box(line_bound); return table->FindLinedStructure(); }
StructuredTable * tesseract::TableRecognizer::RecognizeTable | ( | const TBOX & | guess_box | ) |
Definition at line 728 of file tablerecog.cpp.
{ StructuredTable* table = new StructuredTable(); table->Init(); table->set_text_grid(text_grid_); table->set_line_grid(line_grid_); table->set_max_text_height(max_text_height_); // Try to solve ths simple case, a table with *both* // vertical and horizontal lines. if (RecognizeLinedTable(guess, table)) return table; // Fallback to whitespace if that failed. // TODO(nbeato): Break this apart to take advantage of horizontal // lines or vertical lines when present. if (RecognizeWhitespacedTable(guess, table)) return table; // No table found... delete table; return NULL; }
bool tesseract::TableRecognizer::RecognizeWhitespacedTable | ( | const TBOX & | guess_box, |
StructuredTable * | table | ||
) | [protected] |
Definition at line 867 of file tablerecog.cpp.
{ TBOX best_box = guess_box; // Best borders known. int best_below = 0; // Margin size above best table. int best_above = 0; // Margin size below best table. TBOX adjusted = guess_box; // The search box. // We assume that the guess box is somewhat accurate, so we don't allow // the adjusted border to pass half of the guessed area. This prevents // "negative" tables from forming. const int kMidGuessY = (guess_box.bottom() + guess_box.top()) / 2; // Keeps track of the most columns in an accepted table. The resulting table // may be less than the max, but we don't want to stray too far. int best_cols = 0; // Make sure we find a good border. bool found_good_border = false; // Find the bottom of the table by trying a few different locations. For // each location, the top, left, and right are fixed. We start the search // in a smaller table to favor best_cols getting a good estimate sooner. int last_bottom = MAX_INT32; int bottom = NextHorizontalSplit(guess_box.left(), guess_box.right(), kMidGuessY - min_height_ / 2, true); int top = NextHorizontalSplit(guess_box.left(), guess_box.right(), kMidGuessY + min_height_ / 2, false); adjusted.set_top(top); // Headers/footers can be spaced far from everything. // Make sure that the space below is greater than the space above // the lowest row. int previous_below = 0; const int kMaxChances = 10; int chances = kMaxChances; while (bottom != last_bottom) { adjusted.set_bottom(bottom); if (adjusted.height() >= min_height_) { // Try to fit the grid on the current box. We give it a chance // if the number of columns didn't significantly drop. table->set_bounding_box(adjusted); if (table->FindWhitespacedStructure() && table->column_count() >= best_cols * kRequiredColumns) { if (false && IsWeakTableRow(table, 0)) { // Currently buggy, but was looking promising so disabled. --chances; } else { // We favor 2 things, // 1- Adding rows that have partitioned data. // 2- Better margins (to find header/footer). // For better tables, we just look for multiple cells in the // bottom row with data in them. // For margins, the space below the last row should // be better than a table with the last row removed. chances = kMaxChances; double max_row_height = kMaxRowSize * table->median_cell_height(); if ((table->space_below() * kMarginFactor >= best_below && table->space_below() >= previous_below) || (table->CountFilledCellsInRow(0) > 1 && table->row_height(0) < max_row_height)) { best_box.set_bottom(bottom); best_below = table->space_below(); best_cols = MAX(table->column_count(), best_cols); found_good_border = true; } } previous_below = table->space_below(); } else { --chances; } } if (chances <= 0) break; last_bottom = bottom; bottom = NextHorizontalSplit(guess_box.left(), guess_box.right(), last_bottom, true); } if (!found_good_border) return false; // TODO(nbeato) comments: follow modified code above... put it in a function! found_good_border = false; int last_top = MIN_INT32; top = NextHorizontalSplit(guess_box.left(), guess_box.right(), kMidGuessY + min_height_ / 2, false); int previous_above = 0; chances = kMaxChances; adjusted.set_bottom(best_box.bottom()); while (last_top != top) { adjusted.set_top(top); if (adjusted.height() >= min_height_) { table->set_bounding_box(adjusted); if (table->FindWhitespacedStructure() && table->column_count() >= best_cols * kRequiredColumns) { int last_row = table->row_count() - 1; if (false && IsWeakTableRow(table, last_row)) { // Currently buggy, but was looking promising so disabled. --chances; } else { chances = kMaxChances; double max_row_height = kMaxRowSize * table->median_cell_height(); if ((table->space_above() * kMarginFactor >= best_above && table->space_above() >= previous_above) || (table->CountFilledCellsInRow(last_row) > 1 && table->row_height(last_row) < max_row_height)) { best_box.set_top(top); best_above = table->space_above(); best_cols = MAX(table->column_count(), best_cols); found_good_border = true; } } previous_above = table->space_above(); } else { --chances; } } if (chances <= 0) break; last_top = top; top = NextHorizontalSplit(guess_box.left(), guess_box.right(), last_top, false); } if (!found_good_border) return false; // If we get here, this shouldn't happen. It can be an assert, but // I haven't tested it enough to make it crash things. if (best_box.null_box()) return false; // Given the best locations, fit the box to those locations. table->set_bounding_box(best_box); return table->FindWhitespacedStructure(); }
void tesseract::TableRecognizer::set_line_grid | ( | ColPartitionGrid * | lines | ) |
Definition at line 715 of file tablerecog.cpp.
{ line_grid_ = line_grid; }
void tesseract::TableRecognizer::set_max_text_height | ( | int | height | ) |
Definition at line 724 of file tablerecog.cpp.
{ max_text_height_ = height; }
void tesseract::TableRecognizer::set_min_height | ( | int | height | ) |
Definition at line 718 of file tablerecog.cpp.
{ min_height_ = height; }
void tesseract::TableRecognizer::set_min_width | ( | int | width | ) |
Definition at line 721 of file tablerecog.cpp.
{ min_width_ = width; }
void tesseract::TableRecognizer::set_text_grid | ( | ColPartitionGrid * | text | ) |
Definition at line 712 of file tablerecog.cpp.
{ text_grid_ = text_grid; }
ColPartitionGrid* tesseract::TableRecognizer::line_grid_ [protected] |
Definition at line 368 of file tablerecog.h.
int tesseract::TableRecognizer::max_text_height_ [protected] |
Definition at line 373 of file tablerecog.h.
int tesseract::TableRecognizer::min_height_ [protected] |
Definition at line 370 of file tablerecog.h.
int tesseract::TableRecognizer::min_width_ [protected] |
Definition at line 371 of file tablerecog.h.
ColPartitionGrid* tesseract::TableRecognizer::text_grid_ [protected] |
Definition at line 367 of file tablerecog.h.