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    */
WidthCB()152   WidthCallback WidthCB() {
153     return width_cb_;
154   }
155 
156   /**
157    * Return the coords at which to draw the image backdrop.
158    */
image_origin()159   const ICOORD &image_origin() const {
160     return image_origin_;
161   }
162 
163 protected:
164   /**
165 // Accessors
166  */
vectors()167   TabVector_LIST *vectors() {
168     return &vectors_;
169   }
dead_vectors()170   TabVector_LIST *dead_vectors() {
171     return &dead_vectors_;
172   }
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_
364