/src/tesseract/src/ccstruct/quadlsq.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | * File: quadlsq.cpp (Formerly qlsq.c) |
3 | | * Description: Code for least squares approximation of quadratics. |
4 | | * Author: Ray Smith |
5 | | * |
6 | | * (C) Copyright 1993, 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 "quadlsq.h" |
20 | | |
21 | | #include "tprintf.h" |
22 | | |
23 | | #include <cmath> |
24 | | #include <cstdio> |
25 | | |
26 | | namespace tesseract { |
27 | | |
28 | | // Minimum variance in least squares before backing off to a lower degree. |
29 | | const long double kMinVariance = 1.0L / 1024; |
30 | | |
31 | | /********************************************************************** |
32 | | * QLSQ::clear |
33 | | * |
34 | | * Function to initialize a QLSQ. |
35 | | **********************************************************************/ |
36 | | |
37 | 629k | void QLSQ::clear() { // initialize |
38 | 629k | a = 0.0; |
39 | 629k | b = 0.0; |
40 | 629k | c = 0.0; |
41 | 629k | n = 0; // No elements. |
42 | 629k | sigx = 0.0; // Zero accumulators. |
43 | 629k | sigy = 0.0; |
44 | 629k | sigxx = 0.0; |
45 | 629k | sigxy = 0.0; |
46 | 629k | sigyy = 0.0; |
47 | 629k | sigxxx = 0.0; |
48 | 629k | sigxxy = 0.0; |
49 | 629k | sigxxxx = 0.0; |
50 | 629k | } |
51 | | |
52 | | /********************************************************************** |
53 | | * QLSQ::add |
54 | | * |
55 | | * Add an element to the accumulator. |
56 | | **********************************************************************/ |
57 | | |
58 | 1.53M | void QLSQ::add(double x, double y) { |
59 | 1.53M | n++; // Count elements. |
60 | 1.53M | sigx += x; // Update accumulators. |
61 | 1.53M | sigy += y; |
62 | 1.53M | sigxx += x * x; |
63 | 1.53M | sigxy += x * y; |
64 | 1.53M | sigyy += y * y; |
65 | 1.53M | sigxxx += static_cast<long double>(x) * x * x; |
66 | 1.53M | sigxxy += static_cast<long double>(x) * x * y; |
67 | 1.53M | sigxxxx += static_cast<long double>(x) * x * x * x; |
68 | 1.53M | } |
69 | | |
70 | | /********************************************************************** |
71 | | * QLSQ::remove |
72 | | * |
73 | | * Delete an element from the accumulator. |
74 | | **********************************************************************/ |
75 | | |
76 | 0 | void QLSQ::remove(double x, double y) { |
77 | 0 | if (n <= 0) { |
78 | 0 | tprintf("Can't remove an element from an empty QLSQ accumulator!\n"); |
79 | 0 | return; |
80 | 0 | } |
81 | 0 | n--; // Count elements. |
82 | 0 | sigx -= x; // Update accumulators. |
83 | 0 | sigy -= y; |
84 | 0 | sigxx -= x * x; |
85 | 0 | sigxy -= x * y; |
86 | 0 | sigyy -= y * y; |
87 | 0 | sigxxx -= static_cast<long double>(x) * x * x; |
88 | 0 | sigxxy -= static_cast<long double>(x) * x * y; |
89 | 0 | sigxxxx -= static_cast<long double>(x) * x * x * x; |
90 | 0 | } |
91 | | |
92 | | /********************************************************************** |
93 | | * QLSQ::fit |
94 | | * |
95 | | * Fit the given degree of polynomial and store the result. |
96 | | * This creates a quadratic of the form axx + bx + c, but limited to |
97 | | * the given degree. |
98 | | **********************************************************************/ |
99 | | |
100 | 311k | void QLSQ::fit(int degree) { |
101 | 311k | long double x_variance = |
102 | 311k | static_cast<long double>(sigxx) * n - static_cast<long double>(sigx) * sigx; |
103 | | |
104 | | // Note: for computational efficiency, we do not normalize the variance, |
105 | | // covariance and cube variance here as they are in the same order in both |
106 | | // nominators and denominators. However, we need be careful in value range |
107 | | // check. |
108 | 311k | if (x_variance < kMinVariance * n * n || degree < 1 || n < 2) { |
109 | | // We cannot calculate b reliably so forget a and b, and just work on c. |
110 | 22.4k | a = b = 0.0; |
111 | 22.4k | if (n >= 1 && degree >= 0) { |
112 | 22.4k | c = sigy / n; |
113 | 22.4k | } else { |
114 | 0 | c = 0.0; |
115 | 0 | } |
116 | 22.4k | return; |
117 | 22.4k | } |
118 | 288k | long double top96 = 0.0; // Accurate top. |
119 | 288k | long double bottom96 = 0.0; // Accurate bottom. |
120 | 288k | long double cubevar = sigxxx * n - static_cast<long double>(sigxx) * sigx; |
121 | 288k | long double covariance = |
122 | 288k | static_cast<long double>(sigxy) * n - static_cast<long double>(sigx) * sigy; |
123 | | |
124 | 288k | if (n >= 4 && degree >= 2) { |
125 | 123k | top96 = cubevar * covariance; |
126 | 123k | top96 += x_variance * (static_cast<long double>(sigxx) * sigy - sigxxy * n); |
127 | | |
128 | 123k | bottom96 = cubevar * cubevar; |
129 | 123k | bottom96 -= x_variance * (sigxxxx * n - static_cast<long double>(sigxx) * sigxx); |
130 | 123k | } |
131 | 288k | if (bottom96 >= kMinVariance * n * n * n * n) { |
132 | | // Denominators looking good |
133 | 0 | a = top96 / bottom96; |
134 | 0 | top96 = covariance - cubevar * a; |
135 | 0 | b = top96 / x_variance; |
136 | 288k | } else { |
137 | | // Forget a, and concentrate on b. |
138 | 288k | a = 0.0; |
139 | 288k | b = covariance / x_variance; |
140 | 288k | } |
141 | 288k | c = (sigy - a * sigxx - b * sigx) / n; |
142 | 288k | } |
143 | | |
144 | | } // namespace tesseract |