tesseract  4.0.0-1-g2a2b
ocrblock.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: ocrblock.cpp (Formerly block.c)
3  * Description: BLOCK member functions and iterator functions.
4  * Author: Ray Smith
5  * Created: Fri Mar 15 09:41:28 GMT 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 "ocrblock.h"
21 #include <cstdlib>
22 #include <memory> // std::unique_ptr
23 #include "stepblob.h"
24 #include "tprintf.h"
25 
26 #define BLOCK_LABEL_HEIGHT 150 //char height of block id
27 
34 BLOCK::BLOCK(const char *name, //< filename
35  BOOL8 prop, //< proportional
36  int16_t kern, //< kerning
37  int16_t space, //< spacing
38  int16_t xmin, //< bottom left
39  int16_t ymin, int16_t xmax, //< top right
40  int16_t ymax)
41  : pdblk(xmin, ymin, xmax, ymax),
42  filename(name),
43  re_rotation_(1.0f, 0.0f),
44  classify_rotation_(1.0f, 0.0f),
45  skew_(1.0f, 0.0f) {
46  ICOORDELT_IT left_it = &pdblk.leftside;
47  ICOORDELT_IT right_it = &pdblk.rightside;
48 
49  proportional = prop;
50  right_to_left_ = false;
51  kerning = kern;
52  spacing = space;
53  font_class = -1; //not assigned
54  cell_over_xheight_ = 2.0f;
55  pdblk.hand_poly = nullptr;
56  left_it.set_to_list (&pdblk.leftside);
57  right_it.set_to_list (&pdblk.rightside);
58  //make default box
59  left_it.add_to_end (new ICOORDELT (xmin, ymin));
60  left_it.add_to_end (new ICOORDELT (xmin, ymax));
61  right_it.add_to_end (new ICOORDELT (xmax, ymin));
62  right_it.add_to_end (new ICOORDELT (xmax, ymax));
63 }
64 
71 static int decreasing_top_order(const void *row1, const void *row2) {
72  return (*reinterpret_cast<ROW* const*>(row2))->bounding_box().top() -
73  (*reinterpret_cast<ROW* const*>(row1))->bounding_box().top();
74 }
75 
76 
82 void BLOCK::rotate(const FCOORD& rotation) {
83  pdblk.poly_block()->rotate(rotation);
85 }
86 
87 // Returns the bounding box including the desired combination of upper and
88 // lower noise/diacritic elements.
89 TBOX BLOCK::restricted_bounding_box(bool upper_dots, bool lower_dots) const {
90  TBOX box;
91  // This is a read-only iteration of the rows in the block.
92  ROW_IT it(const_cast<ROW_LIST*>(&rows));
93  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
94  box += it.data()->restricted_bounding_box(upper_dots, lower_dots);
95  }
96  return box;
97 }
98 
108 }
109 
116 void BLOCK::sort_rows() { // order on "top"
117  ROW_IT row_it(&rows);
118 
119  row_it.sort (decreasing_top_order);
120 }
121 
122 
130 void BLOCK::compress() { // squash it up
131  #define ROW_SPACING 5
132 
133  ROW_IT row_it(&rows);
134  ROW *row;
135  ICOORD row_spacing (0, ROW_SPACING);
136 
137  ICOORDELT_IT icoordelt_it;
138 
139  sort_rows();
140 
143  for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
144  row = row_it.data ();
145  row->move (pdblk.box.botleft () - row_spacing -
146  row->bounding_box ().topleft ());
147  pdblk.box += row->bounding_box ();
148  }
149 
150  pdblk.leftside.clear ();
151  icoordelt_it.set_to_list (&pdblk.leftside);
152  icoordelt_it.add_to_end (new ICOORDELT (pdblk.box.left (), pdblk.box.bottom ()));
153  icoordelt_it.add_to_end (new ICOORDELT (pdblk.box.left (), pdblk.box.top ()));
154  pdblk.rightside.clear ();
155  icoordelt_it.set_to_list (&pdblk.rightside);
156  icoordelt_it.add_to_end (new ICOORDELT (pdblk.box.right (), pdblk.box.bottom ()));
157  icoordelt_it.add_to_end (new ICOORDELT (pdblk.box.right (), pdblk.box.top ()));
158 }
159 
160 
168 void BLOCK::check_pitch() { // check prop
169  // tprintf("Missing FFT fixed pitch stuff!\n");
170  pitch = -1;
171 }
172 
173 
180 void BLOCK::compress( // squash it up
181  const ICOORD vec // and move
182  ) {
183  pdblk.box.move (vec);
184  compress();
185 }
186 
187 
194 void BLOCK::print( //print list of sides
195  FILE*, //< file to print on
196  bool dump //< print full detail
197 ) {
198  ICOORDELT_IT it = &pdblk.leftside; //iterator
199 
200  pdblk.box.print ();
201  tprintf ("Proportional= %s\n", proportional ? "TRUE" : "FALSE");
202  tprintf ("Kerning= %d\n", kerning);
203  tprintf ("Spacing= %d\n", spacing);
204  tprintf ("Fixed_pitch=%d\n", pitch);
205  tprintf ("Filename= %s\n", filename.string ());
206 
207  if (dump) {
208  tprintf ("Left side coords are:\n");
209  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
210  tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ());
211  tprintf ("\n");
212  tprintf ("Right side coords are:\n");
213  it.set_to_list (&pdblk.rightside);
214  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
215  tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ());
216  tprintf ("\n");
217  }
218 }
219 
226 BLOCK & BLOCK::operator= ( //assignment
227 const BLOCK & source //from this
228 ) {
229  this->ELIST_LINK::operator= (source);
230  pdblk = source.pdblk;
231  proportional = source.proportional;
232  kerning = source.kerning;
233  spacing = source.spacing;
234  filename = source.filename; //STRINGs assign ok
235  if (!rows.empty ())
236  rows.clear ();
237  re_rotation_ = source.re_rotation_;
238  classify_rotation_ = source.classify_rotation_;
239  skew_ = source.skew_;
240  return *this;
241 }
242 
243 // This function is for finding the approximate (horizontal) distance from
244 // the x-coordinate of the left edge of a symbol to the left edge of the
245 // text block which contains it. We are passed:
246 // segments - output of PB_LINE_IT::get_line() which contains x-coordinate
247 // intervals for the scan line going through the symbol's y-coordinate.
248 // Each element of segments is of the form (x()=start_x, y()=length).
249 // x - the x coordinate of the symbol we're interested in.
250 // margin - return value, the distance from x,y to the left margin of the
251 // block containing it.
252 // If all segments were to the right of x, we return false and 0.
253 static bool LeftMargin(ICOORDELT_LIST *segments, int x, int *margin) {
254  bool found = false;
255  *margin = 0;
256  if (segments->empty())
257  return found;
258  ICOORDELT_IT seg_it(segments);
259  for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) {
260  int cur_margin = x - seg_it.data()->x();
261  if (cur_margin >= 0) {
262  if (!found) {
263  *margin = cur_margin;
264  } else if (cur_margin < *margin) {
265  *margin = cur_margin;
266  }
267  found = true;
268  }
269  }
270  return found;
271 }
272 
273 // This function is for finding the approximate (horizontal) distance from
274 // the x-coordinate of the right edge of a symbol to the right edge of the
275 // text block which contains it. We are passed:
276 // segments - output of PB_LINE_IT::get_line() which contains x-coordinate
277 // intervals for the scan line going through the symbol's y-coordinate.
278 // Each element of segments is of the form (x()=start_x, y()=length).
279 // x - the x coordinate of the symbol we're interested in.
280 // margin - return value, the distance from x,y to the right margin of the
281 // block containing it.
282 // If all segments were to the left of x, we return false and 0.
283 static bool RightMargin(ICOORDELT_LIST *segments, int x, int *margin) {
284  bool found = false;
285  *margin = 0;
286  if (segments->empty())
287  return found;
288  ICOORDELT_IT seg_it(segments);
289  for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) {
290  int cur_margin = seg_it.data()->x() + seg_it.data()->y() - x;
291  if (cur_margin >= 0) {
292  if (!found) {
293  *margin = cur_margin;
294  } else if (cur_margin < *margin) {
295  *margin = cur_margin;
296  }
297  found = true;
298  }
299  }
300  return found;
301 }
302 
303 // Compute the distance from the left and right ends of each row to the
304 // left and right edges of the block's polyblock. Illustration:
305 // ____________________________ _______________________
306 // | Howdy neighbor! | |rectangular blocks look|
307 // | This text is written to| |more like stacked pizza|
308 // |illustrate how useful poly- |boxes. |
309 // |blobs are in ----------- ------ The polyblob|
310 // |dealing with| _________ |for a BLOCK rec-|
311 // |harder layout| /===========\ |ords the possibly|
312 // |issues. | | _ _ | |skewed pseudo-|
313 // | You see this| | |_| \|_| | |rectangular |
314 // |text is flowed| | } | |boundary that|
315 // |around a mid-| \ ____ | |forms the ideal-|
316 // |cloumn portrait._____ \ / __|ized text margin|
317 // | Polyblobs exist| \ / |from which we should|
318 // |to account for insets| | | |measure paragraph|
319 // |which make otherwise| ----- |indentation. |
320 // ----------------------- ----------------------
321 //
322 // If we identify a drop-cap, we measure the left margin for the lines
323 // below the first line relative to one space past the drop cap. The
324 // first line's margin and those past the drop cap area are measured
325 // relative to the enclosing polyblock.
326 //
327 // TODO(rays): Before this will work well, we'll need to adjust the
328 // polyblob tighter around the text near images, as in:
329 // UNLV_AUTO:mag.3G0 page 2
330 // UNLV_AUTO:mag.3G4 page 16
332  if (row_list()->empty() || row_list()->singleton()) {
333  return;
334  }
335 
336  // If Layout analysis was not called, default to this.
338  POLY_BLOCK *pblock = &rect_block;
339  if (pdblk.poly_block() != nullptr) {
340  pblock = pdblk.poly_block();
341  }
342 
343  // Step One: Determine if there is a drop-cap.
344  // TODO(eger): Fix up drop cap code for RTL languages.
345  ROW_IT r_it(row_list());
346  ROW *first_row = r_it.data();
347  ROW *second_row = r_it.data_relative(1);
348 
349  // initialize the bottom of a fictitious drop cap far above the first line.
350  int drop_cap_bottom = first_row->bounding_box().top() +
351  first_row->bounding_box().height();
352  int drop_cap_right = first_row->bounding_box().left();
353  int mid_second_line = second_row->bounding_box().top() -
354  second_row->bounding_box().height() / 2;
355  WERD_IT werd_it(r_it.data()->word_list()); // words of line one
356  if (!werd_it.empty()) {
357  C_BLOB_IT cblob_it(werd_it.data()->cblob_list());
358  for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list();
359  cblob_it.forward()) {
360  TBOX bbox = cblob_it.data()->bounding_box();
361  if (bbox.bottom() <= mid_second_line) {
362  // we found a real drop cap
363  first_row->set_has_drop_cap(true);
364  if (drop_cap_bottom > bbox.bottom())
365  drop_cap_bottom = bbox.bottom();
366  if (drop_cap_right < bbox.right())
367  drop_cap_right = bbox.right();
368  }
369  }
370  }
371 
372  // Step Two: Calculate the margin from the text of each row to the block
373  // (or drop-cap) boundaries.
374  PB_LINE_IT lines(pblock);
375  r_it.set_to_list(row_list());
376  for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) {
377  ROW *row = r_it.data();
378  TBOX row_box = row->bounding_box();
379  int left_y = row->base_line(row_box.left()) + row->x_height();
380  int left_margin;
381  const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments_left(
382  lines.get_line(left_y));
383  LeftMargin(segments_left.get(), row_box.left(), &left_margin);
384 
385  if (row_box.top() >= drop_cap_bottom) {
386  int drop_cap_distance = row_box.left() - row->space() - drop_cap_right;
387  if (drop_cap_distance < 0)
388  drop_cap_distance = 0;
389  if (drop_cap_distance < left_margin)
390  left_margin = drop_cap_distance;
391  }
392 
393  int right_y = row->base_line(row_box.right()) + row->x_height();
394  int right_margin;
395  const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments_right(
396  lines.get_line(right_y));
397  RightMargin(segments_right.get(), row_box.right(), &right_margin);
398  row->set_lmargin(left_margin);
399  row->set_rmargin(right_margin);
400  }
401 }
402 
403 /**********************************************************************
404  * PrintSegmentationStats
405  *
406  * Prints segmentation stats for the given block list.
407  **********************************************************************/
408 
409 void PrintSegmentationStats(BLOCK_LIST* block_list) {
410  int num_blocks = 0;
411  int num_rows = 0;
412  int num_words = 0;
413  int num_blobs = 0;
414  BLOCK_IT block_it(block_list);
415  for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
416  BLOCK* block = block_it.data();
417  ++num_blocks;
418  ROW_IT row_it(block->row_list());
419  for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
420  ++num_rows;
421  ROW* row = row_it.data();
422  // Iterate over all werds in the row.
423  WERD_IT werd_it(row->word_list());
424  for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) {
425  WERD* werd = werd_it.data();
426  ++num_words;
427  num_blobs += werd->cblob_list()->length();
428  }
429  }
430  }
431  tprintf("Block list stats:\nBlocks = %d\nRows = %d\nWords = %d\nBlobs = %d\n",
432  num_blocks, num_rows, num_words, num_blobs);
433 }
434 
435 /**********************************************************************
436  * ExtractBlobsFromSegmentation
437  *
438  * Extracts blobs from the given block list and adds them to the output list.
439  * The block list must have been created by performing a page segmentation.
440  **********************************************************************/
441 
442 void ExtractBlobsFromSegmentation(BLOCK_LIST* blocks,
443  C_BLOB_LIST* output_blob_list) {
444  C_BLOB_IT return_list_it(output_blob_list);
445  BLOCK_IT block_it(blocks);
446  for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
447  BLOCK* block = block_it.data();
448  ROW_IT row_it(block->row_list());
449  for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
450  ROW* row = row_it.data();
451  // Iterate over all werds in the row.
452  WERD_IT werd_it(row->word_list());
453  for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) {
454  WERD* werd = werd_it.data();
455  return_list_it.move_to_last();
456  return_list_it.add_list_after(werd->cblob_list());
457  return_list_it.move_to_last();
458  return_list_it.add_list_after(werd->rej_cblob_list());
459  }
460  }
461  }
462 }
463 
464 /**********************************************************************
465  * RefreshWordBlobsFromNewBlobs()
466  *
467  * Refreshes the words in the block_list by using blobs in the
468  * new_blobs list.
469  * Block list must have word segmentation in it.
470  * It consumes the blobs provided in the new_blobs list. The blobs leftover in
471  * the new_blobs list after the call weren't matched to any blobs of the words
472  * in block list.
473  * The output not_found_blobs is a list of blobs from the original segmentation
474  * in the block_list for which no corresponding new blobs were found.
475  **********************************************************************/
476 
477 void RefreshWordBlobsFromNewBlobs(BLOCK_LIST* block_list,
478  C_BLOB_LIST* new_blobs,
479  C_BLOB_LIST* not_found_blobs) {
480  // Now iterate over all the blobs in the segmentation_block_list_, and just
481  // replace the corresponding c-blobs inside the werds.
482  BLOCK_IT block_it(block_list);
483  for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
484  BLOCK* block = block_it.data();
485  if (block->pdblk.poly_block() != nullptr && !block->pdblk.poly_block()->IsText())
486  continue; // Don't touch non-text blocks.
487  // Iterate over all rows in the block.
488  ROW_IT row_it(block->row_list());
489  for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
490  ROW* row = row_it.data();
491  // Iterate over all werds in the row.
492  WERD_IT werd_it(row->word_list());
493  WERD_LIST new_words;
494  WERD_IT new_words_it(&new_words);
495  for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) {
496  WERD* werd = werd_it.extract();
497  WERD* new_werd = werd->ConstructWerdWithNewBlobs(new_blobs,
498  not_found_blobs);
499  if (new_werd) {
500  // Insert this new werd into the actual row's werd-list. Remove the
501  // existing one.
502  new_words_it.add_after_then_move(new_werd);
503  delete werd;
504  } else {
505  // Reinsert the older word back, for lack of better options.
506  // This is critical since dropping the words messes up segmentation:
507  // eg. 1st word in the row might otherwise have W_FUZZY_NON turned on.
508  new_words_it.add_after_then_move(werd);
509  }
510  }
511  // Get rid of the old word list & replace it with the new one.
512  row->word_list()->clear();
513  werd_it.move_to_first();
514  werd_it.add_list_after(&new_words);
515  }
516  }
517 }
void compute_row_margins()
Definition: ocrblock.cpp:331
ICOORDELT_LIST leftside
Definition: pdblock.h:97
void set_has_drop_cap(bool has)
Definition: ocrrow.h:108
TBOX box
Definition: pdblock.h:99
void reflect_polygon_in_y_axis()
Definition: ocrblock.cpp:105
void move(const ICOORD vec)
Definition: ocrrow.cpp:148
void print() const
Definition: rect.h:278
void RefreshWordBlobsFromNewBlobs(BLOCK_LIST *block_list, C_BLOB_LIST *new_blobs, C_BLOB_LIST *not_found_blobs)
Definition: ocrblock.cpp:477
const char * string() const
Definition: strngs.cpp:196
float base_line(float xpos) const
Definition: ocrrow.h:59
ROW_LIST * row_list()
get rows
Definition: ocrblock.h:118
Definition: rect.h:34
WERD_LIST * word_list()
Definition: ocrrow.h:55
int32_t space() const
Definition: ocrrow.h:79
void rotate(FCOORD rotation)
Definition: polyblk.cpp:183
void move(const ICOORD vec)
Definition: rect.h:157
C_BLOB_LIST * rej_cblob_list()
Definition: werd.h:93
void move_bottom_edge(const int16_t y)
Definition: rect.h:137
void set_rmargin(int16_t rmargin)
Definition: ocrrow.h:98
void set_lmargin(int16_t lmargin)
Definition: ocrrow.h:95
int16_t left() const
Definition: rect.h:72
void print(FILE *fp, bool dump)
dump whole table
Definition: ocrblock.cpp:194
ICOORDELT_LIST rightside
Definition: pdblock.h:98
int16_t top() const
Definition: rect.h:58
float x_height() const
Definition: ocrrow.h:64
integer coordinate
Definition: points.h:32
TBOX * bounding_box()
Definition: polyblk.h:35
TBOX restricted_bounding_box(bool upper_dots, bool lower_dots) const
Definition: ocrblock.cpp:89
ICOORDELT_LIST * get_line(int16_t y)
Definition: polyblk.cpp:341
void operator=(const ELIST_LINK &)
Definition: elst.h:101
BLOCK & operator=(const BLOCK &source)
Definition: ocrblock.cpp:226
void ExtractBlobsFromSegmentation(BLOCK_LIST *blocks, C_BLOB_LIST *output_blob_list)
Definition: ocrblock.cpp:442
POLY_BLOCK * poly_block() const
Definition: pdblock.h:56
#define ELISTIZE(CLASSNAME)
Definition: elst.h:961
Definition: werd.h:59
TBOX bounding_box() const
Definition: ocrrow.h:88
unsigned char BOOL8
Definition: host.h:34
Definition: ocrrow.h:36
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:37
bool IsText() const
Definition: polyblk.h:49
Definition: ocrblock.h:30
C_BLOB_LIST * cblob_list()
Definition: werd.h:98
void PrintSegmentationStats(BLOCK_LIST *block_list)
Definition: ocrblock.cpp:409
void reflect_in_y_axis()
Definition: polyblk.cpp:207
const ICOORD & botleft() const
Definition: rect.h:92
void sort_rows()
decreasing y order
Definition: ocrblock.cpp:116
ICOORD topleft() const
Definition: rect.h:100
void bounding_box(ICOORD &bottom_left, ICOORD &top_right) const
get box
Definition: pdblock.h:60
Definition: points.h:189
int16_t right() const
Definition: rect.h:79
void check_pitch()
check proportional
Definition: ocrblock.cpp:168
WERD * ConstructWerdWithNewBlobs(C_BLOB_LIST *all_blobs, C_BLOB_LIST *orphan_blobs)
Definition: werd.cpp:411
#define ROW_SPACING
int16_t bottom() const
Definition: rect.h:65
PDBLK pdblk
Definition: ocrblock.h:192
void compress()
shrink white space
Definition: ocrblock.cpp:130
int16_t height() const
Definition: rect.h:108
void rotate(const FCOORD &rotation)
Definition: ocrblock.cpp:82