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