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