tesseract  3.03
/usr/local/google/home/jbreiden/tesseract-ocr-read-only/ccstruct/detlinefit.cpp
Go to the documentation of this file.
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.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines