1 /**********************************************************************
2 * File: edgloop.cpp (Formerly edgeloop.c)
3 * Description: Functions to clean up an outline before approximation.
4 * Author: Ray Smith
5 *
6 * (C) Copyright 1991, 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 "scanedg.h"
25
26 #include "edgloop.h"
27
28 namespace tesseract {
29
30 #define MINEDGELENGTH 8 // min decent length
31
32 /**********************************************************************
33 * complete_edge
34 *
35 * Complete the edge by cleaning it up.
36 **********************************************************************/
37
complete_edge(CRACKEDGE * start,C_OUTLINE_IT * outline_it)38 void complete_edge(CRACKEDGE *start, // start of loop
39 C_OUTLINE_IT *outline_it) {
40 ScrollView::Color colour; // colour to draw in
41 int16_t looplength; // steps in loop
42 ICOORD botleft; // bounding box
43 ICOORD topright;
44 C_OUTLINE *outline; // new outline
45
46 // check length etc.
47 colour = check_path_legal(start);
48
49 if (colour == ScrollView::RED || colour == ScrollView::BLUE) {
50 looplength = loop_bounding_box(start, botleft, topright);
51 outline = new C_OUTLINE(start, botleft, topright, looplength);
52 // add to list
53 outline_it->add_after_then_move(outline);
54 }
55 }
56
57 /**********************************************************************
58 * check_path_legal
59 *
60 * Check that the outline is legal for length and for chaincode sum.
61 * The return value is RED for a normal black-inside outline,
62 * BLUE for a white-inside outline, MAGENTA if it is too short,
63 * YELLOW if it is too long, and GREEN if it is illegal.
64 * These colours are used to draw the raw outline.
65 **********************************************************************/
66
check_path_legal(CRACKEDGE * start)67 ScrollView::Color check_path_legal( // certify outline
68 CRACKEDGE *start // start of loop
69 ) {
70 int lastchain; // last chain code
71 int chaindiff; // chain code diff
72 int32_t length; // length of loop
73 int32_t chainsum; // sum of chain diffs
74 CRACKEDGE *edgept; // current point
75 constexpr ERRCODE ED_ILLEGAL_SUM("Illegal sum of chain codes");
76
77 length = 0;
78 chainsum = 0; // sum of chain codes
79 edgept = start;
80 lastchain = edgept->prev->stepdir; // previous chain code
81 do {
82 length++;
83 if (edgept->stepdir != lastchain) {
84 // chain code difference
85 chaindiff = edgept->stepdir - lastchain;
86 if (chaindiff > 2) {
87 chaindiff -= 4;
88 } else if (chaindiff < -2) {
89 chaindiff += 4;
90 }
91 chainsum += chaindiff; // sum differences
92 lastchain = edgept->stepdir;
93 }
94 edgept = edgept->next;
95 } while (edgept != start && length < C_OUTLINE::kMaxOutlineLength);
96
97 if ((chainsum != 4 && chainsum != -4) || edgept != start || length < MINEDGELENGTH) {
98 if (edgept != start) {
99 return ScrollView::YELLOW;
100 } else if (length < MINEDGELENGTH) {
101 return ScrollView::MAGENTA;
102 } else {
103 ED_ILLEGAL_SUM.error("check_path_legal", TESSLOG, "chainsum=%d", chainsum);
104 return ScrollView::GREEN;
105 }
106 }
107 // colour on inside
108 return chainsum < 0 ? ScrollView::BLUE : ScrollView::RED;
109 }
110
111 /**********************************************************************
112 * loop_bounding_box
113 *
114 * Find the bounding box of the edge loop.
115 **********************************************************************/
116
loop_bounding_box(CRACKEDGE * & start,ICOORD & botleft,ICOORD & topright)117 int16_t loop_bounding_box( // get bounding box
118 CRACKEDGE *&start, // edge loop
119 ICOORD &botleft, // bounding box
120 ICOORD &topright) {
121 int16_t length; // length of loop
122 int16_t leftmost; // on top row
123 CRACKEDGE *edgept; // current point
124 CRACKEDGE *realstart; // topleft start
125
126 edgept = start;
127 realstart = start;
128 botleft = topright = ICOORD(edgept->pos.x(), edgept->pos.y());
129 leftmost = edgept->pos.x();
130 length = 0; // coutn length
131 do {
132 edgept = edgept->next;
133 if (edgept->pos.x() < botleft.x()) {
134 // get bounding box
135 botleft.set_x(edgept->pos.x());
136 } else if (edgept->pos.x() > topright.x()) {
137 topright.set_x(edgept->pos.x());
138 }
139 if (edgept->pos.y() < botleft.y()) {
140 // get bounding box
141 botleft.set_y(edgept->pos.y());
142 } else if (edgept->pos.y() > topright.y()) {
143 realstart = edgept;
144 leftmost = edgept->pos.x();
145 topright.set_y(edgept->pos.y());
146 } else if (edgept->pos.y() == topright.y() && edgept->pos.x() < leftmost) {
147 // leftmost on line
148 leftmost = edgept->pos.x();
149 realstart = edgept;
150 }
151 length++; // count elements
152 } while (edgept != start);
153 start = realstart; // shift it to topleft
154 return length;
155 }
156
157 } // namespace tesseract
158