/src/tesseract/src/textord/tabfind.h
Line | Count | Source (jump to first uncovered line) |
1 | | /////////////////////////////////////////////////////////////////////// |
2 | | // File: tabfind.h |
3 | | // Description: Subclass of BBGrid to find tabstops. |
4 | | // Author: Ray Smith |
5 | | // |
6 | | // (C) Copyright 2008, Google Inc. |
7 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | // you may not use this file except in compliance with the License. |
9 | | // You may obtain a copy of the License at |
10 | | // http://www.apache.org/licenses/LICENSE-2.0 |
11 | | // Unless required by applicable law or agreed to in writing, software |
12 | | // distributed under the License is distributed on an "AS IS" BASIS, |
13 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | // See the License for the specific language governing permissions and |
15 | | // limitations under the License. |
16 | | // |
17 | | /////////////////////////////////////////////////////////////////////// |
18 | | |
19 | | #ifndef TESSERACT_TEXTORD_TABFIND_H_ |
20 | | #define TESSERACT_TEXTORD_TABFIND_H_ |
21 | | |
22 | | #include <functional> // for std::function |
23 | | #include "alignedblob.h" |
24 | | #include "linefind.h" |
25 | | #include "tabvector.h" |
26 | | |
27 | | class BLOBNBOX; |
28 | | class BLOBNBOX_LIST; |
29 | | class TO_BLOCK; |
30 | | class ScrollView; |
31 | | struct Pix; |
32 | | |
33 | | namespace tesseract { |
34 | | |
35 | | using WidthCallback = std::function<bool(int)>; |
36 | | |
37 | | struct AlignedBlobParams; |
38 | | class ColPartitionGrid; |
39 | | |
40 | | /** Pixel resolution of column width estimates. */ |
41 | | const int kColumnWidthFactor = 20; |
42 | | |
43 | | /** |
44 | | * The TabFind class contains code to find tab-stops and maintain the |
45 | | * vectors_ list of tab vectors. |
46 | | * Also provides an interface to find neighbouring blobs |
47 | | * in the grid of BLOBNBOXes that is used by multiple subclasses. |
48 | | * Searching is a complex operation because of the need to enforce |
49 | | * rule/separator lines, and tabstop boundaries, (when available), so |
50 | | * as the holder of the list of TabVectors this class provides the functions. |
51 | | */ |
52 | | class TESS_API TabFind : public AlignedBlob { |
53 | | public: |
54 | | TabFind(int gridsize, const ICOORD &bleft, const ICOORD &tright, TabVector_LIST *vlines, |
55 | | int vertical_x, int vertical_y, int resolution); |
56 | | ~TabFind() override; |
57 | | |
58 | | /** |
59 | | * Insert a list of blobs into the given grid (not necessarily this). |
60 | | * See InsertBlob for the other arguments. |
61 | | * It would seem to make more sense to swap this and grid, but this way |
62 | | * around allows grid to not be derived from TabFind, eg a ColPartitionGrid, |
63 | | * while the grid that provides the tab stops(this) has to be derived from |
64 | | * TabFind. |
65 | | */ |
66 | | void InsertBlobsToGrid(bool h_spread, bool v_spread, BLOBNBOX_LIST *blobs, |
67 | | BBGrid<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> *grid); |
68 | | |
69 | | /** |
70 | | * Insert a single blob into the given grid (not necessarily this). |
71 | | * If h_spread, then all cells covered horizontally by the box are |
72 | | * used, otherwise, just the bottom-left. Similarly for v_spread. |
73 | | * A side effect is that the left and right rule edges of the blob are |
74 | | * set according to the tab vectors in this (not grid). |
75 | | */ |
76 | | bool InsertBlob(bool h_spread, bool v_spread, BLOBNBOX *blob, |
77 | | BBGrid<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> *grid); |
78 | | // Calls SetBlobRuleEdges for all the blobs in the given block. |
79 | | void SetBlockRuleEdges(TO_BLOCK *block); |
80 | | // Sets the left and right rule and crossing_rules for the blobs in the given |
81 | | // list by finding the next outermost tabvectors for each blob. |
82 | | void SetBlobRuleEdges(BLOBNBOX_LIST *blobs); |
83 | | |
84 | | // Returns the gutter width of the given TabVector between the given y limits. |
85 | | // Also returns x-shift to be added to the vector to clear any intersecting |
86 | | // blobs. The shift is deducted from the returned gutter. |
87 | | // If ignore_unmergeables is true, then blobs of UnMergeableType are |
88 | | // ignored as if they don't exist. (Used for text on image.) |
89 | | // max_gutter_width is used as the maximum width worth searching for in case |
90 | | // there is nothing near the TabVector. |
91 | | int GutterWidth(int bottom_y, int top_y, const TabVector &v, bool ignore_unmergeables, |
92 | | int max_gutter_width, int *required_shift); |
93 | | /** |
94 | | * Find the gutter width and distance to inner neighbour for the given blob. |
95 | | */ |
96 | | void GutterWidthAndNeighbourGap(int tab_x, int mean_height, int max_gutter, bool left, |
97 | | BLOBNBOX *bbox, int *gutter_width, int *neighbour_gap); |
98 | | |
99 | | /** |
100 | | * Return the x-coord that corresponds to the right edge for the given |
101 | | * box. If there is a rule line to the right that vertically overlaps it, |
102 | | * then return the x-coord of the rule line, otherwise return the right |
103 | | * edge of the page. For details see RightTabForBox below. |
104 | | */ |
105 | | int RightEdgeForBox(const TBOX &box, bool crossing, bool extended); |
106 | | /** |
107 | | * As RightEdgeForBox, but finds the left Edge instead. |
108 | | */ |
109 | | int LeftEdgeForBox(const TBOX &box, bool crossing, bool extended); |
110 | | |
111 | | /** |
112 | | * Return the TabVector that corresponds to the right edge for the given |
113 | | * box. If there is a TabVector to the right that vertically overlaps it, |
114 | | * then return it, otherwise return nullptr. Note that Right and Left refer |
115 | | * to the position of the TabVector, not its type, ie RightTabForBox |
116 | | * returns the nearest TabVector to the right of the box, regardless of |
117 | | * its type. |
118 | | * If a TabVector crosses right through the box (as opposed to grazing one |
119 | | * edge or missing entirely), then crossing false will ignore such a line. |
120 | | * Crossing true will return the line for BOTH left and right edges. |
121 | | * If extended is true, then TabVectors are considered to extend to their |
122 | | * extended_start/end_y, otherwise, just the startpt_ and endpt_. |
123 | | * These functions make use of an internal iterator to the vectors_ list |
124 | | * for speed when used repeatedly on neighbouring boxes. The caveat is |
125 | | * that the iterator must be updated whenever the list is modified. |
126 | | */ |
127 | | TabVector *RightTabForBox(const TBOX &box, bool crossing, bool extended); |
128 | | /** |
129 | | * As RightTabForBox, but finds the left TabVector instead. |
130 | | */ |
131 | | TabVector *LeftTabForBox(const TBOX &box, bool crossing, bool extended); |
132 | | |
133 | | /** |
134 | | * Return true if the given width is close to one of the common |
135 | | * widths in column_widths_. |
136 | | */ |
137 | | bool CommonWidth(int width); |
138 | | /** |
139 | | * Return true if the sizes are more than a |
140 | | * factor of 2 different. |
141 | | */ |
142 | | static bool DifferentSizes(int size1, int size2); |
143 | | /** |
144 | | * Return true if the sizes are more than a |
145 | | * factor of 5 different. |
146 | | */ |
147 | | static bool VeryDifferentSizes(int size1, int size2); |
148 | | |
149 | | /** |
150 | | * Return a callback for testing CommonWidth. |
151 | | */ |
152 | 0 | WidthCallback WidthCB() { |
153 | 0 | return width_cb_; |
154 | 0 | } |
155 | | |
156 | | /** |
157 | | * Return the coords at which to draw the image backdrop. |
158 | | */ |
159 | 0 | const ICOORD &image_origin() const { |
160 | 0 | return image_origin_; |
161 | 0 | } |
162 | | |
163 | | protected: |
164 | | /** |
165 | | // Accessors |
166 | | */ |
167 | 0 | TabVector_LIST *vectors() { |
168 | 0 | return &vectors_; |
169 | 0 | } |
170 | 0 | TabVector_LIST *dead_vectors() { |
171 | 0 | return &dead_vectors_; |
172 | 0 | } |
173 | | |
174 | | /** |
175 | | * Top-level function to find TabVectors in an input page block. |
176 | | * Returns false if the detected skew angle is impossible. |
177 | | * Applies the detected skew angle to deskew the tabs, blobs and part_grid. |
178 | | * tabfind_aligned_gap_fraction should be the value of parameter |
179 | | * textord_tabfind_aligned_gap_fraction |
180 | | */ |
181 | | bool FindTabVectors(TabVector_LIST *hlines, BLOBNBOX_LIST *image_blobs, TO_BLOCK *block, |
182 | | int min_gutter_width, double tabfind_aligned_gap_fraction, |
183 | | ColPartitionGrid *part_grid, FCOORD *deskew, FCOORD *reskew); |
184 | | |
185 | | // Top-level function to not find TabVectors in an input page block, |
186 | | // but setup for single column mode. |
187 | | void DontFindTabVectors(BLOBNBOX_LIST *image_blobs, TO_BLOCK *block, FCOORD *deskew, |
188 | | FCOORD *reskew); |
189 | | |
190 | | // Cleans up the lists of blobs in the block ready for use by TabFind. |
191 | | // Large blobs that look like text are moved to the main blobs list. |
192 | | // Main blobs that are superseded by the image blobs are deleted. |
193 | | void TidyBlobs(TO_BLOCK *block); |
194 | | |
195 | | // Helper function to setup search limits for *TabForBox. |
196 | | void SetupTabSearch(int x, int y, int *min_key, int *max_key); |
197 | | |
198 | | /** |
199 | | * Display the tab vectors found in this grid. |
200 | | */ |
201 | | ScrollView *DisplayTabVectors(ScrollView *tab_win); |
202 | | |
203 | | // First part of FindTabVectors, which may be used twice if the text |
204 | | // is mostly of vertical alignment. If find_vertical_text flag is |
205 | | // true, this finds vertical textlines in possibly rotated blob space. |
206 | | // In other words, when the page has mostly vertical lines and is rotated, |
207 | | // setting this to true will find horizontal lines on the page. |
208 | | // tabfind_aligned_gap_fraction should be the value of parameter |
209 | | // textord_tabfind_aligned_gap_fraction |
210 | | ScrollView *FindInitialTabVectors(BLOBNBOX_LIST *image_blobs, int min_gutter_width, |
211 | | double tabfind_aligned_gap_fraction, TO_BLOCK *block); |
212 | | |
213 | | // Apply the given rotation to the given list of blobs. |
214 | | static void RotateBlobList(const FCOORD &rotation, BLOBNBOX_LIST *blobs); |
215 | | |
216 | | // Flip the vertical and horizontal lines and rotate the grid ready |
217 | | // for working on the rotated image. |
218 | | // The min_gutter_width will be adjusted to the median gutter width between |
219 | | // vertical tabs to set a better threshold for tabboxes in the 2nd pass. |
220 | | void ResetForVerticalText(const FCOORD &rotate, const FCOORD &rerotate, |
221 | | TabVector_LIST *horizontal_lines, int *min_gutter_width); |
222 | | |
223 | | // Clear the grid and get rid of the tab vectors, but not separators, |
224 | | // ready to start again. |
225 | | void Reset(); |
226 | | |
227 | | // Reflect the separator tab vectors and the grids in the y-axis. |
228 | | // Can only be called after Reset! |
229 | | void ReflectInYAxis(); |
230 | | |
231 | | private: |
232 | | // For each box in the grid, decide whether it is a candidate tab-stop, |
233 | | // and if so add it to the left and right tab boxes. |
234 | | // tabfind_aligned_gap_fraction should be the value of parameter |
235 | | // textord_tabfind_aligned_gap_fraction |
236 | | ScrollView *FindTabBoxes(int min_gutter_width, double tabfind_aligned_gap_fraction); |
237 | | |
238 | | // Return true if this box looks like a candidate tab stop, and set |
239 | | // the appropriate tab type(s) to TT_UNCONFIRMED. |
240 | | // tabfind_aligned_gap_fraction should be the value of parameter |
241 | | // textord_tabfind_aligned_gap_fraction |
242 | | bool TestBoxForTabs(BLOBNBOX *bbox, int min_gutter_width, double tabfind_aligned_gap_fraction); |
243 | | |
244 | | // Returns true if there is nothing in the rectangle of width min_gutter to |
245 | | // the left of bbox. |
246 | | bool ConfirmRaggedLeft(BLOBNBOX *bbox, int min_gutter); |
247 | | // Returns true if there is nothing in the rectangle of width min_gutter to |
248 | | // the right of bbox. |
249 | | bool ConfirmRaggedRight(BLOBNBOX *bbox, int min_gutter); |
250 | | // Returns true if there is nothing in the given search_box that vertically |
251 | | // overlaps target_box other than target_box itself. |
252 | | bool NothingYOverlapsInBox(const TBOX &search_box, const TBOX &target_box); |
253 | | |
254 | | // Fills the list of TabVector with the tabstops found in the grid, |
255 | | // and estimates the logical vertical direction. |
256 | | void FindAllTabVectors(int min_gutter_width); |
257 | | // Helper for FindAllTabVectors finds the vectors of a particular type. |
258 | | int FindTabVectors(int search_size_multiple, TabAlignment alignment, int min_gutter_width, |
259 | | TabVector_LIST *vectors, int *vertical_x, int *vertical_y); |
260 | | // Finds a vector corresponding to a tabstop running through the |
261 | | // given box of the given alignment type. |
262 | | // search_size_multiple is a multiple of height used to control |
263 | | // the size of the search. |
264 | | // vertical_x and y are updated with an estimate of the real |
265 | | // vertical direction. (skew finding.) |
266 | | // Returns nullptr if no decent tabstop can be found. |
267 | | TabVector *FindTabVector(int search_size_multiple, int min_gutter_width, TabAlignment alignment, |
268 | | BLOBNBOX *bbox, int *vertical_x, int *vertical_y); |
269 | | |
270 | | // Set the vertical_skew_ member from the given vector and refit |
271 | | // all vectors parallel to the skew vector. |
272 | | void SetVerticalSkewAndParallelize(int vertical_x, int vertical_y); |
273 | | |
274 | | // Sort all the current vectors using the vertical_skew_ vector. |
275 | | void SortVectors(); |
276 | | |
277 | | // Evaluate all the current tab vectors. |
278 | | void EvaluateTabs(); |
279 | | |
280 | | // Trace textlines from one side to the other of each tab vector, saving |
281 | | // the most frequent column widths found in a list so that a given width |
282 | | // can be tested for being a common width with a simple callback function. |
283 | | void ComputeColumnWidths(ScrollView *tab_win, ColPartitionGrid *part_grid); |
284 | | |
285 | | // Finds column width and: |
286 | | // if col_widths is not null (pass1): |
287 | | // pair-up tab vectors with existing ColPartitions and accumulate widths. |
288 | | // else (pass2): |
289 | | // find the largest real partition width for each recorded column width, |
290 | | // to be used as the minimum acceptable width. |
291 | | void ApplyPartitionsToColumnWidths(ColPartitionGrid *part_grid, STATS *col_widths); |
292 | | |
293 | | // Helper makes the list of common column widths in column_widths_ from the |
294 | | // input col_widths. Destroys the content of col_widths by repeatedly |
295 | | // finding the mode and erasing the peak. |
296 | | void MakeColumnWidths(int col_widths_size, STATS *col_widths); |
297 | | |
298 | | // Mark blobs as being in a vertical text line where that is the case. |
299 | | void MarkVerticalText(); |
300 | | |
301 | | // Returns the median gutter width between pairs of matching tab vectors |
302 | | // assuming they are sorted left-to-right. If there are too few data |
303 | | // points (< kMinLinesInColumn), then 0 is returned. |
304 | | int FindMedianGutterWidth(TabVector_LIST *tab_vectors); |
305 | | |
306 | | // Find the next adjacent (to left or right) blob on this text line, |
307 | | // with the constraint that it must vertically significantly overlap |
308 | | // the [top_y, bottom_y] range. |
309 | | // If ignore_images is true, then blobs with aligned_text() < 0 are treated |
310 | | // as if they do not exist. |
311 | | BLOBNBOX *AdjacentBlob(const BLOBNBOX *bbox, bool look_left, bool ignore_images, |
312 | | double min_overlap_fraction, int gap_limit, int top_y, int bottom_y); |
313 | | |
314 | | // Add a bi-directional partner relationship between the left |
315 | | // and the right. If one (or both) of the vectors is a separator, |
316 | | // extend a nearby extendable vector or create a new one of the |
317 | | // correct type, using the given left or right blob as a guide. |
318 | | void AddPartnerVector(BLOBNBOX *left_blob, BLOBNBOX *right_blob, TabVector *left, |
319 | | TabVector *right); |
320 | | |
321 | | /** |
322 | | * Remove separators and unused tabs from the main vectors_ list |
323 | | * to the dead_vectors_ list. |
324 | | */ |
325 | | void CleanupTabs(); |
326 | | |
327 | | /** |
328 | | * Deskew the tab vectors and blobs, computing the rotation and resetting |
329 | | * the storked vertical_skew_. The deskew inverse is returned in reskew. |
330 | | * Returns false if the detected skew angle is impossible. |
331 | | */ |
332 | | bool Deskew(TabVector_LIST *hlines, BLOBNBOX_LIST *image_blobs, TO_BLOCK *block, FCOORD *deskew, |
333 | | FCOORD *reskew); |
334 | | |
335 | | // Compute the rotation required to deskew, and its inverse rotation. |
336 | | void ComputeDeskewVectors(FCOORD *deskew, FCOORD *reskew); |
337 | | |
338 | | /** |
339 | | * Compute and apply constraints to the end positions of TabVectors so |
340 | | * that where possible partners end at the same y coordinate. |
341 | | */ |
342 | | void ApplyTabConstraints(); |
343 | | |
344 | | protected: |
345 | | ICOORD vertical_skew_; ///< Estimate of true vertical in this image. |
346 | | int resolution_; ///< Of source image in pixels per inch. |
347 | | private: |
348 | | ICOORD image_origin_; ///< Top-left of image in deskewed coords |
349 | | TabVector_LIST vectors_; ///< List of rule line and tabstops. |
350 | | TabVector_IT v_it_; ///< Iterator for searching vectors_. |
351 | | TabVector_LIST dead_vectors_; ///< Separators and unpartnered tab vectors. |
352 | | // List of commonly occurring width ranges with x=min and y=max. |
353 | | ICOORDELT_LIST column_widths_; ///< List of commonly occurring width ranges. |
354 | | /** Callback to test an int for being a common width. */ |
355 | | WidthCallback width_cb_; |
356 | | // Sets of bounding boxes that are candidate tab stops. |
357 | | std::vector<BLOBNBOX *> left_tab_boxes_; |
358 | | std::vector<BLOBNBOX *> right_tab_boxes_; |
359 | | }; |
360 | | |
361 | | } // namespace tesseract. |
362 | | |
363 | | #endif // TESSERACT_TEXTORD_TABFIND_H_ |