tesseract
3.03
|
00001 00002 // File: detlinefit.cpp 00003 // Description: Deterministic least median squares line fitting. 00004 // Author: Ray Smith 00005 // Created: Thu Feb 28 14:45:01 PDT 2008 00006 // 00007 // (C) Copyright 2008, Google Inc. 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 // 00019 00020 #include "detlinefit.h" 00021 #include "statistc.h" 00022 #include "ndminx.h" 00023 #include "tprintf.h" 00024 00025 namespace tesseract { 00026 00027 // The number of points to consider at each end. 00028 const int kNumEndPoints = 3; 00029 // The minimum number of points at which to switch to number of points 00030 // for badly fitted lines. 00031 // To ensure a sensible error metric, kMinPointsForErrorCount should be at 00032 // least kMaxRealDistance / (1 - %ile) where %ile is the fractile used in 00033 // ComputeUpperQuartileError. 00034 const int kMinPointsForErrorCount = 16; 00035 // The maximum real distance to use before switching to number of 00036 // mis-fitted points, which will get square-rooted for true distance. 00037 const int kMaxRealDistance = 2.0; 00038 00039 DetLineFit::DetLineFit() : square_length_(0.0) { 00040 } 00041 00042 DetLineFit::~DetLineFit() { 00043 } 00044 00045 // Delete all Added points. 00046 void DetLineFit::Clear() { 00047 pts_.clear(); 00048 distances_.clear(); 00049 } 00050 00051 // Add a new point. Takes a copy - the pt doesn't need to stay in scope. 00052 void DetLineFit::Add(const ICOORD& pt) { 00053 pts_.push_back(PointWidth(pt, 0)); 00054 } 00055 // Associates a half-width with the given point if a point overlaps the 00056 // previous point by more than half the width, and its distance is further 00057 // than the previous point, then the more distant point is ignored in the 00058 // distance calculation. Useful for ignoring i dots and other diacritics. 00059 void DetLineFit::Add(const ICOORD& pt, int halfwidth) { 00060 pts_.push_back(PointWidth(pt, halfwidth)); 00061 } 00062 00063 // Fits a line to the points, ignoring the skip_first initial points and the 00064 // skip_last final points, returning the fitted line as a pair of points, 00065 // and the upper quartile error. 00066 double DetLineFit::Fit(int skip_first, int skip_last, 00067 ICOORD* pt1, ICOORD* pt2) { 00068 // Do something sensible with no points. 00069 if (pts_.empty()) { 00070 pt1->set_x(0); 00071 pt1->set_y(0); 00072 *pt2 = *pt1; 00073 return 0.0; 00074 } 00075 // Count the points and find the first and last kNumEndPoints. 00076 int pt_count = pts_.size(); 00077 ICOORD* starts[kNumEndPoints]; 00078 if (skip_first >= pt_count) skip_first = pt_count - 1; 00079 int start_count = 0; 00080 int end_i = MIN(skip_first + kNumEndPoints, pt_count); 00081 for (int i = skip_first; i < end_i; ++i) { 00082 starts[start_count++] = &pts_[i].pt; 00083 } 00084 ICOORD* ends[kNumEndPoints]; 00085 if (skip_last >= pt_count) skip_last = pt_count - 1; 00086 int end_count = 0; 00087 end_i = MAX(0, pt_count - kNumEndPoints - skip_last); 00088 for (int i = pt_count - 1 - skip_last; i >= end_i; --i) { 00089 ends[end_count++] = &pts_[i].pt; 00090 } 00091 // 1 or 2 points need special treatment. 00092 if (pt_count <= 2) { 00093 *pt1 = *starts[0]; 00094 if (pt_count > 1) 00095 *pt2 = *ends[0]; 00096 else 00097 *pt2 = *pt1; 00098 return 0.0; 00099 } 00100 // Although with between 2 and 2*kNumEndPoints-1 points, there will be 00101 // overlap in the starts, ends sets, this is OK and taken care of by the 00102 // if (*start != *end) test below, which also tests for equal input points. 00103 double best_uq = -1.0; 00104 // Iterate each pair of points and find the best fitting line. 00105 for (int i = 0; i < start_count; ++i) { 00106 ICOORD* start = starts[i]; 00107 for (int j = 0; j < end_count; ++j) { 00108 ICOORD* end = ends[j]; 00109 if (*start != *end) { 00110 ComputeDistances(*start, *end); 00111 // Compute the upper quartile error from the line. 00112 double dist = EvaluateLineFit(); 00113 if (dist < best_uq || best_uq < 0.0) { 00114 best_uq = dist; 00115 *pt1 = *start; 00116 *pt2 = *end; 00117 } 00118 } 00119 } 00120 } 00121 // Finally compute the square root to return the true distance. 00122 return best_uq > 0.0 ? sqrt(best_uq) : best_uq; 00123 } 00124 00125 // Constrained fit with a supplied direction vector. Finds the best line_pt, 00126 // that is one of the supplied points having the median cross product with 00127 // direction, ignoring points that have a cross product outside of the range 00128 // [min_dist, max_dist]. Returns the resulting error metric using the same 00129 // reduced set of points. 00130 // *Makes use of floating point arithmetic* 00131 double DetLineFit::ConstrainedFit(const FCOORD& direction, 00132 double min_dist, double max_dist, 00133 bool debug, ICOORD* line_pt) { 00134 ComputeConstrainedDistances(direction, min_dist, max_dist); 00135 // Do something sensible with no points or computed distances. 00136 if (pts_.empty() || distances_.empty()) { 00137 line_pt->set_x(0); 00138 line_pt->set_y(0); 00139 return 0.0; 00140 } 00141 int median_index = distances_.choose_nth_item(distances_.size() / 2); 00142 *line_pt = distances_[median_index].data; 00143 if (debug) { 00144 tprintf("Constrained fit to dir %g, %g = %d, %d :%d distances:\n", 00145 direction.x(), direction.y(), 00146 line_pt->x(), line_pt->y(), distances_.size()); 00147 for (int i = 0; i < distances_.size(); ++i) { 00148 tprintf("%d: %d, %d -> %g\n", i, distances_[i].data.x(), 00149 distances_[i].data.y(), distances_[i].key); 00150 } 00151 tprintf("Result = %d\n", median_index); 00152 } 00153 // Center distances on the fitted point. 00154 double dist_origin = direction * *line_pt; 00155 for (int i = 0; i < distances_.size(); ++i) { 00156 distances_[i].key -= dist_origin; 00157 } 00158 return sqrt(EvaluateLineFit()); 00159 } 00160 00161 // Returns true if there were enough points at the last call to Fit or 00162 // ConstrainedFit for the fitted points to be used on a badly fitted line. 00163 bool DetLineFit::SufficientPointsForIndependentFit() const { 00164 return distances_.size() >= kMinPointsForErrorCount; 00165 } 00166 00167 // Backwards compatible fit returning a gradient and constant. 00168 // Deprecated. Prefer Fit(ICOORD*, ICOORD*) where possible, but use this 00169 // function in preference to the LMS class. 00170 double DetLineFit::Fit(float* m, float* c) { 00171 ICOORD start, end; 00172 double error = Fit(&start, &end); 00173 if (end.x() != start.x()) { 00174 *m = static_cast<float>(end.y() - start.y()) / (end.x() - start.x()); 00175 *c = start.y() - *m * start.x(); 00176 } else { 00177 *m = 0.0f; 00178 *c = 0.0f; 00179 } 00180 return error; 00181 } 00182 00183 // Backwards compatible constrained fit with a supplied gradient. 00184 // Deprecated. Use ConstrainedFit(const FCOORD& direction) where possible 00185 // to avoid potential difficulties with infinite gradients. 00186 double DetLineFit::ConstrainedFit(double m, float* c) { 00187 // Do something sensible with no points. 00188 if (pts_.empty()) { 00189 *c = 0.0f; 00190 return 0.0; 00191 } 00192 double cos = 1.0 / sqrt(1.0 + m * m); 00193 FCOORD direction(cos, m * cos); 00194 ICOORD line_pt; 00195 double error = ConstrainedFit(direction, -MAX_FLOAT32, MAX_FLOAT32, false, 00196 &line_pt); 00197 *c = line_pt.y() - line_pt.x() * m; 00198 return error; 00199 } 00200 00201 // Computes and returns the squared evaluation metric for a line fit. 00202 double DetLineFit::EvaluateLineFit() { 00203 // Compute the upper quartile error from the line. 00204 double dist = ComputeUpperQuartileError(); 00205 if (distances_.size() >= kMinPointsForErrorCount && 00206 dist > kMaxRealDistance * kMaxRealDistance) { 00207 // Use the number of mis-fitted points as the error metric, as this 00208 // gives a better measure of fit for badly fitted lines where more 00209 // than a quarter are badly fitted. 00210 double threshold = kMaxRealDistance * sqrt(square_length_); 00211 dist = NumberOfMisfittedPoints(threshold); 00212 } 00213 return dist; 00214 } 00215 00216 // Computes the absolute error distances of the points from the line, 00217 // and returns the squared upper-quartile error distance. 00218 double DetLineFit::ComputeUpperQuartileError() { 00219 int num_errors = distances_.size(); 00220 if (num_errors == 0) return 0.0; 00221 // Get the absolute values of the errors. 00222 for (int i = 0; i < num_errors; ++i) { 00223 if (distances_[i].key < 0) distances_[i].key = -distances_[i].key; 00224 } 00225 // Now get the upper quartile distance. 00226 int index = distances_.choose_nth_item(3 * num_errors / 4); 00227 double dist = distances_[index].key; 00228 // The true distance is the square root of the dist squared / square_length. 00229 // Don't bother with the square root. Just return the square distance. 00230 return square_length_ > 0.0 ? dist * dist / square_length_ : 0.0; 00231 } 00232 00233 // Returns the number of sample points that have an error more than threshold. 00234 int DetLineFit::NumberOfMisfittedPoints(double threshold) const { 00235 int num_misfits = 0; 00236 int num_dists = distances_.size(); 00237 // Get the absolute values of the errors. 00238 for (int i = 0; i < num_dists; ++i) { 00239 if (distances_[i].key > threshold) 00240 ++num_misfits; 00241 } 00242 return num_misfits; 00243 } 00244 00245 // Computes all the cross product distances of the points from the line, 00246 // storing the actual (signed) cross products in distances. 00247 // Ignores distances of points that are further away than the previous point, 00248 // and overlaps the previous point by at least half. 00249 void DetLineFit::ComputeDistances(const ICOORD& start, const ICOORD& end) { 00250 distances_.truncate(0); 00251 ICOORD line_vector = end; 00252 line_vector -= start; 00253 square_length_ = line_vector.sqlength(); 00254 int line_length = IntCastRounded(sqrt(square_length_)); 00255 // Compute the distance of each point from the line. 00256 int prev_abs_dist = 0; 00257 int prev_dot = 0; 00258 for (int i = 0; i < pts_.size(); ++i) { 00259 ICOORD pt_vector = pts_[i].pt; 00260 pt_vector -= start; 00261 int dot = line_vector % pt_vector; 00262 // Compute |line_vector||pt_vector|sin(angle between) 00263 int dist = line_vector * pt_vector; 00264 int abs_dist = dist < 0 ? -dist : dist; 00265 if (abs_dist > prev_abs_dist && i > 0) { 00266 // Ignore this point if it overlaps the previous one. 00267 int separation = abs(dot - prev_dot); 00268 if (separation < line_length * pts_[i].halfwidth || 00269 separation < line_length * pts_[i - 1].halfwidth) 00270 continue; 00271 } 00272 distances_.push_back(DistPointPair(dist, pts_[i].pt)); 00273 prev_abs_dist = abs_dist; 00274 prev_dot = dot; 00275 } 00276 } 00277 00278 // Computes all the cross product distances of the points perpendicular to 00279 // the given direction, ignoring distances outside of the give distance range, 00280 // storing the actual (signed) cross products in distances_. 00281 void DetLineFit::ComputeConstrainedDistances(const FCOORD& direction, 00282 double min_dist, double max_dist) { 00283 distances_.truncate(0); 00284 square_length_ = direction.sqlength(); 00285 // Compute the distance of each point from the line. 00286 for (int i = 0; i < pts_.size(); ++i) { 00287 FCOORD pt_vector = pts_[i].pt; 00288 // Compute |line_vector||pt_vector|sin(angle between) 00289 double dist = direction * pt_vector; 00290 if (min_dist <= dist && dist <= max_dist) 00291 distances_.push_back(DistPointPair(dist, pts_[i].pt)); 00292 } 00293 } 00294 00295 } // namespace tesseract.