tesseract  4.0.0-1-g2a2b
stepblob.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: stepblob.cpp (Formerly cblob.c)
3  * Description: Code for C_BLOB class.
4  * Author: Ray Smith
5  * Created: Tue Oct 08 10:41:13 BST 1991
6  *
7  * (C) Copyright 1991, 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 "stepblob.h"
26 #include "allheaders.h" // for pixCreate, pixGetDepth
27 #include "genericvector.h" // for GenericVector
28 #include "host.h" // for TRUE, FALSE
29 #include "points.h" // for operator+=, FCOORD, ICOORD
30 
31 class DENORM;
32 
33 // Max perimeter to width ratio for a baseline position above box bottom.
34 const double kMaxPerimeterWidthRatio = 8.0;
35 
37 /**********************************************************************
38  * position_outline
39  *
40  * Position the outline in the given list at the relevant place
41  * according to its nesting.
42  **********************************************************************/
43 static void position_outline( //put in place
44  C_OUTLINE *outline, //thing to place
45  C_OUTLINE_LIST *destlist //desstination list
46  ) {
47  C_OUTLINE *dest_outline; //outline from dest list
48  C_OUTLINE_IT it = destlist; //iterator
49  //iterator on children
50  C_OUTLINE_IT child_it = outline->child ();
51 
52  if (!it.empty ()) {
53  do {
54  dest_outline = it.data (); //get destination
55  //encloses dest
56  if (*dest_outline < *outline) {
57  //take off list
58  dest_outline = it.extract ();
59  //put this in place
60  it.add_after_then_move (outline);
61  //make it a child
62  child_it.add_to_end (dest_outline);
63  while (!it.at_last ()) {
64  it.forward (); //do rest of list
65  //check for other children
66  dest_outline = it.data ();
67  if (*dest_outline < *outline) {
68  //take off list
69  dest_outline = it.extract ();
70  child_it.add_to_end (dest_outline);
71  //make it a child
72  if (it.empty ())
73  break;
74  }
75  }
76  return; //finished
77  }
78  //enclosed by dest
79  else if (*outline < *dest_outline) {
80  position_outline (outline, dest_outline->child ());
81  //place in child list
82  return; //finished
83  }
84  it.forward ();
85  }
86  while (!it.at_first ());
87  }
88  it.add_to_end (outline); //at outer level
89 }
90 
91 
92 /**********************************************************************
93  * plot_outline_list
94  *
95  * Draw a list of outlines in the given colour and their children
96  * in the child colour.
97  **********************************************************************/
98 
99 #ifndef GRAPHICS_DISABLED
100 static void plot_outline_list( //draw outlines
101  C_OUTLINE_LIST *list, //outline to draw
102  ScrollView* window, //window to draw in
103  ScrollView::Color colour, //colour to use
104  ScrollView::Color child_colour //colour of children
105  ) {
106  C_OUTLINE *outline; //current outline
107  C_OUTLINE_IT it = list; //iterator
108 
109  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
110  outline = it.data ();
111  //draw it
112  outline->plot (window, colour);
113  if (!outline->child ()->empty ())
114  plot_outline_list (outline->child (), window,
115  child_colour, child_colour);
116  }
117 }
118 // Draws the outlines in the given colour, and child_colour, normalized
119 // using the given denorm, making use of sub-pixel accurate information
120 // if available.
121 static void plot_normed_outline_list(const DENORM& denorm,
122  C_OUTLINE_LIST *list,
123  ScrollView::Color colour,
124  ScrollView::Color child_colour,
125  ScrollView* window) {
126  C_OUTLINE_IT it(list);
127  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
128  C_OUTLINE* outline = it.data();
129  outline->plot_normed(denorm, colour, window);
130  if (!outline->child()->empty())
131  plot_normed_outline_list(denorm, outline->child(), child_colour,
132  child_colour, window);
133  }
134 }
135 #endif
136 
137 
138 /**********************************************************************
139  * reverse_outline_list
140  *
141  * Reverse a list of outlines and their children.
142  **********************************************************************/
143 
144 static void reverse_outline_list(C_OUTLINE_LIST *list) {
145  C_OUTLINE_IT it = list; // iterator
146 
147  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
148  C_OUTLINE* outline = it.data();
149  outline->reverse(); // reverse it
150  outline->set_flag(COUT_INVERSE, TRUE);
151  if (!outline->child()->empty())
152  reverse_outline_list(outline->child());
153  }
154 }
155 
156 
157 /**********************************************************************
158  * C_BLOB::C_BLOB
159  *
160  * Constructor to build a C_BLOB from a list of C_OUTLINEs.
161  * The C_OUTLINEs are not copied so the source list is emptied.
162  * The C_OUTLINEs are nested correctly in the blob.
163  **********************************************************************/
164 
165 C_BLOB::C_BLOB(C_OUTLINE_LIST *outline_list) {
166  for (C_OUTLINE_IT ol_it(outline_list); !ol_it.empty(); ol_it.forward()) {
167  C_OUTLINE* outline = ol_it.extract();
168  // Position this outline in appropriate position in the hierarchy.
169  position_outline(outline, &outlines);
170  }
172 }
173 
174 // Simpler constructor to build a blob from a single outline that has
175 // already been fully initialized.
177  C_OUTLINE_IT it(&outlines);
178  it.add_to_end(outline);
179 }
180 
181 // Builds a set of one or more blobs from a list of outlines.
182 // Input: one outline on outline_list contains all the others, but the
183 // nesting and order are undefined.
184 // If good_blob is true, the blob is added to good_blobs_it, unless
185 // an illegal (generation-skipping) parent-child relationship is found.
186 // If so, the parent blob goes to bad_blobs_it, and the immediate children
187 // are promoted to the top level, recursively being sent to good_blobs_it.
188 // If good_blob is false, all created blobs will go to the bad_blobs_it.
189 // Output: outline_list is empty. One or more blobs are added to
190 // good_blobs_it and/or bad_blobs_it.
192  C_OUTLINE_LIST* outline_list,
193  C_BLOB_IT* good_blobs_it,
194  C_BLOB_IT* bad_blobs_it) {
195  // List of top-level outlines with correctly nested children.
196  C_OUTLINE_LIST nested_outlines;
197  for (C_OUTLINE_IT ol_it(outline_list); !ol_it.empty(); ol_it.forward()) {
198  C_OUTLINE* outline = ol_it.extract();
199  // Position this outline in appropriate position in the hierarchy.
200  position_outline(outline, &nested_outlines);
201  }
202  // Check for legal nesting and reassign as required.
203  for (C_OUTLINE_IT ol_it(&nested_outlines); !ol_it.empty(); ol_it.forward()) {
204  C_OUTLINE* outline = ol_it.extract();
205  bool blob_is_good = good_blob;
206  if (!outline->IsLegallyNested()) {
207  // The blob is illegally nested.
208  // Mark it bad, and add all its children to the top-level list.
209  blob_is_good = false;
210  ol_it.add_list_after(outline->child());
211  }
212  C_BLOB* blob = new C_BLOB(outline);
213  // Set inverse flag and reverse if needed.
215  // Put on appropriate list.
216  if (!blob_is_good && bad_blobs_it != nullptr)
217  bad_blobs_it->add_after_then_move(blob);
218  else
219  good_blobs_it->add_after_then_move(blob);
220  }
221 }
222 
223 // Sets the COUT_INVERSE flag appropriately on the outlines and their
224 // children recursively, reversing the outlines if needed so that
225 // everything has an anticlockwise top-level.
227  C_OUTLINE_IT ol_it(&outlines);
228  for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) {
229  C_OUTLINE* outline = ol_it.data();
230  if (outline->turn_direction() < 0) {
231  outline->reverse();
232  reverse_outline_list(outline->child());
233  outline->set_flag(COUT_INVERSE, TRUE);
234  } else {
235  outline->set_flag(COUT_INVERSE, FALSE);
236  }
237  }
238 }
239 
240 
241 // Build and return a fake blob containing a single fake outline with no
242 // steps.
244  C_OUTLINE_LIST outlines;
245  C_OUTLINE::FakeOutline(box, &outlines);
246  return new C_BLOB(&outlines);
247 }
248 
249 /**********************************************************************
250  * C_BLOB::bounding_box
251  *
252  * Return the bounding box of the blob.
253  **********************************************************************/
254 
255 TBOX C_BLOB::bounding_box() const { // bounding box
256  C_OUTLINE *outline; // current outline
257  // This is a read-only iteration of the outlines.
258  C_OUTLINE_IT it = const_cast<C_OUTLINE_LIST*>(&outlines);
259  TBOX box; // bounding box
260 
261  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
262  outline = it.data ();
263  box += outline->bounding_box ();
264  }
265  return box;
266 }
267 
268 
269 /**********************************************************************
270  * C_BLOB::area
271  *
272  * Return the area of the blob.
273  **********************************************************************/
274 
275 int32_t C_BLOB::area() { //area
276  C_OUTLINE *outline; //current outline
277  C_OUTLINE_IT it = &outlines; //outlines of blob
278  int32_t total; //total area
279 
280  total = 0;
281  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
282  outline = it.data ();
283  total += outline->area ();
284  }
285  return total;
286 }
287 
288 /**********************************************************************
289  * C_BLOB::perimeter
290  *
291  * Return the perimeter of the top and 2nd level outlines.
292  **********************************************************************/
293 
294 int32_t C_BLOB::perimeter() {
295  C_OUTLINE *outline; // current outline
296  C_OUTLINE_IT it = &outlines; // outlines of blob
297  int32_t total; // total perimeter
298 
299  total = 0;
300  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
301  outline = it.data();
302  total += outline->perimeter();
303  }
304  return total;
305 }
306 
307 
308 /**********************************************************************
309  * C_BLOB::outer_area
310  *
311  * Return the area of the blob.
312  **********************************************************************/
313 
314 int32_t C_BLOB::outer_area() { //area
315  C_OUTLINE *outline; //current outline
316  C_OUTLINE_IT it = &outlines; //outlines of blob
317  int32_t total; //total area
318 
319  total = 0;
320  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
321  outline = it.data ();
322  total += outline->outer_area ();
323  }
324  return total;
325 }
326 
327 
328 /**********************************************************************
329  * C_BLOB::count_transitions
330  *
331  * Return the total x and y maxes and mins in the blob.
332  * Chlid outlines are not counted.
333  **********************************************************************/
334 
336  int32_t threshold //on size
337  ) {
338  C_OUTLINE *outline; //current outline
339  C_OUTLINE_IT it = &outlines; //outlines of blob
340  int32_t total; //total area
341 
342  total = 0;
343  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
344  outline = it.data ();
345  total += outline->count_transitions (threshold);
346  }
347  return total;
348 }
349 
350 
351 /**********************************************************************
352  * C_BLOB::move
353  *
354  * Move C_BLOB by vector
355  **********************************************************************/
356 
357 void C_BLOB::move( // reposition blob
358  const ICOORD vec // by vector
359  ) {
360  C_OUTLINE_IT it(&outlines); // iterator
361 
362  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
363  it.data ()->move (vec); // move each outline
364 }
365 
366 // Static helper for C_BLOB::rotate to allow recursion of child outlines.
367 static void RotateOutlineList(const FCOORD& rotation,
368  C_OUTLINE_LIST* outlines) {
369  C_OUTLINE_LIST new_outlines;
370  C_OUTLINE_IT src_it(outlines);
371  C_OUTLINE_IT dest_it(&new_outlines);
372  while (!src_it.empty()) {
373  C_OUTLINE* old_outline = src_it.extract();
374  src_it.forward();
375  C_OUTLINE* new_outline = new C_OUTLINE(old_outline, rotation);
376  if (!old_outline->child()->empty()) {
377  RotateOutlineList(rotation, old_outline->child());
378  C_OUTLINE_IT child_it(new_outline->child());
379  child_it.add_list_after(old_outline->child());
380  }
381  delete old_outline;
382  dest_it.add_to_end(new_outline);
383  }
384  src_it.add_list_after(&new_outlines);
385 }
386 
387 /**********************************************************************
388  * C_BLOB::rotate
389  *
390  * Rotate C_BLOB by rotation.
391  * Warning! has to rebuild all the C_OUTLINEs.
392  **********************************************************************/
393 void C_BLOB::rotate(const FCOORD& rotation) {
394  RotateOutlineList(rotation, &outlines);
395 }
396 
397 // Helper calls ComputeEdgeOffsets or ComputeBinaryOffsets recursively on the
398 // outline list and its children.
399 static void ComputeEdgeOffsetsOutlineList(int threshold, Pix* pix,
400  C_OUTLINE_LIST *list) {
401  C_OUTLINE_IT it(list);
402  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
403  C_OUTLINE* outline = it.data();
404  if (pix != nullptr && pixGetDepth(pix) == 8)
405  outline->ComputeEdgeOffsets(threshold, pix);
406  else
407  outline->ComputeBinaryOffsets();
408  if (!outline->child()->empty())
409  ComputeEdgeOffsetsOutlineList(threshold, pix, outline->child());
410  }
411 }
412 
413 // Adds sub-pixel resolution EdgeOffsets for the outlines using greyscale
414 // if the supplied pix is 8-bit or the binary edges if nullptr.
415 void C_BLOB::ComputeEdgeOffsets(int threshold, Pix* pix) {
416  ComputeEdgeOffsetsOutlineList(threshold, pix, &outlines);
417 }
418 
419 // Estimates and returns the baseline position based on the shape of the
420 // outlines.
421 // We first find the minimum y-coord (y_mins) at each x-coord within the blob.
422 // If there is a run of some y or y+1 in y_mins that is longer than the total
423 // number of positions at bottom or bottom+1, subject to the additional
424 // condition that at least one side of the y/y+1 run is higher than y+1, so it
425 // is not a local minimum, then y, not the bottom, makes a good candidate
426 // baseline position for this blob. Eg
427 // | ---|
428 // | |
429 // |- -----------| <= Good candidate baseline position.
430 // |- -|
431 // | -|
432 // |---| <= Bottom of blob
434  TBOX box = bounding_box();
435  int left = box.left();
436  int width = box.width();
437  int bottom = box.bottom();
438  if (outlines.empty() || perimeter() > width * kMaxPerimeterWidthRatio)
439  return bottom; // This is only for non-CJK blobs.
440  // Get the minimum y coordinate at each x-coordinate.
441  GenericVector<int> y_mins;
442  y_mins.init_to_size(width + 1, box.top());
443  C_OUTLINE_IT it(&outlines);
444  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
445  C_OUTLINE* outline = it.data();
446  ICOORD pos = outline->start_pos();
447  for (int s = 0; s < outline->pathlength(); ++s) {
448  if (pos.y() < y_mins[pos.x() - left])
449  y_mins[pos.x() - left] = pos.y();
450  pos += outline->step(s);
451  }
452  }
453  // Find the total extent of the bottom or bottom + 1.
454  int bottom_extent = 0;
455  for (int x = 0; x <= width; ++x) {
456  if (y_mins[x] == bottom || y_mins[x] == bottom + 1)
457  ++bottom_extent;
458  }
459  // Find the lowest run longer than the bottom extent that is not the bottom.
460  int best_min = box.top();
461  int prev_run = 0;
462  int prev_y = box.top();
463  int prev_prev_y = box.top();
464  for (int x = 0; x < width; x += prev_run) {
465  // Find the length of the current run.
466  int y_at_x = y_mins[x];
467  int run = 1;
468  while (x + run <= width && y_mins[x + run] == y_at_x) ++run;
469  if (y_at_x > bottom + 1) {
470  // Possible contender.
471  int total_run = run;
472  // Find extent of current value or +1 to the right of x.
473  while (x + total_run <= width &&
474  (y_mins[x + total_run] == y_at_x ||
475  y_mins[x + total_run] == y_at_x + 1)) ++total_run;
476  // At least one end has to be higher so it is not a local max.
477  if (prev_prev_y > y_at_x + 1 || x + total_run > width ||
478  y_mins[x + total_run] > y_at_x + 1) {
479  // If the prev_run is at y + 1, then we can add that too. There cannot
480  // be a suitable run at y before that or we would have found it already.
481  if (prev_run > 0 && prev_y == y_at_x + 1) total_run += prev_run;
482  if (total_run > bottom_extent && y_at_x < best_min) {
483  best_min = y_at_x;
484  }
485  }
486  }
487  prev_run = run;
488  prev_prev_y = prev_y;
489  prev_y = y_at_x;
490  }
491  return best_min == box.top() ? bottom : best_min;
492 }
493 
494 static void render_outline_list(C_OUTLINE_LIST *list,
495  int left, int top, Pix* pix) {
496  C_OUTLINE_IT it(list);
497  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
498  C_OUTLINE* outline = it.data();
499  outline->render(left, top, pix);
500  if (!outline->child()->empty())
501  render_outline_list(outline->child(), left, top, pix);
502  }
503 }
504 
505 static void render_outline_list_outline(C_OUTLINE_LIST *list,
506  int left, int top, Pix* pix) {
507  C_OUTLINE_IT it(list);
508  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
509  C_OUTLINE* outline = it.data();
510  outline->render_outline(left, top, pix);
511  }
512 }
513 
514 // Returns a Pix rendering of the blob. pixDestroy after use.
516  TBOX box = bounding_box();
517  Pix* pix = pixCreate(box.width(), box.height(), 1);
518  render_outline_list(&outlines, box.left(), box.top(), pix);
519  return pix;
520 }
521 
522 // Returns a Pix rendering of the outline of the blob. (no fill).
523 // pixDestroy after use.
525  TBOX box = bounding_box();
526  Pix* pix = pixCreate(box.width(), box.height(), 1);
527  render_outline_list_outline(&outlines, box.left(), box.top(), pix);
528  return pix;
529 }
530 
531 /**********************************************************************
532  * C_BLOB::plot
533  *
534  * Draw the C_BLOB in the given colour.
535  **********************************************************************/
536 
537 #ifndef GRAPHICS_DISABLED
538 void C_BLOB::plot(ScrollView* window, // window to draw in
539  ScrollView::Color blob_colour, // main colour
540  ScrollView::Color child_colour) { // for holes
541  plot_outline_list(&outlines, window, blob_colour, child_colour);
542 }
543 // Draws the blob in the given colour, and child_colour, normalized
544 // using the given denorm, making use of sub-pixel accurate information
545 // if available.
546 void C_BLOB::plot_normed(const DENORM& denorm,
547  ScrollView::Color blob_colour,
548  ScrollView::Color child_colour,
549  ScrollView* window) {
550  plot_normed_outline_list(denorm, &outlines, blob_colour, child_colour,
551  window);
552 }
553 #endif
void ComputeBinaryOffsets()
Definition: coutln.cpp:838
Pix * render_outline()
Definition: stepblob.cpp:524
#define TRUE
Definition: capi.h:51
void plot_normed(const DENORM &denorm, ScrollView::Color blob_colour, ScrollView::Color child_colour, ScrollView *window)
Definition: stepblob.cpp:546
const double kMaxPerimeterWidthRatio
Definition: stepblob.cpp:34
void ComputeEdgeOffsets(int threshold, Pix *pix)
Definition: coutln.cpp:722
int32_t area() const
Definition: coutln.cpp:256
int16_t y() const
access_function
Definition: points.h:57
Definition: rect.h:34
int32_t perimeter() const
Definition: coutln.cpp:290
const ICOORD & start_pos() const
Definition: coutln.h:148
void ComputeEdgeOffsets(int threshold, Pix *pix)
Definition: stepblob.cpp:415
int32_t perimeter()
Definition: stepblob.cpp:294
int16_t width() const
Definition: rect.h:115
Pix * render()
Definition: stepblob.cpp:515
int16_t left() const
Definition: rect.h:72
int16_t turn_direction() const
Definition: coutln.cpp:538
int16_t top() const
Definition: rect.h:58
void render_outline(int left, int top, Pix *pix) const
Definition: coutln.cpp:917
integer coordinate
Definition: points.h:32
int16_t x() const
access function
Definition: points.h:53
void init_to_size(int size, const T &t)
#define FALSE
Definition: capi.h:52
void render(int left, int top, Pix *pix) const
Definition: coutln.cpp:895
#define ELISTIZE(CLASSNAME)
Definition: elst.h:961
int32_t pathlength() const
Definition: coutln.h:135
const TBOX & bounding_box() const
Definition: coutln.h:113
int32_t outer_area()
Definition: stepblob.cpp:314
void plot(ScrollView *window, ScrollView::Color colour) const
Definition: coutln.cpp:943
C_OUTLINE_LIST * child()
Definition: coutln.h:108
void set_flag(C_OUTLINE_FLAGS mask, bool value)
Definition: coutln.h:102
void plot_normed(const DENORM &denorm, ScrollView::Color colour, ScrollView *window) const
Definition: coutln.cpp:975
static void ConstructBlobsFromOutlines(bool good_blob, C_OUTLINE_LIST *outline_list, C_BLOB_IT *good_blobs_it, C_BLOB_IT *bad_blobs_it)
Definition: stepblob.cpp:191
C_BLOB()=default
void reverse()
Definition: coutln.cpp:566
int16_t EstimateBaselinePosition()
Definition: stepblob.cpp:433
TBOX bounding_box() const
Definition: stepblob.cpp:255
int32_t count_transitions(int32_t threshold)
Definition: coutln.cpp:341
int32_t area()
Definition: stepblob.cpp:275
Definition: points.h:189
bool IsLegallyNested() const
Definition: coutln.cpp:605
void plot(ScrollView *window, ScrollView::Color blob_colour, ScrollView::Color child_colour)
Definition: stepblob.cpp:538
int32_t count_transitions(int32_t threshold)
Definition: stepblob.cpp:335
Pix * pix() const
Definition: normalis.h:246
static void FakeOutline(const TBOX &box, C_OUTLINE_LIST *outlines)
Definition: coutln.cpp:240
class DLLSYM C_OUTLINE
Definition: coutln.h:68
static C_BLOB * FakeBlob(const TBOX &box)
Definition: stepblob.cpp:243
int16_t bottom() const
Definition: rect.h:65
int32_t outer_area() const
Definition: coutln.cpp:309
ICOORD step(int index) const
Definition: coutln.h:144
int16_t height() const
Definition: rect.h:108
void move(const ICOORD vec)
Definition: stepblob.cpp:357
void rotate(const FCOORD &rotation)
Definition: stepblob.cpp:393
void CheckInverseFlagAndDirection()
Definition: stepblob.cpp:226