tesseract
3.03
|
00001 /********************************************************************** 00002 * File: quadlsq.cpp (Formerly qlsq.c) 00003 * Description: Code for least squares approximation of quadratics. 00004 * Author: Ray Smith 00005 * Created: Wed Oct 6 15:14:23 BST 1993 00006 * 00007 * (C) Copyright 1993, Hewlett-Packard Ltd. 00008 ** Licensed under the Apache License, Version 2.0 (the "License"); 00009 ** you may not use this file except in compliance with the License. 00010 ** You may obtain a copy of the License at 00011 ** http://www.apache.org/licenses/LICENSE-2.0 00012 ** Unless required by applicable law or agreed to in writing, software 00013 ** distributed under the License is distributed on an "AS IS" BASIS, 00014 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 ** See the License for the specific language governing permissions and 00016 ** limitations under the License. 00017 * 00018 **********************************************************************/ 00019 00020 #include <stdio.h> 00021 #include <math.h> 00022 #include "quadlsq.h" 00023 #include "tprintf.h" 00024 00025 // Minimum variance in least squares before backing off to a lower degree. 00026 const double kMinVariance = 1.0 / 1024; 00027 00028 /********************************************************************** 00029 * QLSQ::clear 00030 * 00031 * Function to initialize a QLSQ. 00032 **********************************************************************/ 00033 00034 void QLSQ::clear() { // initialize 00035 a = 0.0; 00036 b = 0.0; 00037 c = 0.0; 00038 n = 0; // No elements. 00039 sigx = 0.0; // Zero accumulators. 00040 sigy = 0.0; 00041 sigxx = 0.0; 00042 sigxy = 0.0; 00043 sigyy = 0.0; 00044 sigxxx = 0.0; 00045 sigxxy = 0.0; 00046 sigxxxx = 0.0; 00047 } 00048 00049 00050 /********************************************************************** 00051 * QLSQ::add 00052 * 00053 * Add an element to the accumulator. 00054 **********************************************************************/ 00055 00056 void QLSQ::add(double x, double y) { 00057 n++; // Count elements. 00058 sigx += x; // Update accumulators. 00059 sigy += y; 00060 sigxx += x * x; 00061 sigxy += x * y; 00062 sigyy += y * y; 00063 sigxxx += static_cast<long double>(x) * x * x; 00064 sigxxy += static_cast<long double>(x) * x * y; 00065 sigxxxx += static_cast<long double>(x) * x * x * x; 00066 } 00067 00068 00069 /********************************************************************** 00070 * QLSQ::remove 00071 * 00072 * Delete an element from the accumulator. 00073 **********************************************************************/ 00074 00075 void QLSQ::remove(double x, double y) { 00076 if (n <= 0) { 00077 tprintf("Can't remove an element from an empty QLSQ accumulator!\n"); 00078 return; 00079 } 00080 n--; // Count elements. 00081 sigx -= x; // Update accumulators. 00082 sigy -= y; 00083 sigxx -= x * x; 00084 sigxy -= x * y; 00085 sigyy -= y * y; 00086 sigxxx -= static_cast<long double>(x) * x * x; 00087 sigxxy -= static_cast<long double>(x) * x * y; 00088 sigxxxx -= static_cast<long double>(x) * x * x * x; 00089 } 00090 00091 00092 /********************************************************************** 00093 * QLSQ::fit 00094 * 00095 * Fit the given degree of polynomial and store the result. 00096 * This creates a quadratic of the form axx + bx + c, but limited to 00097 * the given degree. 00098 **********************************************************************/ 00099 00100 void QLSQ::fit(int degree) { 00101 long double x_variance = static_cast<long double>(sigxx) * n - 00102 static_cast<long double>(sigx) * sigx; 00103 00104 // Note: for computational efficiency, we do not normalize the variance, 00105 // covariance and cube variance here as they are in the same order in both 00106 // nominators and denominators. However, we need be careful in value range 00107 // check. 00108 if (x_variance < kMinVariance * n * n || degree < 1 || n < 2) { 00109 // We cannot calculate b reliably so forget a and b, and just work on c. 00110 a = b = 0.0; 00111 if (n >= 1 && degree >= 0) { 00112 c = sigy / n; 00113 } else { 00114 c = 0.0; 00115 } 00116 return; 00117 } 00118 long double top96 = 0.0; // Accurate top. 00119 long double bottom96 = 0.0; // Accurate bottom. 00120 long double cubevar = sigxxx * n - static_cast<long double>(sigxx) * sigx; 00121 long double covariance = static_cast<long double>(sigxy) * n - 00122 static_cast<long double>(sigx) * sigy; 00123 00124 if (n >= 4 && degree >= 2) { 00125 top96 = cubevar * covariance; 00126 top96 += x_variance * (static_cast<long double>(sigxx) * sigy - sigxxy * n); 00127 00128 bottom96 = cubevar * cubevar; 00129 bottom96 -= x_variance * 00130 (sigxxxx * n - static_cast<long double>(sigxx) * sigxx); 00131 } 00132 if (bottom96 >= kMinVariance * n * n * n * n) { 00133 // Denominators looking good 00134 a = top96 / bottom96; 00135 top96 = covariance - cubevar * a; 00136 b = top96 / x_variance; 00137 } else { 00138 // Forget a, and concentrate on b. 00139 a = 0.0; 00140 b = covariance / x_variance; 00141 } 00142 c = (sigy - a * sigxx - b * sigx) / n; 00143 }