tesseract  3.03
tesseract::StructuredTable Class Reference

#include <tablerecog.h>

List of all members.

Public Member Functions

 StructuredTable ()
 ~StructuredTable ()
void Init ()
void set_text_grid (ColPartitionGrid *text)
void set_line_grid (ColPartitionGrid *lines)
void set_max_text_height (int height)
bool is_lined () const
int row_count () const
int column_count () const
int cell_count () const
void set_bounding_box (const TBOX &box)
const TBOXbounding_box () const
int median_cell_height ()
int median_cell_width ()
int row_height (int row) const
int column_width (int column) const
int space_above () const
int space_below () const
bool FindLinedStructure ()
bool FindWhitespacedStructure ()
bool DoesPartitionFit (const ColPartition &part) const
int CountFilledCells ()
int CountFilledCellsInRow (int row)
int CountFilledCellsInColumn (int column)
int CountFilledCells (int row_start, int row_end, int column_start, int column_end)
bool VerifyRowFilled (int row)
double CalculateCellFilledPercentage (int row, int column)
void Display (ScrollView *window, ScrollView::Color color)

Protected Member Functions

void ClearStructure ()
bool VerifyLinedTableCells ()
bool VerifyWhitespacedTable ()
void FindWhitespacedColumns ()
void FindWhitespacedRows ()
void CalculateMargins ()
void UpdateMargins (ColPartitionGrid *grid)
int FindVerticalMargin (ColPartitionGrid *grid, int start_x, bool decrease) const
int FindHorizontalMargin (ColPartitionGrid *grid, int start_y, bool decrease) const
void CalculateStats ()
void AbsorbNearbyLines ()
int CountVerticalIntersections (int x)
int CountHorizontalIntersections (int y)
int CountPartitions (const TBOX &box)

Static Protected Member Functions

static void FindCellSplitLocations (const GenericVector< int > &min_list, const GenericVector< int > &max_list, int max_merged, GenericVector< int > *locations)

Protected Attributes

ColPartitionGridtext_grid_
ColPartitionGridline_grid_
TBOX bounding_box_
GenericVectorEqEq< int > cell_x_
GenericVectorEqEq< int > cell_y_
bool is_lined_
int space_above_
int space_below_
int space_left_
int space_right_
int median_cell_height_
int median_cell_width_
int max_text_height_

Detailed Description

Definition at line 72 of file tablerecog.h.


Constructor & Destructor Documentation

Definition at line 80 of file tablerecog.cpp.

                                  {
}

Member Function Documentation

Definition at line 535 of file tablerecog.cpp.

                                        {
  ColPartitionGridSearch gsearch(line_grid_);
  gsearch.SetUniqueMode(true);

  // Is the closest line above good? Loop multiple times for tables with
  // multi-line (sometimes 2) borders. Limit the number of lines by
  // making sure they stay within a table cell or so.
  ColPartition* line = NULL;
  gsearch.StartVerticalSearch(bounding_box_.left(), bounding_box_.right(),
                              bounding_box_.top());
  while ((line = gsearch.NextVerticalSearch(false)) != NULL) {
    if (!line->IsHorizontalLine())
      break;
    TBOX text_search(bounding_box_.left(), bounding_box_.top() + 1,
                     bounding_box_.right(), line->MidY());
    if (text_search.height() > median_cell_height_ * 2)
      break;
    if (CountPartitions(text_search) > 0)
      break;
    bounding_box_.set_top(line->MidY());
  }
  // As above, is the closest line below good?
  line = NULL;
  gsearch.StartVerticalSearch(bounding_box_.left(), bounding_box_.right(),
                              bounding_box_.bottom());
  while ((line = gsearch.NextVerticalSearch(true)) != NULL) {
    if (!line->IsHorizontalLine())
      break;
    TBOX text_search(bounding_box_.left(), line->MidY(),
                     bounding_box_.right(), bounding_box_.bottom() - 1);
    if (text_search.height() > median_cell_height_ * 2)
      break;
    if (CountPartitions(text_search) > 0)
      break;
    bounding_box_.set_bottom(line->MidY());
  }
  // TODO(nbeato): vertical lines
}

Definition at line 110 of file tablerecog.cpp.

                                                {
  return bounding_box_;
}
double tesseract::StructuredTable::CalculateCellFilledPercentage ( int  row,
int  column 
)

Definition at line 267 of file tablerecog.cpp.

                                                                         {
  ASSERT_HOST(0 <= row && row <= row_count());
  ASSERT_HOST(0 <= column && column <= column_count());
  const TBOX kCellBox(cell_x_[column], cell_y_[row],
                      cell_x_[column + 1], cell_y_[row + 1]);
  ASSERT_HOST(!kCellBox.null_box());

  ColPartitionGridSearch gsearch(text_grid_);
  gsearch.SetUniqueMode(true);
  gsearch.StartRectSearch(kCellBox);
  double area_covered = 0;
  ColPartition* text = NULL;
  while ((text = gsearch.NextRectSearch()) != NULL) {
    if (text->IsTextType())
      area_covered += text->bounding_box().intersection(kCellBox).area();
  }
  return MIN(1.0, area_covered / kCellBox.area());
}

Definition at line 515 of file tablerecog.cpp.

                                     {
  const int kMaxCellHeight = 1000;
  const int kMaxCellWidth = 1000;
  STATS height_stats(0, kMaxCellHeight + 1);
  STATS width_stats(0, kMaxCellWidth + 1);

  for (int i = 0; i < row_count(); ++i)
    height_stats.add(row_height(i), column_count());
  for (int i = 0; i < column_count(); ++i)
    width_stats.add(column_width(i), row_count());

  median_cell_height_ = static_cast<int>(height_stats.median() + 0.5);
  median_cell_width_ = static_cast<int>(width_stats.median() + 0.5);
}

Definition at line 104 of file tablerecog.cpp.

                                      {
  return row_count() * column_count();
}

Definition at line 101 of file tablerecog.cpp.

                                        {
  return cell_x_.length() == 0 ? 0 : cell_x_.length() - 1;
}
int tesseract::StructuredTable::column_width ( int  column) const

Definition at line 123 of file tablerecog.cpp.

                                                  {
  ASSERT_HOST(0 <= column && column < column_count());
  return cell_x_[column + 1] - cell_x_[column];
}

Definition at line 224 of file tablerecog.cpp.

                                      {
  return CountFilledCells(0, row_count() - 1, 0, column_count() - 1);
}
int tesseract::StructuredTable::CountFilledCells ( int  row_start,
int  row_end,
int  column_start,
int  column_end 
)

Definition at line 233 of file tablerecog.cpp.

                                                              {
  ASSERT_HOST(0 <= row_start && row_start <= row_end && row_end < row_count());
  ASSERT_HOST(0 <= column_start && column_start <= column_end &&
              column_end < column_count());
  int cell_count = 0;
  TBOX cell_box;
  for (int row = row_start; row <= row_end; ++row) {
    cell_box.set_bottom(cell_y_[row]);
    cell_box.set_top(cell_y_[row + 1]);
    for (int col = column_start; col <= column_end; ++col) {
      cell_box.set_left(cell_x_[col]);
      cell_box.set_right(cell_x_[col + 1]);
      if (CountPartitions(cell_box) > 0)
        ++cell_count;
    }
  }
  return cell_count;
}

Definition at line 230 of file tablerecog.cpp.

                                                        {
  return CountFilledCells(0, row_count() - 1, column, column);
}

Definition at line 227 of file tablerecog.cpp.

                                                  {
  return CountFilledCells(row, row, 0, column_count() - 1);
}

Definition at line 659 of file tablerecog.cpp.

                                                       {
  int count = 0;
  // Make a small box to keep the search time down.
  const int kGridSize = text_grid_->gridsize();
  TBOX horizontal_box = bounding_box_;
  horizontal_box.set_bottom(y - kGridSize);
  horizontal_box.set_top(y + kGridSize);

  ColPartitionGridSearch gsearch(text_grid_);
  gsearch.SetUniqueMode(true);
  gsearch.StartRectSearch(horizontal_box);
  ColPartition* text = NULL;
  while ((text = gsearch.NextRectSearch()) != NULL) {
    if (!text->IsTextType())
      continue;

    const TBOX& box = text->bounding_box();
    if (box.bottom() < y && y < box.top())
      ++count;
  }
  return count;
}
int tesseract::StructuredTable::CountPartitions ( const TBOX box) [protected]

Definition at line 685 of file tablerecog.cpp.

                                                    {
  ColPartitionGridSearch gsearch(text_grid_);
  gsearch.SetUniqueMode(true);
  gsearch.StartRectSearch(box);
  int count = 0;
  ColPartition* text = NULL;
  while ((text = gsearch.NextRectSearch()) != NULL) {
    if (text->IsTextType())
      ++count;
  }
  return count;
}

Definition at line 635 of file tablerecog.cpp.

                                                     {
  int count = 0;
  // Make a small box to keep the search time down.
  const int kGridSize = text_grid_->gridsize();
  TBOX vertical_box = bounding_box_;
  vertical_box.set_left(x - kGridSize);
  vertical_box.set_right(x + kGridSize);

  ColPartitionGridSearch gsearch(text_grid_);
  gsearch.SetUniqueMode(true);
  gsearch.StartRectSearch(vertical_box);
  ColPartition* text = NULL;
  while ((text = gsearch.NextRectSearch()) != NULL) {
    if (!text->IsTextType())
      continue;
    const TBOX& box = text->bounding_box();
    if (box.left() < x && x < box.right())
      ++count;
  }
  return count;
}

Definition at line 286 of file tablerecog.cpp.

                                                                       {
#ifndef GRAPHICS_DISABLED
  window->Brush(ScrollView::NONE);
  window->Pen(color);
  window->Rectangle(bounding_box_.left(), bounding_box_.bottom(),
                    bounding_box_.right(), bounding_box_.top());
  for (int i = 0; i < cell_x_.length(); i++) {
    window->Line(cell_x_[i], bounding_box_.bottom(),
                 cell_x_[i], bounding_box_.top());
  }
  for (int i = 0; i < cell_y_.length(); i++) {
    window->Line(bounding_box_.left(), cell_y_[i],
                 bounding_box_.right(), cell_y_[i]);
  }
  window->UpdateWindow();
#endif
}

Definition at line 212 of file tablerecog.cpp.

                                                                     {
  const TBOX& box = part.bounding_box();
  for (int i = 0; i < cell_x_.length(); ++i)
    if (box.left() < cell_x_[i] && cell_x_[i] < box.right())
      return false;
  for (int i = 0; i < cell_y_.length(); ++i)
    if (box.bottom() < cell_y_[i] && cell_y_[i] < box.top())
      return false;
  return true;
}
void tesseract::StructuredTable::FindCellSplitLocations ( const GenericVector< int > &  min_list,
const GenericVector< int > &  max_list,
int  max_merged,
GenericVector< int > *  locations 
) [static, protected]

Definition at line 589 of file tablerecog.cpp.

                                                                            {
  locations->clear();
  ASSERT_HOST(min_list.length() == max_list.length());
  if (min_list.length() == 0)
    return;
  ASSERT_HOST(min_list.get(0) < max_list.get(0));
  ASSERT_HOST(min_list.get(min_list.length() - 1) <
              max_list.get(max_list.length() - 1));

  locations->push_back(min_list.get(0));
  int min_index = 0;
  int max_index = 0;
  int stacked_partitions = 0;
  int last_cross_position = MAX_INT32;
  // max_index will expire after min_index.
  // However, we can't "increase" the hill size if min_index expired.
  // So finish processing when min_index expires.
  while (min_index < min_list.length()) {
    // Increase the hill count.
    if (min_list[min_index] < max_list[max_index]) {
      ++stacked_partitions;
      if (last_cross_position != MAX_INT32 &&
          stacked_partitions > max_merged) {
        int mid = (last_cross_position + min_list[min_index]) / 2;
        locations->push_back(mid);
        last_cross_position = MAX_INT32;
      }
      ++min_index;
    } else {
      // Decrease the hill count.
      --stacked_partitions;
      if (last_cross_position == MAX_INT32 &&
          stacked_partitions <= max_merged) {
        last_cross_position = max_list[max_index];
      }
      ++max_index;
    }
  }
  locations->push_back(max_list.get(max_list.length() - 1));
}
int tesseract::StructuredTable::FindHorizontalMargin ( ColPartitionGrid grid,
int  start_y,
bool  decrease 
) const [protected]

Definition at line 498 of file tablerecog.cpp.

                                                               {
  ColPartitionGridSearch gsearch(grid);
  gsearch.SetUniqueMode(true);
  gsearch.StartSideSearch(border, bounding_box_.bottom(), bounding_box_.top());
  ColPartition* part = NULL;
  while ((part = gsearch.NextSideSearch(decrease)) != NULL) {
    if (!part->IsTextType() && !part->IsVerticalLine())
      continue;
    int distance = decrease ? border - part->bounding_box().right()
                            : part->bounding_box().left() - border;
    if (distance >= 0)
      return distance;
  }
  return MAX_INT32;
}

Definition at line 141 of file tablerecog.cpp.

                                         {
  ClearStructure();

  // Search for all of the lines in the current box.
  // Update the cellular structure with the exact lines.
  ColPartitionGridSearch box_search(line_grid_);
  box_search.SetUniqueMode(true);
  box_search.StartRectSearch(bounding_box_);
  ColPartition* line = NULL;

  while ((line = box_search.NextRectSearch()) != NULL) {
    if (line->IsHorizontalLine())
      cell_y_.push_back(line->MidY());
    if (line->IsVerticalLine())
      cell_x_.push_back(line->MidX());
  }

  // HasSignificantLines should guarantee cells.
  // Because that code is a different class, just gracefully
  // return false. This could be an assert.
  if (cell_x_.length() < 3 || cell_y_.length() < 3)
    return false;

  cell_x_.sort();
  cell_y_.sort();

  // Remove duplicates that may have occurred due to split lines.
  cell_x_.compact_sorted();
  cell_y_.compact_sorted();

  // The border should be the extents of line boxes, not middle.
  cell_x_[0] = bounding_box_.left();
  cell_x_[cell_x_.length() - 1] = bounding_box_.right();
  cell_y_[0] = bounding_box_.bottom();
  cell_y_[cell_y_.length() - 1] = bounding_box_.top();

  // Remove duplicates that may have occurred due to moving the borders.
  cell_x_.compact_sorted();
  cell_y_.compact_sorted();

  CalculateMargins();
  CalculateStats();
  is_lined_ = VerifyLinedTableCells();
  return is_lined_;
}
int tesseract::StructuredTable::FindVerticalMargin ( ColPartitionGrid grid,
int  start_x,
bool  decrease 
) const [protected]

Definition at line 481 of file tablerecog.cpp.

                                                             {
  ColPartitionGridSearch gsearch(grid);
  gsearch.SetUniqueMode(true);
  gsearch.StartVerticalSearch(bounding_box_.left(), bounding_box_.right(),
                              border);
  ColPartition* part = NULL;
  while ((part = gsearch.NextVerticalSearch(decrease)) != NULL) {
    if (!part->IsTextType() && !part->IsHorizontalLine())
      continue;
    int distance = decrease ? border - part->bounding_box().top()
                            : part->bounding_box().bottom() - border;
    if (distance >= 0)
      return distance;
  }
  return MAX_INT32;
}

Definition at line 351 of file tablerecog.cpp.

                                             {
  // Set of the extents of all partitions on the page.
  GenericVectorEqEq<int> left_sides;
  GenericVectorEqEq<int> right_sides;

  // Look at each text partition. We want to find the partitions
  // that have extremal left/right sides. These will give us a basis
  // for the table columns.
  ColPartitionGridSearch gsearch(text_grid_);
  gsearch.SetUniqueMode(true);
  gsearch.StartRectSearch(bounding_box_);
  ColPartition* text = NULL;
  while ((text = gsearch.NextRectSearch()) != NULL) {
    if (!text->IsTextType())
      continue;

    ASSERT_HOST(text->bounding_box().left() < text->bounding_box().right());
    int spacing = static_cast<int>(text->median_width() *
                                   kHorizontalSpacing / 2.0 + 0.5);
    left_sides.push_back(text->bounding_box().left() - spacing);
    right_sides.push_back(text->bounding_box().right() + spacing);
  }
  // It causes disaster below, so avoid it!
  if (left_sides.length() == 0 || right_sides.length() == 0)
    return;

  // Since data may be inserted in grid order, we sort the left/right sides.
  left_sides.sort();
  right_sides.sort();

  // At this point, in the "merged list", we expect to have a left side,
  // followed by either more left sides or a right side. The last number
  // should be a right side. We find places where the splits occur by looking
  // for "valleys". If we want to force gap sizes or allow overlap, change
  // the spacing above. If you want to let lines "slice" partitions as long
  // as it is infrequent, change the following function.
  FindCellSplitLocations(left_sides, right_sides, kCellSplitColumnThreshold,
                         &cell_x_);
}

Definition at line 396 of file tablerecog.cpp.

                                          {
  // Set of the extents of all partitions on the page.
  GenericVectorEqEq<int> bottom_sides;
  GenericVectorEqEq<int> top_sides;
  // We will be "shrinking" partitions, so keep the min/max around to
  // make sure the bottom/top lines do not intersect text.
  int min_bottom = MAX_INT32;
  int max_top = MIN_INT32;

  // Look at each text partition. We want to find the partitions
  // that have extremal bottom/top sides. These will give us a basis
  // for the table rows. Because the textlines can be skewed and close due
  // to warping, the height of the partitions is toned down a little bit.
  ColPartitionGridSearch gsearch(text_grid_);
  gsearch.SetUniqueMode(true);
  gsearch.StartRectSearch(bounding_box_);
  ColPartition* text = NULL;
  while ((text = gsearch.NextRectSearch()) != NULL) {
    if (!text->IsTextType())
      continue;

    ASSERT_HOST(text->bounding_box().bottom() < text->bounding_box().top());
    min_bottom = MIN(min_bottom, text->bounding_box().bottom());
    max_top = MAX(max_top, text->bounding_box().top());

    // Ignore "tall" text partitions, as these are usually false positive
    // vertical text or multiple lines pulled together.
    if (text->bounding_box().height() > max_text_height_)
      continue;

    int spacing = static_cast<int>(text->bounding_box().height() *
                                   kVerticalSpacing / 2.0 + 0.5);
    int bottom = text->bounding_box().bottom() - spacing;
    int top = text->bounding_box().top() + spacing;
    // For horizontal text, the factor can be negative. This should
    // probably cause a warning or failure. I haven't actually checked if
    // it happens.
    if (bottom >= top)
      continue;

    bottom_sides.push_back(bottom);
    top_sides.push_back(top);
  }
  // It causes disaster below, so avoid it!
  if (bottom_sides.length() == 0 || top_sides.length() == 0)
    return;

  // Since data may be inserted in grid order, we sort the bottom/top sides.
  bottom_sides.sort();
  top_sides.sort();

  // At this point, in the "merged list", we expect to have a bottom side,
  // followed by either more bottom sides or a top side. The last number
  // should be a top side. We find places where the splits occur by looking
  // for "valleys". If we want to force gap sizes or allow overlap, change
  // the spacing above. If you want to let lines "slice" partitions as long
  // as it is infrequent, change the following function.
  FindCellSplitLocations(bottom_sides, top_sides, kCellSplitRowThreshold,
                         &cell_y_);

  // Recover the min/max correctly since it was shifted.
  cell_y_[0] = min_bottom;
  cell_y_[cell_y_.length() - 1] = max_top;
}

Definition at line 83 of file tablerecog.cpp.

                           {
}

Definition at line 95 of file tablerecog.cpp.

                                     {
  return is_lined_;
}

Definition at line 98 of file tablerecog.cpp.

                                     {
  return cell_y_.length() == 0 ? 0 : cell_y_.length() - 1;
}

Definition at line 119 of file tablerecog.cpp.

                                             {
  ASSERT_HOST(0 <= row && row < row_count());
  return cell_y_[row + 1] - cell_y_[row];
}

Definition at line 107 of file tablerecog.cpp.

                                                      {
  bounding_box_ = box;
}

Definition at line 89 of file tablerecog.cpp.

                                                               {
  line_grid_ = line_grid;
}

Definition at line 92 of file tablerecog.cpp.

                                                    {
  max_text_height_ = height;
}

Definition at line 86 of file tablerecog.cpp.

                                                               {
  text_grid_ = text_grid;
}

Definition at line 127 of file tablerecog.cpp.

                                       {
  return space_above_;
}

Definition at line 130 of file tablerecog.cpp.

                                       {
  return space_below_;
}

Definition at line 471 of file tablerecog.cpp.

                                                          {
  int below = FindVerticalMargin(grid, bounding_box_.bottom(), true);
  space_below_ = MIN(space_below_, below);
  int above = FindVerticalMargin(grid, bounding_box_.top(), false);
  space_above_ = MIN(space_above_, above);
  int left = FindHorizontalMargin(grid, bounding_box_.left(), true);
  space_left_ = MIN(space_left_, left);
  int right = FindHorizontalMargin(grid, bounding_box_.right(), false);
  space_right_ = MIN(space_right_, right);
}

Definition at line 319 of file tablerecog.cpp.

                                            {
  // Function only called when lines exist.
  ASSERT_HOST(cell_y_.length() >= 2 && cell_x_.length() >= 2);
  for (int i = 0; i < cell_y_.length(); ++i) {
    if (CountHorizontalIntersections(cell_y_[i]) > 0)
      return false;
  }
  for (int i = 0; i < cell_x_.length(); ++i) {
    if (CountVerticalIntersections(cell_x_[i]) > 0)
      return false;
  }
  return true;
}

Definition at line 256 of file tablerecog.cpp.

                                             {
  for (int i = 0; i < column_count(); ++i) {
    double area_filled = CalculateCellFilledPercentage(row, i);
    if (area_filled >= kMinFilledArea)
      return true;
  }
  return false;
}

Definition at line 341 of file tablerecog.cpp.

                                             {
  // criteria for a table, must be at least 2x3 or 3x2
  return row_count() >= 2 && column_count() >= 2 && cell_count() >= 6;
}

Member Data Documentation

Definition at line 242 of file tablerecog.h.

Definition at line 243 of file tablerecog.h.

Definition at line 244 of file tablerecog.h.

Definition at line 245 of file tablerecog.h.

Definition at line 238 of file tablerecog.h.

Definition at line 254 of file tablerecog.h.

Definition at line 251 of file tablerecog.h.

Definition at line 252 of file tablerecog.h.

Definition at line 247 of file tablerecog.h.

Definition at line 248 of file tablerecog.h.

Definition at line 249 of file tablerecog.h.

Definition at line 250 of file tablerecog.h.

Definition at line 237 of file tablerecog.h.


The documentation for this class was generated from the following files:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines