tesseract  4.0.0-1-g2a2b
alignedblob.cpp
Go to the documentation of this file.
1 // File: alignedblob.cpp
3 // Description: Subclass of BBGrid to find vertically aligned blobs.
4 // Author: Ray Smith
5 // Created: Fri Mar 21 15:03:01 PST 2008
6 //
7 // (C) Copyright 2008, Google Inc.
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config_auto.h"
22 #endif
23 
24 #include "alignedblob.h"
25 
26 #include <algorithm>
27 
28 INT_VAR(textord_debug_tabfind, 0, "Debug tab finding");
29 INT_VAR(textord_debug_bugs, 0, "Turn on output related to bugs in tab finding");
30 INT_VAR(textord_testregion_left, -1, "Left edge of debug reporting rectangle");
31 INT_VAR(textord_testregion_top, -1, "Top edge of debug reporting rectangle");
32 INT_VAR(textord_testregion_right, INT32_MAX, "Right edge of debug rectangle");
33 INT_VAR(textord_testregion_bottom, INT32_MAX, "Bottom edge of debug rectangle");
34 BOOL_VAR(textord_debug_printable, false, "Make debug windows printable");
35 
36 namespace tesseract {
37 
38 // Fraction of resolution used as alignment tolerance for aligned tabs.
39 const double kAlignedFraction = 0.03125;
40 // Fraction of resolution used as alignment tolerance for ragged tabs.
41 const double kRaggedFraction = 2.5;
42 // Fraction of height used as a minimum gutter gap for aligned blobs.
43 const double kAlignedGapFraction = 0.75;
44 // Fraction of height used as a minimum gutter gap for ragged tabs.
45 const double kRaggedGapFraction = 1.0;
46 // Constant number of pixels used as alignment tolerance for line finding.
47 const int kVLineAlignment = 3;
48 // Constant number of pixels used as gutter gap tolerance for line finding.
49 const int kVLineGutter = 1;
50 // Constant number of pixels used as the search size for line finding.
51 const int kVLineSearchSize = 150;
52 // Min number of points to accept for a ragged tab stop.
53 const int kMinRaggedTabs = 5;
54 // Min number of points to accept for an aligned tab stop.
55 const int kMinAlignedTabs = 4;
56 // Constant number of pixels minimum height of a vertical line.
57 const int kVLineMinLength = 500;
58 // Minimum gradient for a vertical tab vector. Used to prune away junk
59 // tab vectors with what would be a ridiculously large skew angle.
60 // Value corresponds to tan(90 - max allowed skew angle)
61 const double kMinTabGradient = 4.0;
62 // Tolerance to skew on top of current estimate of skew. Divide x or y length
63 // by kMaxSkewFactor to get the y or x skew distance.
64 // If the angle is small, the angle in degrees is roughly 60/kMaxSkewFactor.
65 const int kMaxSkewFactor = 15;
66 
67 // Constructor to set the parameters for finding aligned and ragged tabs.
68 // Vertical_x and vertical_y are the current estimates of the true vertical
69 // direction (up) in the image. Height is the height of the starter blob.
70 // v_gap_multiple is the multiple of height that will be used as a limit
71 // on vertical gap before giving up and calling the line ended.
72 // resolution is the original image resolution, and align0 indicates the
73 // type of tab stop to be found.
74 AlignedBlobParams::AlignedBlobParams(int vertical_x, int vertical_y,
75  int height, int v_gap_multiple,
76  int min_gutter_width,
77  int resolution, TabAlignment align0)
78  : right_tab(align0 == TA_RIGHT_RAGGED || align0 == TA_RIGHT_ALIGNED),
79  ragged(align0 == TA_LEFT_RAGGED || align0 == TA_RIGHT_RAGGED),
80  alignment(align0),
81  confirmed_type(TT_CONFIRMED),
82  min_length(0) {
83  // Set the tolerances according to the type of line sought.
84  // For tab search, these are based on the image resolution for most, or
85  // the height of the starting blob for the maximum vertical gap.
86  max_v_gap = height * v_gap_multiple;
87  if (ragged) {
88  // In the case of a ragged edge, we are much more generous with the
89  // inside alignment fraction, but also require a much bigger gutter.
91  if (alignment == TA_RIGHT_RAGGED) {
92  l_align_tolerance = static_cast<int>(resolution * kRaggedFraction + 0.5);
93  r_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
94  } else {
95  l_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
96  r_align_tolerance = static_cast<int>(resolution * kRaggedFraction + 0.5);
97  }
99  } else {
101  l_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
102  r_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
104  }
105  min_gutter = static_cast<int>(height * gutter_fraction + 0.5);
106  if (min_gutter < min_gutter_width)
107  min_gutter = min_gutter_width;
108  // Fit the vertical vector into an ICOORD, which is 16 bit.
109  set_vertical(vertical_x, vertical_y);
110 }
111 
112 // Constructor to set the parameters for finding vertical lines.
113 // Vertical_x and vertical_y are the current estimates of the true vertical
114 // direction (up) in the image. Width is the width of the starter blob.
115 AlignedBlobParams::AlignedBlobParams(int vertical_x, int vertical_y,
116  int width)
117  : gutter_fraction(0.0),
118  right_tab(false),
119  ragged(false),
120  alignment(TA_SEPARATOR),
121  confirmed_type(TT_VLINE),
122  max_v_gap(kVLineSearchSize),
123  min_gutter(kVLineGutter),
124  min_points(1),
125  min_length(kVLineMinLength) {
126  // Compute threshold for left and right alignment.
127  l_align_tolerance = std::max(kVLineAlignment, width);
128  r_align_tolerance = std::max(kVLineAlignment, width);
129 
130  // Fit the vertical vector into an ICOORD, which is 16 bit.
131  set_vertical(vertical_x, vertical_y);
132 }
133 
134 // Fit the vertical vector into an ICOORD, which is 16 bit.
135 void AlignedBlobParams::set_vertical(int vertical_x, int vertical_y) {
136  int factor = 1;
137  if (vertical_y > INT16_MAX)
138  factor = vertical_y / INT16_MAX + 1;
139  vertical.set_x(vertical_x / factor);
140  vertical.set_y(vertical_y / factor);
141 }
142 
143 
145  const ICOORD& bleft, const ICOORD& tright)
146  : BlobGrid(gridsize, bleft, tright) {
147 }
148 
149 // Return true if the given coordinates are within the test rectangle
150 // and the debug level is at least the given detail level.
151 bool AlignedBlob::WithinTestRegion(int detail_level, int x, int y) {
152  if (textord_debug_tabfind < detail_level)
153  return false;
155  y <= textord_testregion_top && y >= textord_testregion_bottom;
156 }
157 
158 // Display the tab codes of the BLOBNBOXes in this grid.
159 ScrollView* AlignedBlob::DisplayTabs(const char* window_name,
160  ScrollView* tab_win) {
161 #ifndef GRAPHICS_DISABLED
162  if (tab_win == nullptr)
163  tab_win = MakeWindow(0, 50, window_name);
164  // For every tab in the grid, display it.
166  gsearch.StartFullSearch();
167  BLOBNBOX* bbox;
168  while ((bbox = gsearch.NextFullSearch()) != nullptr) {
169  const TBOX& box = bbox->bounding_box();
170  int left_x = box.left();
171  int right_x = box.right();
172  int top_y = box.top();
173  int bottom_y = box.bottom();
174  TabType tabtype = bbox->left_tab_type();
175  if (tabtype != TT_NONE) {
176  if (tabtype == TT_MAYBE_ALIGNED)
177  tab_win->Pen(ScrollView::BLUE);
178  else if (tabtype == TT_MAYBE_RAGGED)
179  tab_win->Pen(ScrollView::YELLOW);
180  else if (tabtype == TT_CONFIRMED)
181  tab_win->Pen(ScrollView::GREEN);
182  else
183  tab_win->Pen(ScrollView::GREY);
184  tab_win->Line(left_x, top_y, left_x, bottom_y);
185  }
186  tabtype = bbox->right_tab_type();
187  if (tabtype != TT_NONE) {
188  if (tabtype == TT_MAYBE_ALIGNED)
189  tab_win->Pen(ScrollView::MAGENTA);
190  else if (tabtype == TT_MAYBE_RAGGED)
191  tab_win->Pen(ScrollView::ORANGE);
192  else if (tabtype == TT_CONFIRMED)
193  tab_win->Pen(ScrollView::RED);
194  else
195  tab_win->Pen(ScrollView::GREY);
196  tab_win->Line(right_x, top_y, right_x, bottom_y);
197  }
198  }
199  tab_win->Update();
200 #endif
201  return tab_win;
202 }
203 
204 // Helper returns true if the total number of line_crossings of all the blobs
205 // in the list is at least 2.
206 static bool AtLeast2LineCrossings(BLOBNBOX_CLIST* blobs) {
207  BLOBNBOX_C_IT it(blobs);
208  int total_crossings = 0;
209  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
210  total_crossings += it.data()->line_crossings();
211  }
212  return total_crossings >= 2;
213 }
214 
215 // Destructor.
216 // It is defined here, so the compiler can create a single vtable
217 // instead of weak vtables in every compilation unit.
218 AlignedBlob::~AlignedBlob() = default;
219 
220 // Finds a vector corresponding to a set of vertically aligned blob edges
221 // running through the given box. The type of vector returned and the
222 // search parameters are determined by the AlignedBlobParams.
223 // vertical_x and y are updated with an estimate of the real
224 // vertical direction. (skew finding.)
225 // Returns nullptr if no decent vector can be found.
227  BLOBNBOX* bbox,
228  int* vertical_x,
229  int* vertical_y) {
230  int ext_start_y, ext_end_y;
231  BLOBNBOX_CLIST good_points;
232  // Search up and then down from the starting bbox.
233  TBOX box = bbox->bounding_box();
234  bool debug = WithinTestRegion(2, box.left(), box.bottom());
235  int pt_count = AlignTabs(align_params, false, bbox, &good_points, &ext_end_y);
236  pt_count += AlignTabs(align_params, true, bbox, &good_points, &ext_start_y);
237  BLOBNBOX_C_IT it(&good_points);
238  it.move_to_last();
239  box = it.data()->bounding_box();
240  int end_y = box.top();
241  int end_x = align_params.right_tab ? box.right() : box.left();
242  it.move_to_first();
243  box = it.data()->bounding_box();
244  int start_x = align_params.right_tab ? box.right() : box.left();
245  int start_y = box.bottom();
246  // Acceptable tab vectors must have a minimum number of points,
247  // have a minimum acceptable length, and have a minimum gradient.
248  // The gradient corresponds to the skew angle.
249  // Ragged tabs don't need to satisfy the gradient condition, as they
250  // will always end up parallel to the vertical direction.
251  bool at_least_2_crossings = AtLeast2LineCrossings(&good_points);
252  if ((pt_count >= align_params.min_points &&
253  end_y - start_y >= align_params.min_length &&
254  (align_params.ragged ||
255  end_y - start_y >= abs(end_x - start_x) * kMinTabGradient)) ||
256  at_least_2_crossings) {
257  int confirmed_points = 0;
258  // Count existing confirmed points to see if vector is acceptable.
259  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
260  bbox = it.data();
261  if (align_params.right_tab) {
262  if (bbox->right_tab_type() == align_params.confirmed_type)
263  ++confirmed_points;
264  } else {
265  if (bbox->left_tab_type() == align_params.confirmed_type)
266  ++confirmed_points;
267  }
268  }
269  // Ragged vectors are not allowed to use too many already used points.
270  if (!align_params.ragged ||
271  confirmed_points + confirmed_points < pt_count) {
272  const TBOX& box = bbox->bounding_box();
273  if (debug) {
274  tprintf("Confirming tab vector of %d pts starting at %d,%d\n",
275  pt_count, box.left(), box.bottom());
276  }
277  // Flag all the aligned neighbours as confirmed .
278  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
279  bbox = it.data();
280  if (align_params.right_tab) {
281  bbox->set_right_tab_type(align_params.confirmed_type);
282  } else {
283  bbox->set_left_tab_type(align_params.confirmed_type);
284  }
285  if (debug) {
286  bbox->bounding_box().print();
287  }
288  }
289  // Now make the vector and return it.
290  TabVector* result = TabVector::FitVector(align_params.alignment,
291  align_params.vertical,
292  ext_start_y, ext_end_y,
293  &good_points,
294  vertical_x, vertical_y);
295  result->set_intersects_other_lines(at_least_2_crossings);
296  if (debug) {
297  tprintf("Box was %d, %d\n", box.left(), box.bottom());
298  result->Print("After fitting");
299  }
300  return result;
301  } else if (debug) {
302  tprintf("Ragged tab used too many used points: %d out of %d\n",
303  confirmed_points, pt_count);
304  }
305  } else if (debug) {
306  tprintf("Tab vector failed basic tests: pt count %d vs min %d, "
307  "length %d vs min %d, min grad %g\n",
308  pt_count, align_params.min_points, end_y - start_y,
309  align_params.min_length, abs(end_x - start_x) * kMinTabGradient);
310  }
311  return nullptr;
312 }
313 
314 // Find a set of blobs that are aligned in the given vertical
315 // direction with the given blob. Returns a list of aligned
316 // blobs and the number in the list.
317 // For other parameters see FindAlignedBlob below.
318 int AlignedBlob::AlignTabs(const AlignedBlobParams& params,
319  bool top_to_bottom, BLOBNBOX* bbox,
320  BLOBNBOX_CLIST* good_points, int* end_y) {
321  int ptcount = 0;
322  BLOBNBOX_C_IT it(good_points);
323 
324  TBOX box = bbox->bounding_box();
325  bool debug = WithinTestRegion(2, box.left(), box.bottom());
326  if (debug) {
327  tprintf("Starting alignment run at blob:");
328  box.print();
329  }
330  int x_start = params.right_tab ? box.right() : box.left();
331  while (bbox != nullptr) {
332  // Add the blob to the list if the appropriate side is a tab candidate,
333  // or if we are working on a ragged tab.
334  TabType type = params.right_tab ? bbox->right_tab_type()
335  : bbox->left_tab_type();
336  if (((type != TT_NONE && type != TT_MAYBE_RAGGED) || params.ragged) &&
337  (it.empty() || it.data() != bbox)) {
338  if (top_to_bottom)
339  it.add_before_then_move(bbox);
340  else
341  it.add_after_then_move(bbox);
342  ++ptcount;
343  }
344  // Find the next blob that is aligned with the current one.
345  // FindAlignedBlob guarantees that forward progress will be made in the
346  // top_to_bottom direction, and therefore eventually it will return nullptr,
347  // making this while (bbox != nullptr) loop safe.
348  bbox = FindAlignedBlob(params, top_to_bottom, bbox, x_start, end_y);
349  if (bbox != nullptr) {
350  box = bbox->bounding_box();
351  if (!params.ragged)
352  x_start = params.right_tab ? box.right() : box.left();
353  }
354  }
355  if (debug) {
356  tprintf("Alignment run ended with %d pts at blob:", ptcount);
357  box.print();
358  }
359  return ptcount;
360 }
361 
362 // Search vertically for a blob that is aligned with the input bbox.
363 // The search parameters are determined by AlignedBlobParams.
364 // top_to_bottom tells whether to search down or up.
365 // The return value is nullptr if nothing was found in the search box
366 // or if a blob was found in the gutter. On a nullptr return, end_y
367 // is set to the edge of the search box or the leading edge of the
368 // gutter blob if one was found.
369 BLOBNBOX* AlignedBlob::FindAlignedBlob(const AlignedBlobParams& p,
370  bool top_to_bottom, BLOBNBOX* bbox,
371  int x_start, int* end_y) {
372  TBOX box = bbox->bounding_box();
373  // If there are separator lines, get the column edges.
374  int left_column_edge = bbox->left_rule();
375  int right_column_edge = bbox->right_rule();
376  // start_y is used to guarantee that forward progress is made and the
377  // search does not go into an infinite loop. New blobs must extend the
378  // line beyond start_y.
379  int start_y = top_to_bottom ? box.bottom() : box.top();
380  if (WithinTestRegion(2, x_start, start_y)) {
381  tprintf("Column edges for blob at (%d,%d)->(%d,%d) are [%d, %d]\n",
382  box.left(), box.top(), box.right(), box.bottom(),
383  left_column_edge, right_column_edge);
384  }
385  // Compute skew tolerance.
386  int skew_tolerance = p.max_v_gap / kMaxSkewFactor;
387  // Calculate xmin and xmax of the search box so that it contains
388  // all possibly relevant boxes up to p.max_v_gap above or below accoording
389  // to top_to_bottom.
390  // Start with a notion of vertical with the current estimate.
391  int x2 = (p.max_v_gap * p.vertical.x() + p.vertical.y()/2) / p.vertical.y();
392  if (top_to_bottom) {
393  x2 = x_start - x2;
394  *end_y = start_y - p.max_v_gap;
395  } else {
396  x2 = x_start + x2;
397  *end_y = start_y + p.max_v_gap;
398  }
399  // Expand the box by an additional skew tolerance
400  int xmin = std::min(x_start, x2) - skew_tolerance;
401  int xmax = std::max(x_start, x2) + skew_tolerance;
402  // Now add direction-specific tolerances.
403  if (p.right_tab) {
404  xmax += p.min_gutter;
405  xmin -= p.l_align_tolerance;
406  } else {
407  xmax += p.r_align_tolerance;
408  xmin -= p.min_gutter;
409  }
410  // Setup a vertical search for an aligned blob.
411  GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> vsearch(this);
412  if (WithinTestRegion(2, x_start, start_y))
413  tprintf("Starting %s %s search at %d-%d,%d, search_size=%d, gutter=%d\n",
414  p.ragged ? "Ragged" : "Aligned", p.right_tab ? "Right" : "Left",
415  xmin, xmax, start_y, p.max_v_gap, p.min_gutter);
416  vsearch.StartVerticalSearch(xmin, xmax, start_y);
417  // result stores the best real return value.
418  BLOBNBOX* result = nullptr;
419  // The backup_result is not a tab candidate and can be used if no
420  // real tab candidate result is found.
421  BLOBNBOX* backup_result = nullptr;
422  // neighbour is the blob that is currently being investigated.
423  BLOBNBOX* neighbour = nullptr;
424  while ((neighbour = vsearch.NextVerticalSearch(top_to_bottom)) != nullptr) {
425  if (neighbour == bbox)
426  continue;
427  TBOX nbox = neighbour->bounding_box();
428  int n_y = (nbox.top() + nbox.bottom()) / 2;
429  if ((!top_to_bottom && n_y > start_y + p.max_v_gap) ||
430  (top_to_bottom && n_y < start_y - p.max_v_gap)) {
431  if (WithinTestRegion(2, x_start, start_y))
432  tprintf("Neighbour too far at (%d,%d)->(%d,%d)\n",
433  nbox.left(), nbox.bottom(), nbox.right(), nbox.top());
434  break; // Gone far enough.
435  }
436  // It is CRITICAL to ensure that forward progress is made, (strictly
437  // in/decreasing n_y) or the caller could loop infinitely, while
438  // waiting for a sequence of blobs in a line to end.
439  // NextVerticalSearch alone does not guarantee this, as there may be
440  // more than one blob in a grid cell. See comment in AlignTabs.
441  if ((n_y < start_y) != top_to_bottom || nbox.y_overlap(box))
442  continue; // Only look in the required direction.
443  if (result != nullptr && result->bounding_box().y_gap(nbox) > gridsize())
444  return result; // This result is clear.
445  if (backup_result != nullptr && p.ragged && result == nullptr &&
446  backup_result->bounding_box().y_gap(nbox) > gridsize())
447  return backup_result; // This result is clear.
448 
449  // If the neighbouring blob is the wrong side of a separator line, then it
450  // "doesn't exist" as far as we are concerned.
451  int x_at_n_y = x_start + (n_y - start_y) * p.vertical.x() / p.vertical.y();
452  if (x_at_n_y < neighbour->left_crossing_rule() ||
453  x_at_n_y > neighbour->right_crossing_rule())
454  continue; // Separator line in the way.
455  int n_left = nbox.left();
456  int n_right = nbox.right();
457  int n_x = p.right_tab ? n_right : n_left;
458  if (WithinTestRegion(2, x_start, start_y))
459  tprintf("neighbour at (%d,%d)->(%d,%d), n_x=%d, n_y=%d, xatn=%d\n",
460  nbox.left(), nbox.bottom(), nbox.right(), nbox.top(),
461  n_x, n_y, x_at_n_y);
462  if (p.right_tab &&
463  n_left < x_at_n_y + p.min_gutter &&
464  n_right > x_at_n_y + p.r_align_tolerance &&
465  (p.ragged || n_left < x_at_n_y + p.gutter_fraction * nbox.height())) {
466  // In the gutter so end of line.
467  if (bbox->right_tab_type() >= TT_MAYBE_ALIGNED)
469  *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
470  if (WithinTestRegion(2, x_start, start_y))
471  tprintf("gutter\n");
472  return nullptr;
473  }
474  if (!p.right_tab &&
475  n_left < x_at_n_y - p.l_align_tolerance &&
476  n_right > x_at_n_y - p.min_gutter &&
477  (p.ragged || n_right > x_at_n_y - p.gutter_fraction * nbox.height())) {
478  // In the gutter so end of line.
479  if (bbox->left_tab_type() >= TT_MAYBE_ALIGNED)
481  *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
482  if (WithinTestRegion(2, x_start, start_y))
483  tprintf("gutter\n");
484  return nullptr;
485  }
486  if ((p.right_tab && neighbour->leader_on_right()) ||
487  (!p.right_tab && neighbour->leader_on_left()))
488  continue; // Neighbours of leaders are not allowed to be used.
489  if (n_x <= x_at_n_y + p.r_align_tolerance &&
490  n_x >= x_at_n_y - p.l_align_tolerance) {
491  // Aligned so keep it. If it is a marked tab save it as result,
492  // otherwise keep it as backup_result to return in case of later failure.
493  if (WithinTestRegion(2, x_start, start_y))
494  tprintf("aligned, seeking%d, l=%d, r=%d\n",
495  p.right_tab, neighbour->left_tab_type(),
496  neighbour->right_tab_type());
497  TabType n_type = p.right_tab ? neighbour->right_tab_type()
498  : neighbour->left_tab_type();
499  if (n_type != TT_NONE && (p.ragged || n_type != TT_MAYBE_RAGGED)) {
500  if (result == nullptr) {
501  result = neighbour;
502  } else {
503  // Keep the closest neighbour by Euclidean distance.
504  // This prevents it from picking a tab blob in another column.
505  const TBOX& old_box = result->bounding_box();
506  int x_diff = p.right_tab ? old_box.right() : old_box.left();
507  x_diff -= x_at_n_y;
508  int y_diff = (old_box.top() + old_box.bottom()) / 2 - start_y;
509  int old_dist = x_diff * x_diff + y_diff * y_diff;
510  x_diff = n_x - x_at_n_y;
511  y_diff = n_y - start_y;
512  int new_dist = x_diff * x_diff + y_diff * y_diff;
513  if (new_dist < old_dist)
514  result = neighbour;
515  }
516  } else if (backup_result == nullptr) {
517  if (WithinTestRegion(2, x_start, start_y))
518  tprintf("Backup\n");
519  backup_result = neighbour;
520  } else {
521  TBOX backup_box = backup_result->bounding_box();
522  if ((p.right_tab && backup_box.right() < nbox.right()) ||
523  (!p.right_tab && backup_box.left() > nbox.left())) {
524  if (WithinTestRegion(2, x_start, start_y))
525  tprintf("Better backup\n");
526  backup_result = neighbour;
527  }
528  }
529  }
530  }
531  return result != nullptr ? result : backup_result;
532 }
533 
534 } // namespace tesseract.
const int kMinAlignedTabs
Definition: alignedblob.cpp:55
ScrollView * MakeWindow(int x, int y, const char *window_name)
Definition: bbgrid.h:591
#define BOOL_VAR(name, val, comment)
Definition: params.h:279
int gridsize() const
Definition: bbgrid.h:64
void print() const
Definition: rect.h:278
BBC * NextFullSearch()
Definition: bbgrid.h:677
int y_gap(const TBOX &box) const
Definition: rect.h:233
void set_x(int16_t xin)
rewrite function
Definition: points.h:62
int right_crossing_rule() const
Definition: blobbox.h:332
const int kVLineSearchSize
Definition: alignedblob.cpp:51
Definition: rect.h:34
int textord_testregion_right
Definition: alignedblob.cpp:32
static bool WithinTestRegion(int detail_level, int x, int y)
TabVector * FindVerticalAlignment(AlignedBlobParams align_params, BLOBNBOX *bbox, int *vertical_x, int *vertical_y)
const double kRaggedFraction
Definition: alignedblob.cpp:41
AlignedBlobParams(int vertical_x, int vertical_y, int height, int v_gap_multiple, int min_gutter_width, int resolution, TabAlignment alignment0)
Definition: alignedblob.cpp:74
const double kAlignedGapFraction
Definition: alignedblob.cpp:43
const double kRaggedGapFraction
Definition: alignedblob.cpp:45
static void Update()
Definition: scrollview.cpp:711
void set_right_tab_type(TabType new_type)
Definition: blobbox.h:281
bool textord_debug_printable
Definition: alignedblob.cpp:34
int16_t left() const
Definition: rect.h:72
const double kMinTabGradient
Definition: alignedblob.cpp:61
const int kVLineGutter
Definition: alignedblob.cpp:49
int16_t top() const
Definition: rect.h:58
bool leader_on_right() const
Definition: blobbox.h:365
void set_vertical(int vertical_x, int vertical_y)
integer coordinate
Definition: points.h:32
int textord_debug_tabfind
Definition: alignedblob.cpp:28
TabType right_tab_type() const
Definition: blobbox.h:278
TabType left_tab_type() const
Definition: blobbox.h:272
int textord_testregion_top
Definition: alignedblob.cpp:31
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:37
ScrollView * DisplayTabs(const char *window_name, ScrollView *tab_win)
AlignedBlob(int gridsize, const ICOORD &bleft, const ICOORD &tright)
const int kVLineMinLength
Definition: alignedblob.cpp:57
const int kVLineAlignment
Definition: alignedblob.cpp:47
bool y_overlap(const TBOX &box) const
Definition: rect.h:428
int textord_testregion_left
Definition: alignedblob.cpp:30
static TabVector * FitVector(TabAlignment alignment, ICOORD vertical, int extended_start_y, int extended_end_y, BLOBNBOX_CLIST *good_points, int *vertical_x, int *vertical_y)
Definition: tabvector.cpp:177
const TBOX & bounding_box() const
Definition: blobbox.h:231
int left_rule() const
Definition: blobbox.h:314
bool leader_on_left() const
Definition: blobbox.h:359
int16_t right() const
Definition: rect.h:79
void set_intersects_other_lines(bool value)
Definition: tabvector.h:182
int textord_debug_bugs
Definition: alignedblob.cpp:29
void StartFullSearch()
Definition: bbgrid.h:667
void Pen(Color color)
Definition: scrollview.cpp:722
void set_y(int16_t yin)
rewrite function
Definition: points.h:66
int16_t bottom() const
Definition: rect.h:65
void set_left_tab_type(TabType new_type)
Definition: blobbox.h:275
const int kMaxSkewFactor
Definition: alignedblob.cpp:65
int right_rule() const
Definition: blobbox.h:320
TabType
Definition: blobbox.h:60
void Print(const char *prefix)
Definition: tabvector.cpp:520
int16_t height() const
Definition: rect.h:108
const int kMinRaggedTabs
Definition: alignedblob.cpp:53
#define INT_VAR(name, val, comment)
Definition: params.h:276
void Line(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:534
const double kAlignedFraction
Definition: alignedblob.cpp:39
int textord_testregion_bottom
Definition: alignedblob.cpp:33