tesseract  4.00.00dev
blobs.cpp
Go to the documentation of this file.
1 /* -*-C-*-
2  ********************************************************************************
3  *
4  * File: blobs.c (Formerly blobs.c)
5  * Description: Blob definition
6  * Author: Mark Seaman, OCR Technology
7  * Created: Fri Oct 27 15:39:52 1989
8  * Modified: Thu Mar 28 15:33:26 1991 (Mark Seaman) marks@hpgrlt
9  * Language: C
10  * Package: N/A
11  * Status: Experimental (Do Not Distribute)
12  *
13  * (c) Copyright 1989, Hewlett-Packard Company.
14  ** Licensed under the Apache License, Version 2.0 (the "License");
15  ** you may not use this file except in compliance with the License.
16  ** You may obtain a copy of the License at
17  ** http://www.apache.org/licenses/LICENSE-2.0
18  ** Unless required by applicable law or agreed to in writing, software
19  ** distributed under the License is distributed on an "AS IS" BASIS,
20  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  ** See the License for the specific language governing permissions and
22  ** limitations under the License.
23  *
24  *********************************************************************************/
25 
26 /*----------------------------------------------------------------------
27  I n c l u d e s
28 ----------------------------------------------------------------------*/
29 // Include automatically generated configuration file if running autoconf.
30 #ifdef HAVE_CONFIG_H
31 #include "config_auto.h"
32 #endif
33 
34 #include "blobs.h"
35 #include "ccstruct.h"
36 #include "clst.h"
37 #include "cutil.h"
38 #include "emalloc.h"
39 #include "helpers.h"
40 #include "linlsq.h"
41 #include "ndminx.h"
42 #include "normalis.h"
43 #include "ocrblock.h"
44 #include "ocrrow.h"
45 #include "points.h"
46 #include "polyaprx.h"
47 #include "structures.h"
48 #include "werd.h"
49 
51 
52 // A Vector representing the "vertical" direction when measuring the
53 // divisiblity of blobs into multiple blobs just by separating outlines.
54 // See divisible_blob below for the use.
56 // A vector representing the "vertical" direction for italic text for use
57 // when separating outlines. Using it actually deteriorates final accuracy,
58 // so it is only used for ApplyBoxes chopping to get a better segmentation.
60 
61 /*----------------------------------------------------------------------
62  F u n c t i o n s
63 ----------------------------------------------------------------------*/
64 
66 
67 // Returns true when the two line segments cross each other.
68 // (Moved from outlines.cpp).
69 // Finds where the projected lines would cross and then checks to see if the
70 // point of intersection lies on both of the line segments. If it does
71 // then these two segments cross.
72 /* static */
73 bool TPOINT::IsCrossed(const TPOINT& a0, const TPOINT& a1, const TPOINT& b0,
74  const TPOINT& b1) {
75  int b0a1xb0b1, b0b1xb0a0;
76  int a1b1xa1a0, a1a0xa1b0;
77 
78  TPOINT b0a1, b0a0, a1b1, b0b1, a1a0;
79 
80  b0a1.x = a1.x - b0.x;
81  b0a0.x = a0.x - b0.x;
82  a1b1.x = b1.x - a1.x;
83  b0b1.x = b1.x - b0.x;
84  a1a0.x = a0.x - a1.x;
85  b0a1.y = a1.y - b0.y;
86  b0a0.y = a0.y - b0.y;
87  a1b1.y = b1.y - a1.y;
88  b0b1.y = b1.y - b0.y;
89  a1a0.y = a0.y - a1.y;
90 
91  b0a1xb0b1 = CROSS(b0a1, b0b1);
92  b0b1xb0a0 = CROSS(b0b1, b0a0);
93  a1b1xa1a0 = CROSS(a1b1, a1a0);
94  // For clarity, we want CROSS(a1a0,a1b0) here but we have b0a1 instead of a1b0
95  // so use -CROSS(a1b0,b0a1) instead, which is the same.
96  a1a0xa1b0 = -CROSS(a1a0, b0a1);
97 
98  return ((b0a1xb0b1 > 0 && b0b1xb0a0 > 0) ||
99  (b0a1xb0b1 < 0 && b0b1xb0a0 < 0)) &&
100  ((a1b1xa1a0 > 0 && a1a0xa1b0 > 0) || (a1b1xa1a0 < 0 && a1a0xa1b0 < 0));
101 }
102 
103 // Consume the circular list of EDGEPTs to make a TESSLINE.
105  TESSLINE* result = new TESSLINE;
106  result->loop = outline;
107  if (outline->src_outline != NULL) {
108  // ASSUMPTION: This function is only ever called from ApproximateOutline
109  // and therefore either all points have a src_outline or all do not.
110  // Just as SetupFromPos sets the vectors from the vertices, setup the
111  // step_count members to indicate the (positive) number of original
112  // C_OUTLINE steps to the next vertex.
113  EDGEPT* pt = outline;
114  do {
115  pt->step_count = pt->next->start_step - pt->start_step;
116  if (pt->step_count < 0)
117  pt->step_count += pt->src_outline->pathlength();
118  pt = pt->next;
119  } while (pt != outline);
120  }
121  result->SetupFromPos();
122  return result;
123 }
124 
125 // Copies the data and the outline, but leaves next untouched.
126 void TESSLINE::CopyFrom(const TESSLINE& src) {
127  Clear();
128  topleft = src.topleft;
129  botright = src.botright;
130  start = src.start;
131  is_hole = src.is_hole;
132  if (src.loop != NULL) {
133  EDGEPT* prevpt = NULL;
134  EDGEPT* newpt = NULL;
135  EDGEPT* srcpt = src.loop;
136  do {
137  newpt = new EDGEPT(*srcpt);
138  if (prevpt == NULL) {
139  loop = newpt;
140  } else {
141  newpt->prev = prevpt;
142  prevpt->next = newpt;
143  }
144  prevpt = newpt;
145  srcpt = srcpt->next;
146  } while (srcpt != src.loop);
147  loop->prev = newpt;
148  newpt->next = loop;
149  }
150 }
151 
152 // Deletes owned data.
154  if (loop == NULL)
155  return;
156 
157  EDGEPT* this_edge = loop;
158  do {
159  EDGEPT* next_edge = this_edge->next;
160  delete this_edge;
161  this_edge = next_edge;
162  } while (this_edge != loop);
163  loop = NULL;
164 }
165 
166 // Normalize in-place using the DENORM.
167 void TESSLINE::Normalize(const DENORM& denorm) {
168  EDGEPT* pt = loop;
169  do {
170  denorm.LocalNormTransform(pt->pos, &pt->pos);
171  pt = pt->next;
172  } while (pt != loop);
173  SetupFromPos();
174 }
175 
176 // Rotates by the given rotation in place.
177 void TESSLINE::Rotate(const FCOORD rot) {
178  EDGEPT* pt = loop;
179  do {
180  int tmp = static_cast<int>(floor(pt->pos.x * rot.x() -
181  pt->pos.y * rot.y() + 0.5));
182  pt->pos.y = static_cast<int>(floor(pt->pos.y * rot.x() +
183  pt->pos.x * rot.y() + 0.5));
184  pt->pos.x = tmp;
185  pt = pt->next;
186  } while (pt != loop);
187  SetupFromPos();
188 }
189 
190 // Moves by the given vec in place.
191 void TESSLINE::Move(const ICOORD vec) {
192  EDGEPT* pt = loop;
193  do {
194  pt->pos.x += vec.x();
195  pt->pos.y += vec.y();
196  pt = pt->next;
197  } while (pt != loop);
198  SetupFromPos();
199 }
200 
201 // Scales by the given factor in place.
202 void TESSLINE::Scale(float factor) {
203  EDGEPT* pt = loop;
204  do {
205  pt->pos.x = static_cast<int>(floor(pt->pos.x * factor + 0.5));
206  pt->pos.y = static_cast<int>(floor(pt->pos.y * factor + 0.5));
207  pt = pt->next;
208  } while (pt != loop);
209  SetupFromPos();
210 }
211 
212 // Sets up the start and vec members of the loop from the pos members.
214  EDGEPT* pt = loop;
215  do {
216  pt->vec.x = pt->next->pos.x - pt->pos.x;
217  pt->vec.y = pt->next->pos.y - pt->pos.y;
218  pt = pt->next;
219  } while (pt != loop);
220  start = pt->pos;
221  ComputeBoundingBox();
222 }
223 
224 // Recomputes the bounding box from the points in the loop.
226  int minx = MAX_INT32;
227  int miny = MAX_INT32;
228  int maxx = -MAX_INT32;
229  int maxy = -MAX_INT32;
230 
231  // Find boundaries.
232  start = loop->pos;
233  EDGEPT* this_edge = loop;
234  do {
235  if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) {
236  if (this_edge->pos.x < minx)
237  minx = this_edge->pos.x;
238  if (this_edge->pos.y < miny)
239  miny = this_edge->pos.y;
240  if (this_edge->pos.x > maxx)
241  maxx = this_edge->pos.x;
242  if (this_edge->pos.y > maxy)
243  maxy = this_edge->pos.y;
244  }
245  this_edge = this_edge->next;
246  } while (this_edge != loop);
247  // Reset bounds.
248  topleft.x = minx;
249  topleft.y = maxy;
250  botright.x = maxx;
251  botright.y = miny;
252 }
253 
254 // Computes the min and max cross product of the outline points with the
255 // given vec and returns the results in min_xp and max_xp. Geometrically
256 // this is the left and right edge of the outline perpendicular to the
257 // given direction, but to get the distance units correct, you would
258 // have to divide by the modulus of vec.
260  int* min_xp, int* max_xp) const {
261  *min_xp = MAX_INT32;
262  *max_xp = MIN_INT32;
263  EDGEPT* this_edge = loop;
264  do {
265  if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) {
266  int product = CROSS(this_edge->pos, vec);
267  UpdateRange(product, min_xp, max_xp);
268  }
269  this_edge = this_edge->next;
270  } while (this_edge != loop);
271 }
272 
274  return TBOX(topleft.x, botright.y, botright.x, topleft.y);
275 }
276 
277 #ifndef GRAPHICS_DISABLED
279  ScrollView::Color child_color) {
280  if (is_hole)
281  window->Pen(child_color);
282  else
283  window->Pen(color);
284  window->SetCursor(start.x, start.y);
285  EDGEPT* pt = loop;
286  do {
287  bool prev_hidden = pt->IsHidden();
288  pt = pt->next;
289  if (prev_hidden)
290  window->SetCursor(pt->pos.x, pt->pos.y);
291  else
292  window->DrawTo(pt->pos.x, pt->pos.y);
293  } while (pt != loop);
294 }
295 #endif // GRAPHICS_DISABLED
296 
297 // Returns the first non-hidden EDGEPT that has a different src_outline to
298 // its predecessor, or, if all the same, the lowest indexed point.
300  EDGEPT* best_start = loop;
301  int best_step = loop->start_step;
302  // Iterate the polygon.
303  EDGEPT* pt = loop;
304  do {
305  if (pt->IsHidden()) continue;
306  if (pt->prev->IsHidden() || pt->prev->src_outline != pt->src_outline)
307  return pt; // Qualifies as the best.
308  if (pt->start_step < best_step) {
309  best_step = pt->start_step;
310  best_start = pt;
311  }
312  } while ((pt = pt->next) != loop);
313  return best_start;
314 }
315 
316 // Iterate the given list of outlines, converting to TESSLINE by polygonal
317 // approximation and recursively any children, returning the current tail
318 // of the resulting list of TESSLINEs.
319 static TESSLINE** ApproximateOutlineList(bool allow_detailed_fx,
320  C_OUTLINE_LIST* outlines,
321  bool children,
322  TESSLINE** tail) {
323  C_OUTLINE_IT ol_it(outlines);
324  for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) {
325  C_OUTLINE* outline = ol_it.data();
326  if (outline->pathlength() > 0) {
327  TESSLINE* tessline = ApproximateOutline(allow_detailed_fx, outline);
328  tessline->is_hole = children;
329  *tail = tessline;
330  tail = &tessline->next;
331  }
332  if (!outline->child()->empty()) {
333  tail = ApproximateOutlineList(allow_detailed_fx, outline->child(), true,
334  tail);
335  }
336  }
337  return tail;
338 }
339 
340 // Factory to build a TBLOB from a C_BLOB with polygonal approximation along
341 // the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB
342 // contain pointers to the input C_OUTLINEs that enable higher-resolution
343 // feature extraction that does not use the polygonal approximation.
344 TBLOB* TBLOB::PolygonalCopy(bool allow_detailed_fx, C_BLOB* src) {
345  TBLOB* tblob = new TBLOB;
346  ApproximateOutlineList(allow_detailed_fx, src->out_list(), false,
347  &tblob->outlines);
348  return tblob;
349 }
350 
351 // Factory builds a blob with no outlines, but copies the other member data.
353  TBLOB* blob = new TBLOB;
354  blob->denorm_ = src.denorm_;
355  return blob;
356 }
357 
358 // Normalizes the blob for classification only if needed.
359 // (Normally this means a non-zero classify rotation.)
360 // If no Normalization is needed, then NULL is returned, and the input blob
361 // can be used directly. Otherwise a new TBLOB is returned which must be
362 // deleted after use.
364  TBLOB* rotated_blob = NULL;
365  // If necessary, copy the blob and rotate it. The rotation is always
366  // +/- 90 degrees, as 180 was already taken care of.
367  if (denorm_.block() != NULL &&
368  denorm_.block()->classify_rotation().y() != 0.0) {
369  TBOX box = bounding_box();
370  int x_middle = (box.left() + box.right()) / 2;
371  int y_middle = (box.top() + box.bottom()) / 2;
372  rotated_blob = new TBLOB(*this);
373  const FCOORD& rotation = denorm_.block()->classify_rotation();
374  // Move the rotated blob back to the same y-position so that we
375  // can still distinguish similar glyphs with differeny y-position.
376  float target_y = kBlnBaselineOffset +
377  (rotation.y() > 0 ? x_middle - box.left() : box.right() - x_middle);
378  rotated_blob->Normalize(NULL, &rotation, &denorm_, x_middle, y_middle,
379  1.0f, 1.0f, 0.0f, target_y,
380  denorm_.inverse(), denorm_.pix());
381  }
382  return rotated_blob;
383 }
384 
385 // Copies the data and the outline, but leaves next untouched.
386 void TBLOB::CopyFrom(const TBLOB& src) {
387  Clear();
388  TESSLINE* prev_outline = NULL;
389  for (TESSLINE* srcline = src.outlines; srcline != NULL;
390  srcline = srcline->next) {
391  TESSLINE* new_outline = new TESSLINE(*srcline);
392  if (outlines == NULL)
393  outlines = new_outline;
394  else
395  prev_outline->next = new_outline;
396  prev_outline = new_outline;
397  }
398  denorm_ = src.denorm_;
399 }
400 
401 // Deletes owned data.
402 void TBLOB::Clear() {
403  for (TESSLINE* next_outline = NULL; outlines != NULL;
404  outlines = next_outline) {
405  next_outline = outlines->next;
406  delete outlines;
407  }
408 }
409 
410 // Sets up the built-in DENORM and normalizes the blob in-place.
411 // For parameters see DENORM::SetupNormalization, plus the inverse flag for
412 // this blob and the Pix for the full image.
413 void TBLOB::Normalize(const BLOCK* block,
414  const FCOORD* rotation,
415  const DENORM* predecessor,
416  float x_origin, float y_origin,
417  float x_scale, float y_scale,
418  float final_xshift, float final_yshift,
419  bool inverse, Pix* pix) {
420  denorm_.SetupNormalization(block, rotation, predecessor, x_origin, y_origin,
421  x_scale, y_scale, final_xshift, final_yshift);
422  denorm_.set_inverse(inverse);
423  denorm_.set_pix(pix);
424  // TODO(rays) outline->Normalize is more accurate, but breaks tests due
425  // the changes it makes. Reinstate this code with a retraining.
426  // The reason this change is troublesome is that it normalizes for the
427  // baseline value computed independently at each x-coord. If the baseline
428  // is not horizontal, this introduces shear into the normalized blob, which
429  // is useful on the rare occasions that the baseline is really curved, but
430  // the baselines need to be stabilized the rest of the time.
431 #if 0
432  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
433  outline->Normalize(denorm_);
434  }
435 #else
436  denorm_.LocalNormBlob(this);
437 #endif
438 }
439 
440 // Rotates by the given rotation in place.
441 void TBLOB::Rotate(const FCOORD rotation) {
442  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
443  outline->Rotate(rotation);
444  }
445 }
446 
447 // Moves by the given vec in place.
448 void TBLOB::Move(const ICOORD vec) {
449  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
450  outline->Move(vec);
451  }
452 }
453 
454 // Scales by the given factor in place.
455 void TBLOB::Scale(float factor) {
456  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
457  outline->Scale(factor);
458  }
459 }
460 
461 // Recomputes the bounding boxes of the outlines.
463  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
464  outline->ComputeBoundingBox();
465  }
466 }
467 
468 // Returns the number of outlines.
469 int TBLOB::NumOutlines() const {
470  int result = 0;
471  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next)
472  ++result;
473  return result;
474 }
475 
476 /**********************************************************************
477  * TBLOB::bounding_box()
478  *
479  * Compute the bounding_box of a compound blob, defined to be the
480  * bounding box of the union of all top-level outlines in the blob.
481  **********************************************************************/
483  if (outlines == NULL)
484  return TBOX(0, 0, 0, 0);
485  TESSLINE *outline = outlines;
486  TBOX box = outline->bounding_box();
487  for (outline = outline->next; outline != NULL; outline = outline->next) {
488  box += outline->bounding_box();
489  }
490  return box;
491 }
492 
493 // Finds and deletes any duplicate outlines in this blob, without deleting
494 // their EDGEPTs.
496  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
497  TESSLINE* last_outline = outline;
498  for (TESSLINE* other_outline = outline->next; other_outline != NULL;
499  last_outline = other_outline, other_outline = other_outline->next) {
500  if (outline->SameBox(*other_outline)) {
501  last_outline->next = other_outline->next;
502  // This doesn't leak - the outlines share the EDGEPTs.
503  other_outline->loop = NULL;
504  delete other_outline;
505  other_outline = last_outline;
506  // If it is part of a cut, then it can't be a hole any more.
507  outline->is_hole = false;
508  }
509  }
510  }
511 }
512 
513 // Swaps the outlines of *this and next if needed to keep the centers in
514 // increasing x.
516  TBOX box = bounding_box();
517  TBOX next_box = next->bounding_box();
518  if (box.x_middle() > next_box.x_middle()) {
519  Swap(&outlines, &next->outlines);
520  }
521 }
522 
523 #ifndef GRAPHICS_DISABLED
525  ScrollView::Color child_color) {
526  for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next)
527  outline->plot(window, color, child_color);
528 }
529 #endif // GRAPHICS_DISABLED
530 
531 // Computes the center of mass and second moments for the old baseline and
532 // 2nd moment normalizations. Returns the outline length.
533 // The input denorm should be the normalizations that have been applied from
534 // the image to the current state of this TBLOB.
535 int TBLOB::ComputeMoments(FCOORD* center, FCOORD* second_moments) const {
536  // Compute 1st and 2nd moments of the original outline.
537  LLSQ accumulator;
538  TBOX box = bounding_box();
539  // Iterate the outlines, accumulating edges relative the box.botleft().
540  CollectEdges(box, NULL, &accumulator, NULL, NULL);
541  *center = accumulator.mean_point() + box.botleft();
542  // The 2nd moments are just the standard deviation of the point positions.
543  double x2nd = sqrt(accumulator.x_variance());
544  double y2nd = sqrt(accumulator.y_variance());
545  if (x2nd < 1.0) x2nd = 1.0;
546  if (y2nd < 1.0) y2nd = 1.0;
547  second_moments->set_x(x2nd);
548  second_moments->set_y(y2nd);
549  return accumulator.count();
550 }
551 
552 // Computes the precise bounding box of the coords that are generated by
553 // GetEdgeCoords. This may be different from the bounding box of the polygon.
554 void TBLOB::GetPreciseBoundingBox(TBOX* precise_box) const {
555  TBOX box = bounding_box();
556  *precise_box = TBOX();
557  CollectEdges(box, precise_box, NULL, NULL, NULL);
558  precise_box->move(box.botleft());
559 }
560 
561 // Adds edges to the given vectors.
562 // For all the edge steps in all the outlines, or polygonal approximation
563 // where there are no edge steps, collects the steps into x_coords/y_coords.
564 // x_coords is a collection of the x-coords of vertical edges for each
565 // y-coord starting at box.bottom().
566 // y_coords is a collection of the y-coords of horizontal edges for each
567 // x-coord starting at box.left().
568 // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom.
569 // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1.
570 void TBLOB::GetEdgeCoords(const TBOX& box,
571  GenericVector<GenericVector<int> >* x_coords,
572  GenericVector<GenericVector<int> >* y_coords) const {
573  GenericVector<int> empty;
574  x_coords->init_to_size(box.height(), empty);
575  y_coords->init_to_size(box.width(), empty);
576  CollectEdges(box, NULL, NULL, x_coords, y_coords);
577  // Sort the output vectors.
578  for (int i = 0; i < x_coords->size(); ++i)
579  (*x_coords)[i].sort();
580  for (int i = 0; i < y_coords->size(); ++i)
581  (*y_coords)[i].sort();
582 }
583 
584 // Accumulates the segment between pt1 and pt2 in the LLSQ, quantizing over
585 // the integer coordinate grid to properly weight long vectors.
586 static void SegmentLLSQ(const FCOORD& pt1, const FCOORD& pt2,
587  LLSQ* accumulator) {
588  FCOORD step(pt2);
589  step -= pt1;
590  int xstart = IntCastRounded(MIN(pt1.x(), pt2.x()));
591  int xend = IntCastRounded(MAX(pt1.x(), pt2.x()));
592  int ystart = IntCastRounded(MIN(pt1.y(), pt2.y()));
593  int yend = IntCastRounded(MAX(pt1.y(), pt2.y()));
594  if (xstart == xend && ystart == yend) return; // Nothing to do.
595  double weight = step.length() / (xend - xstart + yend - ystart);
596  // Compute and save the y-position at the middle of each x-step.
597  for (int x = xstart; x < xend; ++x) {
598  double y = pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x();
599  accumulator->add(x + 0.5, y, weight);
600  }
601  // Compute and save the x-position at the middle of each y-step.
602  for (int y = ystart; y < yend; ++y) {
603  double x = pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y();
604  accumulator->add(x, y + 0.5, weight);
605  }
606 }
607 
608 // Adds any edges from a single segment of outline between pt1 and pt2 to
609 // the x_coords, y_coords vectors. pt1 and pt2 should be relative to the
610 // bottom-left of the bounding box, hence indices to x_coords, y_coords
611 // are clipped to ([0,x_limit], [0,y_limit]).
612 // See GetEdgeCoords above for a description of x_coords, y_coords.
613 static void SegmentCoords(const FCOORD& pt1, const FCOORD& pt2,
614  int x_limit, int y_limit,
615  GenericVector<GenericVector<int> >* x_coords,
616  GenericVector<GenericVector<int> >* y_coords) {
617  FCOORD step(pt2);
618  step -= pt1;
619  int start = ClipToRange(IntCastRounded(MIN(pt1.x(), pt2.x())), 0, x_limit);
620  int end = ClipToRange(IntCastRounded(MAX(pt1.x(), pt2.x())), 0, x_limit);
621  for (int x = start; x < end; ++x) {
622  int y = IntCastRounded(pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x());
623  (*y_coords)[x].push_back(y);
624  }
625  start = ClipToRange(IntCastRounded(MIN(pt1.y(), pt2.y())), 0, y_limit);
626  end = ClipToRange(IntCastRounded(MAX(pt1.y(), pt2.y())), 0, y_limit);
627  for (int y = start; y < end; ++y) {
628  int x = IntCastRounded(pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y());
629  (*x_coords)[y].push_back(x);
630  }
631 }
632 
633 // Adds any edges from a single segment of outline between pt1 and pt2 to
634 // the bbox such that it guarantees to contain anything produced by
635 // SegmentCoords.
636 static void SegmentBBox(const FCOORD& pt1, const FCOORD& pt2, TBOX* bbox) {
637  FCOORD step(pt2);
638  step -= pt1;
639  int x1 = IntCastRounded(MIN(pt1.x(), pt2.x()));
640  int x2 = IntCastRounded(MAX(pt1.x(), pt2.x()));
641  if (x2 > x1) {
642  int y1 = IntCastRounded(pt1.y() + step.y() * (x1 + 0.5 - pt1.x()) /
643  step.x());
644  int y2 = IntCastRounded(pt1.y() + step.y() * (x2 - 0.5 - pt1.x()) /
645  step.x());
646  TBOX point(x1, MIN(y1, y2), x2, MAX(y1, y2));
647  *bbox += point;
648  }
649  int y1 = IntCastRounded(MIN(pt1.y(), pt2.y()));
650  int y2 = IntCastRounded(MAX(pt1.y(), pt2.y()));
651  if (y2 > y1) {
652  int x1 = IntCastRounded(pt1.x() + step.x() * (y1 + 0.5 - pt1.y()) /
653  step.y());
654  int x2 = IntCastRounded(pt1.x() + step.x() * (y2 - 0.5 - pt1.y()) /
655  step.y());
656  TBOX point(MIN(x1, x2), y1, MAX(x1, x2), y2);
657  *bbox += point;
658  }
659 }
660 
661 // Collects edges into the given bounding box, LLSQ accumulator and/or x_coords,
662 // y_coords vectors.
663 // For a description of x_coords/y_coords, see GetEdgeCoords above.
664 // Startpt to lastpt, inclusive, MUST have the same src_outline member,
665 // which may be NULL. The vector from lastpt to its next is included in
666 // the accumulation. Hidden edges should be excluded by the caller.
667 // The input denorm should be the normalizations that have been applied from
668 // the image to the current state of the TBLOB from which startpt, lastpt come.
669 // box is the bounding box of the blob from which the EDGEPTs are taken and
670 // indices into x_coords, y_coords are offset by box.botleft().
671 static void CollectEdgesOfRun(const EDGEPT* startpt, const EDGEPT* lastpt,
672  const DENORM& denorm, const TBOX& box,
673  TBOX* bounding_box,
674  LLSQ* accumulator,
675  GenericVector<GenericVector<int> > *x_coords,
676  GenericVector<GenericVector<int> > *y_coords) {
677  const C_OUTLINE* outline = startpt->src_outline;
678  int x_limit = box.width() - 1;
679  int y_limit = box.height() - 1;
680  if (outline != NULL) {
681  // Use higher-resolution edge points stored on the outline.
682  // The outline coordinates may not match the binary image because of the
683  // rotation for vertical text lines, but the root_denorm IS the matching
684  // start of the DENORM chain.
685  const DENORM* root_denorm = denorm.RootDenorm();
686  int step_length = outline->pathlength();
687  int start_index = startpt->start_step;
688  // Note that if this run straddles the wrap-around point of the outline,
689  // that lastpt->start_step may have a lower index than startpt->start_step,
690  // and we want to use an end_index that allows us to use a positive
691  // increment, so we add step_length if necessary, but that may be beyond the
692  // bounds of the outline steps/ due to wrap-around, so we use % step_length
693  // everywhere, except for start_index.
694  int end_index = lastpt->start_step + lastpt->step_count;
695  if (end_index <= start_index)
696  end_index += step_length;
697  // pos is the integer coordinates of the binary image steps.
698  ICOORD pos = outline->position_at_index(start_index);
699  FCOORD origin(box.left(), box.bottom());
700  // f_pos is a floating-point version of pos that offers improved edge
701  // positioning using greyscale information or smoothing of edge steps.
702  FCOORD f_pos = outline->sub_pixel_pos_at_index(pos, start_index);
703  // pos_normed is f_pos after the appropriate normalization, and relative
704  // to origin.
705  // prev_normed is the previous value of pos_normed.
706  FCOORD prev_normed;
707  denorm.NormTransform(root_denorm, f_pos, &prev_normed);
708  prev_normed -= origin;
709  for (int index = start_index; index < end_index; ++index) {
710  ICOORD step = outline->step(index % step_length);
711  // Only use the point if its edge strength is positive. This excludes
712  // points that don't provide useful information, eg
713  // ___________
714  // |___________
715  // The vertical step provides only noisy, damaging information, as even
716  // with a greyscale image, the positioning of the edge there may be a
717  // fictitious extrapolation, so previous processing has eliminated it.
718  if (outline->edge_strength_at_index(index % step_length) > 0) {
719  FCOORD f_pos = outline->sub_pixel_pos_at_index(pos,
720  index % step_length);
721  FCOORD pos_normed;
722  denorm.NormTransform(root_denorm, f_pos, &pos_normed);
723  pos_normed -= origin;
724  // Accumulate the information that is selected by the caller.
725  if (bounding_box != NULL) {
726  SegmentBBox(pos_normed, prev_normed, bounding_box);
727  }
728  if (accumulator != NULL) {
729  SegmentLLSQ(pos_normed, prev_normed, accumulator);
730  }
731  if (x_coords != NULL && y_coords != NULL) {
732  SegmentCoords(pos_normed, prev_normed, x_limit, y_limit,
733  x_coords, y_coords);
734  }
735  prev_normed = pos_normed;
736  }
737  pos += step;
738  }
739  } else {
740  // There is no outline, so we are forced to use the polygonal approximation.
741  const EDGEPT* endpt = lastpt->next;
742  const EDGEPT* pt = startpt;
743  do {
744  FCOORD next_pos(pt->next->pos.x - box.left(),
745  pt->next->pos.y - box.bottom());
746  FCOORD pos(pt->pos.x - box.left(), pt->pos.y - box.bottom());
747  if (bounding_box != NULL) {
748  SegmentBBox(next_pos, pos, bounding_box);
749  }
750  if (accumulator != NULL) {
751  SegmentLLSQ(next_pos, pos, accumulator);
752  }
753  if (x_coords != NULL && y_coords != NULL) {
754  SegmentCoords(next_pos, pos, x_limit, y_limit, x_coords, y_coords);
755  }
756  } while ((pt = pt->next) != endpt);
757  }
758 }
759 
760 // For all the edge steps in all the outlines, or polygonal approximation
761 // where there are no edge steps, collects the steps into the bounding_box,
762 // llsq and/or the x_coords/y_coords. Both are used in different kinds of
763 // normalization.
764 // For a description of x_coords, y_coords, see GetEdgeCoords above.
765 void TBLOB::CollectEdges(const TBOX& box,
766  TBOX* bounding_box, LLSQ* llsq,
767  GenericVector<GenericVector<int> >* x_coords,
768  GenericVector<GenericVector<int> >* y_coords) const {
769  // Iterate the outlines.
770  for (const TESSLINE* ol = outlines; ol != NULL; ol = ol->next) {
771  // Iterate the polygon.
772  EDGEPT* loop_pt = ol->FindBestStartPt();
773  EDGEPT* pt = loop_pt;
774  if (pt == NULL) continue;
775  do {
776  if (pt->IsHidden()) continue;
777  // Find a run of equal src_outline.
778  EDGEPT* last_pt = pt;
779  do {
780  last_pt = last_pt->next;
781  } while (last_pt != loop_pt && !last_pt->IsHidden() &&
782  last_pt->src_outline == pt->src_outline);
783  last_pt = last_pt->prev;
784  CollectEdgesOfRun(pt, last_pt, denorm_, box,
785  bounding_box, llsq, x_coords, y_coords);
786  pt = last_pt;
787  } while ((pt = pt->next) != loop_pt);
788  }
789 }
790 
791 // Factory to build a TWERD from a (C_BLOB) WERD, with polygonal
792 // approximation along the way.
793 TWERD* TWERD::PolygonalCopy(bool allow_detailed_fx, WERD* src) {
794  TWERD* tessword = new TWERD;
795  tessword->latin_script = src->flag(W_SCRIPT_IS_LATIN);
796  C_BLOB_IT b_it(src->cblob_list());
797  for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
798  C_BLOB* blob = b_it.data();
799  TBLOB* tblob = TBLOB::PolygonalCopy(allow_detailed_fx, blob);
800  tessword->blobs.push_back(tblob);
801  }
802  return tessword;
803 }
804 
805 // Baseline normalizes the blobs in-place, recording the normalization in the
806 // DENORMs in the blobs.
807 void TWERD::BLNormalize(const BLOCK* block, const ROW* row, Pix* pix,
808  bool inverse, float x_height, float baseline_shift,
809  bool numeric_mode, tesseract::OcrEngineMode hint,
810  const TBOX* norm_box,
811  DENORM* word_denorm) {
812  TBOX word_box = bounding_box();
813  if (norm_box != NULL) word_box = *norm_box;
814  float word_middle = (word_box.left() + word_box.right()) / 2.0f;
815  float input_y_offset = 0.0f;
816  float final_y_offset = static_cast<float>(kBlnBaselineOffset);
817  float scale = kBlnXHeight / x_height;
818  if (row == NULL) {
819  word_middle = word_box.left();
820  input_y_offset = word_box.bottom();
821  final_y_offset = 0.0f;
822  } else {
823  input_y_offset = row->base_line(word_middle) + baseline_shift;
824  }
825  for (int b = 0; b < blobs.size(); ++b) {
826  TBLOB* blob = blobs[b];
827  TBOX blob_box = blob->bounding_box();
828  float mid_x = (blob_box.left() + blob_box.right()) / 2.0f;
829  float baseline = input_y_offset;
830  float blob_scale = scale;
831  if (numeric_mode) {
832  baseline = blob_box.bottom();
833  blob_scale = ClipToRange(kBlnXHeight * 4.0f / (3 * blob_box.height()),
834  scale, scale * 1.5f);
835  } else if (row != NULL) {
836  baseline = row->base_line(mid_x) + baseline_shift;
837  }
838  // The image will be 8-bit grey if the input was grey or color. Note that in
839  // a grey image 0 is black and 255 is white. If the input was binary, then
840  // the pix will be binary and 0 is white, with 1 being black.
841  // To tell the difference pixGetDepth() will return 8 or 1.
842  // The inverse flag will be true iff the word has been determined to be
843  // white on black, and is independent of whether the pix is 8 bit or 1 bit.
844  blob->Normalize(block, NULL, NULL, word_middle, baseline, blob_scale,
845  blob_scale, 0.0f, final_y_offset, inverse, pix);
846  }
847  if (word_denorm != NULL) {
848  word_denorm->SetupNormalization(block, NULL, NULL, word_middle,
849  input_y_offset, scale, scale,
850  0.0f, final_y_offset);
851  word_denorm->set_inverse(inverse);
852  word_denorm->set_pix(pix);
853  }
854 }
855 
856 // Copies the data and the blobs, but leaves next untouched.
857 void TWERD::CopyFrom(const TWERD& src) {
858  Clear();
859  latin_script = src.latin_script;
860  for (int b = 0; b < src.blobs.size(); ++b) {
861  TBLOB* new_blob = new TBLOB(*src.blobs[b]);
862  blobs.push_back(new_blob);
863  }
864 }
865 
866 // Deletes owned data.
867 void TWERD::Clear() {
868  blobs.delete_data_pointers();
869  blobs.clear();
870 }
871 
872 // Recomputes the bounding boxes of the blobs.
874  for (int b = 0; b < blobs.size(); ++b) {
875  blobs[b]->ComputeBoundingBoxes();
876  }
877 }
878 
880  TBOX result;
881  for (int b = 0; b < blobs.size(); ++b) {
882  TBOX box = blobs[b]->bounding_box();
883  result += box;
884  }
885  return result;
886 }
887 
888 // Merges the blobs from start to end, not including end, and deletes
889 // the blobs between start and end.
890 void TWERD::MergeBlobs(int start, int end) {
891  if (start >= blobs.size() - 1) return; // Nothing to do.
892  TESSLINE* outline = blobs[start]->outlines;
893  for (int i = start + 1; i < end && i < blobs.size(); ++i) {
894  TBLOB* next_blob = blobs[i];
895  // Take the outlines from the next blob.
896  if (outline == NULL) {
897  blobs[start]->outlines = next_blob->outlines;
898  outline = blobs[start]->outlines;
899  } else {
900  while (outline->next != NULL)
901  outline = outline->next;
902  outline->next = next_blob->outlines;
903  next_blob->outlines = NULL;
904  }
905  // Delete the next blob and move on.
906  delete next_blob;
907  blobs[i] = NULL;
908  }
909  // Remove dead blobs from the vector.
910  for (int i = start + 1; i < end && start + 1 < blobs.size(); ++i) {
911  blobs.remove(start + 1);
912  }
913 }
914 
915 #ifndef GRAPHICS_DISABLED
916 void TWERD::plot(ScrollView* window) {
918  for (int b = 0; b < blobs.size(); ++b) {
919  blobs[b]->plot(window, color, ScrollView::BROWN);
920  color = WERD::NextColor(color);
921  }
922 }
923 #endif // GRAPHICS_DISABLED
924 
925 /**********************************************************************
926  * divisible_blob
927  *
928  * Returns true if the blob contains multiple outlines than can be
929  * separated using divide_blobs. Sets the location to be used in the
930  * call to divide_blobs.
931  **********************************************************************/
932 bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT* location) {
933  if (blob->outlines == NULL || blob->outlines->next == NULL)
934  return false; // Need at least 2 outlines for it to be possible.
935  int max_gap = 0;
936  TPOINT vertical = italic_blob ? kDivisibleVerticalItalic
938  for (TESSLINE* outline1 = blob->outlines; outline1 != NULL;
939  outline1 = outline1->next) {
940  if (outline1->is_hole)
941  continue; // Holes do not count as separable.
942  TPOINT mid_pt1(
943  static_cast<inT16>((outline1->topleft.x + outline1->botright.x) / 2),
944  static_cast<inT16>((outline1->topleft.y + outline1->botright.y) / 2));
945  int mid_prod1 = CROSS(mid_pt1, vertical);
946  int min_prod1, max_prod1;
947  outline1->MinMaxCrossProduct(vertical, &min_prod1, &max_prod1);
948  for (TESSLINE* outline2 = outline1->next; outline2 != NULL;
949  outline2 = outline2->next) {
950  if (outline2->is_hole)
951  continue; // Holes do not count as separable.
952  TPOINT mid_pt2(
953  static_cast<inT16>((outline2->topleft.x + outline2->botright.x) / 2),
954  static_cast<inT16>((outline2->topleft.y + outline2->botright.y) / 2));
955  int mid_prod2 = CROSS(mid_pt2, vertical);
956  int min_prod2, max_prod2;
957  outline2->MinMaxCrossProduct(vertical, &min_prod2, &max_prod2);
958  int mid_gap = abs(mid_prod2 - mid_prod1);
959  int overlap = MIN(max_prod1, max_prod2) - MAX(min_prod1, min_prod2);
960  if (mid_gap - overlap / 4 > max_gap) {
961  max_gap = mid_gap - overlap / 4;
962  *location = mid_pt1;
963  *location += mid_pt2;
964  *location /= 2;
965  }
966  }
967  }
968  // Use the y component of the vertical vector as an approximation to its
969  // length.
970  return max_gap > vertical.y;
971 }
972 
973 /**********************************************************************
974  * divide_blobs
975  *
976  * Create two blobs by grouping the outlines in the appropriate blob.
977  * The outlines that are beyond the location point are moved to the
978  * other blob. The ones whose x location is less than that point are
979  * retained in the original blob.
980  **********************************************************************/
981 void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob,
982  const TPOINT& location) {
983  TPOINT vertical = italic_blob ? kDivisibleVerticalItalic
985  TESSLINE *outline1 = NULL;
986  TESSLINE *outline2 = NULL;
987 
988  TESSLINE *outline = blob->outlines;
989  blob->outlines = NULL;
990  int location_prod = CROSS(location, vertical);
991 
992  while (outline != NULL) {
993  TPOINT mid_pt(
994  static_cast<inT16>((outline->topleft.x + outline->botright.x) / 2),
995  static_cast<inT16>((outline->topleft.y + outline->botright.y) / 2));
996  int mid_prod = CROSS(mid_pt, vertical);
997  if (mid_prod < location_prod) {
998  // Outline is in left blob.
999  if (outline1)
1000  outline1->next = outline;
1001  else
1002  blob->outlines = outline;
1003  outline1 = outline;
1004  } else {
1005  // Outline is in right blob.
1006  if (outline2)
1007  outline2->next = outline;
1008  else
1009  other_blob->outlines = outline;
1010  outline2 = outline;
1011  }
1012  outline = outline->next;
1013  }
1014 
1015  if (outline1)
1016  outline1->next = NULL;
1017  if (outline2)
1018  outline2->next = NULL;
1019 }
int start_step
Definition: blobs.h:173
static ScrollView::Color NextColor(ScrollView::Color colour)
Definition: werd.cpp:306
ICOORD position_at_index(int index) const
Definition: coutln.h:151
void Clear()
Definition: blobs.cpp:402
TESSLINE * next
Definition: blobs.h:258
void Swap(T *p1, T *p2)
Definition: helpers.h:97
C_OUTLINE_LIST * out_list()
Definition: stepblob.h:64
const DENORM * RootDenorm() const
Definition: normalis.h:260
void ComputeBoundingBoxes()
Definition: blobs.cpp:462
C_OUTLINE_LIST * child()
Definition: coutln.h:106
void plot(ScrollView *window, ScrollView::Color color, ScrollView::Color child_color)
Definition: blobs.cpp:278
TPOINT pos
Definition: blobs.h:163
const TPOINT kDivisibleVerticalUpright(0, 1)
double x_variance() const
Definition: linlsq.h:79
Definition: points.h:189
TPOINT start
Definition: blobs.h:255
void NormTransform(const DENORM *first_norm, const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:334
void set_inverse(bool value)
Definition: normalis.h:257
void Rotate(const FCOORD rotation)
Definition: blobs.cpp:177
void plot(ScrollView *window)
Definition: blobs.cpp:916
void init_to_size(int size, T t)
C_OUTLINE * src_outline
Definition: blobs.h:171
FCOORD mean_point() const
Definition: linlsq.cpp:167
inT32 count() const
Definition: linlsq.h:41
void add(double x, double y)
Definition: linlsq.cpp:49
void EliminateDuplicateOutlines()
Definition: blobs.cpp:495
void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob, const TPOINT &location)
Definition: blobs.cpp:981
#define MAX_INT32
Definition: host.h:62
void CorrectBlobOrder(TBLOB *next)
Definition: blobs.cpp:515
void MinMaxCrossProduct(const TPOINT vec, int *min_xp, int *max_xp) const
Definition: blobs.cpp:259
EDGEPT * prev
Definition: blobs.h:170
#define CROSS(a, b)
Definition: vecfuncs.h:52
void Rotate(const FCOORD rotation)
Definition: blobs.cpp:441
TESSLINE * outlines
Definition: blobs.h:377
const int kBlnXHeight
Definition: normalis.h:28
int push_back(T object)
inT16 x() const
access function
Definition: points.h:52
CLISTIZE(EDGEPT)
int NumOutlines() const
Definition: blobs.cpp:469
void Scale(float factor)
Definition: blobs.cpp:455
const int kBlnBaselineOffset
Definition: normalis.h:29
void SetupNormalization(const BLOCK *block, const FCOORD *rotation, const DENORM *predecessor, float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, float final_yshift)
Definition: normalis.cpp:95
void set_x(float xin)
rewrite function
Definition: points.h:216
void CopyFrom(const TWERD &src)
Definition: blobs.cpp:857
int IntCastRounded(double x)
Definition: helpers.h:179
void ComputeBoundingBoxes()
Definition: blobs.cpp:873
int size() const
Definition: genericvector.h:72
void LocalNormTransform(const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:305
void set_y(float yin)
rewrite function
Definition: points.h:220
void SetCursor(int x, int y)
Definition: scrollview.cpp:525
TBOX bounding_box() const
Definition: blobs.cpp:273
void Move(const ICOORD vec)
Definition: blobs.cpp:448
BOOL8 flag(WERD_FLAGS mask) const
Definition: werd.h:128
inT16 left() const
Definition: rect.h:68
Definition: blobs.h:395
bool is_hole
Definition: blobs.h:256
TPOINT botright
Definition: blobs.h:254
bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT *location)
Definition: blobs.cpp:932
void BLNormalize(const BLOCK *block, const ROW *row, Pix *pix, bool inverse, float x_height, float baseline_shift, bool numeric_mode, tesseract::OcrEngineMode hint, const TBOX *norm_box, DENORM *word_denorm)
Definition: blobs.cpp:807
void Clear()
Definition: blobs.cpp:153
VECTOR vec
Definition: blobs.h:164
EDGEPT * loop
Definition: blobs.h:257
int edge_strength_at_index(int index) const
Definition: coutln.h:185
bool IsHidden() const
Definition: blobs.h:153
void Clear()
Definition: blobs.cpp:867
TBLOB * ClassifyNormalizeIfNeeded() const
Definition: blobs.cpp:363
inT16 x
Definition: blobs.h:71
inT16 y() const
access_function
Definition: points.h:56
TPOINT topleft
Definition: blobs.h:253
int step_count
Definition: blobs.h:174
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
Definition: helpers.h:122
void SetupFromPos()
Definition: blobs.cpp:213
EDGEPT * next
Definition: blobs.h:169
void Normalize(const BLOCK *block, const FCOORD *rotation, const DENORM *predecessor, float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, float final_yshift, bool inverse, Pix *pix)
Definition: blobs.cpp:413
void CopyFrom(const TBLOB &src)
Definition: blobs.cpp:386
Definition: blobs.h:76
void ComputeBoundingBox()
Definition: blobs.cpp:225
inT16 top() const
Definition: rect.h:54
#define MIN_INT32
Definition: host.h:70
#define MAX(x, y)
Definition: ndminx.h:24
inT16 y
Definition: blobs.h:72
Definition: rect.h:30
GenericVector< TBLOB * > blobs
Definition: blobs.h:436
bool latin_script
Definition: blobs.h:437
const TPOINT kDivisibleVerticalItalic(1, 5)
void plot(ScrollView *window, ScrollView::Color color, ScrollView::Color child_color)
Definition: blobs.cpp:524
#define MIN(x, y)
Definition: ndminx.h:28
ICOORD step(int index) const
Definition: coutln.h:142
Definition: blobs.h:261
FCOORD sub_pixel_pos_at_index(const ICOORD &pos, int index) const
Definition: coutln.h:161
Definition: linlsq.h:26
C_BLOB_LIST * cblob_list()
Definition: werd.h:100
Definition: blobs.h:50
inT16 height() const
Definition: rect.h:104
float y() const
Definition: points.h:212
inT16 right() const
Definition: rect.h:75
inT16 width() const
Definition: rect.h:111
static TBLOB * ShallowCopy(const TBLOB &src)
Definition: blobs.cpp:352
static bool IsCrossed(const TPOINT &a0, const TPOINT &a1, const TPOINT &b0, const TPOINT &b1)
Definition: blobs.cpp:73
void MergeBlobs(int start, int end)
Definition: blobs.cpp:890
void Move(const ICOORD vec)
Definition: blobs.cpp:191
voidpf uLong int origin
Definition: ioapi.h:42
inT16 bottom() const
Definition: rect.h:61
void Normalize(const DENORM &denorm)
Definition: blobs.cpp:167
double y_variance() const
Definition: linlsq.h:85
float length() const
find length
Definition: points.h:230
void GetEdgeCoords(const TBOX &box, GenericVector< GenericVector< int > > *x_coords, GenericVector< GenericVector< int > > *y_coords) const
Definition: blobs.cpp:570
void move(const ICOORD vec)
Definition: rect.h:153
const ICOORD & botleft() const
Definition: rect.h:88
static TESSLINE * BuildFromOutlineList(EDGEPT *outline)
Definition: blobs.cpp:104
float base_line(float xpos) const
Definition: ocrrow.h:56
inT32 pathlength() const
Definition: coutln.h:133
static TBLOB * PolygonalCopy(bool allow_detailed_fx, C_BLOB *src)
Definition: blobs.cpp:344
void Scale(float factor)
Definition: blobs.cpp:202
int ComputeMoments(FCOORD *center, FCOORD *second_moments) const
Definition: blobs.cpp:535
TBOX bounding_box() const
Definition: blobs.cpp:482
Definition: werd.h:60
float x() const
Definition: points.h:209
void UpdateRange(const T1 &x, T2 *lower_bound, T2 *upper_bound)
Definition: helpers.h:132
Definition: ocrrow.h:32
void GetPreciseBoundingBox(TBOX *precise_box) const
Definition: blobs.cpp:554
static TWERD * PolygonalCopy(bool allow_detailed_fx, WERD *src)
Definition: blobs.cpp:793
Definition: ocrblock.h:30
EDGEPT * FindBestStartPt() const
Definition: blobs.cpp:299
void set_pix(Pix *pix)
Definition: normalis.h:251
void CopyFrom(const TESSLINE &src)
Definition: blobs.cpp:126
void Pen(Color color)
Definition: scrollview.cpp:726
TBOX bounding_box() const
Definition: blobs.cpp:879
TESSLINE * ApproximateOutline(bool allow_detailed_fx, C_OUTLINE *c_outline)
Definition: polyaprx.cpp:60
int x_middle() const
Definition: rect.h:81
void DrawTo(int x, int y)
Definition: scrollview.cpp:531
integer coordinate
Definition: points.h:30