Coverage Report

Created: 2024-02-28 06:46

/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