tesseract  4.00.00dev
scanedg.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: scanedg.cpp (Formerly scanedge.c)
3  * Description: Raster scanning crack based edge extractor.
4  * Author: Ray Smith
5  * Created: Fri Mar 22 16:11:50 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 "scanedg.h"
21 
22 #include <memory> // std::unique_ptr
23 
24 #include "allheaders.h"
25 #include "edgloop.h"
26 
27 #define WHITE_PIX 1 /*thresholded colours */
28 #define BLACK_PIX 0
29 // Flips between WHITE_PIX and BLACK_PIX.
30 #define FLIP_COLOUR(pix) (1-(pix))
31 
32 /**********************************************************************
33  * block_edges
34  *
35  * Extract edges from a PDBLK.
36  **********************************************************************/
37 
38 void block_edges(Pix *t_pix, // thresholded image
39  PDBLK *block, // block in image
40  C_OUTLINE_IT* outline_it) {
41  ICOORD bleft; // bounding box
42  ICOORD tright;
43  BLOCK_LINE_IT line_it = block; // line iterator
44 
45  int width = pixGetWidth(t_pix);
46  int height = pixGetHeight(t_pix);
47  int wpl = pixGetWpl(t_pix);
48  // lines in progress
49  CRACKEDGE **ptrline = new CRACKEDGE*[width + 1];
50  CRACKEDGE *free_cracks = NULL;
51 
52  block->bounding_box(bleft, tright); // block box
53  int block_width = tright.x() - bleft.x();
54  for (int x = block_width; x >= 0; x--)
55  ptrline[x] = NULL; // no lines in progress
56 
57  uinT8* bwline = new uinT8[width];
58 
59  uinT8 margin = WHITE_PIX;
60 
61  for (int y = tright.y() - 1; y >= bleft.y() - 1; y--) {
62  if (y >= bleft.y() && y < tright.y()) {
63  // Get the binary pixels from the image.
64  l_uint32* line = pixGetData(t_pix) + wpl * (height - 1 - y);
65  for (int x = 0; x < block_width; ++x) {
66  bwline[x] = GET_DATA_BIT(line, x + bleft.x()) ^ 1;
67  }
68  make_margins(block, &line_it, bwline, margin, bleft.x(), tright.x(), y);
69  } else {
70  memset(bwline, margin, block_width * sizeof(bwline[0]));
71  }
72  line_edges(bleft.x(), y, block_width,
73  margin, bwline, ptrline, &free_cracks, outline_it);
74  }
75 
76  free_crackedges(free_cracks); // really free them
77  delete[] ptrline;
78  delete[] bwline;
79 }
80 
81 
82 /**********************************************************************
83  * make_margins
84  *
85  * Get an image line and set to margin non-text pixels.
86  **********************************************************************/
87 
88 void make_margins( //get a line
89  PDBLK *block, //block in image
90  BLOCK_LINE_IT *line_it, //for old style
91  uinT8 *pixels, //pixels to strip
92  uinT8 margin, //white-out pixel
93  inT16 left, //block edges
94  inT16 right,
95  inT16 y //line coord
96  ) {
97  PB_LINE_IT *lines;
98  ICOORDELT_IT seg_it;
99  inT32 start; //of segment
100  inT16 xext; //of segment
101  int xindex; //index to pixel
102 
103  if (block->poly_block () != NULL) {
104  lines = new PB_LINE_IT (block->poly_block ());
105  const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments(
106  lines->get_line(y));
107  if (!segments->empty ()) {
108  seg_it.set_to_list(segments.get());
109  seg_it.mark_cycle_pt ();
110  start = seg_it.data ()->x ();
111  xext = seg_it.data ()->y ();
112  for (xindex = left; xindex < right; xindex++) {
113  if (xindex >= start && !seg_it.cycled_list ()) {
114  xindex = start + xext - 1;
115  seg_it.forward ();
116  start = seg_it.data ()->x ();
117  xext = seg_it.data ()->y ();
118  }
119  else
120  pixels[xindex - left] = margin;
121  }
122  }
123  else {
124  for (xindex = left; xindex < right; xindex++)
125  pixels[xindex - left] = margin;
126  }
127  delete lines;
128  }
129  else {
130  start = line_it->get_line (y, xext);
131  for (xindex = left; xindex < start; xindex++)
132  pixels[xindex - left] = margin;
133  for (xindex = start + xext; xindex < right; xindex++)
134  pixels[xindex - left] = margin;
135  }
136 }
137 
138 /**********************************************************************
139  * line_edges
140  *
141  * Scan a line for edges and update the edges in progress.
142  * When edges close into loops, send them for approximation.
143  **********************************************************************/
144 
145 void line_edges(inT16 x, // coord of line start
146  inT16 y, // coord of line
147  inT16 xext, // width of line
148  uinT8 uppercolour, // start of prev line
149  uinT8 * bwpos, // thresholded line
150  CRACKEDGE ** prevline, // edges in progress
151  CRACKEDGE **free_cracks,
152  C_OUTLINE_IT* outline_it) {
153  CrackPos pos = {free_cracks, x, y };
154  int xmax; // max x coord
155  int colour; // of current pixel
156  int prevcolour; // of previous pixel
157  CRACKEDGE *current; // current h edge
158  CRACKEDGE *newcurrent; // new h edge
159 
160  xmax = x + xext; // max allowable coord
161  prevcolour = uppercolour; // forced plain margin
162  current = NULL; // nothing yet
163 
164  // do each pixel
165  for (; pos.x < xmax; pos.x++, prevline++) {
166  colour = *bwpos++; // current pixel
167  if (*prevline != NULL) {
168  // changed above
169  // change colour
170  uppercolour = FLIP_COLOUR(uppercolour);
171  if (colour == prevcolour) {
172  if (colour == uppercolour) {
173  // finish a line
174  join_edges(current, *prevline, free_cracks, outline_it);
175  current = NULL; // no edge now
176  } else {
177  // new horiz edge
178  current = h_edge(uppercolour - colour, *prevline, &pos);
179  }
180  *prevline = NULL; // no change this time
181  } else {
182  if (colour == uppercolour)
183  *prevline = v_edge(colour - prevcolour, *prevline, &pos);
184  // 8 vs 4 connection
185  else if (colour == WHITE_PIX) {
186  join_edges(current, *prevline, free_cracks, outline_it);
187  current = h_edge(uppercolour - colour, NULL, &pos);
188  *prevline = v_edge(colour - prevcolour, current, &pos);
189  } else {
190  newcurrent = h_edge(uppercolour - colour, *prevline, &pos);
191  *prevline = v_edge(colour - prevcolour, current, &pos);
192  current = newcurrent; // right going h edge
193  }
194  prevcolour = colour; // remember new colour
195  }
196  } else {
197  if (colour != prevcolour) {
198  *prevline = current = v_edge(colour - prevcolour, current, &pos);
199  prevcolour = colour;
200  }
201  if (colour != uppercolour)
202  current = h_edge(uppercolour - colour, current, &pos);
203  else
204  current = NULL; // no edge now
205  }
206  }
207  if (current != NULL) {
208  // out of block
209  if (*prevline != NULL) { // got one to join to?
210  join_edges(current, *prevline, free_cracks, outline_it);
211  *prevline = NULL; // tidy now
212  } else {
213  // fake vertical
214  *prevline = v_edge(FLIP_COLOUR(prevcolour)-prevcolour, current, &pos);
215  }
216  } else if (*prevline != NULL) {
217  //continue fake
218  *prevline = v_edge(FLIP_COLOUR(prevcolour)-prevcolour, *prevline, &pos);
219  }
220 }
221 
222 
223 /**********************************************************************
224  * h_edge
225  *
226  * Create a new horizontal CRACKEDGE and join it to the given edge.
227  **********************************************************************/
228 
229 CRACKEDGE *h_edge(int sign, // sign of edge
230  CRACKEDGE* join, // edge to join to
231  CrackPos* pos) {
232  CRACKEDGE *newpt; // return value
233 
234  if (*pos->free_cracks != NULL) {
235  newpt = *pos->free_cracks;
236  *pos->free_cracks = newpt->next; // get one fast
237  } else {
238  newpt = new CRACKEDGE;
239  }
240  newpt->pos.set_y(pos->y + 1); // coords of pt
241  newpt->stepy = 0; // edge is horizontal
242 
243  if (sign > 0) {
244  newpt->pos.set_x(pos->x + 1); // start location
245  newpt->stepx = -1;
246  newpt->stepdir = 0;
247  } else {
248  newpt->pos.set_x(pos->x); // start location
249  newpt->stepx = 1;
250  newpt->stepdir = 2;
251  }
252 
253  if (join == NULL) {
254  newpt->next = newpt; // ptrs to other ends
255  newpt->prev = newpt;
256  } else {
257  if (newpt->pos.x() + newpt->stepx == join->pos.x()
258  && newpt->pos.y() == join->pos.y()) {
259  newpt->prev = join->prev; // update other ends
260  newpt->prev->next = newpt;
261  newpt->next = join; // join up
262  join->prev = newpt;
263  } else {
264  newpt->next = join->next; // update other ends
265  newpt->next->prev = newpt;
266  newpt->prev = join; // join up
267  join->next = newpt;
268  }
269  }
270  return newpt;
271 }
272 
273 
274 /**********************************************************************
275  * v_edge
276  *
277  * Create a new vertical CRACKEDGE and join it to the given edge.
278  **********************************************************************/
279 
280 CRACKEDGE *v_edge(int sign, // sign of edge
281  CRACKEDGE* join,
282  CrackPos* pos) {
283  CRACKEDGE *newpt; // return value
284 
285  if (*pos->free_cracks != NULL) {
286  newpt = *pos->free_cracks;
287  *pos->free_cracks = newpt->next; // get one fast
288  } else {
289  newpt = new CRACKEDGE;
290  }
291  newpt->pos.set_x(pos->x); // coords of pt
292  newpt->stepx = 0; // edge is vertical
293 
294  if (sign > 0) {
295  newpt->pos.set_y(pos->y); // start location
296  newpt->stepy = 1;
297  newpt->stepdir = 3;
298  } else {
299  newpt->pos.set_y(pos->y + 1); // start location
300  newpt->stepy = -1;
301  newpt->stepdir = 1;
302  }
303 
304  if (join == NULL) {
305  newpt->next = newpt; //ptrs to other ends
306  newpt->prev = newpt;
307  } else {
308  if (newpt->pos.x() == join->pos.x()
309  && newpt->pos.y() + newpt->stepy == join->pos.y()) {
310  newpt->prev = join->prev; // update other ends
311  newpt->prev->next = newpt;
312  newpt->next = join; // join up
313  join->prev = newpt;
314  } else {
315  newpt->next = join->next; // update other ends
316  newpt->next->prev = newpt;
317  newpt->prev = join; // join up
318  join->next = newpt;
319  }
320  }
321  return newpt;
322 }
323 
324 
325 /**********************************************************************
326  * join_edges
327  *
328  * Join 2 edges together. Send the outline for approximation when a
329  * closed loop is formed.
330  **********************************************************************/
331 
332 void join_edges(CRACKEDGE *edge1, // edges to join
333  CRACKEDGE *edge2, // no specific order
334  CRACKEDGE **free_cracks,
335  C_OUTLINE_IT* outline_it) {
336  if (edge1->pos.x() + edge1->stepx != edge2->pos.x()
337  || edge1->pos.y() + edge1->stepy != edge2->pos.y()) {
338  CRACKEDGE *tempedge = edge1;
339  edge1 = edge2; // swap around
340  edge2 = tempedge;
341  }
342 
343  if (edge1->next == edge2) {
344  // already closed
345  complete_edge(edge1, outline_it);
346  // attach freelist to end
347  edge1->prev->next = *free_cracks;
348  *free_cracks = edge1; // and free list
349  } else {
350  // update opposite ends
351  edge2->prev->next = edge1->next;
352  edge1->next->prev = edge2->prev;
353  edge1->next = edge2; // make joins
354  edge2->prev = edge1;
355  }
356 }
357 
358 
359 /**********************************************************************
360  * free_crackedges
361  *
362  * Really free the CRACKEDGEs by giving them back to delete.
363  **********************************************************************/
364 
366  CRACKEDGE *current; // current edge to free
367  CRACKEDGE *next; // next one to free
368 
369  for (current = start; current != NULL; current = next) {
370  next = current->next;
371  delete current; // delete them all
372  }
373 }
CRACKEDGE * v_edge(int sign, CRACKEDGE *join, CrackPos *pos)
Definition: scanedg.cpp:280
void block_edges(Pix *t_pix, PDBLK *block, C_OUTLINE_IT *outline_it)
Definition: scanedg.cpp:38
void set_y(inT16 yin)
rewrite function
Definition: points.h:65
void make_margins(PDBLK *block, BLOCK_LINE_IT *line_it, uinT8 *pixels, uinT8 margin, inT16 left, inT16 right, inT16 y)
Definition: scanedg.cpp:88
inT8 stepdir
Definition: crakedge.h:33
CRACKEDGE ** free_cracks
Definition: scanedg.h:31
void complete_edge(CRACKEDGE *start, C_OUTLINE_IT *outline_it)
Definition: edgloop.cpp:37
inT16 x() const
access function
Definition: points.h:52
void set_x(inT16 xin)
rewrite function
Definition: points.h:61
ICOORD pos
Definition: crakedge.h:30
int16_t inT16
Definition: host.h:36
inT16 get_line(inT16 y, inT16 &xext)
Definition: pdblock.cpp:345
uint8_t uinT8
Definition: host.h:35
inT8 stepx
Definition: crakedge.h:31
CRACKEDGE * prev
Definition: crakedge.h:34
void bounding_box(ICOORD &bottom_left, ICOORD &top_right) const
get box
Definition: pdblock.h:59
rectangle iterator
Definition: pdblock.h:144
CRACKEDGE * h_edge(int sign, CRACKEDGE *join, CrackPos *pos)
Definition: scanedg.cpp:229
void free_crackedges(CRACKEDGE *start)
Definition: scanedg.cpp:365
int32_t inT32
Definition: host.h:38
CRACKEDGE * next
Definition: crakedge.h:35
void line_edges(inT16 x, inT16 y, inT16 xext, uinT8 uppercolour, uinT8 *bwpos, CRACKEDGE **prevline, CRACKEDGE **free_cracks, C_OUTLINE_IT *outline_it)
Definition: scanedg.cpp:145
void join_edges(CRACKEDGE *edge1, CRACKEDGE *edge2, CRACKEDGE **free_cracks, C_OUTLINE_IT *outline_it)
Definition: scanedg.cpp:332
int y
Definition: scanedg.h:33
POLY_BLOCK * poly_block() const
Definition: pdblock.h:55
inT16 y() const
access_function
Definition: points.h:56
#define WHITE_PIX
Definition: scanedg.cpp:27
#define FLIP_COLOUR(pix)
Definition: scanedg.cpp:30
LIST join(LIST list1, LIST list2)
Definition: oldlist.cpp:236
int x
Definition: scanedg.h:32
integer coordinate
Definition: points.h:30
page block
Definition: pdblock.h:32
inT8 stepy
Definition: crakedge.h:32
ICOORDELT_LIST * get_line(inT16 y)
Definition: polyblk.cpp:344