/src/tesseract/src/ccstruct/quspline.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * File: quspline.cpp (Formerly qspline.c) |
3 | | * Description: Code for the QSPLINE 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 | | // Include automatically generated configuration file if running autoconf. |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include "config_auto.h" |
22 | | #endif |
23 | | |
24 | | #include "quspline.h" |
25 | | |
26 | | #include "points.h" // for ICOORD |
27 | | #include "quadlsq.h" // for QLSQ |
28 | | #include "quadratc.h" // for QUAD_COEFFS |
29 | | |
30 | | #include <allheaders.h> // for pixRenderPolyline, pixGetDepth, pixGetHeight |
31 | | #include "pix.h" // for L_CLEAR_PIXELS, L_SET_PIXELS, Pix (ptr only) |
32 | | |
33 | | namespace tesseract { |
34 | | |
35 | 0 | #define QSPLINE_PRECISION 16 // no of steps to draw |
36 | | |
37 | | /********************************************************************** |
38 | | * QSPLINE::QSPLINE |
39 | | * |
40 | | * Constructor to build a QSPLINE given the components used in the old code. |
41 | | **********************************************************************/ |
42 | | |
43 | | QSPLINE::QSPLINE( // constructor |
44 | | int32_t count, // no of segments |
45 | | int32_t *xstarts, // start coords |
46 | | double *coeffs // coefficients |
47 | 319k | ) { |
48 | 319k | int32_t index; // segment index |
49 | | |
50 | | // get memory |
51 | 319k | xcoords = new int32_t[count + 1]; |
52 | 319k | quadratics = new QUAD_COEFFS[count]; |
53 | 319k | segments = count; |
54 | 915k | for (index = 0; index < segments; index++) { |
55 | | // copy them |
56 | 595k | xcoords[index] = xstarts[index]; |
57 | 595k | quadratics[index] = |
58 | 595k | QUAD_COEFFS(coeffs[index * 3], coeffs[index * 3 + 1], coeffs[index * 3 + 2]); |
59 | 595k | } |
60 | | // right edge |
61 | 319k | xcoords[index] = xstarts[index]; |
62 | 319k | } |
63 | | |
64 | | /********************************************************************** |
65 | | * QSPLINE::QSPLINE |
66 | | * |
67 | | * Constructor to build a QSPLINE by appproximation of points. |
68 | | **********************************************************************/ |
69 | | |
70 | | QSPLINE::QSPLINE( // constructor |
71 | | int xstarts[], // spline boundaries |
72 | | int segcount, // no of segments |
73 | | int xpts[], // points to fit |
74 | | int ypts[], int pointcount, // no of pts |
75 | | int degree // fit required |
76 | 138k | ) { |
77 | 138k | int pointindex; /*no along text line */ |
78 | 138k | int segment; /*segment no */ |
79 | 138k | int32_t *ptcounts; // no in each segment |
80 | 138k | QLSQ qlsq; /*accumulator */ |
81 | | |
82 | 138k | segments = segcount; |
83 | 138k | xcoords = new int32_t[segcount + 1]; |
84 | 138k | ptcounts = new int32_t[segcount + 1]; |
85 | 138k | quadratics = new QUAD_COEFFS[segcount]; |
86 | 138k | memmove(xcoords, xstarts, (segcount + 1) * sizeof(int32_t)); |
87 | 138k | ptcounts[0] = 0; /*none in any yet */ |
88 | 1.27M | for (segment = 0, pointindex = 0; pointindex < pointcount; pointindex++) { |
89 | 1.39M | while (segment < segcount && xpts[pointindex] >= xstarts[segment]) { |
90 | 265k | segment++; /*try next segment */ |
91 | | /*cumulative counts */ |
92 | 265k | ptcounts[segment] = ptcounts[segment - 1]; |
93 | 265k | } |
94 | 1.13M | ptcounts[segment]++; /*no in previous partition */ |
95 | 1.13M | } |
96 | 138k | while (segment < segcount) { |
97 | 0 | segment++; |
98 | | /*zero the rest */ |
99 | 0 | ptcounts[segment] = ptcounts[segment - 1]; |
100 | 0 | } |
101 | | |
102 | 403k | for (segment = 0; segment < segcount; segment++) { |
103 | 265k | qlsq.clear(); |
104 | | /*first blob */ |
105 | 265k | pointindex = ptcounts[segment]; |
106 | 265k | if (pointindex > 0 && xpts[pointindex] != xpts[pointindex - 1] && |
107 | 127k | xpts[pointindex] != xstarts[segment]) { |
108 | 111k | qlsq.add(xstarts[segment], |
109 | 111k | ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) * |
110 | 111k | (xstarts[segment] - xpts[pointindex - 1]) / |
111 | 111k | (xpts[pointindex] - xpts[pointindex - 1])); |
112 | 111k | } |
113 | 1.39M | for (; pointindex < ptcounts[segment + 1]; pointindex++) { |
114 | 1.13M | qlsq.add(xpts[pointindex], ypts[pointindex]); |
115 | 1.13M | } |
116 | 265k | if (pointindex > 0 && pointindex < pointcount && xpts[pointindex] != xstarts[segment + 1]) { |
117 | 111k | qlsq.add(xstarts[segment + 1], |
118 | 111k | ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) * |
119 | 111k | (xstarts[segment + 1] - xpts[pointindex - 1]) / |
120 | 111k | (xpts[pointindex] - xpts[pointindex - 1])); |
121 | 111k | } |
122 | 265k | qlsq.fit(degree); |
123 | 265k | quadratics[segment].a = qlsq.get_a(); |
124 | 265k | quadratics[segment].b = qlsq.get_b(); |
125 | 265k | quadratics[segment].c = qlsq.get_c(); |
126 | 265k | } |
127 | 138k | delete[] ptcounts; |
128 | 138k | } |
129 | | |
130 | | /********************************************************************** |
131 | | * QSPLINE::QSPLINE |
132 | | * |
133 | | * Constructor to build a QSPLINE from another. |
134 | | **********************************************************************/ |
135 | | |
136 | | QSPLINE::QSPLINE( // constructor |
137 | 0 | const QSPLINE &src) { |
138 | 0 | segments = 0; |
139 | 0 | xcoords = nullptr; |
140 | 0 | quadratics = nullptr; |
141 | 0 | *this = src; |
142 | 0 | } |
143 | | |
144 | | /********************************************************************** |
145 | | * QSPLINE::~QSPLINE |
146 | | * |
147 | | * Destroy a QSPLINE. |
148 | | **********************************************************************/ |
149 | | |
150 | 915k | QSPLINE::~QSPLINE() { |
151 | 915k | delete[] xcoords; |
152 | 915k | delete[] quadratics; |
153 | 915k | } |
154 | | |
155 | | /********************************************************************** |
156 | | * QSPLINE::operator= |
157 | | * |
158 | | * Copy a QSPLINE |
159 | | **********************************************************************/ |
160 | | |
161 | | QSPLINE &QSPLINE::operator=( // assignment |
162 | 639k | const QSPLINE &source) { |
163 | 639k | delete[] xcoords; |
164 | 639k | delete[] quadratics; |
165 | | |
166 | 639k | segments = source.segments; |
167 | 639k | xcoords = new int32_t[segments + 1]; |
168 | 639k | quadratics = new QUAD_COEFFS[segments]; |
169 | 639k | memmove(xcoords, source.xcoords, (segments + 1) * sizeof(int32_t)); |
170 | 639k | memmove(quadratics, source.quadratics, segments * sizeof(QUAD_COEFFS)); |
171 | 639k | return *this; |
172 | 639k | } |
173 | | |
174 | | /********************************************************************** |
175 | | * QSPLINE::step |
176 | | * |
177 | | * Return the total of the step functions between the given coords. |
178 | | **********************************************************************/ |
179 | | |
180 | | double QSPLINE::step( // find step functions |
181 | | double x1, // between coords |
182 | 1.61M | double x2) { |
183 | 1.61M | int index1, index2; // indices of coords |
184 | 1.61M | double total; /*total steps */ |
185 | | |
186 | 1.61M | index1 = spline_index(x1); |
187 | 1.61M | index2 = spline_index(x2); |
188 | 1.61M | total = 0; |
189 | 1.72M | while (index1 < index2) { |
190 | 109k | total += static_cast<double>(quadratics[index1 + 1].y(static_cast<float>(xcoords[index1 + 1]))); |
191 | 109k | total -= static_cast<double>(quadratics[index1].y(static_cast<float>(xcoords[index1 + 1]))); |
192 | 109k | index1++; /*next segment */ |
193 | 109k | } |
194 | 1.61M | return total; /*total steps */ |
195 | 1.61M | } |
196 | | |
197 | | /********************************************************************** |
198 | | * QSPLINE::y |
199 | | * |
200 | | * Return the y value at the given x value. |
201 | | **********************************************************************/ |
202 | | |
203 | | double QSPLINE::y( // evaluate |
204 | | double x // coord to evaluate at |
205 | 22.3M | ) const { |
206 | 22.3M | int32_t index; // segment index |
207 | | |
208 | 22.3M | index = spline_index(x); |
209 | 22.3M | return quadratics[index].y(x); // in correct segment |
210 | 22.3M | } |
211 | | |
212 | | /********************************************************************** |
213 | | * QSPLINE::spline_index |
214 | | * |
215 | | * Return the index to the largest xcoord not greater than x. |
216 | | **********************************************************************/ |
217 | | |
218 | | int32_t QSPLINE::spline_index( // evaluate |
219 | | double x // coord to evaluate at |
220 | 25.6M | ) const { |
221 | 25.6M | int32_t index; // segment index |
222 | 25.6M | int32_t bottom; // bottom of range |
223 | 25.6M | int32_t top; // top of range |
224 | | |
225 | 25.6M | bottom = 0; |
226 | 25.6M | top = segments; |
227 | 67.7M | while (top - bottom > 1) { |
228 | 42.1M | index = (top + bottom) / 2; // centre of range |
229 | 42.1M | if (x >= xcoords[index]) { |
230 | 22.0M | bottom = index; // new min |
231 | 22.0M | } else { |
232 | 20.0M | top = index; // new max |
233 | 20.0M | } |
234 | 42.1M | } |
235 | 25.6M | return bottom; |
236 | 25.6M | } |
237 | | |
238 | | /********************************************************************** |
239 | | * QSPLINE::move |
240 | | * |
241 | | * Reposition spline by vector |
242 | | **********************************************************************/ |
243 | | |
244 | | void QSPLINE::move( // reposition spline |
245 | | ICOORD vec // by vector |
246 | 3.09k | ) { |
247 | 3.09k | int32_t segment; // index of segment |
248 | 3.09k | int16_t x_shift = vec.x(); |
249 | | |
250 | 18.4k | for (segment = 0; segment < segments; segment++) { |
251 | 15.3k | xcoords[segment] += x_shift; |
252 | 15.3k | quadratics[segment].move(vec); |
253 | 15.3k | } |
254 | 3.09k | xcoords[segment] += x_shift; |
255 | 3.09k | } |
256 | | |
257 | | /********************************************************************** |
258 | | * QSPLINE::overlap |
259 | | * |
260 | | * Return true if spline2 overlaps this by no more than fraction less |
261 | | * than the bounds of this. |
262 | | **********************************************************************/ |
263 | | |
264 | | bool QSPLINE::overlap( // test overlap |
265 | | QSPLINE *spline2, // 2 cannot be smaller |
266 | | double fraction // by more than this |
267 | 0 | ) { |
268 | 0 | int leftlimit = xcoords[1]; /*common left limit */ |
269 | 0 | int rightlimit = xcoords[segments - 1]; /*common right limit */ |
270 | | /*or too non-overlap */ |
271 | 0 | return !(spline2->segments < 3 || |
272 | 0 | spline2->xcoords[1] > leftlimit + fraction * (rightlimit - leftlimit) || |
273 | 0 | spline2->xcoords[spline2->segments - 1] < |
274 | 0 | rightlimit - fraction * (rightlimit - leftlimit)); |
275 | 0 | } |
276 | | |
277 | | /********************************************************************** |
278 | | * extrapolate_spline |
279 | | * |
280 | | * Extrapolates the spline linearly using the same gradient as the |
281 | | * quadratic has at either end. |
282 | | **********************************************************************/ |
283 | | |
284 | | void QSPLINE::extrapolate( // linear extrapolation |
285 | | double gradient, // gradient to use |
286 | | int xmin, // new left edge |
287 | | int xmax // new right edge |
288 | 195k | ) { |
289 | 195k | int segment; /*current segment of spline */ |
290 | 195k | int dest_segment; // dest index |
291 | 195k | int32_t *xstarts; // new boundaries |
292 | 195k | QUAD_COEFFS *quads; // new ones |
293 | 195k | int increment; // in size |
294 | | |
295 | 195k | increment = xmin < xcoords[0] ? 1 : 0; |
296 | 195k | if (xmax > xcoords[segments]) { |
297 | 189k | increment++; |
298 | 189k | } |
299 | 195k | if (increment == 0) { |
300 | 5.97k | return; |
301 | 5.97k | } |
302 | 190k | xstarts = new int32_t[segments + 1 + increment]; |
303 | 190k | quads = new QUAD_COEFFS[segments + increment]; |
304 | 190k | if (xmin < xcoords[0]) { |
305 | 117k | xstarts[0] = xmin; |
306 | 117k | quads[0].a = 0; |
307 | 117k | quads[0].b = gradient; |
308 | 117k | quads[0].c = y(xcoords[0]) - quads[0].b * xcoords[0]; |
309 | 117k | dest_segment = 1; |
310 | 117k | } else { |
311 | 72.3k | dest_segment = 0; |
312 | 72.3k | } |
313 | 501k | for (segment = 0; segment < segments; segment++) { |
314 | 311k | xstarts[dest_segment] = xcoords[segment]; |
315 | 311k | quads[dest_segment] = quadratics[segment]; |
316 | 311k | dest_segment++; |
317 | 311k | } |
318 | 190k | xstarts[dest_segment] = xcoords[segment]; |
319 | 190k | if (xmax > xcoords[segments]) { |
320 | 189k | quads[dest_segment].a = 0; |
321 | 189k | quads[dest_segment].b = gradient; |
322 | 189k | quads[dest_segment].c = y(xcoords[segments]) - quads[dest_segment].b * xcoords[segments]; |
323 | 189k | dest_segment++; |
324 | 189k | xstarts[dest_segment] = xmax + 1; |
325 | 189k | } |
326 | 190k | segments = dest_segment; |
327 | 190k | delete[] xcoords; |
328 | 190k | delete[] quadratics; |
329 | 190k | xcoords = xstarts; |
330 | 190k | quadratics = quads; |
331 | 190k | } |
332 | | |
333 | | /********************************************************************** |
334 | | * QSPLINE::plot |
335 | | * |
336 | | * Draw the QSPLINE in the given colour. |
337 | | **********************************************************************/ |
338 | | |
339 | | #ifndef GRAPHICS_DISABLED |
340 | | void QSPLINE::plot( // draw it |
341 | | ScrollView *window, // window to draw in |
342 | | ScrollView::Color colour // colour to draw in |
343 | | ) const { |
344 | | int32_t segment; // index of segment |
345 | | int16_t step; // index of poly piece |
346 | | double increment; // x increment |
347 | | double x; // x coord |
348 | | |
349 | | window->Pen(colour); |
350 | | for (segment = 0; segment < segments; segment++) { |
351 | | increment = static_cast<double>(xcoords[segment + 1] - xcoords[segment]) / QSPLINE_PRECISION; |
352 | | x = xcoords[segment]; |
353 | | for (step = 0; step <= QSPLINE_PRECISION; step++) { |
354 | | if (segment == 0 && step == 0) { |
355 | | window->SetCursor(x, quadratics[segment].y(x)); |
356 | | } else { |
357 | | window->DrawTo(x, quadratics[segment].y(x)); |
358 | | } |
359 | | x += increment; |
360 | | } |
361 | | } |
362 | | } |
363 | | #endif |
364 | | |
365 | 0 | void QSPLINE::plot(Image pix) const { |
366 | 0 | if (pix == nullptr) { |
367 | 0 | return; |
368 | 0 | } |
369 | | |
370 | 0 | int32_t segment; // Index of segment |
371 | 0 | int16_t step; // Index of poly piece |
372 | 0 | double increment; // x increment |
373 | 0 | double x; // x coord |
374 | 0 | auto height = static_cast<double>(pixGetHeight(pix)); |
375 | 0 | Pta *points = ptaCreate(QSPLINE_PRECISION * segments); |
376 | 0 | const int kLineWidth = 5; |
377 | |
|
378 | 0 | for (segment = 0; segment < segments; segment++) { |
379 | 0 | increment = static_cast<double>((xcoords[segment + 1] - xcoords[segment])) / QSPLINE_PRECISION; |
380 | 0 | x = xcoords[segment]; |
381 | 0 | for (step = 0; step <= QSPLINE_PRECISION; step++) { |
382 | 0 | double y = height - quadratics[segment].y(x); |
383 | 0 | ptaAddPt(points, x, y); |
384 | 0 | x += increment; |
385 | 0 | } |
386 | 0 | } |
387 | |
|
388 | 0 | switch (pixGetDepth(pix)) { |
389 | 0 | case 1: |
390 | 0 | pixRenderPolyline(pix, points, kLineWidth, L_SET_PIXELS, 1); |
391 | 0 | break; |
392 | 0 | case 32: |
393 | 0 | pixRenderPolylineArb(pix, points, kLineWidth, 255, 0, 0, 1); |
394 | 0 | break; |
395 | 0 | default: |
396 | 0 | pixRenderPolyline(pix, points, kLineWidth, L_CLEAR_PIXELS, 1); |
397 | 0 | break; |
398 | 0 | } |
399 | 0 | ptaDestroy(&points); |
400 | 0 | } |
401 | | |
402 | | } // namespace tesseract |