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