tesseract
3.03
|
#include <baselinedetect.h>
Public Member Functions | |
BaselineRow (double line_size, TO_ROW *to_row) | |
const TBOX & | bounding_box () const |
void | SetupOldLineParameters (TO_ROW *row) const |
void | Print () const |
double | BaselineAngle () const |
double | SpaceBetween (const BaselineRow &other) const |
double | PerpDisp (const FCOORD &direction) const |
double | StraightYAtX (double x) const |
bool | FitBaseline (bool use_box_bottoms) |
void | AdjustBaselineToParallel (int debug, const FCOORD &direction) |
double | AdjustBaselineToGrid (int debug, const FCOORD &direction, double line_spacing, double line_offset) |
Definition at line 40 of file baselinedetect.h.
tesseract::BaselineRow::BaselineRow | ( | double | line_size, |
TO_ROW * | to_row | ||
) |
Definition at line 65 of file baselinedetect.cpp.
: blobs_(to_row->blob_list()), baseline_pt1_(0.0f, 0.0f), baseline_pt2_(0.0f, 0.0f), baseline_error_(0.0), good_baseline_(false) { ComputeBoundingBox(); // Compute a scale factor for rounding to ints. disp_quant_factor_ = kOffsetQuantizationFactor * line_spacing; fit_halfrange_ = kFitHalfrangeFactor * line_spacing; max_baseline_error_ = kMaxBaselineError * line_spacing; }
double tesseract::BaselineRow::AdjustBaselineToGrid | ( | int | debug, |
const FCOORD & | direction, | ||
double | line_spacing, | ||
double | line_offset | ||
) |
Definition at line 229 of file baselinedetect.cpp.
{ if (blobs_->empty()) { if (debug > 1) { tprintf("Row empty at:"); bounding_box_.print(); } return line_offset; } // Find the displacement_modes_ entry nearest to the grid. double best_error = 0.0; int best_index = -1; for (int i = 0; i < displacement_modes_.size(); ++i) { double blob_y = displacement_modes_[i]; double error = BaselineBlock::SpacingModelError(blob_y, line_spacing, line_offset); if (debug > 1) { tprintf("Mode at %g has error %g from model \n", blob_y, error); } if (best_index < 0 || error < best_error) { best_error = error; best_index = i; } } // We will move the baseline only if the chosen mode is close enough to the // model. double model_margin = max_baseline_error_ - best_error; if (best_index >= 0 && model_margin > 0.0) { // But if the current baseline is already close to the mode there is no // point, and only the potential to damage accuracy by changing its angle. double perp_disp = PerpDisp(direction); double shift = displacement_modes_[best_index] - perp_disp; if (fabs(shift) > max_baseline_error_) { if (debug > 1) { tprintf("Attempting linespacing model fit with mode %g to row at:", displacement_modes_[best_index]); bounding_box_.print(); } FitConstrainedIfBetter(debug, direction, model_margin, displacement_modes_[best_index]); } else if (debug > 1) { tprintf("Linespacing model only moves current line by %g for row at:", shift); bounding_box_.print(); } } else if (debug > 1) { tprintf("Linespacing model not close enough to any mode for row at:"); bounding_box_.print(); } return fmod(PerpDisp(direction), line_spacing); }
void tesseract::BaselineRow::AdjustBaselineToParallel | ( | int | debug, |
const FCOORD & | direction | ||
) |
Definition at line 214 of file baselinedetect.cpp.
double tesseract::BaselineRow::BaselineAngle | ( | ) | const |
Definition at line 98 of file baselinedetect.cpp.
{ FCOORD baseline_dir(baseline_pt2_ - baseline_pt1_); double angle = baseline_dir.angle(); // Baseline directions are only unique in a range of pi so constrain to // [-pi/2, pi/2]. return fmod(angle + M_PI * 1.5, M_PI) - M_PI * 0.5; }
const TBOX& tesseract::BaselineRow::bounding_box | ( | ) | const [inline] |
Definition at line 44 of file baselinedetect.h.
{
return bounding_box_;
}
bool tesseract::BaselineRow::FitBaseline | ( | bool | use_box_bottoms | ) |
Definition at line 142 of file baselinedetect.cpp.
{ // Deterministic fitting is used wherever possible. fitter_.Clear(); // Linear least squares is a backup if the DetLineFit produces a bad line. LLSQ llsq; BLOBNBOX_IT blob_it(blobs_); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { BLOBNBOX* blob = blob_it.data(); if (!use_box_bottoms) blob->EstimateBaselinePosition(); const TBOX& box = blob->bounding_box(); int x_middle = (box.left() + box.right()) / 2; #ifdef kDebugYCoord if (box.bottom() < kDebugYCoord && box.top() > kDebugYCoord) { tprintf("Box bottom = %d, baseline pos=%d for box at:", box.bottom(), blob->baseline_position()); box.print(); } #endif fitter_.Add(ICOORD(x_middle, blob->baseline_position()), box.width() / 2); llsq.add(x_middle, blob->baseline_position()); } // Fit the line. ICOORD pt1, pt2; baseline_error_ = fitter_.Fit(&pt1, &pt2); baseline_pt1_ = pt1; baseline_pt2_ = pt2; if (baseline_error_ > max_baseline_error_ && fitter_.SufficientPointsForIndependentFit()) { // The fit was bad but there were plenty of points, so try skipping // the first and last few, and use the new line if it dramatically improves // the error of fit. double error = fitter_.Fit(kNumSkipPoints, kNumSkipPoints, &pt1, &pt2); if (error < baseline_error_ / 2.0) { baseline_error_ = error; baseline_pt1_ = pt1; baseline_pt2_ = pt2; } } int debug = 0; #ifdef kDebugYCoord Print(); debug = bounding_box_.bottom() < kDebugYCoord && bounding_box_.top() > kDebugYCoord ? 3 : 2; #endif // Now we obtained a direction from that fit, see if we can improve the // fit using the same direction and some other start point. FCOORD direction(pt2 - pt1); double target_offset = direction * pt1; good_baseline_ = false; FitConstrainedIfBetter(debug, direction, 0.0, target_offset); // Wild lines can be produced because DetLineFit allows vertical lines, but // vertical text has been rotated so angles over pi/4 should be disallowed. // Near vertical lines can still be produced by vertically aligned components // on very short lines. double angle = BaselineAngle(); if (fabs(angle) > M_PI * 0.25) { // Use the llsq fit as a backup. baseline_pt1_ = llsq.mean_point(); baseline_pt2_ = baseline_pt1_ + FCOORD(1.0f, llsq.m()); // TODO(rays) get rid of this when m and c are no longer used. double m = llsq.m(); double c = llsq.c(m); baseline_error_ = llsq.rms(m, c); good_baseline_ = false; } return good_baseline_; }
double tesseract::BaselineRow::PerpDisp | ( | const FCOORD & | direction | ) | const |
Definition at line 121 of file baselinedetect.cpp.
{ float middle_x = (bounding_box_.left() + bounding_box_.right()) / 2.0f; FCOORD middle_pos(middle_x, StraightYAtX(middle_x)); return direction * middle_pos / direction.length(); }
void tesseract::BaselineRow::Print | ( | ) | const |
Definition at line 87 of file baselinedetect.cpp.
{ tprintf("Baseline (%g,%g)->(%g,%g), angle=%g, intercept=%g\n", baseline_pt1_.x(), baseline_pt1_.y(), baseline_pt2_.x(), baseline_pt2_.y(), BaselineAngle(), StraightYAtX(0.0)); tprintf("Quant factor=%g, error=%g, good=%d, box:", disp_quant_factor_, baseline_error_, good_baseline_); bounding_box_.print(); }
void tesseract::BaselineRow::SetupOldLineParameters | ( | TO_ROW * | row | ) | const |
Definition at line 77 of file baselinedetect.cpp.
{ // TODO(rays) get rid of this when m and c are no longer used. double gradient = tan(BaselineAngle()); // para_c is the actual intercept of the baseline on the y-axis. float para_c = StraightYAtX(0.0); row->set_line(gradient, para_c, baseline_error_); row->set_parallel_line(gradient, para_c, baseline_error_); }
double tesseract::BaselineRow::SpaceBetween | ( | const BaselineRow & | other | ) | const |
Definition at line 108 of file baselinedetect.cpp.
{ // Find the x-centre of overlap of the lines. float x = (MAX(bounding_box_.left(), other.bounding_box_.left()) + MIN(bounding_box_.right(), other.bounding_box_.right())) / 2; // Find the vertical centre between them. float y = (StraightYAtX(x) + other.StraightYAtX(x)) / 2.0f; // Find the perpendicular distance of (x,y) from each line. FCOORD pt(x, y); return PerpDistanceFromBaseline(pt) + other.PerpDistanceFromBaseline(pt); }
double tesseract::BaselineRow::StraightYAtX | ( | double | x | ) | const |
Definition at line 129 of file baselinedetect.cpp.