tesseract v5.3.3.20231005
tesseract::C_OUTLINE Class Reference

#include <coutln.h>

Inheritance diagram for tesseract::C_OUTLINE:
tesseract::ELIST_LINK

Public Member Functions

 C_OUTLINE ()
 
 ~C_OUTLINE ()
 
bool flag (C_OUTLINE_FLAGS mask) const
 
void set_flag (C_OUTLINE_FLAGS mask, bool value)
 
C_OUTLINE_LIST * child ()
 
const TBOXbounding_box () const
 
void set_step (int16_t stepindex, int8_t stepdir)
 
void set_step (int16_t stepindex, DIR128 stepdir)
 
int32_t pathlength () const
 
DIR128 step_dir (int index) const
 
ICOORD step (int index) const
 
const ICOORDstart_pos () const
 
ICOORD position_at_index (int index) const
 
FCOORD sub_pixel_pos_at_index (const ICOORD &pos, int index) const
 
int direction_at_index (int index) const
 
int edge_strength_at_index (int index) const
 
int chain_code (int index) const
 
bool operator> (C_OUTLINE &other) const
 
C_OUTLINE::area

Compute the area of the outline.

int32_t area () const
 
C_OUTLINE::perimeter

Compute the perimeter of the outline and its first level children.

int32_t perimeter () const
 
C_OUTLINE::outer_area

Compute the area of the outline.

int32_t outer_area () const
 
C_OUTLINE::count_transitions

Compute the number of x and y maxes and mins in the outline.

Parameters
thresholdwinding number on size
int32_t count_transitions (int32_t threshold)
 
C_OUTLINE::operator<
Returns
true if the left operand is inside the right one.
Parameters
otherother outline
bool operator< (const C_OUTLINE &other) const
 
C_OUTLINE::winding_number
Returns
the winding number of the outline around the given point.
Parameters
pointpoint to wind around
int16_t winding_number (ICOORD testpt) const
 
int16_t turn_direction () const
 
C_OUTLINE::reverse

Reverse the direction of an outline.

void reverse ()
 
C_OUTLINE::move

Move C_OUTLINE by vector

Parameters
vecvector to reposition OUTLINE by
void move (const ICOORD vec)
 
bool IsLegallyNested () const
 
void RemoveSmallRecursive (int min_size, C_OUTLINE_IT *it)
 
void ComputeEdgeOffsets (int threshold, Image pix)
 
void ComputeBinaryOffsets ()
 
void render (int left, int top, Image pix) const
 
void render_outline (int left, int top, Image pix) const
 
C_OUTLINE::plot

Draw the outline in the given colour.

Parameters
windowwindow to draw in
colourcolour to draw in
void plot (ScrollView *window, ScrollView::Color colour) const
 
void plot_normed (const DENORM &denorm, ScrollView::Color colour, ScrollView *window) const
 
- Public Member Functions inherited from tesseract::ELIST_LINK
 ELIST_LINK ()
 
 ELIST_LINK (const ELIST_LINK &)
 
void operator= (const ELIST_LINK &)
 

Static Public Member Functions

static C_OUTLINEdeep_copy (const C_OUTLINE *src)
 

Static Public Attributes

static const int kMaxOutlineLength = 16000
 

C_OUTLINE::C_OUTLINE

Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE.

Parameters
srclineoutline to rotate
rotationrotate to coord
 C_OUTLINE (CRACKEDGE *startpt, ICOORD bot_left, ICOORD top_right, int16_t length)
 
 C_OUTLINE (ICOORD startpt, DIR128 *new_steps, int16_t length)
 
 C_OUTLINE (C_OUTLINE *srcline, FCOORD rotation)
 
static void FakeOutline (const TBOX &box, C_OUTLINE_LIST *outlines)
 

C_OUTLINE::operator=

Assignment - deep copy data

Parameters
sourceassign from this
C_OUTLINEoperator= (const C_OUTLINE &source)
 
static ICOORD chain_step (int chaindir)
 

Detailed Description

Definition at line 75 of file coutln.h.

Constructor & Destructor Documentation

◆ C_OUTLINE() [1/4]

tesseract::C_OUTLINE::C_OUTLINE ( )
inline

Definition at line 77 of file coutln.h.

77 {
78 stepcount = 0;
79 offsets = nullptr;
80 }

◆ C_OUTLINE() [2/4]

tesseract::C_OUTLINE::C_OUTLINE ( CRACKEDGE startpt,
ICOORD  bot_left,
ICOORD  top_right,
int16_t  length 
)

Definition at line 57 of file coutln.cpp.

58 : box(bot_left, top_right), start(startpt->pos), offsets(nullptr) {
59 int16_t stepindex; // index to step
60 CRACKEDGE *edgept; // current point
61
62 stepcount = length; // no of steps
63 if (length == 0) {
64 return;
65 }
66 // get memory
67 steps.resize(step_mem());
68 edgept = startpt;
69
70 for (stepindex = 0; stepindex < length; stepindex++) {
71 // set compact step
72 set_step(stepindex, edgept->stepdir);
73 edgept = edgept->next;
74 }
75}
void set_step(int16_t stepindex, int8_t stepdir)
Definition: coutln.h:116

◆ C_OUTLINE() [3/4]

tesseract::C_OUTLINE::C_OUTLINE ( ICOORD  startpt,
DIR128 new_steps,
int16_t  length 
)

Definition at line 82 of file coutln.cpp.

88 : start(startpt), offsets(nullptr) {
89 int8_t dirdiff; // direction difference
90 DIR128 prevdir; // previous direction
91 DIR128 dir; // current direction
92 DIR128 lastdir; // dir of last step
93 TBOX new_box; // easy bounding
94 int16_t stepindex; // index to step
95 int16_t srcindex; // source steps
96 ICOORD pos; // current position
97
98 pos = startpt;
99 stepcount = length; // No. of steps.
100 ASSERT_HOST(length >= 0);
101 steps.resize(step_mem()); // Get memory.
102
103 lastdir = new_steps[length - 1];
104 prevdir = lastdir;
105 for (stepindex = 0, srcindex = 0; srcindex < length; stepindex++, srcindex++) {
106 new_box = TBOX(pos, pos);
107 box += new_box;
108 // copy steps
109 dir = new_steps[srcindex];
110 set_step(stepindex, dir);
111 dirdiff = dir - prevdir;
112 pos += step(stepindex);
113 if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
114 stepindex -= 2; // cancel there-and-back
115 prevdir = stepindex >= 0 ? step_dir(stepindex) : lastdir;
116 } else {
117 prevdir = dir;
118 }
119 }
120 ASSERT_HOST(pos.x() == startpt.x() && pos.y() == startpt.y());
121 do {
122 dirdiff = step_dir(stepindex - 1) - step_dir(0);
123 if (dirdiff == 64 || dirdiff == -64) {
124 start += step(0);
125 stepindex -= 2; // cancel there-and-back
126 for (int i = 0; i < stepindex; ++i) {
127 set_step(i, step_dir(i + 1));
128 }
129 }
130 } while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
131 stepcount = stepindex;
132 ASSERT_HOST(stepcount >= 4);
133}
#define ASSERT_HOST(x)
Definition: errcode.h:54
@ TBOX
ICOORD step(int index) const
Definition: coutln.h:143
DIR128 step_dir(int index) const
Definition: coutln.h:138

◆ C_OUTLINE() [4/4]

tesseract::C_OUTLINE::C_OUTLINE ( C_OUTLINE srcline,
FCOORD  rotation 
)

Definition at line 143 of file coutln.cpp.

143 : offsets(nullptr) {
144 TBOX new_box; // easy bounding
145 int16_t stepindex; // index to step
146 int16_t dirdiff; // direction change
147 ICOORD pos; // current position
148 ICOORD prevpos; // previous dest point
149
150 ICOORD destpos; // destination point
151 int16_t destindex = INT16_MAX; // index to step
152 DIR128 dir; // coded direction
153 uint8_t new_step;
154
155 stepcount = srcline->stepcount * 2;
156 if (stepcount == 0) {
157 box = srcline->box;
158 box.rotate(rotation);
159 return;
160 }
161 // get memory
162 steps.resize(step_mem());
163
164 for (int iteration = 0; iteration < 2; ++iteration) {
165 DIR128 round1 = iteration == 0 ? 32 : 0;
166 DIR128 round2 = iteration != 0 ? 32 : 0;
167 pos = srcline->start;
168 prevpos = pos;
169 prevpos.rotate(rotation);
170 start = prevpos;
171 box = TBOX(start, start);
172 destindex = 0;
173 for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) {
174 pos += srcline->step(stepindex);
175 destpos = pos;
176 destpos.rotate(rotation);
177 // tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y());
178 while (destpos.x() != prevpos.x() || destpos.y() != prevpos.y()) {
179 dir = DIR128(FCOORD(destpos - prevpos));
180 dir += 64; // turn to step style
181 new_step = dir.get_dir();
182 // tprintf(" %i\n", new_step);
183 if (new_step & 31) {
184 set_step(destindex++, dir + round1);
185 prevpos += step(destindex - 1);
186 if (destindex < 2 ||
187 ((dirdiff = step_dir(destindex - 1) - step_dir(destindex - 2)) != -64 &&
188 dirdiff != 64)) {
189 set_step(destindex++, dir + round2);
190 prevpos += step(destindex - 1);
191 } else {
192 prevpos -= step(destindex - 1);
193 destindex--;
194 prevpos -= step(destindex - 1);
195 set_step(destindex - 1, dir + round2);
196 prevpos += step(destindex - 1);
197 }
198 } else {
199 set_step(destindex++, dir);
200 prevpos += step(destindex - 1);
201 }
202 while (destindex >= 2 &&
203 ((dirdiff = step_dir(destindex - 1) - step_dir(destindex - 2)) == -64 ||
204 dirdiff == 64)) {
205 prevpos -= step(destindex - 1);
206 prevpos -= step(destindex - 2);
207 destindex -= 2; // Forget u turn
208 }
209 // ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() ==
210 // destpos.y());
211 new_box = TBOX(destpos, destpos);
212 box += new_box;
213 }
214 }
215 ASSERT_HOST(destpos.x() == start.x() && destpos.y() == start.y());
216 while (destindex > 1) {
217 dirdiff = step_dir(destindex - 1) - step_dir(0);
218 if (dirdiff != 64 && dirdiff != -64) {
219 break;
220 }
221 start += step(0);
222 destindex -= 2;
223 for (int i = 0; i < destindex; ++i) {
224 set_step(i, step_dir(i + 1));
225 }
226 }
227 if (destindex >= 4) {
228 break;
229 }
230 }
231 ASSERT_HOST(destindex <= stepcount);
232 stepcount = destindex;
233 destpos = start;
234 for (stepindex = 0; stepindex < stepcount; stepindex++) {
235 destpos += step(stepindex);
236 }
237 ASSERT_HOST(destpos.x() == start.x() && destpos.y() == start.y());
238}
TDimension y() const
access_function
Definition: points.h:62
TDimension x() const
access function
Definition: points.h:58
void rotate(const FCOORD &vec)
Definition: rect.h:210

◆ ~C_OUTLINE()

tesseract::C_OUTLINE::~C_OUTLINE ( )
inline

Definition at line 94 of file coutln.h.

94 { // destructor
95 delete[] offsets;
96 }

Member Function Documentation

◆ area()

int32_t tesseract::C_OUTLINE::area ( ) const

Definition at line 257 of file coutln.cpp.

257 {
258 int stepindex; // current step
259 int32_t total_steps; // steps to do
260 int32_t total; // total area
261 ICOORD pos; // position of point
262 ICOORD next_step; // step to next pix
263 // We aren't going to modify the list, or its contents, but there is
264 // no const iterator.
265 C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST *>(&children));
266
267 pos = start_pos();
268 total_steps = pathlength();
269 total = 0;
270 for (stepindex = 0; stepindex < total_steps; stepindex++) {
271 // all intersected
272 next_step = step(stepindex);
273 if (next_step.x() < 0) {
274 total += pos.y();
275 } else if (next_step.x() > 0) {
276 total -= pos.y();
277 }
278 pos += next_step;
279 }
280 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
281 total += it.data()->area(); // add areas of children
282 }
283
284 return total;
285}
int32_t pathlength() const
Definition: coutln.h:134
const ICOORD & start_pos() const
Definition: coutln.h:147

◆ bounding_box()

const TBOX & tesseract::C_OUTLINE::bounding_box ( ) const
inline

Definition at line 113 of file coutln.h.

113 {
114 return box;
115 }

◆ chain_code()

int tesseract::C_OUTLINE::chain_code ( int  index) const
inline

Definition at line 197 of file coutln.h.

197 { // index of step
198 return (steps[index / 4] >> (index % 4 * 2)) & STEP_MASK;
199 }
#define STEP_MASK
Definition: coutln.h:43

◆ chain_step()

ICOORD tesseract::C_OUTLINE::chain_step ( int  chaindir)
static

Definition at line 1058 of file coutln.cpp.

1058 {
1059 return step_coords[chaindir % 4];
1060}

◆ child()

C_OUTLINE_LIST * tesseract::C_OUTLINE::child ( )
inline

Definition at line 108 of file coutln.h.

108 { // get child list
109 return &children;
110 }

◆ ComputeBinaryOffsets()

void tesseract::C_OUTLINE::ComputeBinaryOffsets ( )

Adds sub-pixel resolution EdgeOffsets for the outline using only a binary image source.

Runs a sliding window of 5 edge steps over the outline, maintaining a count of the number of steps in each of the 4 directions in the window, and a sum of the x or y position of each step (as appropriate to its direction.) Ignores single-count steps EXCEPT the sharp U-turn and smoothes out the perpendicular direction. Eg

* ___              ___       Chain code from the left:
*    |___    ___   ___|      222122212223221232223000
*        |___|  |_|          Corresponding counts of each direction:
*                          0   00000000000000000123
*                          1   11121111001111100000
*                          2   44434443443333343321
*                          3   00000001111111112111
* Count of direction at center 41434143413313143313
* Step gets used?              YNYYYNYYYNYYNYNYYYyY (y= U-turn exception)
* Path redrawn showing only the used points:
* ___              ___
*     ___    ___   ___|
*         ___    _
* 

Sub-pixel edge position cannot be shown well with ASCII-art, but each horizontal step's y position is the mean of the y positions of the steps in the same direction in the sliding window, which makes a much smoother outline, without losing important detail.

Definition at line 850 of file coutln.cpp.

850 {
851 delete[] offsets;
852 offsets = new EdgeOffset[stepcount];
853 // Count of the number of steps in each direction in the sliding window.
854 int dir_counts[4];
855 // Sum of the positions (y for a horizontal step, x for vertical) in each
856 // direction in the sliding window.
857 int pos_totals[4];
858 memset(dir_counts, 0, sizeof(dir_counts));
859 memset(pos_totals, 0, sizeof(pos_totals));
860 ICOORD pos = start;
861 ICOORD tail_pos = pos;
862 // tail_pos is the trailing position, with the next point to be lost from
863 // the window.
864 tail_pos -= step(stepcount - 1);
865 tail_pos -= step(stepcount - 2);
866 // head_pos is the leading position, with the next point to be added to the
867 // window.
868 ICOORD head_pos = tail_pos;
869 // Set up the initial window with 4 points in [-2, 2)
870 for (int s = -2; s < 2; ++s) {
871 increment_step(s, 1, &head_pos, dir_counts, pos_totals);
872 }
873 for (int s = 0; s < stepcount; pos += step(s++)) {
874 // At step s, s in the middle of [s-2, s+2].
875 increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals);
876 int dir_index = chain_code(s);
877 ICOORD step_vec = step(s);
878 int best_diff = 0;
879 int offset = 0;
880 // Use only steps that have a count of >=2 OR the strong U-turn with a
881 // single d and 2 at d-1 and 2 at d+1 (mod 4).
882 if (dir_counts[dir_index] >= 2 ||
883 (dir_counts[dir_index] == 1 && dir_counts[Modulo(dir_index - 1, 4)] == 2 &&
884 dir_counts[Modulo(dir_index + 1, 4)] == 2)) {
885 // Valid step direction.
886 best_diff = dir_counts[dir_index];
887 int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y();
888 // The offset proposes that the actual step should be positioned at
889 // the mean position of the steps in the window of the same direction.
890 // See ASCII art above.
891 offset = pos_totals[dir_index] - best_diff * edge_pos;
892 }
893 offsets[s].offset_numerator = ClipToRange<int>(offset, -INT8_MAX, INT8_MAX);
894 offsets[s].pixel_diff = ClipToRange<int>(best_diff, 0, UINT8_MAX);
895 // The direction is just the vector from start to end of the window.
896 FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y());
897 offsets[s].direction = direction.to_direction();
898 increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals);
899 }
900}
int Modulo(int a, int b)
Definition: helpers.h:153
uint8_t direction
Definition: coutln.h:69
uint8_t pixel_diff
Definition: coutln.h:68
int8_t offset_numerator
Definition: coutln.h:67
int chain_code(int index) const
Definition: coutln.h:197

◆ ComputeEdgeOffsets()

void tesseract::C_OUTLINE::ComputeEdgeOffsets ( int  threshold,
Image  pix 
)

Adds sub-pixel resolution EdgeOffsets for the outline if the supplied pix is 8-bit. Does nothing otherwise. Operation: Consider the following near-horizontal line:

*   _________
*            |________
*                     |________
* 

At every position along this line, the gradient direction will be close to vertical. Extrapoaltion/interpolation of the position of the threshold that was used to binarize the image gives a more precise vertical position for each horizontal step, and the conflict in step direction and gradient direction can be used to ignore the vertical steps.

Definition at line 738 of file coutln.cpp.

738 {
739 if (pixGetDepth(pix) != 8) {
740 return;
741 }
742 const l_uint32 *data = pixGetData(pix);
743 int wpl = pixGetWpl(pix);
744 int width = pixGetWidth(pix);
745 int height = pixGetHeight(pix);
746 bool negative = flag(COUT_INVERSE);
747 delete[] offsets;
748 offsets = new EdgeOffset[stepcount];
749 ICOORD pos = start;
750 ICOORD prev_gradient;
751 ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &prev_gradient);
752 for (int s = 0; s < stepcount; ++s) {
753 ICOORD step_vec = step(s);
754 TPOINT pt1(pos);
755 pos += step_vec;
756 TPOINT pt2(pos);
757 ICOORD next_gradient;
758 ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &next_gradient);
759 // Use the sum of the prev and next as the working gradient.
760 ICOORD gradient = prev_gradient + next_gradient;
761 // best_diff will be manipulated to be always positive.
762 int best_diff = 0;
763 // offset will be the extrapolation of the location of the greyscale
764 // threshold from the edge with the largest difference, relative to the
765 // location of the binary edge.
766 int offset = 0;
767 if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) {
768 // Horizontal step. diff_sign == 1 indicates black above.
769 int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1;
770 int x = std::min(pt1.x, pt2.x);
771 int y = height - pt1.y;
772 int best_sum = 0;
773 int best_y = y;
774 EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height, &best_diff, &best_sum, &best_y);
775 // Find the strongest edge.
776 int test_y = y;
777 do {
778 ++test_y;
779 } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum,
780 &best_y));
781 test_y = y;
782 do {
783 --test_y;
784 } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum,
785 &best_y));
786 offset = diff_sign * (best_sum / 2 - threshold) + (y - best_y) * best_diff;
787 } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) {
788 // Vertical step. diff_sign == 1 indicates black on the left.
789 int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1;
790 int x = pt1.x;
791 int y = height - std::max(pt1.y, pt2.y);
792 const l_uint32 *line = pixGetData(pix) + y * wpl;
793 int best_sum = 0;
794 int best_x = x;
795 EvaluateHorizontalDiff(line, diff_sign, x, width, &best_diff, &best_sum, &best_x);
796 // Find the strongest edge.
797 int test_x = x;
798 do {
799 ++test_x;
800 } while (
801 EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x));
802 test_x = x;
803 do {
804 --test_x;
805 } while (
806 EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x));
807 offset = diff_sign * (threshold - best_sum / 2) + (best_x - x) * best_diff;
808 }
809 offsets[s].offset_numerator = ClipToRange<int>(offset, -INT8_MAX, INT8_MAX);
810 offsets[s].pixel_diff = ClipToRange<int>(best_diff, 0, UINT8_MAX);
811 if (negative) {
812 gradient = -gradient;
813 }
814 // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2)
815 // to convert from gradient direction to edge direction.
816 offsets[s].direction = Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256);
817 prev_gradient = next_gradient;
818 }
819}
@ TPOINT
const double y
@ COUT_INVERSE
Definition: coutln.h:46
bool flag(C_OUTLINE_FLAGS mask) const
Definition: coutln.h:98
static uint8_t binary_angle_plus_pi(double angle)
Definition: points.cpp:136

◆ count_transitions()

int32_t tesseract::C_OUTLINE::count_transitions ( int32_t  threshold)

Definition at line 347 of file coutln.cpp.

347 {
348 bool first_was_max_x; // what was first
349 bool first_was_max_y;
350 bool looking_for_max_x; // what is next
351 bool looking_for_min_x;
352 bool looking_for_max_y; // what is next
353 bool looking_for_min_y;
354 int stepindex; // current step
355 int32_t total_steps; // steps to do
356 // current limits
357 int32_t max_x, min_x, max_y, min_y;
358 int32_t initial_x, initial_y; // initial limits
359 int32_t total; // total changes
360 ICOORD pos; // position of point
361 ICOORD next_step; // step to next pix
362
363 pos = start_pos();
364 total_steps = pathlength();
365 total = 0;
366 max_x = min_x = pos.x();
367 max_y = min_y = pos.y();
368 looking_for_max_x = true;
369 looking_for_min_x = true;
370 looking_for_max_y = true;
371 looking_for_min_y = true;
372 first_was_max_x = false;
373 first_was_max_y = false;
374 initial_x = pos.x();
375 initial_y = pos.y(); // stop uninit warning
376 for (stepindex = 0; stepindex < total_steps; stepindex++) {
377 // all intersected
378 next_step = step(stepindex);
379 pos += next_step;
380 if (next_step.x() < 0) {
381 if (looking_for_max_x && pos.x() < min_x) {
382 min_x = pos.x();
383 }
384 if (looking_for_min_x && max_x - pos.x() > threshold) {
385 if (looking_for_max_x) {
386 initial_x = max_x;
387 first_was_max_x = false;
388 }
389 total++;
390 looking_for_max_x = true;
391 looking_for_min_x = false;
392 min_x = pos.x(); // reset min
393 }
394 } else if (next_step.x() > 0) {
395 if (looking_for_min_x && pos.x() > max_x) {
396 max_x = pos.x();
397 }
398 if (looking_for_max_x && pos.x() - min_x > threshold) {
399 if (looking_for_min_x) {
400 initial_x = min_x; // remember first min
401 first_was_max_x = true;
402 }
403 total++;
404 looking_for_max_x = false;
405 looking_for_min_x = true;
406 max_x = pos.x();
407 }
408 } else if (next_step.y() < 0) {
409 if (looking_for_max_y && pos.y() < min_y) {
410 min_y = pos.y();
411 }
412 if (looking_for_min_y && max_y - pos.y() > threshold) {
413 if (looking_for_max_y) {
414 initial_y = max_y; // remember first max
415 first_was_max_y = false;
416 }
417 total++;
418 looking_for_max_y = true;
419 looking_for_min_y = false;
420 min_y = pos.y(); // reset min
421 }
422 } else {
423 if (looking_for_min_y && pos.y() > max_y) {
424 max_y = pos.y();
425 }
426 if (looking_for_max_y && pos.y() - min_y > threshold) {
427 if (looking_for_min_y) {
428 initial_y = min_y; // remember first min
429 first_was_max_y = true;
430 }
431 total++;
432 looking_for_max_y = false;
433 looking_for_min_y = true;
434 max_y = pos.y();
435 }
436 }
437 }
438 if (first_was_max_x && looking_for_min_x) {
439 if (max_x - initial_x > threshold) {
440 total++;
441 } else {
442 total--;
443 }
444 } else if (!first_was_max_x && looking_for_max_x) {
445 if (initial_x - min_x > threshold) {
446 total++;
447 } else {
448 total--;
449 }
450 }
451 if (first_was_max_y && looking_for_min_y) {
452 if (max_y - initial_y > threshold) {
453 total++;
454 } else {
455 total--;
456 }
457 } else if (!first_was_max_y && looking_for_max_y) {
458 if (initial_y - min_y > threshold) {
459 total++;
460 } else {
461 total--;
462 }
463 }
464
465 return total;
466}

◆ deep_copy()

static C_OUTLINE * tesseract::C_OUTLINE::deep_copy ( const C_OUTLINE src)
inlinestatic

Definition at line 261 of file coutln.h.

261 {
262 auto *outline = new C_OUTLINE;
263 *outline = *src;
264 return outline;
265 }

◆ direction_at_index()

int tesseract::C_OUTLINE::direction_at_index ( int  index) const
inline

Definition at line 178 of file coutln.h.

178 {
179 if (offsets != nullptr && offsets[index].pixel_diff > 0) {
180 return offsets[index].direction;
181 }
182 return -1;
183 }

◆ edge_strength_at_index()

int tesseract::C_OUTLINE::edge_strength_at_index ( int  index) const
inline

Definition at line 188 of file coutln.h.

188 {
189 if (offsets != nullptr) {
190 return offsets[index].pixel_diff;
191 }
192 return 1;
193 }

◆ FakeOutline()

void tesseract::C_OUTLINE::FakeOutline ( const TBOX box,
C_OUTLINE_LIST *  outlines 
)
static

Definition at line 241 of file coutln.cpp.

241 {
242 C_OUTLINE_IT ol_it(outlines);
243 // Make a C_OUTLINE from the bounds. This is a bit of a hack,
244 // as there is no outline, just a bounding box, but it works nicely.
245 CRACKEDGE start;
246 start.pos = box.topleft();
247 auto *outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0);
248 ol_it.add_to_end(outline);
249}
ICOORD botright() const
Definition: rect.h:106
ICOORD topleft() const
Definition: rect.h:110

◆ flag()

bool tesseract::C_OUTLINE::flag ( C_OUTLINE_FLAGS  mask) const
inline

Definition at line 98 of file coutln.h.

99 { // flag to test
100 return flags[mask];
101 }

◆ IsLegallyNested()

bool tesseract::C_OUTLINE::IsLegallyNested ( ) const

Returns true if *this and its children are legally nested. The outer area of a child should have the opposite sign to the parent. If not, it means we have discarded an outline in between (probably due to excessive length).

Definition at line 620 of file coutln.cpp.

620 {
621 if (stepcount == 0) {
622 return true;
623 }
624 int64_t parent_area = outer_area();
625 // We aren't going to modify the list, or its contents, but there is
626 // no const iterator.
627 C_OUTLINE_IT child_it(const_cast<C_OUTLINE_LIST *>(&children));
628 for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) {
629 const C_OUTLINE *child = child_it.data();
630 if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested()) {
631 return false;
632 }
633 }
634 return true;
635}
int32_t outer_area() const
Definition: coutln.cpp:313
C_OUTLINE_LIST * child()
Definition: coutln.h:108

◆ move()

void tesseract::C_OUTLINE::move ( const ICOORD  vec)

Definition at line 603 of file coutln.cpp.

603 {
604 C_OUTLINE_IT it(&children); // iterator
605
606 box.move(vec);
607 start += vec;
608
609 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
610 it.data()->move(vec); // move child outlines
611 }
612}
void move(const ICOORD vec)
Definition: rect.h:170

◆ operator<()

bool tesseract::C_OUTLINE::operator< ( const C_OUTLINE other) const

Definition at line 475 of file coutln.cpp.

475 {
476 int16_t count = 0; // winding count
477 ICOORD pos; // position of point
478 int32_t stepindex; // index to cstep
479
480 if (!box.overlap(other.box)) {
481 return false; // can't be contained
482 }
483 if (stepcount == 0) {
484 return other.box.contains(this->box);
485 }
486
487 pos = start;
488 for (stepindex = 0; stepindex < stepcount && (count = other.winding_number(pos)) == INTERSECTING;
489 stepindex++) {
490 pos += step(stepindex); // try all points
491 }
492 if (count == INTERSECTING) {
493 // all intersected
494 pos = other.start;
495 for (stepindex = 0;
496 stepindex < other.stepcount && (count = winding_number(pos)) == INTERSECTING;
497 stepindex++) {
498 // try other way round
499 pos += other.step(stepindex);
500 }
501 return count == INTERSECTING || count == 0;
502 }
503 return count != 0;
504}
#define INTERSECTING
Definition: coutln.h:40
int * count
int16_t winding_number(ICOORD testpt) const
Definition: coutln.cpp:513
bool overlap(const TBOX &box) const
Definition: rect.h:363

◆ operator=()

C_OUTLINE & tesseract::C_OUTLINE::operator= ( const C_OUTLINE source)

Definition at line 1017 of file coutln.cpp.

1017 {
1018 box = source.box;
1019 start = source.start;
1020 if (!children.empty()) {
1021 children.clear();
1022 }
1023 children.deep_copy(&source.children, &deep_copy);
1024 delete[] offsets;
1025 offsets = nullptr;
1026 stepcount = source.stepcount;
1027 if (stepcount > 0) {
1028 steps.resize(step_mem());
1029 memmove(&steps[0], &source.steps[0], step_mem());
1030 if (source.offsets != nullptr) {
1031 offsets = new EdgeOffset[stepcount];
1032 memcpy(offsets, source.offsets, stepcount * sizeof(*offsets));
1033 }
1034 }
1035 return *this;
1036}
static C_OUTLINE * deep_copy(const C_OUTLINE *src)
Definition: coutln.h:261

◆ operator>()

bool tesseract::C_OUTLINE::operator> ( C_OUTLINE other) const
inline

Definition at line 209 of file coutln.h.

210 {
211 return other < *this; // use the < to do it
212 }

◆ outer_area()

int32_t tesseract::C_OUTLINE::outer_area ( ) const

Definition at line 313 of file coutln.cpp.

313 {
314 int stepindex; // current step
315 int32_t total_steps; // steps to do
316 int32_t total; // total area
317 ICOORD pos; // position of point
318 ICOORD next_step; // step to next pix
319
320 pos = start_pos();
321 total_steps = pathlength();
322 if (total_steps == 0) {
323 return box.area();
324 }
325 total = 0;
326 for (stepindex = 0; stepindex < total_steps; stepindex++) {
327 // all intersected
328 next_step = step(stepindex);
329 if (next_step.x() < 0) {
330 total += pos.y();
331 } else if (next_step.x() > 0) {
332 total -= pos.y();
333 }
334 pos += next_step;
335 }
336
337 return total;
338}
int32_t area() const
Definition: rect.h:134

◆ pathlength()

int32_t tesseract::C_OUTLINE::pathlength ( ) const
inline

Definition at line 134 of file coutln.h.

134 { // get path length
135 return stepcount;
136 }

◆ perimeter()

int32_t tesseract::C_OUTLINE::perimeter ( ) const

Definition at line 293 of file coutln.cpp.

293 {
294 int32_t total_steps; // Return value.
295 // We aren't going to modify the list, or its contents, but there is
296 // no const iterator.
297 C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST *>(&children));
298
299 total_steps = pathlength();
300 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
301 total_steps += it.data()->pathlength(); // Add perimeters of children.
302 }
303
304 return total_steps;
305}

◆ plot()

void tesseract::C_OUTLINE::plot ( ScrollView window,
ScrollView::Color  colour 
) const

Definition at line 952 of file coutln.cpp.

952 {
953 int16_t stepindex; // index to cstep
954 ICOORD pos; // current position
955 DIR128 stepdir; // direction of step
956
957 pos = start; // current position
958 window->Pen(colour);
959 if (stepcount == 0) {
960 window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
961 return;
962 }
963 window->SetCursor(pos.x(), pos.y());
964
965 stepindex = 0;
966 while (stepindex < stepcount) {
967 pos += step(stepindex); // step to next
968 stepdir = step_dir(stepindex);
969 stepindex++; // count steps
970 // merge straight lines
971 while (stepindex < stepcount && stepdir.get_dir() == step_dir(stepindex).get_dir()) {
972 pos += step(stepindex);
973 stepindex++;
974 }
975 window->DrawTo(pos.x(), pos.y());
976 }
977}
TDimension left() const
Definition: rect.h:82
TDimension top() const
Definition: rect.h:68
TDimension right() const
Definition: rect.h:89
TDimension bottom() const
Definition: rect.h:75

◆ plot_normed()

void tesseract::C_OUTLINE::plot_normed ( const DENORM denorm,
ScrollView::Color  colour,
ScrollView window 
) const

Draws the outline in the given colour, normalized using the given denorm, making use of sub-pixel accurate information if available.

Definition at line 983 of file coutln.cpp.

984 {
985 window->Pen(colour);
986 if (stepcount == 0) {
987 window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
988 return;
989 }
990 const DENORM *root_denorm = denorm.RootDenorm();
991 ICOORD pos = start; // current position
992 FCOORD f_pos = sub_pixel_pos_at_index(pos, 0);
993 FCOORD pos_normed;
994 denorm.NormTransform(root_denorm, f_pos, &pos_normed);
995 window->SetCursor(IntCastRounded(pos_normed.x()), IntCastRounded(pos_normed.y()));
996 for (int s = 0; s < stepcount; pos += step(s++)) {
997 int edge_weight = edge_strength_at_index(s);
998 if (edge_weight == 0) {
999 // This point has conflicting gradient and step direction, so ignore it.
1000 continue;
1001 }
1002 FCOORD f_pos = sub_pixel_pos_at_index(pos, s);
1003 FCOORD pos_normed;
1004 denorm.NormTransform(root_denorm, f_pos, &pos_normed);
1005 window->DrawTo(IntCastRounded(pos_normed.x()), IntCastRounded(pos_normed.y()));
1006 }
1007}
int IntCastRounded(double x)
Definition: helpers.h:170
FCOORD sub_pixel_pos_at_index(const ICOORD &pos, int index) const
Definition: coutln.h:163
int edge_strength_at_index(int index) const
Definition: coutln.h:188

◆ position_at_index()

ICOORD tesseract::C_OUTLINE::position_at_index ( int  index) const
inline

Definition at line 152 of file coutln.h.

152 {
153 ICOORD pos = start;
154 for (int i = 0; i < index; ++i) {
155 pos += step(i);
156 }
157 return pos;
158 }

◆ RemoveSmallRecursive()

void tesseract::C_OUTLINE::RemoveSmallRecursive ( int  min_size,
C_OUTLINE_IT *  it 
)

If this outline is smaller than the given min_size, delete this and remove from its list, via *it, after checking that *it points to this. Otherwise, if any children of this are too small, delete them. On entry, *it must be an iterator pointing to this. If this gets deleted then this is extracted from *it, so an iteration can continue.

Parameters
min_sizeminimum size for outline
itoutline iterator

Definition at line 646 of file coutln.cpp.

646 {
647 if (box.width() < min_size || box.height() < min_size) {
648 ASSERT_HOST(this == it->data());
649 delete it->extract(); // Too small so get rid of it and any children.
650 } else if (!children.empty()) {
651 // Search the children of this, deleting any that are too small.
652 C_OUTLINE_IT child_it(&children);
653 for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) {
654 C_OUTLINE *child = child_it.data();
655 child->RemoveSmallRecursive(min_size, &child_it);
656 }
657 }
658}
TDimension height() const
Definition: rect.h:118
TDimension width() const
Definition: rect.h:126

◆ render()

void tesseract::C_OUTLINE::render ( int  left,
int  top,
Image  pix 
) const

Renders the outline to the given pix, with left and top being the coords of the upper-left corner of the pix.

Definition at line 906 of file coutln.cpp.

906 {
907 ICOORD pos = start;
908 for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
909 ICOORD next_step = step(stepindex);
910 if (next_step.y() < 0) {
911 pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1, PIX_NOT(PIX_DST), nullptr, 0, 0);
912 } else if (next_step.y() > 0) {
913 pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1, PIX_NOT(PIX_DST), nullptr, 0, 0);
914 }
915 pos += next_step;
916 }
917}

◆ render_outline()

void tesseract::C_OUTLINE::render_outline ( int  left,
int  top,
Image  pix 
) const

Renders just the outline to the given pix (no fill), with left and top being the coords of the upper-left corner of the pix.

Parameters
leftcoord
topcoord
pixthe pix to outline

Definition at line 926 of file coutln.cpp.

926 {
927 ICOORD pos = start;
928 for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
929 ICOORD next_step = step(stepindex);
930 if (next_step.y() < 0) {
931 pixSetPixel(pix, pos.x() - left, top - pos.y(), 1);
932 } else if (next_step.y() > 0) {
933 pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1);
934 } else if (next_step.x() < 0) {
935 pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1);
936 } else if (next_step.x() > 0) {
937 pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1);
938 }
939 pos += next_step;
940 }
941}

◆ reverse()

void tesseract::C_OUTLINE::reverse ( )

Definition at line 580 of file coutln.cpp.

580 { // reverse drection
581 DIR128 halfturn = MODULUS / 2; // amount to shift
582 DIR128 stepdir; // direction of step
583 int16_t stepindex; // index to cstep
584 int16_t farindex; // index to other side
585 int16_t halfsteps; // half of stepcount
586
587 halfsteps = (stepcount + 1) / 2;
588 for (stepindex = 0; stepindex < halfsteps; stepindex++) {
589 farindex = stepcount - stepindex - 1;
590 stepdir = step_dir(stepindex);
591 set_step(stepindex, step_dir(farindex) + halfturn);
592 set_step(farindex, stepdir + halfturn);
593 }
594}
#define MODULUS
Definition: mod128.h:26

◆ set_flag()

void tesseract::C_OUTLINE::set_flag ( C_OUTLINE_FLAGS  mask,
bool  value 
)
inline

Definition at line 102 of file coutln.h.

104 { // value to set
105 flags.set(mask, value);
106 }
int value

◆ set_step() [1/2]

void tesseract::C_OUTLINE::set_step ( int16_t  stepindex,
DIR128  stepdir 
)
inline

Definition at line 124 of file coutln.h.

126 { // direction
127 // clean it
128 int8_t chaindir = stepdir.get_dir() >> (DIRBITS - 2);
129 // difference
130 set_step(stepindex, chaindir);
131 // squeeze 4 into byte
132 }
#define DIRBITS
Definition: mod128.h:27

◆ set_step() [2/2]

void tesseract::C_OUTLINE::set_step ( int16_t  stepindex,
int8_t  stepdir 
)
inline

Definition at line 116 of file coutln.h.

118 { // chain code
119 int shift = stepindex % 4 * 2;
120 uint8_t mask = 3 << shift;
121 steps[stepindex / 4] = ((stepdir << shift) & mask) | (steps[stepindex / 4] & ~mask);
122 // squeeze 4 into byte
123 }

◆ start_pos()

const ICOORD & tesseract::C_OUTLINE::start_pos ( ) const
inline

Definition at line 147 of file coutln.h.

147 {
148 return start;
149 }

◆ step()

ICOORD tesseract::C_OUTLINE::step ( int  index) const
inline

Definition at line 143 of file coutln.h.

143 { // index of step
144 return step_coords[chain_code(index)];
145 }

◆ step_dir()

DIR128 tesseract::C_OUTLINE::step_dir ( int  index) const
inline

Definition at line 138 of file coutln.h.

138 {
139 return DIR128(
140 static_cast<int16_t>(((steps[index / 4] >> (index % 4 * 2)) & STEP_MASK) << (DIRBITS - 2)));
141 }

◆ sub_pixel_pos_at_index()

FCOORD tesseract::C_OUTLINE::sub_pixel_pos_at_index ( const ICOORD pos,
int  index 
) const
inline

Definition at line 163 of file coutln.h.

163 {
164 const ICOORD &step_to_next(step(index));
165 FCOORD f_pos(pos.x() + step_to_next.x() / 2.0f, pos.y() + step_to_next.y() / 2.0f);
166 if (offsets != nullptr && offsets[index].pixel_diff > 0) {
167 float offset = offsets[index].offset_numerator;
168 offset /= offsets[index].pixel_diff;
169 if (step_to_next.x() != 0) {
170 f_pos.set_y(f_pos.y() + offset);
171 } else {
172 f_pos.set_x(f_pos.x() + offset);
173 }
174 }
175 return f_pos;
176 }

◆ turn_direction()

int16_t tesseract::C_OUTLINE::turn_direction ( ) const

C_OUTLINE::turn_direction

Returns
the sum direction delta of the outline.

Definition at line 551 of file coutln.cpp.

551 { // winding number
552 DIR128 prevdir; // previous direction
553 DIR128 dir; // current direction
554 int16_t stepindex; // index to cstep
555 int8_t dirdiff; // direction difference
556 int16_t count; // winding count
557
558 if (stepcount == 0) {
559 return 128;
560 }
561 count = 0;
562 prevdir = step_dir(stepcount - 1);
563 for (stepindex = 0; stepindex < stepcount; stepindex++) {
564 dir = step_dir(stepindex);
565 dirdiff = dir - prevdir;
566 ASSERT_HOST(dirdiff == 0 || dirdiff == 32 || dirdiff == -32);
567 count += dirdiff;
568 prevdir = dir;
569 }
570 ASSERT_HOST(count == 128 || count == -128);
571 return count; // winding number
572}

◆ winding_number()

int16_t tesseract::C_OUTLINE::winding_number ( ICOORD  testpt) const

Definition at line 513 of file coutln.cpp.

513 {
514 int16_t stepindex; // index to cstep
515 int16_t count; // winding count
516 ICOORD vec; // to current point
517 ICOORD stepvec; // step vector
518 int32_t cross; // cross product
519
520 vec = start - point; // vector to it
521 count = 0;
522 for (stepindex = 0; stepindex < stepcount; stepindex++) {
523 stepvec = step(stepindex); // get the step
524 // crossing the line
525 if (vec.y() <= 0 && vec.y() + stepvec.y() > 0) {
526 cross = vec * stepvec; // cross product
527 if (cross > 0) {
528 count++; // crossing right half
529 } else if (cross == 0) {
530 return INTERSECTING; // going through point
531 }
532 } else if (vec.y() > 0 && vec.y() + stepvec.y() <= 0) {
533 cross = vec * stepvec;
534 if (cross < 0) {
535 count--; // crossing back
536 } else if (cross == 0) {
537 return INTERSECTING; // illegal
538 }
539 }
540 vec += stepvec; // sum vectors
541 }
542 return count; // winding number
543}

Member Data Documentation

◆ kMaxOutlineLength

const int tesseract::C_OUTLINE::kMaxOutlineLength = 16000
static

Definition at line 273 of file coutln.h.


The documentation for this class was generated from the following files: