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