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