tesseract  4.0.0-1-g2a2b
blobbox.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: blobbox.cpp (Formerly blobnbox.c)
3  * Description: Code for the textord blob class.
4  * Author: Ray Smith
5  * Created: Thu Jul 30 09:08:51 BST 1992
6  *
7  * (C) Copyright 1992, Hewlett-Packard Ltd.
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  *
18  **********************************************************************/
19 
20 // Include automatically generated configuration file if running autoconf.
21 #ifdef HAVE_CONFIG_H
22 #include "config_auto.h"
23 #endif
24 
25 #include "blobbox.h"
26 #include <algorithm> // for max, min
27 #include <cstdint> // for INT32_MAX, INT16_MAX
28 #include "allheaders.h" // for pixGetHeight, pixGetPixel
29 #include "blobs.h" // for TPOINT
30 #include "coutln.h" // for C_OUTLINE_IT, C_OUTLINE, C_OUTLINE_LIST
31 #include "environ.h" // for l_uint32
32 #include "helpers.h" // for UpdateRange, IntCastRounded
33 #include "host.h" // for NearlyEqual, TRUE
34 #include "points.h" // for operator+=, ICOORD::rotate
35 
36 struct Pix;
37 
38 #define PROJECTION_MARGIN 10 //arbitrary
39 
43 
44 // Up to 30 degrees is allowed for rotations of diacritic blobs.
45 const double kCosSmallAngle = 0.866;
46 // Min aspect ratio for a joined word to indicate an obvious flow direction.
47 const double kDefiniteAspectRatio = 2.0;
48 // Multiple of short length in perimeter to make a joined word.
49 const double kComplexShapePerimeterRatio = 1.5;
50 // Min multiple of linesize for medium-sized blobs in ReFilterBlobs.
51 const double kMinMediumSizeRatio = 0.25;
52 // Max multiple of linesize for medium-sized blobs in ReFilterBlobs.
53 const double kMaxMediumSizeRatio = 4.0;
54 
55 // Rotates the box and the underlying blob.
56 void BLOBNBOX::rotate(FCOORD rotation) {
57  cblob_ptr->rotate(rotation);
58  rotate_box(rotation);
59  compute_bounding_box();
60 }
61 
62 // Reflect the box in the y-axis, leaving the underlying blob untouched.
64  int left = -box.right();
65  box.set_right(-box.left());
66  box.set_left(left);
67 }
68 
69 // Rotates the box by the angle given by rotation.
70 // If the blob is a diacritic, then only small rotations for skew
71 // correction can be applied.
72 void BLOBNBOX::rotate_box(FCOORD rotation) {
73  if (IsDiacritic()) {
74  ASSERT_HOST(rotation.x() >= kCosSmallAngle)
75  ICOORD top_pt((box.left() + box.right()) / 2, base_char_top_);
76  ICOORD bottom_pt(top_pt.x(), base_char_bottom_);
77  top_pt.rotate(rotation);
78  base_char_top_ = top_pt.y();
79  bottom_pt.rotate(rotation);
80  base_char_bottom_ = bottom_pt.y();
81  box.rotate(rotation);
82  } else {
83  box.rotate(rotation);
84  set_diacritic_box(box);
85  }
86 }
87 
88 /**********************************************************************
89  * BLOBNBOX::merge
90  *
91  * Merge this blob with the given blob, which should be after this.
92  **********************************************************************/
93 void BLOBNBOX::merge( //merge blobs
94  BLOBNBOX *nextblob //blob to join with
95  ) {
96  box += nextblob->box; //merge boxes
97  set_diacritic_box(box);
98  nextblob->joined = TRUE;
99 }
100 
101 
102 // Merge this with other, taking the outlines from other.
103 // Other is not deleted, but left for the caller to handle.
105  if (other->cblob_ptr != nullptr) {
106  C_OUTLINE_IT ol_it(cblob_ptr->out_list());
107  ol_it.add_list_after(other->cblob_ptr->out_list());
108  }
110 }
111 
112 
113 /**********************************************************************
114  * BLOBNBOX::chop
115  *
116  * Chop this blob into equal sized pieces using the x height as a guide.
117  * The blob is not actually chopped. Instead, fake blobs are inserted
118  * with the relevant bounding boxes.
119  **********************************************************************/
120 
121 void BLOBNBOX::chop( //chop blobs
122  BLOBNBOX_IT *start_it, //location of this
123  BLOBNBOX_IT *end_it, //iterator
124  FCOORD rotation, //for landscape
125  float xheight //of line
126  ) {
127  int16_t blobcount; //no of blobs
128  BLOBNBOX *newblob; //fake blob
129  BLOBNBOX *blob; //current blob
130  int16_t blobindex; //number of chop
131  int16_t leftx; //left edge of blob
132  float blobwidth; //width of each
133  float rightx; //right edge to scan
134  float ymin, ymax; //limits of new blob
135  float test_ymin, test_ymax; //limits of part blob
136  ICOORD bl, tr; //corners of box
137  BLOBNBOX_IT blob_it; //blob iterator
138 
139  //get no of chops
140  blobcount = (int16_t) floor (box.width () / xheight);
141  if (blobcount > 1 && cblob_ptr != nullptr) {
142  //width of each
143  blobwidth = (float) (box.width () + 1) / blobcount;
144  for (blobindex = blobcount - 1, rightx = box.right ();
145  blobindex >= 0; blobindex--, rightx -= blobwidth) {
146  ymin = (float) INT32_MAX;
147  ymax = (float) -INT32_MAX;
148  blob_it = *start_it;
149  do {
150  blob = blob_it.data ();
151  find_cblob_vlimits(blob->cblob_ptr, rightx - blobwidth,
152  rightx,
153  /*rotation, */ test_ymin, test_ymax);
154  blob_it.forward ();
155  UpdateRange(test_ymin, test_ymax, &ymin, &ymax);
156  }
157  while (blob != end_it->data ());
158  if (ymin < ymax) {
159  leftx = (int16_t) floor (rightx - blobwidth);
160  if (leftx < box.left ())
161  leftx = box.left (); //clip to real box
162  bl = ICOORD (leftx, (int16_t) floor (ymin));
163  tr = ICOORD ((int16_t) ceil (rightx), (int16_t) ceil (ymax));
164  if (blobindex == 0)
165  box = TBOX (bl, tr); //change box
166  else {
167  newblob = new BLOBNBOX;
168  //box is all it has
169  newblob->box = TBOX (bl, tr);
170  //stay on current
171  newblob->base_char_top_ = tr.y();
172  newblob->base_char_bottom_ = bl.y();
173  end_it->add_after_stay_put (newblob);
174  }
175  }
176  }
177  }
178 }
179 
180 // Returns the box gaps between this and its neighbours_ in an array
181 // indexed by BlobNeighbourDir.
182 void BLOBNBOX::NeighbourGaps(int gaps[BND_COUNT]) const {
183  for (int dir = 0; dir < BND_COUNT; ++dir) {
184  gaps[dir] = INT16_MAX;
185  BLOBNBOX* neighbour = neighbours_[dir];
186  if (neighbour != nullptr) {
187  const TBOX& n_box = neighbour->bounding_box();
188  if (dir == BND_LEFT || dir == BND_RIGHT) {
189  gaps[dir] = box.x_gap(n_box);
190  } else {
191  gaps[dir] = box.y_gap(n_box);
192  }
193  }
194  }
195 }
196 // Returns the min and max horizontal and vertical gaps (from NeighbourGaps)
197 // modified so that if the max exceeds the max dimension of the blob, and
198 // the min is less, the max is replaced with the min.
199 // The objective is to catch cases where there is only a single neighbour
200 // and avoid reporting the other gap as a ridiculously large number
201 void BLOBNBOX::MinMaxGapsClipped(int* h_min, int* h_max,
202  int* v_min, int* v_max) const {
203  int max_dimension = std::max(box.width(), box.height());
204  int gaps[BND_COUNT];
205  NeighbourGaps(gaps);
206  *h_min = std::min(gaps[BND_LEFT], gaps[BND_RIGHT]);
207  *h_max = std::max(gaps[BND_LEFT], gaps[BND_RIGHT]);
208  if (*h_max > max_dimension && *h_min < max_dimension) *h_max = *h_min;
209  *v_min = std::min(gaps[BND_ABOVE], gaps[BND_BELOW]);
210  *v_max = std::max(gaps[BND_ABOVE], gaps[BND_BELOW]);
211  if (*v_max > max_dimension && *v_min < max_dimension) *v_max = *v_min;
212 }
213 
214 // Nulls out any neighbours that are DeletableNoise to remove references.
216  for (int dir = 0; dir < BND_COUNT; ++dir) {
217  BLOBNBOX* neighbour = neighbours_[dir];
218  if (neighbour != nullptr && neighbour->DeletableNoise()) {
219  neighbours_[dir] = nullptr;
220  good_stroke_neighbours_[dir] = false;
221  }
222  }
223 }
224 
225 // Returns positive if there is at least one side neighbour that has a similar
226 // stroke width and is not on the other side of a rule line.
228  int score = 0;
229  for (int dir = 0; dir < BND_COUNT; ++dir) {
230  BlobNeighbourDir bnd = static_cast<BlobNeighbourDir>(dir);
231  if (good_stroke_neighbour(bnd))
232  ++score;
233  }
234  return score;
235 }
236 
237 // Returns the number of side neighbours that are of type BRT_NOISE.
239  int count = 0;
240  for (int dir = 0; dir < BND_COUNT; ++dir) {
241  BlobNeighbourDir bnd = static_cast<BlobNeighbourDir>(dir);
242  BLOBNBOX* blob = neighbour(bnd);
243  if (blob != nullptr && blob->region_type() == BRT_NOISE)
244  ++count;
245  }
246  return count;
247 }
248 
249 // Returns true, and sets vert_possible/horz_possible if the blob has some
250 // feature that makes it individually appear to flow one way.
251 // eg if it has a high aspect ratio, yet has a complex shape, such as a
252 // joined word in Latin, Arabic, or Hindi, rather than being a -, I, l, 1 etc.
254  if (cblob() == nullptr) return false;
255  int box_perimeter = 2 * (box.height() + box.width());
256  if (box.width() > box.height() * kDefiniteAspectRatio) {
257  // Attempt to distinguish a wide joined word from a dash.
258  // If it is a dash, then its perimeter is approximately
259  // 2 * (box width + stroke width), but more if the outline is noisy,
260  // so perimeter - 2*(box width + stroke width) should be close to zero.
261  // A complex shape such as a joined word should have a much larger value.
262  int perimeter = cblob()->perimeter();
263  if (vert_stroke_width() > 0 || perimeter <= 0)
264  perimeter -= 2 * vert_stroke_width();
265  else
266  perimeter -= 4 * cblob()->area() / perimeter;
267  perimeter -= 2 * box.width();
268  // Use a multiple of the box perimeter as a threshold.
269  if (perimeter > kComplexShapePerimeterRatio * box_perimeter) {
270  set_vert_possible(false);
271  set_horz_possible(true);
272  return true;
273  }
274  }
275  if (box.height() > box.width() * kDefiniteAspectRatio) {
276  // As above, but for a putative vertical word vs a I/1/l.
277  int perimeter = cblob()->perimeter();
278  if (horz_stroke_width() > 0 || perimeter <= 0)
279  perimeter -= 2 * horz_stroke_width();
280  else
281  perimeter -= 4 * cblob()->area() / perimeter;
282  perimeter -= 2 * box.height();
283  if (perimeter > kComplexShapePerimeterRatio * box_perimeter) {
284  set_vert_possible(true);
285  set_horz_possible(false);
286  return true;
287  }
288  }
289  return false;
290 }
291 
292 // Returns true if there is no tabstop violation in merging this and other.
293 bool BLOBNBOX::ConfirmNoTabViolation(const BLOBNBOX& other) const {
294  if (box.left() < other.box.left() && box.left() < other.left_rule_)
295  return false;
296  if (other.box.left() < box.left() && other.box.left() < left_rule_)
297  return false;
298  if (box.right() > other.box.right() && box.right() > other.right_rule_)
299  return false;
300  if (other.box.right() > box.right() && other.box.right() > right_rule_)
301  return false;
302  return true;
303 }
304 
305 // Returns true if other has a similar stroke width to this.
307  double fractional_tolerance,
308  double constant_tolerance) const {
309  // The perimeter-based width is used as a backup in case there is
310  // no information in the blob.
311  double p_width = area_stroke_width();
312  double n_p_width = other.area_stroke_width();
313  float h_tolerance = horz_stroke_width_ * fractional_tolerance
314  + constant_tolerance;
315  float v_tolerance = vert_stroke_width_ * fractional_tolerance
316  + constant_tolerance;
317  double p_tolerance = p_width * fractional_tolerance
318  + constant_tolerance;
319  bool h_zero = horz_stroke_width_ == 0.0f || other.horz_stroke_width_ == 0.0f;
320  bool v_zero = vert_stroke_width_ == 0.0f || other.vert_stroke_width_ == 0.0f;
321  bool h_ok = !h_zero && NearlyEqual(horz_stroke_width_,
322  other.horz_stroke_width_, h_tolerance);
323  bool v_ok = !v_zero && NearlyEqual(vert_stroke_width_,
324  other.vert_stroke_width_, v_tolerance);
325  bool p_ok = h_zero && v_zero && NearlyEqual(p_width, n_p_width, p_tolerance);
326  // For a match, at least one of the horizontal and vertical widths
327  // must match, and the other one must either match or be zero.
328  // Only if both are zero will we look at the perimeter metric.
329  return p_ok || ((v_ok || h_ok) && (h_ok || h_zero) && (v_ok || v_zero));
330 }
331 
332 // Returns a bounding box of the outline contained within the
333 // given horizontal range.
334 TBOX BLOBNBOX::BoundsWithinLimits(int left, int right) {
335  FCOORD no_rotation(1.0f, 0.0f);
336  float top = box.top();
337  float bottom = box.bottom();
338  if (cblob_ptr != nullptr) {
339  find_cblob_limits(cblob_ptr, static_cast<float>(left),
340  static_cast<float>(right), no_rotation,
341  bottom, top);
342  }
343 
344  if (top < bottom) {
345  top = box.top();
346  bottom = box.bottom();
347  }
348  FCOORD bot_left(left, bottom);
349  FCOORD top_right(right, top);
350  TBOX shrunken_box(bot_left);
351  TBOX shrunken_box2(top_right);
352  shrunken_box += shrunken_box2;
353  return shrunken_box;
354 }
355 
356 // Estimates and stores the baseline position based on the shape of the
357 // outline.
359  baseline_y_ = box.bottom(); // The default.
360  if (cblob_ptr == nullptr) return;
361  baseline_y_ = cblob_ptr->EstimateBaselinePosition();
362 }
363 
364 // Helper to call CleanNeighbours on all blobs on the list.
365 void BLOBNBOX::CleanNeighbours(BLOBNBOX_LIST* blobs) {
366  BLOBNBOX_IT blob_it(blobs);
367  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
368  blob_it.data()->CleanNeighbours();
369  }
370 }
371 
372 // Helper to delete all the deletable blobs on the list.
373 void BLOBNBOX::DeleteNoiseBlobs(BLOBNBOX_LIST* blobs) {
374  BLOBNBOX_IT blob_it(blobs);
375  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
376  BLOBNBOX* blob = blob_it.data();
377  if (blob->DeletableNoise()) {
378  delete blob->cblob();
379  delete blob_it.extract();
380  }
381  }
382 }
383 
384 // Helper to compute edge offsets for all the blobs on the list.
385 // See coutln.h for an explanation of edge offsets.
386 void BLOBNBOX::ComputeEdgeOffsets(Pix* thresholds, Pix* grey,
387  BLOBNBOX_LIST* blobs) {
388  int grey_height = 0;
389  int thr_height = 0;
390  int scale_factor = 1;
391  if (thresholds != nullptr && grey != nullptr) {
392  grey_height = pixGetHeight(grey);
393  thr_height = pixGetHeight(thresholds);
394  scale_factor =
395  IntCastRounded(static_cast<double>(grey_height) / thr_height);
396  }
397  BLOBNBOX_IT blob_it(blobs);
398  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
399  BLOBNBOX* blob = blob_it.data();
400  if (blob->cblob() != nullptr) {
401  // Get the threshold that applies to this blob.
402  l_uint32 threshold = 128;
403  if (thresholds != nullptr && grey != nullptr) {
404  const TBOX& box = blob->cblob()->bounding_box();
405  // Transform the coordinates if required.
406  TPOINT pt((box.left() + box.right()) / 2,
407  (box.top() + box.bottom()) / 2);
408  pixGetPixel(thresholds, pt.x / scale_factor,
409  thr_height - 1 - pt.y / scale_factor, &threshold);
410  }
411  blob->cblob()->ComputeEdgeOffsets(threshold, grey);
412  }
413  }
414 }
415 
416 
417 #ifndef GRAPHICS_DISABLED
418 // Helper to draw all the blobs on the list in the given body_colour,
419 // with child outlines in the child_colour.
420 void BLOBNBOX::PlotBlobs(BLOBNBOX_LIST* list,
421  ScrollView::Color body_colour,
422  ScrollView::Color child_colour,
423  ScrollView* win) {
424  BLOBNBOX_IT it(list);
425  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
426  it.data()->plot(win, body_colour, child_colour);
427  }
428 }
429 
430 // Helper to draw only DeletableNoise blobs (unowned, BRT_NOISE) on the
431 // given list in the given body_colour, with child outlines in the
432 // child_colour.
433 void BLOBNBOX::PlotNoiseBlobs(BLOBNBOX_LIST* list,
434  ScrollView::Color body_colour,
435  ScrollView::Color child_colour,
436  ScrollView* win) {
437  BLOBNBOX_IT it(list);
438  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
439  BLOBNBOX* blob = it.data();
440  if (blob->DeletableNoise())
441  blob->plot(win, body_colour, child_colour);
442  }
443 }
444 
446  BlobTextFlowType flow_type) {
447  switch (region_type) {
448  case BRT_HLINE:
449  return ScrollView::BROWN;
450  case BRT_VLINE:
451  return ScrollView::DARK_GREEN;
452  case BRT_RECTIMAGE:
453  return ScrollView::RED;
454  case BRT_POLYIMAGE:
455  return ScrollView::ORANGE;
456  case BRT_UNKNOWN:
457  return flow_type == BTFT_NONTEXT ? ScrollView::CYAN : ScrollView::WHITE;
458  case BRT_VERT_TEXT:
459  if (flow_type == BTFT_STRONG_CHAIN || flow_type == BTFT_TEXT_ON_IMAGE)
460  return ScrollView::GREEN;
461  if (flow_type == BTFT_CHAIN)
462  return ScrollView::LIME_GREEN;
463  return ScrollView::YELLOW;
464  case BRT_TEXT:
465  if (flow_type == BTFT_STRONG_CHAIN)
466  return ScrollView::BLUE;
467  if (flow_type == BTFT_TEXT_ON_IMAGE)
468  return ScrollView::LIGHT_BLUE;
469  if (flow_type == BTFT_CHAIN)
471  if (flow_type == BTFT_LEADER)
472  return ScrollView::WHEAT;
473  if (flow_type == BTFT_NONTEXT)
474  return ScrollView::PINK;
475  return ScrollView::MAGENTA;
476  default:
477  return ScrollView::GREY;
478  }
479 }
480 
481 // Keep in sync with BlobRegionType.
483  return TextlineColor(region_type_, flow_);
484 }
485 
486 void BLOBNBOX::plot(ScrollView* window, // window to draw in
487  ScrollView::Color blob_colour, // for outer bits
488  ScrollView::Color child_colour) { // for holes
489  if (cblob_ptr != nullptr)
490  cblob_ptr->plot(window, blob_colour, child_colour);
491 }
492 #endif
493 /**********************************************************************
494  * find_cblob_limits
495  *
496  * Scan the outlines of the cblob to locate the y min and max
497  * between the given x limits.
498  **********************************************************************/
499 
500 void find_cblob_limits( //get y limits
501  C_BLOB *blob, //blob to search
502  float leftx, //x limits
503  float rightx,
504  FCOORD rotation, //for landscape
505  float &ymin, //output y limits
506  float &ymax) {
507  int16_t stepindex; //current point
508  ICOORD pos; //current coords
509  ICOORD vec; //rotated step
510  C_OUTLINE *outline; //current outline
511  //outlines
512  C_OUTLINE_IT out_it = blob->out_list ();
513 
514  ymin = (float) INT32_MAX;
515  ymax = (float) -INT32_MAX;
516  for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
517  outline = out_it.data ();
518  pos = outline->start_pos (); //get coords
519  pos.rotate (rotation);
520  for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) {
521  //inside
522  if (pos.x () >= leftx && pos.x () <= rightx) {
523  UpdateRange(pos.y(), &ymin, &ymax);
524  }
525  vec = outline->step (stepindex);
526  vec.rotate (rotation);
527  pos += vec; //move to next
528  }
529  }
530 }
531 
532 
533 /**********************************************************************
534  * find_cblob_vlimits
535  *
536  * Scan the outlines of the cblob to locate the y min and max
537  * between the given x limits.
538  **********************************************************************/
539 
540 void find_cblob_vlimits( //get y limits
541  C_BLOB *blob, //blob to search
542  float leftx, //x limits
543  float rightx,
544  float &ymin, //output y limits
545  float &ymax) {
546  int16_t stepindex; //current point
547  ICOORD pos; //current coords
548  ICOORD vec; //rotated step
549  C_OUTLINE *outline; //current outline
550  //outlines
551  C_OUTLINE_IT out_it = blob->out_list ();
552 
553  ymin = (float) INT32_MAX;
554  ymax = (float) -INT32_MAX;
555  for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
556  outline = out_it.data ();
557  pos = outline->start_pos (); //get coords
558  for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) {
559  //inside
560  if (pos.x () >= leftx && pos.x () <= rightx) {
561  UpdateRange(pos.y(), &ymin, &ymax);
562  }
563  vec = outline->step (stepindex);
564  pos += vec; //move to next
565  }
566  }
567 }
568 
569 
570 /**********************************************************************
571  * find_cblob_hlimits
572  *
573  * Scan the outlines of the cblob to locate the x min and max
574  * between the given y limits.
575  **********************************************************************/
576 
577 void find_cblob_hlimits( //get x limits
578  C_BLOB *blob, //blob to search
579  float bottomy, //y limits
580  float topy,
581  float &xmin, //output x limits
582  float &xmax) {
583  int16_t stepindex; //current point
584  ICOORD pos; //current coords
585  ICOORD vec; //rotated step
586  C_OUTLINE *outline; //current outline
587  //outlines
588  C_OUTLINE_IT out_it = blob->out_list ();
589 
590  xmin = (float) INT32_MAX;
591  xmax = (float) -INT32_MAX;
592  for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
593  outline = out_it.data ();
594  pos = outline->start_pos (); //get coords
595  for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) {
596  //inside
597  if (pos.y () >= bottomy && pos.y () <= topy) {
598  UpdateRange(pos.x(), &xmin, &xmax);
599  }
600  vec = outline->step (stepindex);
601  pos += vec; //move to next
602  }
603  }
604 }
605 
606 /**********************************************************************
607  * crotate_cblob
608  *
609  * Rotate the copy by the given vector and return a C_BLOB.
610  **********************************************************************/
611 
612 C_BLOB *crotate_cblob( //rotate it
613  C_BLOB *blob, //blob to search
614  FCOORD rotation //for landscape
615  ) {
616  C_OUTLINE_LIST out_list; //output outlines
617  //input outlines
618  C_OUTLINE_IT in_it = blob->out_list ();
619  //output outlines
620  C_OUTLINE_IT out_it = &out_list;
621 
622  for (in_it.mark_cycle_pt (); !in_it.cycled_list (); in_it.forward ()) {
623  out_it.add_after_then_move (new C_OUTLINE (in_it.data (), rotation));
624  }
625  return new C_BLOB (&out_list);
626 }
627 
628 
629 /**********************************************************************
630  * box_next
631  *
632  * Compute the bounding box of this blob with merging of x overlaps
633  * but no pre-chopping.
634  * Then move the iterator on to the start of the next blob.
635  **********************************************************************/
636 
637 TBOX box_next( //get bounding box
638  BLOBNBOX_IT *it //iterator to blobds
639  ) {
640  BLOBNBOX *blob; //current blob
641  TBOX result; //total box
642 
643  blob = it->data ();
644  result = blob->bounding_box ();
645  do {
646  it->forward ();
647  blob = it->data ();
648  if (blob->cblob() == nullptr)
649  //was pre-chopped
650  result += blob->bounding_box ();
651  }
652  //until next real blob
653  while ((blob->cblob() == nullptr) || blob->joined_to_prev());
654  return result;
655 }
656 
657 
658 /**********************************************************************
659  * box_next_pre_chopped
660  *
661  * Compute the bounding box of this blob with merging of x overlaps
662  * but WITH pre-chopping.
663  * Then move the iterator on to the start of the next pre-chopped blob.
664  **********************************************************************/
665 
666 TBOX box_next_pre_chopped( //get bounding box
667  BLOBNBOX_IT *it //iterator to blobds
668  ) {
669  BLOBNBOX *blob; //current blob
670  TBOX result; //total box
671 
672  blob = it->data ();
673  result = blob->bounding_box ();
674  do {
675  it->forward ();
676  blob = it->data ();
677  }
678  //until next real blob
679  while (blob->joined_to_prev ());
680  return result;
681 }
682 
683 
684 /**********************************************************************
685  * TO_ROW::TO_ROW
686  *
687  * Constructor to make a row from a blob.
688  **********************************************************************/
689 
690 TO_ROW::TO_ROW ( //constructor
691 BLOBNBOX * blob, //first blob
692 float top, //corrected top
693 float bottom, //of row
694 float row_size //ideal
695 ) {
696  clear();
697  y_min = bottom;
698  y_max = top;
699  initial_y_min = bottom;
700 
701  float diff; //in size
702  BLOBNBOX_IT it = &blobs; //list of blobs
703 
704  it.add_to_end (blob);
705  diff = top - bottom - row_size;
706  if (diff > 0) {
707  y_max -= diff / 2;
708  y_min += diff / 2;
709  }
710  //very small object
711  else if ((top - bottom) * 3 < row_size) {
712  diff = row_size / 3 + bottom - top;
713  y_max += diff / 2;
714  y_min -= diff / 2;
715  }
716 }
717 
718 void TO_ROW::print() const {
719  tprintf("pitch=%d, fp=%g, fps=%g, fpns=%g, prs=%g, prns=%g,"
720  " spacing=%g xh=%g y_origin=%g xev=%d, asc=%g, desc=%g,"
721  " body=%g, minsp=%d maxnsp=%d, thr=%d kern=%g sp=%g\n",
725  space_size);
726 }
727 
728 /**********************************************************************
729  * TO_ROW:add_blob
730  *
731  * Add the blob to the end of the row.
732  **********************************************************************/
733 
734 void TO_ROW::add_blob( //constructor
735  BLOBNBOX *blob, //first blob
736  float top, //corrected top
737  float bottom, //of row
738  float row_size //ideal
739  ) {
740  float allowed; //allowed expansion
741  float available; //expansion
742  BLOBNBOX_IT it = &blobs; //list of blobs
743 
744  it.add_to_end (blob);
745  allowed = row_size + y_min - y_max;
746  if (allowed > 0) {
747  available = top > y_max ? top - y_max : 0;
748  if (bottom < y_min)
749  //total available
750  available += y_min - bottom;
751  if (available > 0) {
752  available += available; //do it gradually
753  if (available < allowed)
754  available = allowed;
755  if (bottom < y_min)
756  y_min -= (y_min - bottom) * allowed / available;
757  if (top > y_max)
758  y_max += (top - y_max) * allowed / available;
759  }
760  }
761 }
762 
763 
764 /**********************************************************************
765  * TO_ROW:insert_blob
766  *
767  * Add the blob to the row in the correct position.
768  **********************************************************************/
769 
770 void TO_ROW::insert_blob( //constructor
771  BLOBNBOX *blob //first blob
772  ) {
773  BLOBNBOX_IT it = &blobs; //list of blobs
774 
775  if (it.empty ())
776  it.add_before_then_move (blob);
777  else {
778  it.mark_cycle_pt ();
779  while (!it.cycled_list ()
780  && it.data ()->bounding_box ().left () <=
781  blob->bounding_box ().left ())
782  it.forward ();
783  if (it.cycled_list ())
784  it.add_to_end (blob);
785  else
786  it.add_before_stay_put (blob);
787  }
788 }
789 
790 
791 /**********************************************************************
792  * TO_ROW::compute_vertical_projection
793  *
794  * Compute the vertical projection of a TO_ROW from its blobs.
795  **********************************************************************/
796 
797 void TO_ROW::compute_vertical_projection() { //project whole row
798  TBOX row_box; //bound of row
799  BLOBNBOX *blob; //current blob
800  TBOX blob_box; //bounding box
801  BLOBNBOX_IT blob_it = blob_list ();
802 
803  if (blob_it.empty ())
804  return;
805  row_box = blob_it.data ()->bounding_box ();
806  for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ())
807  row_box += blob_it.data ()->bounding_box ();
808 
810  row_box.right () + PROJECTION_MARGIN);
811  projection_left = row_box.left () - PROJECTION_MARGIN;
813  for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) {
814  blob = blob_it.data();
815  if (blob->cblob() != nullptr)
817  }
818 }
819 
820 
821 /**********************************************************************
822  * TO_ROW::clear
823  *
824  * Zero out all scalar members.
825  **********************************************************************/
826 void TO_ROW::clear() {
827  all_caps = false;
828  used_dm_model = false;
829  projection_left = 0;
830  projection_right = 0;
832  fixed_pitch = 0.0;
833  fp_space = 0.0;
834  fp_nonsp = 0.0;
835  pr_space = 0.0;
836  pr_nonsp = 0.0;
837  spacing = 0.0;
838  xheight = 0.0;
839  xheight_evidence = 0;
840  body_size = 0.0;
841  ascrise = 0.0;
842  descdrop = 0.0;
843  min_space = 0;
844  max_nonspace = 0;
845  space_threshold = 0;
846  kern_size = 0.0;
847  space_size = 0.0;
848  y_min = 0.0;
849  y_max = 0.0;
850  initial_y_min = 0.0;
851  m = 0.0;
852  c = 0.0;
853  error = 0.0;
854  para_c = 0.0;
855  para_error = 0.0;
856  y_origin = 0.0;
857  credibility = 0.0;
858  num_repeated_sets_ = -1;
859 }
860 
861 
862 /**********************************************************************
863  * vertical_cblob_projection
864  *
865  * Compute the vertical projection of a cblob from its outlines
866  * and add to the given STATS.
867  **********************************************************************/
868 
869 void vertical_cblob_projection( //project outlines
870  C_BLOB *blob, //blob to project
871  STATS *stats //output
872  ) {
873  //outlines of blob
874  C_OUTLINE_IT out_it = blob->out_list ();
875 
876  for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
877  vertical_coutline_projection (out_it.data (), stats);
878  }
879 }
880 
881 
882 /**********************************************************************
883  * vertical_coutline_projection
884  *
885  * Compute the vertical projection of a outline from its outlines
886  * and add to the given STATS.
887  **********************************************************************/
888 
889 void vertical_coutline_projection( //project outlines
890  C_OUTLINE *outline, //outline to project
891  STATS *stats //output
892  ) {
893  ICOORD pos; //current point
894  ICOORD step; //edge step
895  int32_t length; //of outline
896  int16_t stepindex; //current step
897  C_OUTLINE_IT out_it = outline->child ();
898 
899  pos = outline->start_pos ();
900  length = outline->pathlength ();
901  for (stepindex = 0; stepindex < length; stepindex++) {
902  step = outline->step (stepindex);
903  if (step.x () > 0) {
904  stats->add (pos.x (), -pos.y ());
905  } else if (step.x () < 0) {
906  stats->add (pos.x () - 1, pos.y ());
907  }
908  pos += step;
909  }
910 
911  for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
912  vertical_coutline_projection (out_it.data (), stats);
913  }
914 }
915 
916 
917 /**********************************************************************
918  * TO_BLOCK::TO_BLOCK
919  *
920  * Constructor to make a TO_BLOCK from a real block.
921  **********************************************************************/
922 
923 TO_BLOCK::TO_BLOCK( //make a block
924  BLOCK *src_block //real block
925  ) {
926  clear();
927  block = src_block;
928 }
929 
930 static void clear_blobnboxes(BLOBNBOX_LIST* boxes) {
931  BLOBNBOX_IT it = boxes;
932  // A BLOBNBOX generally doesn't own its blobs, so if they do, you
933  // have to delete them explicitly.
934  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
935  BLOBNBOX* box = it.data();
936  delete box->cblob();
937  }
938 }
939 
940 /**********************************************************************
941  * TO_BLOCK::clear
942  *
943  * Zero out all scalar members.
944  **********************************************************************/
946  block = nullptr;
948  line_spacing = 0.0;
949  line_size = 0.0;
950  max_blob_size = 0.0;
951  baseline_offset = 0.0;
952  xheight = 0.0;
953  fixed_pitch = 0.0;
954  kern_size = 0.0;
955  space_size = 0.0;
956  min_space = 0;
957  max_nonspace = 0;
958  fp_space = 0.0;
959  fp_nonsp = 0.0;
960  pr_space = 0.0;
961  pr_nonsp = 0.0;
962  key_row = nullptr;
963 }
964 
965 
967  // Any residual BLOBNBOXes at this stage own their blobs, so delete them.
968  clear_blobnboxes(&blobs);
969  clear_blobnboxes(&underlines);
970  clear_blobnboxes(&noise_blobs);
971  clear_blobnboxes(&small_blobs);
972  clear_blobnboxes(&large_blobs);
973 }
974 
975 // Helper function to divide the input blobs over noise, small, medium
976 // and large lists. Blobs small in height and (small in width or large in width)
977 // go in the noise list. Dash (-) candidates go in the small list, and
978 // medium and large are by height.
979 // SIDE-EFFECT: reset all blobs to initial state by calling Init().
980 static void SizeFilterBlobs(int min_height, int max_height,
981  BLOBNBOX_LIST* src_list,
982  BLOBNBOX_LIST* noise_list,
983  BLOBNBOX_LIST* small_list,
984  BLOBNBOX_LIST* medium_list,
985  BLOBNBOX_LIST* large_list) {
986  BLOBNBOX_IT noise_it(noise_list);
987  BLOBNBOX_IT small_it(small_list);
988  BLOBNBOX_IT medium_it(medium_list);
989  BLOBNBOX_IT large_it(large_list);
990  for (BLOBNBOX_IT src_it(src_list); !src_it.empty(); src_it.forward()) {
991  BLOBNBOX* blob = src_it.extract();
992  blob->ReInit();
993  int width = blob->bounding_box().width();
994  int height = blob->bounding_box().height();
995  if (height < min_height &&
996  (width < min_height || width > max_height))
997  noise_it.add_after_then_move(blob);
998  else if (height > max_height)
999  large_it.add_after_then_move(blob);
1000  else if (height < min_height)
1001  small_it.add_after_then_move(blob);
1002  else
1003  medium_it.add_after_then_move(blob);
1004  }
1005 }
1006 
1007 // Reorganize the blob lists with a different definition of small, medium
1008 // and large, compared to the original definition.
1009 // Height is still the primary filter key, but medium width blobs of small
1010 // height become small, and very wide blobs of small height stay noise, along
1011 // with small dot-shaped blobs.
1013  int min_height = IntCastRounded(kMinMediumSizeRatio * line_size);
1014  int max_height = IntCastRounded(kMaxMediumSizeRatio * line_size);
1015  BLOBNBOX_LIST noise_list;
1016  BLOBNBOX_LIST small_list;
1017  BLOBNBOX_LIST medium_list;
1018  BLOBNBOX_LIST large_list;
1019  SizeFilterBlobs(min_height, max_height, &blobs,
1020  &noise_list, &small_list, &medium_list, &large_list);
1021  SizeFilterBlobs(min_height, max_height, &large_blobs,
1022  &noise_list, &small_list, &medium_list, &large_list);
1023  SizeFilterBlobs(min_height, max_height, &small_blobs,
1024  &noise_list, &small_list, &medium_list, &large_list);
1025  SizeFilterBlobs(min_height, max_height, &noise_blobs,
1026  &noise_list, &small_list, &medium_list, &large_list);
1027  BLOBNBOX_IT blob_it(&blobs);
1028  blob_it.add_list_after(&medium_list);
1029  blob_it.set_to_list(&large_blobs);
1030  blob_it.add_list_after(&large_list);
1031  blob_it.set_to_list(&small_blobs);
1032  blob_it.add_list_after(&small_list);
1033  blob_it.set_to_list(&noise_blobs);
1034  blob_it.add_list_after(&noise_list);
1035 }
1036 
1037 // Deletes noise blobs from all lists where not owned by a ColPartition.
1047 }
1048 
1049 // Computes and stores the edge offsets on each blob for use in feature
1050 // extraction, using greyscale if the supplied grey and thresholds pixes
1051 // are 8-bit or otherwise (if nullptr or not 8 bit) the original binary
1052 // edge step outlines.
1053 // Thresholds must either be the same size as grey or an integer down-scale
1054 // of grey.
1055 // See coutln.h for an explanation of edge offsets.
1056 void TO_BLOCK::ComputeEdgeOffsets(Pix* thresholds, Pix* grey) {
1057  BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &blobs);
1058  BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &small_blobs);
1059  BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &noise_blobs);
1060 }
1061 
1062 #ifndef GRAPHICS_DISABLED
1063 // Draw the noise blobs from all lists in red.
1069 }
1070 
1071 // Draw the blobs on the various lists in the block in different colors.
1075  win);
1077  win);
1079 }
1080 
1081 /**********************************************************************
1082  * plot_blob_list
1083  *
1084  * Draw a list of blobs.
1085  **********************************************************************/
1086 
1087 void plot_blob_list(ScrollView* win, // window to draw in
1088  BLOBNBOX_LIST *list, // blob list
1089  ScrollView::Color body_colour, // colour to draw
1090  ScrollView::Color child_colour) { // colour of child
1091  BLOBNBOX_IT it = list;
1092  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1093  it.data()->plot(win, body_colour, child_colour);
1094  }
1095 }
1096 #endif // GRAPHICS_DISABLED
float fp_space
Definition: blobbox.h:807
bool used_dm_model
Definition: blobbox.h:660
void ReSetAndReFilterBlobs()
Definition: blobbox.cpp:1012
float area_stroke_width() const
Definition: blobbox.h:350
TBOX box_next_pre_chopped(BLOBNBOX_IT *it)
Definition: blobbox.cpp:666
bool DeletableNoise() const
Definition: blobbox.h:204
BlobNeighbourDir
Definition: blobbox.h:88
float descdrop
Definition: blobbox.h:673
int GoodTextBlob() const
Definition: blobbox.cpp:227
void set_vert_possible(bool value)
Definition: blobbox.h:305
void insert_blob(BLOBNBOX *blob)
Definition: blobbox.cpp:770
bool DefiniteIndividualFlow()
Definition: blobbox.cpp:253
TO_ROW * key_row
Definition: blobbox.h:811
int xheight_evidence
Definition: blobbox.h:671
static void PlotBlobs(BLOBNBOX_LIST *list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView *win)
Definition: blobbox.cpp:420
#define TRUE
Definition: capi.h:51
void compute_vertical_projection()
Definition: blobbox.cpp:797
void rotate(const FCOORD &vec)
Definition: rect.h:197
float horz_stroke_width() const
Definition: blobbox.h:338
int NoisyNeighbours() const
Definition: blobbox.cpp:238
float fixed_pitch
Definition: blobbox.h:802
int y_gap(const TBOX &box) const
Definition: rect.h:233
void set_diacritic_box(const TBOX &diacritic_box)
Definition: blobbox.h:399
float fixed_pitch
Definition: blobbox.h:664
TO_ROW()
Definition: blobbox.h:561
static void PlotNoiseBlobs(BLOBNBOX_LIST *list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView *win)
Definition: blobbox.cpp:433
int count(LIST var_list)
Definition: oldlist.cpp:98
void add_blob(BLOBNBOX *blob, float top, float bottom, float row_size)
Definition: blobbox.cpp:734
TBOX BoundsWithinLimits(int left, int right)
Definition: blobbox.cpp:334
int16_t y() const
access_function
Definition: points.h:57
float kern_size
Definition: blobbox.h:803
float spacing
Definition: blobbox.h:669
bool IsDiacritic() const
Definition: blobbox.h:381
BlobRegionType
Definition: blobbox.h:73
Definition: rect.h:34
const ICOORD & start_pos() const
Definition: coutln.h:148
void merge(BLOBNBOX *nextblob)
Definition: blobbox.cpp:93
int x_gap(const TBOX &box) const
Definition: rect.h:225
void plot_noise_blobs(ScrollView *to_win)
Definition: blobbox.cpp:1064
void plot(ScrollView *window, ScrollView::Color blob_colour, ScrollView::Color child_colour)
Definition: blobbox.cpp:486
float fp_nonsp
Definition: blobbox.h:808
int16_t projection_right
Definition: blobbox.h:662
void plot_blob_list(ScrollView *win, BLOBNBOX_LIST *list, ScrollView::Color body_colour, ScrollView::Color child_colour)
Definition: blobbox.cpp:1087
void ReInit()
Definition: blobbox.h:482
BlobTextFlowType
Definition: blobbox.h:115
void MinMaxGapsClipped(int *h_min, int *h_max, int *v_min, int *v_max) const
Definition: blobbox.cpp:201
void ComputeEdgeOffsets(int threshold, Pix *pix)
Definition: stepblob.cpp:415
float space_size
Definition: blobbox.h:680
Definition: statistc.h:33
TBOX box_next(BLOBNBOX_IT *it)
Definition: blobbox.cpp:637
float pr_nonsp
Definition: blobbox.h:668
int32_t max_nonspace
Definition: blobbox.h:806
int32_t perimeter()
Definition: stepblob.cpp:294
float space_size
Definition: blobbox.h:804
float line_spacing
Definition: blobbox.h:792
void set_right(int x)
Definition: rect.h:82
#define ELIST2IZE(CLASSNAME)
Definition: elst2.h:961
int16_t width() const
Definition: rect.h:115
const double kComplexShapePerimeterRatio
Definition: blobbox.cpp:49
float xheight
Definition: blobbox.h:670
void reflect_box_in_y_axis()
Definition: blobbox.cpp:63
void plot_graded_blobs(ScrollView *to_win)
Definition: blobbox.cpp:1072
void set_horz_possible(bool value)
Definition: blobbox.h:311
float max_blob_size
Definition: blobbox.h:799
int16_t left() const
Definition: rect.h:72
void rotate_box(FCOORD rotation)
Definition: blobbox.cpp:72
float pr_space
Definition: blobbox.h:667
bool NearlyEqual(T x, T y, T tolerance)
Definition: host.h:60
float baseline_offset
Definition: blobbox.h:800
const double kMinMediumSizeRatio
Definition: blobbox.cpp:51
int16_t top() const
Definition: rect.h:58
PITCH_TYPE pitch_decision
Definition: blobbox.h:791
bool good_stroke_neighbour(BlobNeighbourDir n) const
Definition: blobbox.h:374
integer coordinate
Definition: points.h:32
int16_t x() const
access function
Definition: points.h:53
STATS projection
Definition: blobbox.h:684
void EstimateBaselinePosition()
Definition: blobbox.cpp:358
float fp_space
Definition: blobbox.h:665
TO_BLOCK()
Definition: blobbox.h:708
float kern_size
Definition: blobbox.h:679
BlobRegionType region_type() const
Definition: blobbox.h:284
bool set_range(int32_t min_bucket_value, int32_t max_bucket_value_plus_1)
Definition: statistc.cpp:63
bool joined_to_prev() const
Definition: blobbox.h:257
int IntCastRounded(double x)
Definition: helpers.h:168
void find_cblob_hlimits(C_BLOB *blob, float bottomy, float topy, float &xmin, float &xmax)
Definition: blobbox.cpp:577
const double kMaxMediumSizeRatio
Definition: blobbox.cpp:53
const double kCosSmallAngle
Definition: blobbox.cpp:45
float xheight
Definition: blobbox.h:801
int32_t min_space
Definition: blobbox.h:805
const double kDefiniteAspectRatio
Definition: blobbox.cpp:47
void NeighbourGaps(int gaps[BND_COUNT]) const
Definition: blobbox.cpp:182
#define ELISTIZE(CLASSNAME)
Definition: elst.h:961
int32_t pathlength() const
Definition: coutln.h:135
~TO_BLOCK()
Definition: blobbox.cpp:966
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:37
float vert_stroke_width() const
Definition: blobbox.h:344
void ComputeEdgeOffsets(Pix *thresholds, Pix *grey)
Definition: blobbox.cpp:1056
int32_t space_threshold
Definition: blobbox.h:678
float ascrise
Definition: blobbox.h:672
void chop(BLOBNBOX_IT *start_it, BLOBNBOX_IT *blob_it, FCOORD rotation, float xheight)
Definition: blobbox.cpp:121
Definition: ocrblock.h:30
void CleanNeighbours()
Definition: blobbox.cpp:215
float pr_nonsp
Definition: blobbox.h:810
void DeleteUnownedNoise()
Definition: blobbox.cpp:1038
static void ComputeEdgeOffsets(Pix *thresholds, Pix *grey, BLOBNBOX_LIST *blobs)
Definition: blobbox.cpp:386
void rotate(const FCOORD &vec)
Definition: points.h:537
void add(int32_t value, int32_t count)
Definition: statistc.cpp:100
C_OUTLINE_LIST * child()
Definition: coutln.h:108
int16_t x
Definition: blobs.h:78
BLOCK * block
Definition: blobbox.h:790
void set_left(int x)
Definition: rect.h:75
void print() const
Definition: blobbox.cpp:718
int16_t EstimateBaselinePosition()
Definition: stepblob.cpp:433
TBOX bounding_box() const
Definition: stepblob.cpp:255
C_BLOB * crotate_cblob(C_BLOB *blob, FCOORD rotation)
Definition: blobbox.cpp:612
float pr_space
Definition: blobbox.h:809
C_OUTLINE_LIST * out_list()
Definition: stepblob.h:70
void vertical_coutline_projection(C_OUTLINE *outline, STATS *stats)
Definition: blobbox.cpp:889
void find_cblob_limits(C_BLOB *blob, float leftx, float rightx, FCOORD rotation, float &ymin, float &ymax)
Definition: blobbox.cpp:500
int32_t area()
Definition: stepblob.cpp:275
Definition: points.h:189
const TBOX & bounding_box() const
Definition: blobbox.h:231
int16_t projection_left
Definition: blobbox.h:661
void rotate(FCOORD rotation)
Definition: blobbox.cpp:56
int32_t min_space
Definition: blobbox.h:676
bool ConfirmNoTabViolation(const BLOBNBOX &other) const
Definition: blobbox.cpp:293
void plot(ScrollView *window, ScrollView::Color blob_colour, ScrollView::Color child_colour)
Definition: stepblob.cpp:538
float body_size
Definition: blobbox.h:674
float fp_nonsp
Definition: blobbox.h:666
int16_t right() const
Definition: rect.h:79
float x() const
Definition: points.h:208
BLOBNBOX_LIST blobs
Definition: blobbox.h:785
ScrollView::Color BoxColor() const
Definition: blobbox.cpp:482
bool all_caps
Definition: blobbox.h:659
static void DeleteNoiseBlobs(BLOBNBOX_LIST *blobs)
Definition: blobbox.cpp:373
BLOBNBOX_LIST large_blobs
Definition: blobbox.h:789
#define PROJECTION_MARGIN
Definition: blobbox.cpp:38
int32_t max_nonspace
Definition: blobbox.h:677
Definition: blobs.h:57
int16_t y
Definition: blobs.h:79
BLOBNBOX * neighbour(BlobNeighbourDir n) const
Definition: blobbox.h:371
int16_t bottom() const
Definition: rect.h:65
BLOBNBOX_LIST underlines
Definition: blobbox.h:786
BLOBNBOX()
Definition: blobbox.h:147
void find_cblob_vlimits(C_BLOB *blob, float leftx, float rightx, float &ymin, float &ymax)
Definition: blobbox.cpp:540
ICOORD step(int index) const
Definition: coutln.h:144
int16_t height() const
Definition: rect.h:108
void vertical_cblob_projection(C_BLOB *blob, STATS *stats)
Definition: blobbox.cpp:869
void really_merge(BLOBNBOX *other)
Definition: blobbox.cpp:104
C_BLOB * cblob() const
Definition: blobbox.h:269
BLOBNBOX_LIST * blob_list()
Definition: blobbox.h:612
void clear()
Definition: blobbox.cpp:945
void compute_bounding_box()
Definition: blobbox.h:241
BLOBNBOX_LIST small_blobs
Definition: blobbox.h:788
bool MatchingStrokeWidth(const BLOBNBOX &other, double fractional_tolerance, double constant_tolerance) const
Definition: blobbox.cpp:306
BLOBNBOX_LIST noise_blobs
Definition: blobbox.h:787
void UpdateRange(const T1 &x, T2 *lower_bound, T2 *upper_bound)
Definition: helpers.h:121
float line_size
Definition: blobbox.h:798
#define ASSERT_HOST(x)
Definition: errcode.h:84
PITCH_TYPE pitch_decision
Definition: blobbox.h:663
static ScrollView::Color TextlineColor(BlobRegionType region_type, BlobTextFlowType flow_type)
Definition: blobbox.cpp:445