/src/tesseract/src/ccstruct/coutln.h
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * File: coutln.h |
3 | | * Description: Code for the C_OUTLINE class. |
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 | | #ifndef COUTLN_H |
20 | | #define COUTLN_H |
21 | | |
22 | | #include "elst.h" // for ELIST_ITERATOR, ELISTIZEH, ELIST_LINK |
23 | | #include "mod128.h" // for DIR128, DIRBITS |
24 | | #include "points.h" // for ICOORD, FCOORD |
25 | | #include "rect.h" // for TBOX |
26 | | #include "scrollview.h" // for ScrollView, ScrollView::Color |
27 | | |
28 | | #include <tesseract/export.h> // for DLLSYM |
29 | | |
30 | | #include <array> |
31 | | #include <bitset> // for std::bitset<16> |
32 | | #include <cstdint> // for int16_t, int32_t |
33 | | |
34 | | struct Pix; |
35 | | |
36 | | namespace tesseract { |
37 | | |
38 | | class CRACKEDGE; |
39 | | class DENORM; |
40 | | |
41 | 9.49M | #define INTERSECTING INT16_MAX // no winding number |
42 | | |
43 | | // mask to get step |
44 | 3.28G | #define STEP_MASK 3 |
45 | | |
46 | | enum C_OUTLINE_FLAGS { |
47 | | COUT_INVERSE // White on black blob |
48 | | }; |
49 | | |
50 | | // Simple struct to hold the 3 values needed to compute a more precise edge |
51 | | // position and direction. The offset_numerator is the difference between the |
52 | | // grey threshold and the mean pixel value. pixel_diff is the difference between |
53 | | // the pixels in the edge. Consider the following row of pixels: p1 p2 p3 p4 p5 |
54 | | // Say the image was thresholded at threshold t, making p1, p2, p3 black |
55 | | // and p4, p5 white (p1, p2, p3 < t, and p4, p5 >= t), but suppose that |
56 | | // max(p[i+1] - p[i]) is p3 - p2. Then the extrapolated position of the edge, |
57 | | // based on the maximum gradient, is at the crack between p2 and p3 plus the |
58 | | // offset (t - (p2+p3)/2)/(p3 - p2). We store the pixel difference p3-p2 |
59 | | // denominator in pixel_diff and the offset numerator, relative to the original |
60 | | // binary edge (t - (p2+p3)/2) - (p3 -p2) in offset_numerator. |
61 | | // The sign of offset_numerator and pixel_diff are manipulated to ensure |
62 | | // that the pixel_diff, which will be used as a weight, is always positive. |
63 | | // The direction stores the quantized feature direction for the given step |
64 | | // computed from the edge gradient. (Using binary_angle_plus_pi.) |
65 | | // If the pixel_diff is zero, it means that the direction of the gradient |
66 | | // is in conflict with the step direction, so this step is to be ignored. |
67 | | struct EdgeOffset { |
68 | | int8_t offset_numerator; |
69 | | uint8_t pixel_diff; |
70 | | uint8_t direction; |
71 | | }; |
72 | | |
73 | | class C_OUTLINE; // forward declaration |
74 | | |
75 | | ELISTIZEH(C_OUTLINE) |
76 | | class C_OUTLINE : public ELIST<C_OUTLINE>::LINK { |
77 | | public: |
78 | 787k | C_OUTLINE() { |
79 | 787k | stepcount = 0; |
80 | 787k | offsets = nullptr; |
81 | 787k | } |
82 | | C_OUTLINE( // constructor |
83 | | CRACKEDGE *startpt, // from edge detector |
84 | | ICOORD bot_left, // bounding box //length of loop |
85 | | ICOORD top_right, int16_t length); |
86 | | C_OUTLINE(ICOORD startpt, // start of loop |
87 | | DIR128 *new_steps, // steps in loop |
88 | | int16_t length); // length of loop |
89 | | // outline to copy |
90 | | C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation); // and rotate |
91 | | |
92 | | // Build a fake outline, given just a bounding box and append to the list. |
93 | | static void FakeOutline(const TBOX &box, C_OUTLINE_LIST *outlines); |
94 | | |
95 | 4.07M | ~C_OUTLINE() { // destructor |
96 | 4.07M | delete[] offsets; |
97 | 4.07M | } |
98 | | |
99 | | bool flag( // test flag |
100 | 5.33M | C_OUTLINE_FLAGS mask) const { // flag to test |
101 | 5.33M | return flags[mask]; |
102 | 5.33M | } |
103 | | void set_flag( // set flag value |
104 | | C_OUTLINE_FLAGS mask, // flag to test |
105 | 3.06M | bool value) { // value to set |
106 | 3.06M | flags.set(mask, value); |
107 | 3.06M | } |
108 | | |
109 | 16.1M | C_OUTLINE_LIST *child() { // get child list |
110 | 16.1M | return &children; |
111 | 16.1M | } |
112 | | |
113 | | // access function |
114 | 172M | const TBOX &bounding_box() const { |
115 | 172M | return box; |
116 | 172M | } |
117 | | void set_step( // set a step |
118 | | int16_t stepindex, // index of step |
119 | 140M | int8_t stepdir) { // chain code |
120 | 140M | int shift = stepindex % 4 * 2; |
121 | 140M | uint8_t mask = 3 << shift; |
122 | 140M | steps[stepindex / 4] = ((stepdir << shift) & mask) | (steps[stepindex / 4] & ~mask); |
123 | | // squeeze 4 into byte |
124 | 140M | } |
125 | | void set_step( // set a step |
126 | | int16_t stepindex, // index of step |
127 | 58.8M | DIR128 stepdir) { // direction |
128 | | // clean it |
129 | 58.8M | int8_t chaindir = stepdir.get_dir() >> (DIRBITS - 2); |
130 | | // difference |
131 | 58.8M | set_step(stepindex, chaindir); |
132 | | // squeeze 4 into byte |
133 | 58.8M | } |
134 | | |
135 | 452M | int32_t pathlength() const { // get path length |
136 | 452M | return stepcount; |
137 | 452M | } |
138 | | // Return step at a given index as a DIR128. |
139 | 307M | DIR128 step_dir(int index) const { |
140 | 307M | return DIR128( |
141 | 307M | static_cast<int16_t>(((steps[index / 4] >> (index % 4 * 2)) & STEP_MASK) << (DIRBITS - 2))); |
142 | 307M | } |
143 | | // Return the step vector for the given outline position. |
144 | 2.76G | ICOORD step(int index) const { // index of step |
145 | 2.76G | return step_coords[chain_code(index)]; |
146 | 2.76G | } |
147 | | // get start position |
148 | 30.6M | const ICOORD &start_pos() const { |
149 | 30.6M | return start; |
150 | 30.6M | } |
151 | | // Returns the position at the given index on the outline. |
152 | | // NOT to be used lightly, as it has to iterate the outline to find out. |
153 | 0 | ICOORD position_at_index(int index) const { |
154 | 0 | ICOORD pos = start; |
155 | 0 | for (int i = 0; i < index; ++i) { |
156 | 0 | pos += step(i); |
157 | 0 | } |
158 | 0 | return pos; |
159 | 0 | } |
160 | | // Returns the sub-pixel accurate position given the integer position pos |
161 | | // at the given index on the outline. pos may be a return value of |
162 | | // position_at_index, or computed by repeatedly adding step to the |
163 | | // start_pos() in the usual way. |
164 | 0 | FCOORD sub_pixel_pos_at_index(const ICOORD &pos, int index) const { |
165 | 0 | const ICOORD &step_to_next(step(index)); |
166 | 0 | FCOORD f_pos(pos.x() + step_to_next.x() / 2.0f, pos.y() + step_to_next.y() / 2.0f); |
167 | 0 | if (offsets != nullptr && offsets[index].pixel_diff > 0) { |
168 | 0 | float offset = offsets[index].offset_numerator; |
169 | 0 | offset /= offsets[index].pixel_diff; |
170 | 0 | if (step_to_next.x() != 0) { |
171 | 0 | f_pos.set_y(f_pos.y() + offset); |
172 | 0 | } else { |
173 | 0 | f_pos.set_x(f_pos.x() + offset); |
174 | 0 | } |
175 | 0 | } |
176 | 0 | return f_pos; |
177 | 0 | } |
178 | | // Returns the step direction for the given index or -1 if there is none. |
179 | 0 | int direction_at_index(int index) const { |
180 | 0 | if (offsets != nullptr && offsets[index].pixel_diff > 0) { |
181 | 0 | return offsets[index].direction; |
182 | 0 | } |
183 | 0 | return -1; |
184 | 0 | } |
185 | | // Returns the edge strength for the given index. |
186 | | // If there are no recorded edge strengths, returns 1 (assuming the image |
187 | | // is binary). Returns 0 if the gradient direction conflicts with the |
188 | | // step direction, indicating that this position could be skipped. |
189 | 0 | int edge_strength_at_index(int index) const { |
190 | 0 | if (offsets != nullptr) { |
191 | 0 | return offsets[index].pixel_diff; |
192 | 0 | } |
193 | 0 | return 1; |
194 | 0 | } |
195 | | // Return the step as a chain code (0-3) related to the standard feature |
196 | | // direction of binary_angle_plus_pi by: |
197 | | // chain_code * 64 = feature direction. |
198 | 2.98G | int chain_code(int index) const { // index of step |
199 | 2.98G | return (steps[index / 4] >> (index % 4 * 2)) & STEP_MASK; |
200 | 2.98G | } |
201 | | |
202 | | int32_t area() const; // Returns area of self and 1st level children. |
203 | | int32_t perimeter() const; // Total perimeter of self and 1st level children. |
204 | | int32_t outer_area() const; // Returns area of self only. |
205 | | int32_t count_transitions( // count maxima |
206 | | int32_t threshold); // size threshold |
207 | | |
208 | | bool operator<( // containment test |
209 | | const C_OUTLINE &other) const; |
210 | | bool operator>( // containment test |
211 | 0 | C_OUTLINE &other) const { |
212 | 0 | return other < *this; // use the < to do it |
213 | 0 | } |
214 | | int16_t winding_number( // get winding number |
215 | | ICOORD testpt) const; // around this point |
216 | | // get direction |
217 | | int16_t turn_direction() const; |
218 | | void reverse(); // reverse direction |
219 | | |
220 | | void move( // reposition outline |
221 | | const ICOORD vec); // by vector |
222 | | |
223 | | // Returns true if *this and its children are legally nested. |
224 | | // The outer area of a child should have the opposite sign to the |
225 | | // parent. If not, it means we have discarded an outline in between |
226 | | // (probably due to excessive length). |
227 | | bool IsLegallyNested() const; |
228 | | |
229 | | // If this outline is smaller than the given min_size, delete this and |
230 | | // remove from its list, via *it, after checking that *it points to this. |
231 | | // Otherwise, if any children of this are too small, delete them. |
232 | | // On entry, *it must be an iterator pointing to this. If this gets deleted |
233 | | // then this is extracted from *it, so an iteration can continue. |
234 | | void RemoveSmallRecursive(int min_size, C_OUTLINE_IT *it); |
235 | | |
236 | | // Adds sub-pixel resolution EdgeOffsets for the outline if the supplied |
237 | | // pix is 8-bit. Does nothing otherwise. |
238 | | void ComputeEdgeOffsets(int threshold, Image pix); |
239 | | // Adds sub-pixel resolution EdgeOffsets for the outline using only |
240 | | // a binary image source. |
241 | | void ComputeBinaryOffsets(); |
242 | | |
243 | | // Renders the outline to the given pix, with left and top being |
244 | | // the coords of the upper-left corner of the pix. |
245 | | void render(int left, int top, Image pix) const; |
246 | | |
247 | | // Renders just the outline to the given pix (no fill), with left and top |
248 | | // being the coords of the upper-left corner of the pix. |
249 | | void render_outline(int left, int top, Image pix) const; |
250 | | |
251 | | #ifndef GRAPHICS_DISABLED |
252 | | void plot( // draw one |
253 | | ScrollView *window, // window to draw in |
254 | | ScrollView::Color colour) const; // colour to draw it |
255 | | // Draws the outline in the given colour, normalized using the given denorm, |
256 | | // making use of sub-pixel accurate information if available. |
257 | | void plot_normed(const DENORM &denorm, ScrollView::Color colour, ScrollView *window) const; |
258 | | #endif // !GRAPHICS_DISABLED |
259 | | |
260 | | C_OUTLINE &operator=(const C_OUTLINE &source); |
261 | | |
262 | 787k | static C_OUTLINE *deep_copy(const C_OUTLINE *src) { |
263 | 787k | auto *outline = new C_OUTLINE; |
264 | 787k | *outline = *src; |
265 | 787k | return outline; |
266 | 787k | } |
267 | | |
268 | | static ICOORD chain_step(int chaindir); |
269 | | |
270 | | // The maximum length of any outline. The stepcount is stored as 16 bits, |
271 | | // but it is probably not a good idea to increase this constant by much |
272 | | // and switch to 32 bits, as it plays an important role in keeping huge |
273 | | // outlines invisible, which prevents bad speed behavior. |
274 | | static const int kMaxOutlineLength = 16000; |
275 | | |
276 | | private: |
277 | | // Helper for ComputeBinaryOffsets. Increments pos, dir_counts, pos_totals |
278 | | // by the step, increment, and vertical step ? x : y position * increment |
279 | | // at step s Mod stepcount respectively. Used to add or subtract the |
280 | | // direction and position to/from accumulators of a small neighbourhood. |
281 | | void increment_step(int s, int increment, ICOORD *pos, int *dir_counts, int *pos_totals) const; |
282 | 4.67M | int step_mem() const { |
283 | 4.67M | return (stepcount + 3) / 4; |
284 | 4.67M | } |
285 | | |
286 | | TBOX box; // bounding box |
287 | | ICOORD start; // start coord |
288 | | int16_t stepcount; // no of steps |
289 | | std::bitset<16> flags; // flags about outline |
290 | | std::vector<uint8_t> steps; // step array |
291 | | EdgeOffset *offsets; // Higher precision edge. |
292 | | C_OUTLINE_LIST children; // child elements |
293 | | static std::array<ICOORD, 4> step_coords; |
294 | | }; |
295 | | |
296 | | } // namespace tesseract |
297 | | |
298 | | #endif |