tesseract  5.0.0-alpha-619-ge9db
tablerecog_test.cc
Go to the documentation of this file.
1 // (C) Copyright 2017, Google Inc.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 // http://www.apache.org/licenses/LICENSE-2.0
6 // Unless required by applicable law or agreed to in writing, software
7 // distributed under the License is distributed on an "AS IS" BASIS,
8 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 // See the License for the specific language governing permissions and
10 // limitations under the License.
11 
12 #include <memory>
13 
14 #include "colpartition.h"
15 #include "colpartitiongrid.h"
16 #include "tablerecog.h"
17 
18 #include "include_gunit.h"
19 
21 using tesseract::ColPartition_LIST;
23 
24 namespace {
25 
26 class TestableTableRecognizer : public tesseract::TableRecognizer {
27  public:
33 };
34 
35 class TestableStructuredTable : public tesseract::StructuredTable {
36  public:
43 
44  void InjectCellY(int y) {
45  cell_y_.push_back(y);
46  cell_y_.sort();
47  }
48  void InjectCellX(int x) {
49  cell_x_.push_back(x);
50  cell_x_.sort();
51  }
52 
53  void ExpectCellX(int x_min, int second, int add, int almost_done, int x_max) {
54  ASSERT_EQ(0, (almost_done - second) % add);
55  EXPECT_EQ(3 + (almost_done - second) / add, cell_x_.size());
56  EXPECT_EQ(x_min, cell_x_.get(0));
57  EXPECT_EQ(x_max, cell_x_.get(cell_x_.size() - 1));
58  for (int i = 1; i < cell_x_.size() - 1; ++i) {
59  EXPECT_EQ(second + add * (i - 1), cell_x_.get(i));
60  }
61  }
62 
63  void ExpectSortedX() {
64  EXPECT_GT(cell_x_.size(), 0);
65  for (int i = 1; i < cell_x_.size(); ++i) {
66  EXPECT_LT(cell_x_.get(i - 1), cell_x_.get(i));
67  }
68  }
69 };
70 
71 class SharedTest : public testing::Test {
72  protected:
73  void SetUp() {
74  std::locale::global(std::locale(""));
75  ICOORD bleft(0, 0);
76  ICOORD tright(1000, 1000);
77  text_grid_.reset(new ColPartitionGrid(5, bleft, tright));
78  line_grid_.reset(new ColPartitionGrid(5, bleft, tright));
79  }
80 
81  void TearDown() {
82  tesseract::ColPartition_IT memory(&allocated_parts_);
83  for (memory.mark_cycle_pt(); !memory.cycled_list(); memory.forward()) {
84  memory.data()->DeleteBoxes();
85  }
86  }
87 
88  void InsertPartitions() {
89  for (int row = 0; row < 800; row += 20)
90  for (int col = 0; col < 500; col += 25)
91  InsertPartition(col + 1, row + 1, col + 24, row + 19);
92  }
93 
94  void InsertPartition(int left, int bottom, int right, int top) {
95  TBOX box(left, bottom, right, top);
96  ColPartition* part =
97  ColPartition::FakePartition(box, PT_FLOWING_TEXT, BRT_TEXT, BTFT_NONE);
98  part->set_median_width(3);
99  part->set_median_height(3);
100  text_grid_->InsertBBox(true, true, part);
101 
102  tesseract::ColPartition_IT add_it(&allocated_parts_);
103  add_it.add_after_stay_put(part);
104  }
105 
106  void InsertLines() {
107  line_box_.set_to_given_coords(
108  100 - line_grid_->gridsize(), 10 - line_grid_->gridsize(),
109  450 + line_grid_->gridsize(), 50 + line_grid_->gridsize());
110  for (int i = 10; i <= 50; i += 10) InsertHorizontalLine(100, 450, i);
111  for (int i = 100; i <= 450; i += 50) InsertVerticalLine(i, 10, 50);
112 
113  for (int i = 100; i <= 200; i += 20) InsertHorizontalLine(0, 100, i);
114  }
115 
116  void InsertHorizontalLine(int left, int right, int y) {
117  TBOX box(left, y - line_grid_->gridsize(), right,
118  y + line_grid_->gridsize());
119  ColPartition* part =
120  ColPartition::FakePartition(box, PT_HORZ_LINE, BRT_HLINE, BTFT_NONE);
121  line_grid_->InsertBBox(true, true, part);
122 
123  tesseract::ColPartition_IT add_it(&allocated_parts_);
124  add_it.add_after_stay_put(part);
125  }
126  void InsertVerticalLine(int x, int bottom, int top) {
127  TBOX box(x - line_grid_->gridsize(), bottom, x + line_grid_->gridsize(),
128  top);
129  ColPartition* part =
130  ColPartition::FakePartition(box, PT_VERT_LINE, BRT_VLINE, BTFT_NONE);
131  line_grid_->InsertBBox(true, true, part);
132 
133  tesseract::ColPartition_IT add_it(&allocated_parts_);
134  add_it.add_after_stay_put(part);
135  }
136 
137  void InsertCellsInLines() {
138  for (int y = 10; y <= 50; y += 10)
139  for (int x = 100; x <= 450; x += 50)
140  InsertPartition(x + 1, y + 1, x + 49, y + 9);
141  }
142 
143  TBOX line_box_;
144  std::unique_ptr<ColPartitionGrid> text_grid_;
145  std::unique_ptr<ColPartitionGrid> line_grid_;
146  ColPartition_LIST allocated_parts_;
147 };
148 
149 class TableRecognizerTest : public SharedTest {
150  protected:
151  void SetUp() {
152  SharedTest::SetUp();
153  recognizer_.reset(new TestableTableRecognizer());
154  recognizer_->Init();
155  recognizer_->set_text_grid(text_grid_.get());
156  recognizer_->set_line_grid(line_grid_.get());
157  }
158 
159  std::unique_ptr<TestableTableRecognizer> recognizer_;
160 };
161 
162 class StructuredTableTest : public SharedTest {
163  protected:
164  void SetUp() {
165  SharedTest::SetUp();
166  table_.reset(new TestableStructuredTable());
167  table_->Init();
168  table_->set_text_grid(text_grid_.get());
169  table_->set_line_grid(line_grid_.get());
170  }
171 
172  std::unique_ptr<TestableStructuredTable> table_;
173 };
174 
175 TEST_F(TableRecognizerTest, HasSignificantLinesBasicPass) {
176  InsertLines();
177  TBOX smaller_guess(120, 15, 370, 45);
178  TBOX larger_guess(90, 5, 490, 70);
179  EXPECT_TRUE(recognizer_->HasSignificantLines(line_box_));
180  EXPECT_TRUE(recognizer_->HasSignificantLines(larger_guess));
181  EXPECT_TRUE(recognizer_->HasSignificantLines(smaller_guess));
182 }
183 
184 TEST_F(TableRecognizerTest, HasSignificantLinesBasicFail) {
185  InsertLines();
186  TBOX box(370, 35, 500, 45);
187  EXPECT_FALSE(recognizer_->HasSignificantLines(box));
188 }
189 
190 TEST_F(TableRecognizerTest, HasSignificantLinesHorizontalOnlyFails) {
191  InsertLines();
192  TBOX box(0, 100, 200, 200);
193  EXPECT_FALSE(recognizer_->HasSignificantLines(box));
194 }
195 
196 TEST_F(TableRecognizerTest, FindLinesBoundingBoxBasic) {
197  InsertLines();
198  TBOX box(0, 0, 200, 50);
199  bool result = recognizer_->FindLinesBoundingBox(&box);
200  EXPECT_TRUE(result);
201  EXPECT_EQ(line_box_.left(), box.left());
202  EXPECT_EQ(line_box_.right(), box.right());
203  EXPECT_EQ(line_box_.bottom(), box.bottom());
204  EXPECT_EQ(line_box_.top(), box.top());
205 }
206 
207 TEST_F(TableRecognizerTest, RecognizeLinedTableBasic) {
208  InsertLines();
209  TBOX guess(120, 15, 370, 45);
211  table.set_text_grid(text_grid_.get());
212  table.set_line_grid(line_grid_.get());
213 
214  EXPECT_TRUE(recognizer_->RecognizeLinedTable(guess, &table));
215  EXPECT_EQ(line_box_.bottom(), table.bounding_box().bottom());
216  EXPECT_EQ(line_box_.top(), table.bounding_box().top());
217  EXPECT_EQ(line_box_.left(), table.bounding_box().left());
218  EXPECT_EQ(line_box_.right(), table.bounding_box().right());
219  EXPECT_EQ(line_box_.area(), table.bounding_box().area());
220  EXPECT_EQ(7, table.column_count());
221  EXPECT_EQ(4, table.row_count());
222  EXPECT_EQ(28, table.cell_count());
223  EXPECT_TRUE(table.is_lined());
224 }
225 
226 TEST_F(TableRecognizerTest, RecognizeWhitespacedTableBasic) {
227  InsertPartitions();
228  TBOX guess(0, 0, 500, 800);
229 
231  table.set_text_grid(text_grid_.get());
232  table.set_line_grid(line_grid_.get());
233  EXPECT_TRUE(recognizer_->RecognizeWhitespacedTable(guess, &table));
234  EXPECT_EQ(1, table.bounding_box().bottom());
235  EXPECT_EQ(799, table.bounding_box().top());
236  EXPECT_EQ(1, table.bounding_box().left());
237  EXPECT_EQ(499, table.bounding_box().right());
238  EXPECT_EQ(798 * 498, table.bounding_box().area());
239  EXPECT_EQ(500 / 25, table.column_count());
240  EXPECT_EQ(800 / 20, table.row_count());
241  EXPECT_EQ(500 * 800 / 20 / 25, table.cell_count());
242  EXPECT_FALSE(table.is_lined());
243 }
244 
245 TEST_F(StructuredTableTest, CountVerticalIntersectionsAll) {
246  table_->set_bounding_box(TBOX(0, 0, 1000, 1000));
247  InsertPartition(0, 0, 100, 10);
248  InsertPartition(1, 12, 43, 21);
249  EXPECT_EQ(2, table_->CountVerticalIntersections(4));
250  EXPECT_EQ(2, table_->CountVerticalIntersections(20));
251  EXPECT_EQ(2, table_->CountVerticalIntersections(40));
252  EXPECT_EQ(1, table_->CountVerticalIntersections(50));
253  EXPECT_EQ(1, table_->CountVerticalIntersections(60));
254  EXPECT_EQ(1, table_->CountVerticalIntersections(80));
255  EXPECT_EQ(1, table_->CountVerticalIntersections(95));
256  EXPECT_EQ(0, table_->CountVerticalIntersections(104));
257  EXPECT_EQ(0, table_->CountVerticalIntersections(150));
258 }
259 
260 TEST_F(StructuredTableTest, CountHorizontalIntersectionsAll) {
261  table_->set_bounding_box(TBOX(0, 0, 1000, 1000));
262  InsertPartition(0, 3, 100, 10);
263  InsertPartition(110, 5, 200, 16);
264 
265  EXPECT_EQ(0, table_->CountHorizontalIntersections(0));
266  EXPECT_EQ(1, table_->CountHorizontalIntersections(4));
267  EXPECT_EQ(2, table_->CountHorizontalIntersections(8));
268  EXPECT_EQ(1, table_->CountHorizontalIntersections(12));
269  EXPECT_EQ(0, table_->CountHorizontalIntersections(20));
270 }
271 
272 TEST_F(StructuredTableTest, VerifyLinedTableBasicPass) {
273  for (int y = 10; y <= 50; y += 10) table_->InjectCellY(y);
274  for (int x = 100; x <= 450; x += 50) table_->InjectCellX(x);
275  InsertLines();
276  InsertCellsInLines();
277  table_->set_bounding_box(line_box_);
278  EXPECT_TRUE(table_->VerifyLinedTableCells());
279 }
280 
281 TEST_F(StructuredTableTest, VerifyLinedTableHorizontalFail) {
282  for (int y = 10; y <= 50; y += 10) table_->InjectCellY(y);
283  for (int x = 100; x <= 450; x += 50) table_->InjectCellX(x);
284  InsertLines();
285  InsertCellsInLines();
286  InsertPartition(101, 11, 299, 19);
287  table_->set_bounding_box(line_box_);
288  EXPECT_FALSE(table_->VerifyLinedTableCells());
289 }
290 
291 TEST_F(StructuredTableTest, VerifyLinedTableVerticalFail) {
292  for (int y = 10; y <= 50; y += 10) table_->InjectCellY(y);
293  for (int x = 100; x <= 450; x += 50) table_->InjectCellX(x);
294  InsertLines();
295  InsertCellsInLines();
296  InsertPartition(151, 21, 199, 39);
297  table_->set_bounding_box(line_box_);
298  EXPECT_FALSE(table_->VerifyLinedTableCells());
299 }
300 
301 TEST_F(StructuredTableTest, FindWhitespacedColumnsBasic) {
302  InsertPartitions();
303  TBOX guess(0, 0, 500, 800);
304  table_->set_bounding_box(guess);
305  table_->FindWhitespacedColumns();
306  table_->ExpectCellX(1, 25, 25, 475, 499);
307 }
308 
309 TEST_F(StructuredTableTest, FindWhitespacedColumnsSorted) {
310  InsertPartitions();
311  TBOX guess(0, 0, 500, 800);
312  table_->set_bounding_box(guess);
313  table_->FindWhitespacedColumns();
314  table_->ExpectSortedX();
315 }
316 
317 // TODO(nbeato): check failure cases
318 // TODO(nbeato): check Recognize processes correctly on trivial real examples.
319 
320 } // namespace
TBOX
Definition: cleanapi_test.cc:19
BTFT_NONE
Definition: blobbox.h:114
tesseract::StructuredTable::FindWhitespacedStructure
bool FindWhitespacedStructure()
Definition: tablerecog.cpp:187
tesseract::StructuredTable::FindLinedStructure
bool FindLinedStructure()
Definition: tablerecog.cpp:140
tesseract::TableRecognizer
Definition: tablerecog.h:257
tesseract::StructuredTable::cell_count
int cell_count() const
Definition: tablerecog.cpp:103
tesseract::ColPartition::set_median_height
void set_median_height(int height)
Definition: colpartition.h:139
ICOORD
integer coordinate
Definition: points.h:30
TBOX::top
int16_t top() const
Definition: rect.h:57
TBOX::area
int32_t area() const
Definition: rect.h:121
colpartition.h
include_gunit.h
tesseract::TEST_F
TEST_F(EquationFinderTest, IdentifySpecialText)
Definition: equationdetect_test.cc:181
tesseract::StructuredTable::set_text_grid
void set_text_grid(ColPartitionGrid *text)
Definition: tablerecog.cpp:85
BRT_HLINE
Definition: blobbox.h:73
tesseract::StructuredTable::row_count
int row_count() const
Definition: tablerecog.cpp:97
tesseract::ColPartition
Definition: colpartition.h:67
tesseract::StructuredTable::CountVerticalIntersections
int CountVerticalIntersections(int x)
Definition: tablerecog.cpp:638
BRT_TEXT
Definition: blobbox.h:79
tesseract::TableRecognizer::FindLinesBoundingBox
bool FindLinesBoundingBox(TBOX *bounding_box)
Definition: tablerecog.cpp:814
TBOX::bottom
int16_t bottom() const
Definition: rect.h:64
tesseract::TableRecognizer::RecognizeTable
StructuredTable * RecognizeTable(const TBOX &guess_box)
Definition: tablerecog.cpp:735
PT_VERT_LINE
Definition: capi.h:121
tesseract::StructuredTable::FindWhitespacedColumns
void FindWhitespacedColumns()
Definition: tablerecog.cpp:354
tesseract::StructuredTable::column_count
int column_count() const
Definition: tablerecog.cpp:100
tesseract::StructuredTable::set_line_grid
void set_line_grid(ColPartitionGrid *lines)
Definition: tablerecog.cpp:88
tesseract::StructuredTable::VerifyLinedTableCells
bool VerifyLinedTableCells()
Definition: tablerecog.cpp:322
tesseract::StructuredTable
Definition: tablerecog.h:72
tesseract::TableRecognizer::HasSignificantLines
bool HasSignificantLines(const TBOX &guess)
Definition: tablerecog.cpp:775
BRT_VLINE
Definition: blobbox.h:74
tesseract::StructuredTable::is_lined
bool is_lined() const
Definition: tablerecog.cpp:94
tesseract::StructuredTable::CountHorizontalIntersections
int CountHorizontalIntersections(int y)
Definition: tablerecog.cpp:662
TBOX::left
int16_t left() const
Definition: rect.h:71
tesseract::ColPartitionGrid
Definition: colpartitiongrid.h:32
PT_FLOWING_TEXT
Definition: capi.h:109
TBOX::right
int16_t right() const
Definition: rect.h:78
tesseract::TableRecognizer::RecognizeWhitespacedTable
bool RecognizeWhitespacedTable(const TBOX &guess_box, StructuredTable *table)
Definition: tablerecog.cpp:874
tablerecog.h
PT_HORZ_LINE
Definition: capi.h:120
colpartitiongrid.h
tesseract::StructuredTable::bounding_box
const TBOX & bounding_box() const
Definition: tablerecog.cpp:109
tesseract::ColPartition::set_median_width
void set_median_width(int width)
Definition: colpartition.h:145
TBOX
Definition: rect.h:33
tesseract::TableRecognizer::RecognizeLinedTable
bool RecognizeLinedTable(const TBOX &guess_box, StructuredTable *table)
Definition: tablerecog.cpp:758