Tesseract 3.02

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- Class List
- Class Hierarchy
- Class Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Public Member Functions | Static Public Member Functions
tesseract::TabVector Class Reference

`#include <tabvector.h>`

Inheritance diagram for tesseract::TabVector:

List of all members.

## Public Member Functions
| TabVector ()
| ~TabVector ()
| TabVector (const TabVector &src, TabAlignment alignment, const ICOORD &vertical_skew, BLOBNBOX *blob)
TabVector * | ShallowCopy () const
const ICOORD & | startpt () const
const ICOORD & | endpt () const
int | extended_ymax () const
int | extended_ymin () const
int | sort_key () const
int | mean_width () const
void | set_top_constraints (TabConstraint_LIST *constraints)
void | set_bottom_constraints (TabConstraint_LIST *constraints)
TabVector_CLIST * | partners ()
void | set_startpt (const ICOORD &start)
void | set_endpt (const ICOORD &end)
bool | intersects_other_lines () const
void | set_intersects_other_lines (bool value)
int | XAtY (int y) const
int | VOverlap (const TabVector &other) const
int | VOverlap (int top_y, int bottom_y) const
int | ExtendedOverlap (int top_y, int bottom_y) const
bool | IsLeftTab () const
bool | IsRightTab () const
bool | IsSeparator () const
bool | IsCenterTab () const
bool | IsRagged () const
bool | IsLeftOf (const TabVector &other) const
bool | Partnerless ()
int | BoxCount ()
void | Freeze ()
void | XYFlip ()
void | ReflectInYAxis ()
void | ExtendToBox (BLOBNBOX *blob)
void | SetYStart (int start_y)
void | SetYEnd (int end_y)
void | Rotate (const FCOORD &rotation)
void | SetupConstraints ()
void | SetupPartnerConstraints ()
void | SetupPartnerConstraints (TabVector *partner)
void | ApplyConstraints ()
bool | SimilarTo (const ICOORD &vertical, const TabVector &other, BlobGrid *grid) const
void | MergeWith (const ICOORD &vertical, TabVector *other)
void | AddPartner (TabVector *partner)
bool | IsAPartner (const TabVector *other)
void | Print (const char *prefix)
void | Debug (const char *prefix)
void | Display (ScrollView *tab_win)
void | FitAndEvaluateIfNeeded (const ICOORD &vertical, TabFind *finder)
void | Evaluate (const ICOORD &vertical, TabFind *finder)
bool | Fit (ICOORD vertical, bool force_parallel)
TabVector * | VerticalTextlinePartner ()
TabVector * | GetSinglePartner ()
Public Member Functions inherited from ELIST2_LINK
| ELIST2_LINK ()
| ELIST2_LINK (const ELIST2_LINK &)
void | operator= (const ELIST2_LINK &)

## Static Public Member Functions
static TabVector * | FitVector (TabAlignment alignment, ICOORD vertical, int extended_start_y, int extended_end_y, BLOBNBOX_CLIST *good_points, int *vertical_x, int *vertical_y)
static int | SortKey (const ICOORD &vertical, int x, int y)
static int | XAtY (const ICOORD &vertical, int sort_key, int y)
static int | SortVectorsByKey (const void *v1, const void *v2)
static void | MergeSimilarTabVectors (const ICOORD &vertical, TabVector_LIST *vectors, BlobGrid *grid)

## Detailed Description

Definition at line 111 of file tabvector.h.

## Constructor & Destructor Documentation

tesseract::TabVector::TabVector | ( |  | ) |
| inline

Definition at line 113 of file tabvector.h.

{
// TODO(rays) fix this in elst.h line 1076, where it should use the
// copy constructor instead of operator=.
}

tesseract::TabVector::~TabVector | ( |  | ) |

Definition at line 171 of file tabvector.cpp.

{
}

tesseract::TabVector::TabVector | ( | const TabVector & | src,
|  | TabAlignment | alignment,
|  | const ICOORD & | vertical_skew,
|  | BLOBNBOX * | blob
| ) |  |

Definition at line 205 of file tabvector.cpp.

: extended_ymin_(src.extended_ymin_), extended_ymax_(src.extended_ymax_),
sort_key_(0), percent_score_(0), mean_width_(0),
needs_refit_(true), needs_evaluation_(true), intersects_other_lines_(false),
alignment_(alignment),
top_constraints_(NULL), bottom_constraints_(NULL) {
BLOBNBOX_C_IT it(&boxes_);
it.add_to_end(blob);
TBOX box = blob->bounding_box();
if (IsLeftTab()) {
startpt_ = box.botleft();
endpt_ = box.topleft();
} else {
startpt_ = box.botright();
endpt_ = box.topright();
}
sort_key_ = SortKey(vertical_skew,
(startpt_.x() + endpt_.x()) / 2,
(startpt_.y() + endpt_.y()) / 2);
if (textord_debug_tabfind > 3)
Print("Constructed a new tab vector:");
}

## Member Function Documentation

void tesseract::TabVector::AddPartner | ( | TabVector * | partner | ) |

Definition at line 493 of file tabvector.cpp.

{
if (IsSeparator() || partner->IsSeparator())
return;
TabVector_C_IT it(&partners_);
if (!it.empty()) {
it.move_to_last();
if (it.data() == partner)
return;
}
it.add_after_then_move(partner);
}

void tesseract::TabVector::ApplyConstraints | ( |  | ) |

Definition at line 354 of file tabvector.cpp.

{
if (top_constraints_ != NULL)
TabConstraint::ApplyConstraints(top_constraints_);
if (bottom_constraints_ != NULL)
TabConstraint::ApplyConstraints(bottom_constraints_);
}

int tesseract::TabVector::BoxCount | ( |  | ) |
| inline

Definition at line 245 of file tabvector.h.

{
return boxes_.length();
}

void tesseract::TabVector::Debug | ( | const char * | prefix | ) |

Definition at line 540 of file tabvector.cpp.

{
Print(prefix);
BLOBNBOX_C_IT it(&boxes_);
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* bbox = it.data();
const TBOX& box = bbox->bounding_box();
tprintf("Box at (%d,%d)->(%d,%d)\n",
box.left(), box.bottom(), box.right(), box.top());
}
}

void tesseract::TabVector::Display | ( | ScrollView * | tab_win | ) |

Definition at line 552 of file tabvector.cpp.

{
#ifndef GRAPHICS_DISABLED
if (textord_debug_printable)
tab_win->Pen(ScrollView::BLUE);
else if (alignment_ == TA_LEFT_ALIGNED)
tab_win->Pen(ScrollView::LIME_GREEN);
else if (alignment_ == TA_LEFT_RAGGED)
tab_win->Pen(ScrollView::DARK_GREEN);
else if (alignment_ == TA_RIGHT_ALIGNED)
tab_win->Pen(ScrollView::PINK);
else if (alignment_ == TA_RIGHT_RAGGED)
tab_win->Pen(ScrollView::CORAL);
else
tab_win->Pen(ScrollView::WHITE);
tab_win->Line(startpt_.x(), startpt_.y(), endpt_.x(), endpt_.y());
tab_win->Pen(ScrollView::GREY);
tab_win->Line(startpt_.x(), startpt_.y(), startpt_.x(), extended_ymin_);
tab_win->Line(endpt_.x(), extended_ymax_, endpt_.x(), endpt_.y());
char score_buf[64];
snprintf(score_buf, sizeof(score_buf), "%d", percent_score_);
tab_win->TextAttributes("Times", 50, false, false, false);
tab_win->Text(startpt_.x(), startpt_.y(), score_buf);
#endif
}

const ICOORD& tesseract::TabVector::endpt | ( |  | ) | const
| inline

Definition at line 149 of file tabvector.h.

{
return endpt_;
}

void tesseract::TabVector::Evaluate | ( | const ICOORD & | vertical,
|  | TabFind * | finder
| ) |  |

Definition at line 592 of file tabvector.cpp.

{
bool debug = false;
needs_evaluation_ = false;
int length = endpt_.y() - startpt_.y();
if (length == 0 || boxes_.empty()) {
percent_score_ = 0;
Print("Zero length in evaluate");
return;
}
// Compute the mean box height.
BLOBNBOX_C_IT it(&boxes_);
int mean_height = 0;
int height_count = 0;
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* bbox = it.data();
const TBOX& box = bbox->bounding_box();
int height = box.height();
mean_height += height;
++height_count;
}
mean_height /= height_count;
int max_gutter = kGutterMultiple * mean_height;
if (IsRagged()) {
// Ragged edges face a tougher test in that the gap must always be within
// the height of the blob.
max_gutter = kGutterToNeighbourRatio * mean_height;
}
STATS gutters(0, max_gutter + 1);
// Evaluate the boxes for their goodness, calculating the coverage as we go.
// Remove boxes that are not good and shorten the list to the first and
// last good boxes.
int num_deleted_boxes = 0;
bool text_on_image = false;
int good_length = 0;
const TBOX* prev_good_box = NULL;
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* bbox = it.data();
const TBOX& box = bbox->bounding_box();
int mid_y = (box.top() + box.bottom()) / 2;
if (TabFind::WithinTestRegion(2, XAtY(box.bottom()), box.bottom())) {
if (!debug) {
tprintf("After already deleting %d boxes, ", num_deleted_boxes);
Print("Starting evaluation");
}
debug = true;
}
// A good box is one where the nearest neighbour on the inside is closer
// than half the distance to the nearest neighbour on the outside
// (of the putative column).
bool left = IsLeftTab();
int tab_x = XAtY(mid_y);
int gutter_width;
int neighbour_gap;
finder->GutterWidthAndNeighbourGap(tab_x, mean_height, max_gutter, left,
bbox, &gutter_width, &neighbour_gap);
if (debug) {
tprintf("Box (%d,%d)->(%d,%d) has gutter %d, ndist %d\n",
box.left(), box.bottom(), box.right(), box.top(),
gutter_width, neighbour_gap);
}
// Now we can make the test.
if (neighbour_gap * kGutterToNeighbourRatio <= gutter_width) {
// A good box contributes its height to the good_length.
good_length += box.top() - box.bottom();
gutters.add(gutter_width, 1);
// Two good boxes together contribute the gap between them
// to the good_length as well, as long as the gap is not
// too big.
if (prev_good_box != NULL) {
int vertical_gap = box.bottom() - prev_good_box->top();
double size1 = sqrt(static_cast<double>(prev_good_box->area()));
double size2 = sqrt(static_cast<double>(box.area()));
if (vertical_gap < kMaxFillinMultiple * MIN(size1, size2))
good_length += vertical_gap;
if (debug) {
tprintf("Box and prev good, gap=%d, target %g, goodlength=%d\n",
vertical_gap, kMaxFillinMultiple * MIN(size1, size2),
good_length);
}
} else {
// Adjust the start to the first good box.
SetYStart(box.bottom());
}
prev_good_box = &box;
if (bbox->flow() == BTFT_TEXT_ON_IMAGE)
text_on_image = true;
} else {
// Get rid of boxes that are not good.
if (debug) {
tprintf("Bad Box (%d,%d)->(%d,%d) with gutter %d, ndist %d\n",
box.left(), box.bottom(), box.right(), box.top(),
gutter_width, neighbour_gap);
}
it.extract();
++num_deleted_boxes;
}
}
if (debug) {
Print("Evaluating:");
}
// If there are any good boxes, do it again, except this time get rid of
// boxes that have a gutter that is a small fraction of the mean gutter.
// This filters out ends that run into a coincidental gap in the text.
int search_top = endpt_.y();
int search_bottom = startpt_.y();
int median_gutter = IntCastRounded(gutters.median());
if (gutters.get_total() > 0) {
prev_good_box = NULL;
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* bbox = it.data();
const TBOX& box = bbox->bounding_box();
int mid_y = (box.top() + box.bottom()) / 2;
// A good box is one where the gutter width is at least some constant
// fraction of the mean gutter width.
bool left = IsLeftTab();
int tab_x = XAtY(mid_y);
int max_gutter = kGutterMultiple * mean_height;
if (IsRagged()) {
// Ragged edges face a tougher test in that the gap must always be
// within the height of the blob.
max_gutter = kGutterToNeighbourRatio * mean_height;
}
int gutter_width;
int neighbour_gap;
finder->GutterWidthAndNeighbourGap(tab_x, mean_height, max_gutter, left,
bbox, &gutter_width, &neighbour_gap);
// Now we can make the test.
if (gutter_width >= median_gutter * kMinGutterFraction) {
if (prev_good_box == NULL) {
// Adjust the start to the first good box.
SetYStart(box.bottom());
search_bottom = box.top();
}
prev_good_box = &box;
search_top = box.bottom();
} else {
// Get rid of boxes that are not good.
if (debug) {
tprintf("Bad Box (%d,%d)->(%d,%d) with gutter %d, mean gutter %d\n",
box.left(), box.bottom(), box.right(), box.top(),
gutter_width, median_gutter);
}
it.extract();
++num_deleted_boxes = true;
}
}
}
// If there has been a good box, adjust the end.
if (prev_good_box != NULL) {
SetYEnd(prev_good_box->top());
// Compute the percentage of the vector that is occupied by good boxes.
int length = endpt_.y() - startpt_.y();
percent_score_ = 100 * good_length / length;
if (num_deleted_boxes > 0) {
needs_refit_ = true;
FitAndEvaluateIfNeeded(vertical, finder);
if (boxes_.empty())
return;
}
// Test the gutter over the whole vector, instead of just at the boxes.
int required_shift;
if (search_bottom > search_top) {
search_bottom = startpt_.y();
search_top = endpt_.y();
}
double min_gutter_width = kLineCountReciprocal / boxes_.length();
min_gutter_width += IsRagged() ? kMinRaggedGutter : kMinAlignedGutter;
min_gutter_width *= mean_height;
int max_gutter_width = IntCastRounded(min_gutter_width) + 1;
if (median_gutter > max_gutter_width)
max_gutter_width = median_gutter;
int gutter_width = finder->GutterWidth(search_bottom, search_top, *this,
text_on_image, max_gutter_width,
&required_shift);
if (gutter_width < min_gutter_width) {
if (debug) {
tprintf("Rejecting bad tab Vector with %d gutter vs %g min\n",
gutter_width, min_gutter_width);
}
boxes_.shallow_clear();
percent_score_ = 0;
} else if (debug) {
tprintf("Final gutter %d, vs limit of %g, required shift = %d\n",
gutter_width, min_gutter_width, required_shift);
}
} else {
// There are no good boxes left, so score is 0.
percent_score_ = 0;
}
if (debug) {
Print("Evaluation complete:");
}
}

int tesseract::TabVector::extended_ymax | ( |  | ) | const
| inline

Definition at line 152 of file tabvector.h.

{
return extended_ymax_;
}

int tesseract::TabVector::extended_ymin | ( |  | ) | const
| inline

Definition at line 155 of file tabvector.h.

{
return extended_ymin_;
}

int tesseract::TabVector::ExtendedOverlap | ( | int | top_y,
|  | int | bottom_y
| ) |  | const
| inline

Definition at line 208 of file tabvector.h.

{
return MIN(top_y, extended_ymax_) - MAX(bottom_y, extended_ymin_);
}

void tesseract::TabVector::ExtendToBox | ( | BLOBNBOX * | blob | ) |

Definition at line 247 of file tabvector.cpp.

{
TBOX new_box = new_blob->bounding_box();
BLOBNBOX_C_IT it(&boxes_);
if (!it.empty()) {
BLOBNBOX* blob = it.data();
TBOX box = blob->bounding_box();
while (!it.at_last() && box.top() <= new_box.top()) {
if (blob == new_blob)
return; // We have it already.
it.forward();
blob = it.data();
box = blob->bounding_box();
}
if (box.top() >= new_box.top()) {
it.add_before_stay_put(new_blob);
needs_refit_ = true;
return;
}
}
needs_refit_ = true;
it.add_after_stay_put(new_blob);
}

bool tesseract::TabVector::Fit | ( | ICOORD | vertical,
|  | bool | force_parallel
| ) |  |

Definition at line 793 of file tabvector.cpp.

{
needs_refit_ = false;
if (boxes_.empty()) {
// Don't refit something with no boxes, as that only happens
// in Evaluate, and we don't want to end up with a zero vector.
if (!force_parallel)
return false;
// If we are forcing parallel, then we just need to set the sort_key_.
ICOORD midpt = startpt_;
midpt += endpt_;
midpt /= 2;
sort_key_ = SortKey(vertical, midpt.x(), midpt.y());
return startpt_.y() != endpt_.y();
}
if (!force_parallel && !IsRagged()) {
// Use a fitted line as the vertical.
DetLineFit linepoints;
BLOBNBOX_C_IT it(&boxes_);
// Fit a line to all the boxes in the list.
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* bbox = it.data();
TBOX box = bbox->bounding_box();
int x1 = IsRightTab() ? box.right() : box.left();
ICOORD boxpt(x1, box.bottom());
linepoints.Add(boxpt);
if (it.at_last()) {
ICOORD top_pt(x1, box.top());
linepoints.Add(top_pt);
}
}
linepoints.Fit(&startpt_, &endpt_);
if (startpt_.y() != endpt_.y()) {
vertical = endpt_;
vertical -= startpt_;
}
}
int start_y = startpt_.y();
int end_y = endpt_.y();
sort_key_ = IsLeftTab() ? MAX_INT32 : -MAX_INT32;
BLOBNBOX_C_IT it(&boxes_);
// Choose a line parallel to the vertical such that all boxes are on the
// correct side of it.
mean_width_ = 0;
int width_count = 0;
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* bbox = it.data();
TBOX box = bbox->bounding_box();
mean_width_ += box.width();
++width_count;
int x1 = IsRightTab() ? box.right() : box.left();
// Test both the bottom and the top, as one will be more extreme, depending
// on the direction of skew.
int bottom_y = box.bottom();
int top_y = box.top();
int key = SortKey(vertical, x1, bottom_y);
if (IsLeftTab() == (key < sort_key_)) {
sort_key_ = key;
startpt_ = ICOORD(x1, bottom_y);
}
key = SortKey(vertical, x1, top_y);
if (IsLeftTab() == (key < sort_key_)) {
sort_key_ = key;
startpt_ = ICOORD(x1, top_y);
}
if (it.at_first())
start_y = bottom_y;
if (it.at_last())
end_y = top_y;
}
if (width_count > 0) {
mean_width_ = (mean_width_ + width_count - 1) / width_count;
}
endpt_ = startpt_ + vertical;
needs_evaluation_ = true;
if (start_y != end_y) {
// Set the ends of the vector to fully include the first and last blobs.
startpt_.set_x(XAtY(vertical, sort_key_, start_y));
startpt_.set_y(start_y);
endpt_.set_x(XAtY(vertical, sort_key_, end_y));
endpt_.set_y(end_y);
return true;
}
return false;
}

void tesseract::TabVector::FitAndEvaluateIfNeeded | ( | const ICOORD & | vertical,
|  | TabFind * | finder
| ) |  |

Definition at line 578 of file tabvector.cpp.

{
if (needs_refit_)
Fit(vertical, true);
if (needs_evaluation_)
Evaluate(vertical, finder);
}

TabVector * tesseract::TabVector::FitVector | ( | TabAlignment | alignment,
|  | ICOORD | vertical,
|  | int | extended_start_y,
|  | int | extended_end_y,
|  | BLOBNBOX_CLIST * | good_points,
|  | int * | vertical_x,
|  | int * | vertical_y
| ) |  |
| static

Definition at line 183 of file tabvector.cpp.

{
TabVector* vector = new TabVector(extended_start_y, extended_end_y,
alignment, good_points);
if (!vector->Fit(vertical, false)) {
delete vector;
return NULL;
}
if (!vector->IsRagged()) {
vertical = vector->endpt_ - vector->startpt_;
int weight = vector->BoxCount();
*vertical_x += vertical.x() * weight;
*vertical_y += vertical.y() * weight;
}
return vector;
}

void tesseract::TabVector::Freeze | ( |  | ) |
| inline

Definition at line 250 of file tabvector.h.

{
boxes_.shallow_clear();
}

TabVector * tesseract::TabVector::GetSinglePartner | ( |  | ) |

Definition at line 879 of file tabvector.cpp.

{
if (!partners_.singleton())
return NULL;
TabVector_C_IT partner_it(&partners_);
TabVector* partner = partner_it.data();
return partner;
}

bool tesseract::TabVector::intersects_other_lines | ( |  | ) | const
| inline

Definition at line 179 of file tabvector.h.

{
return intersects_other_lines_;
}

bool tesseract::TabVector::IsAPartner | ( | const TabVector * | other | ) |

Definition at line 506 of file tabvector.cpp.

{
TabVector_C_IT it(&partners_);
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
if (it.data() == other)
return true;
}
return false;
}

bool tesseract::TabVector::IsCenterTab | ( |  | ) | const
| inline

Definition at line 225 of file tabvector.h.

{
return alignment_ == TA_CENTER_JUSTIFIED;
}

bool tesseract::TabVector::IsLeftOf | ( | const TabVector & | other | ) | const
| inline

Definition at line 235 of file tabvector.h.

{
return sort_key_ < other.sort_key_;
}

bool tesseract::TabVector::IsLeftTab | ( |  | ) | const
| inline

Definition at line 213 of file tabvector.h.

{
return alignment_ == TA_LEFT_ALIGNED || alignment_ == TA_LEFT_RAGGED;
}

bool tesseract::TabVector::IsRagged | ( |  | ) | const
| inline

Definition at line 229 of file tabvector.h.

{
return alignment_ == TA_LEFT_RAGGED || alignment_ == TA_RIGHT_RAGGED;
}

bool tesseract::TabVector::IsRightTab | ( |  | ) | const
| inline

Definition at line 217 of file tabvector.h.

{
return alignment_ == TA_RIGHT_ALIGNED || alignment_ == TA_RIGHT_RAGGED;
}

bool tesseract::TabVector::IsSeparator | ( |  | ) | const
| inline

Definition at line 221 of file tabvector.h.

{
return alignment_ == TA_SEPARATOR;
}

int tesseract::TabVector::mean_width | ( |  | ) | const
| inline

Definition at line 161 of file tabvector.h.

{
return mean_width_;
}

void tesseract::TabVector::MergeSimilarTabVectors | ( | const ICOORD & | vertical,
|  | TabVector_LIST * | vectors,
|  | BlobGrid * | grid
| ) |  |
| static

Definition at line 362 of file tabvector.cpp.

{
TabVector_IT it1(vectors);
for (it1.mark_cycle_pt(); !it1.cycled_list(); it1.forward()) {
TabVector* v1 = it1.data();
TabVector_IT it2(it1);
for (it2.forward(); !it2.at_first(); it2.forward()) {
TabVector* v2 = it2.data();
if (v2->SimilarTo(vertical, *v1, grid)) {
// Merge into the forward one, in case the combined vector now
// overlaps one in between.
if (textord_debug_tabfind) {
v2->Print("Merging");
v1->Print("by deleting");
}
v2->MergeWith(vertical, it1.extract());
if (textord_debug_tabfind) {
v2->Print("Producing");
}
ICOORD merged_vector = v2->endpt();
merged_vector -= v2->startpt();
if (abs(merged_vector.x()) > 100) {
v2->Print("Garbage result of merge?");
}
break;
}
}
}
}

void tesseract::TabVector::MergeWith | ( | const ICOORD & | vertical,
|  | TabVector * | other
| ) |  |

Definition at line 459 of file tabvector.cpp.

{
extended_ymin_ = MIN(extended_ymin_, other->extended_ymin_);
extended_ymax_ = MAX(extended_ymax_, other->extended_ymax_);
if (other->IsRagged()) {
alignment_ = other->alignment_;
}
// Merge sort the two lists of boxes.
BLOBNBOX_C_IT it1(&boxes_);
BLOBNBOX_C_IT it2(&other->boxes_);
while (!it2.empty()) {
BLOBNBOX* bbox2 = it2.extract();
it2.forward();
TBOX box2 = bbox2->bounding_box();
BLOBNBOX* bbox1 = it1.data();
TBOX box1 = bbox1->bounding_box();
while (box1.bottom() < box2.bottom() && !it1.at_last()) {
it1.forward();
bbox1 = it1.data();
box1 = bbox1->bounding_box();
}
if (box1.bottom() < box2.bottom()) {
it1.add_to_end(bbox2);
} else if (bbox1 != bbox2) {
it1.add_before_stay_put(bbox2);
}
}
Fit(vertical, true);
other->Delete(this);
}

bool tesseract::TabVector::Partnerless | ( |  | ) |
| inline

Definition at line 240 of file tabvector.h.

{
return partners_.empty();
}

TabVector_CLIST* tesseract::TabVector::partners | ( |  | ) |
| inline

Definition at line 170 of file tabvector.h.

{
return &partners_;
}

void tesseract::TabVector::Print | ( | const char * | prefix | ) |

Definition at line 526 of file tabvector.cpp.

{
if (this == NULL) {
tprintf("%s <null>\n", prefix);
} else {
tprintf("%s %s (%d,%d)->(%d,%d) w=%d s=%d, sort key=%d, boxes=%d,"
" partners=%d\n",
prefix, kAlignmentNames[alignment_],
startpt_.x(), startpt_.y(), endpt_.x(), endpt_.y(),
mean_width_, percent_score_, sort_key_,
boxes_.length(), partners_.length());
}
}

void tesseract::TabVector::ReflectInYAxis | ( |  | ) |
| inline

Definition at line 265 of file tabvector.h.

{
startpt_.set_x(-startpt_.x());
endpt_.set_x(-endpt_.x());
sort_key_ = -sort_key_;
if (alignment_ == TA_LEFT_ALIGNED)
alignment_ = TA_RIGHT_ALIGNED;
else if (alignment_ == TA_RIGHT_ALIGNED)
alignment_ = TA_LEFT_ALIGNED;
if (alignment_ == TA_LEFT_RAGGED)
alignment_ = TA_RIGHT_RAGGED;
else if (alignment_ == TA_RIGHT_RAGGED)
alignment_ = TA_LEFT_RAGGED;
}

void tesseract::TabVector::Rotate | ( | const FCOORD & | rotation | ) |

Definition at line 282 of file tabvector.cpp.

{
startpt_.rotate(rotation);
endpt_.rotate(rotation);
int dx = endpt_.x() - startpt_.x();
int dy = endpt_.y() - startpt_.y();
if ((dy < 0 && abs(dy) > abs(dx)) || (dx < 0 && abs(dx) > abs(dy))) {
// Need to flip start/end.
ICOORD tmp = startpt_;
startpt_ = endpt_;
endpt_ = tmp;
}
}

void tesseract::TabVector::set_bottom_constraints | ( | TabConstraint_LIST * | constraints | ) |
| inline

Definition at line 167 of file tabvector.h.

{
bottom_constraints_ = constraints;
}

void tesseract::TabVector::set_endpt | ( | const ICOORD & | end | ) |
| inline

Definition at line 176 of file tabvector.h.

{
endpt_ = end;
}

void tesseract::TabVector::set_intersects_other_lines | ( | bool | value | ) |
| inline

Definition at line 182 of file tabvector.h.

{
intersects_other_lines_ = value;
}

void tesseract::TabVector::set_startpt | ( | const ICOORD & | start | ) |
| inline

Definition at line 173 of file tabvector.h.

{
startpt_ = start;
}

void tesseract::TabVector::set_top_constraints | ( | TabConstraint_LIST * | constraints | ) |
| inline

Definition at line 164 of file tabvector.h.

{
top_constraints_ = constraints;
}

void tesseract::TabVector::SetupConstraints | ( |  | ) |

Definition at line 297 of file tabvector.cpp.

{
TabConstraint::CreateConstraint(this, false);
TabConstraint::CreateConstraint(this, true);
}

void tesseract::TabVector::SetupPartnerConstraints | ( |  | ) |

Definition at line 303 of file tabvector.cpp.

{
// With the first and last partner, we want a common bottom and top,
// respectively, and for each change of partner, we want a common
// top of first with bottom of next.
TabVector_C_IT it(&partners_);
TabVector* prev_partner = NULL;
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
TabVector* partner = it.data();
if (partner->top_constraints_ == NULL ||
partner->bottom_constraints_ == NULL) {
partner->Print("Impossible: has no constraints");
Print("This vector has it as a partner");
continue;
}
if (prev_partner == NULL) {
// This is the first partner, so common bottom.
if (TabConstraint::CompatibleConstraints(bottom_constraints_,
partner->bottom_constraints_))
TabConstraint::MergeConstraints(bottom_constraints_,
partner->bottom_constraints_);
} else {
// We need prev top to be common with partner bottom.
if (TabConstraint::CompatibleConstraints(prev_partner->top_constraints_,
partner->bottom_constraints_))
TabConstraint::MergeConstraints(prev_partner->top_constraints_,
partner->bottom_constraints_);
}
prev_partner = partner;
if (it.at_last()) {
// This is the last partner, so common top.
if (TabConstraint::CompatibleConstraints(top_constraints_,
partner->top_constraints_))
TabConstraint::MergeConstraints(top_constraints_,
partner->top_constraints_);
}
}
}

void tesseract::TabVector::SetupPartnerConstraints | ( | TabVector * | partner | ) |

Definition at line 342 of file tabvector.cpp.

{
if (TabConstraint::CompatibleConstraints(bottom_constraints_,
partner->bottom_constraints_))
TabConstraint::MergeConstraints(bottom_constraints_,
partner->bottom_constraints_);
if (TabConstraint::CompatibleConstraints(top_constraints_,
partner->top_constraints_))
TabConstraint::MergeConstraints(top_constraints_,
partner->top_constraints_);
}

void tesseract::TabVector::SetYEnd | ( | int | end_y | ) |

Definition at line 276 of file tabvector.cpp.

{
endpt_.set_x(XAtY(end_y));
endpt_.set_y(end_y);
}

void tesseract::TabVector::SetYStart | ( | int | start_y | ) |

Definition at line 271 of file tabvector.cpp.

{
startpt_.set_x(XAtY(start_y));
startpt_.set_y(start_y);
}

TabVector * tesseract::TabVector::ShallowCopy | ( |  | ) | const

Definition at line 234 of file tabvector.cpp.

{
TabVector* copy = new TabVector();
copy->startpt_ = startpt_;
copy->endpt_ = endpt_;
copy->alignment_ = alignment_;
copy->extended_ymax_ = extended_ymax_;
copy->extended_ymin_ = extended_ymin_;
copy->intersects_other_lines_ = intersects_other_lines_;
return copy;
}

bool tesseract::TabVector::SimilarTo | ( | const ICOORD & | vertical,
|  | const TabVector & | other,
|  | BlobGrid * | grid
| ) |  | const

Definition at line 395 of file tabvector.cpp.

{
if ((IsRightTab() && other.IsRightTab()) ||
(IsLeftTab() && other.IsLeftTab())) {
// If they don't overlap, at least in extensions, then there is no chance.
if (ExtendedOverlap(other.extended_ymax_, other.extended_ymin_) < 0)
return false;
// A fast approximation to the scale factor of the sort_key_.
int v_scale = abs(vertical.y());
if (v_scale == 0)
v_scale = 1;
// If they are close enough, then OK.
if (sort_key_ + kSimilarVectorDist * v_scale >= other.sort_key_ &&
sort_key_ - kSimilarVectorDist * v_scale <= other.sort_key_)
return true;
// Ragged tabs get a bigger threshold.
if (!IsRagged() || !other.IsRagged() ||
sort_key_ + kSimilarRaggedDist * v_scale < other.sort_key_ ||
sort_key_ - kSimilarRaggedDist * v_scale > other.sort_key_)
return false;
if (grid == NULL) {
// There is nothing else to test!
return true;
}
// If there is nothing in the rectangle between the vector that is going to
// move, and the place it is moving to, then they can be merged.
// Setup a vertical search for any blob.
const TabVector* mover = (IsRightTab() &&
sort_key_ < other.sort_key_) ? this : &other;
int top_y = mover->endpt_.y();
int bottom_y = mover->startpt_.y();
int left = MIN(mover->XAtY(top_y), mover->XAtY(bottom_y));
int right = MAX(mover->XAtY(top_y), mover->XAtY(bottom_y));
int shift = abs(sort_key_ - other.sort_key_) / v_scale;
if (IsRightTab()) {
right += shift;
} else {
left -= shift;
}
GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> vsearch(grid);
vsearch.StartVerticalSearch(left, right, top_y);
BLOBNBOX* blob;
while ((blob = vsearch.NextVerticalSearch(true)) != NULL) {
TBOX box = blob->bounding_box();
if (box.top() > bottom_y)
return true; // Nothing found.
if (box.bottom() < top_y)
continue; // Doesn't overlap.
int left_at_box = XAtY(box.bottom());
int right_at_box = left_at_box;
if (IsRightTab())
right_at_box += shift;
else
left_at_box -= shift;
if (MIN(right_at_box, box.right()) > MAX(left_at_box, box.left()))
return false;
}
return true; // Nothing found.
}
return false;
}

int tesseract::TabVector::sort_key | ( |  | ) | const
| inline

Definition at line 158 of file tabvector.h.

{
return sort_key_;
}

static int tesseract::TabVector::SortKey | ( | const ICOORD & | vertical,
|  | int | x,
|  | int | y
| ) |  |
| inline static

Definition at line 280 of file tabvector.h.

{
ICOORD pt(x, y);
return pt * vertical;
}

static int tesseract::TabVector::SortVectorsByKey | ( | const void * | v1,
|  | const void * | v2
| ) |  |
| inline static

Definition at line 294 of file tabvector.h.

{
const TabVector* tv1 = *reinterpret_cast<const TabVector* const *>(v1);
const TabVector* tv2 = *reinterpret_cast<const TabVector* const *>(v2);
return tv1->sort_key_ - tv2->sort_key_;
}

const ICOORD& tesseract::TabVector::startpt | ( |  | ) | const
| inline

Definition at line 146 of file tabvector.h.

{
return startpt_;
}

TabVector * tesseract::TabVector::VerticalTextlinePartner | ( |  | ) |

Definition at line 889 of file tabvector.cpp.

{
if (!partners_.singleton())
return NULL;
TabVector_C_IT partner_it(&partners_);
TabVector* partner = partner_it.data();
BLOBNBOX_C_IT box_it1(&boxes_);
BLOBNBOX_C_IT box_it2(&partner->boxes_);
// Count how many boxes are also in the other list.
// At the same time, gather the mean width and median vertical gap.
if (textord_debug_tabfind > 1) {
Print("Testing for vertical text");
partner->Print(" partner");
}
int num_matched = 0;
int num_unmatched = 0;
int total_widths = 0;
int width = startpt().x() - partner->startpt().x();
if (width < 0)
width = -width;
STATS gaps(0, width * 2);
BLOBNBOX* prev_bbox = NULL;
box_it2.mark_cycle_pt();
for (box_it1.mark_cycle_pt(); !box_it1.cycled_list(); box_it1.forward()) {
BLOBNBOX* bbox = box_it1.data();
TBOX box = bbox->bounding_box();
if (prev_bbox != NULL) {
gaps.add(box.bottom() - prev_bbox->bounding_box().top(), 1);
}
while (!box_it2.cycled_list() && box_it2.data() != bbox &&
box_it2.data()->bounding_box().bottom() < box.bottom()) {
box_it2.forward();
}
if (!box_it2.cycled_list() && box_it2.data() == bbox &&
bbox->region_type() >= BRT_UNKNOWN &&
(prev_bbox == NULL || prev_bbox->region_type() >= BRT_UNKNOWN))
++num_matched;
else
++num_unmatched;
total_widths += box.width();
prev_bbox = bbox;
}
double avg_width = total_widths * 1.0 / (num_unmatched + num_matched);
double max_gap = textord_tabvector_vertical_gap_fraction * avg_width;
int min_box_match = static_cast<int>((num_matched + num_unmatched) *
textord_tabvector_vertical_box_ratio);
bool is_vertical = (gaps.get_total() > 0 &&
num_matched >= min_box_match &&
gaps.median() <= max_gap);
if (textord_debug_tabfind > 1) {
tprintf("gaps=%d, matched=%d, unmatched=%d, min_match=%d "
"median gap=%.2f, width=%.2f max_gap=%.2f Vertical=%s\n",
gaps.get_total(), num_matched, num_unmatched, min_box_match,
gaps.median(), avg_width, max_gap, is_vertical?"Yes":"No");
}
return (is_vertical) ? partner : NULL;
}

int tesseract::TabVector::VOverlap | ( | const TabVector & | other | ) | const
| inline

Definition at line 199 of file tabvector.h.

{
return MIN(other.endpt_.y(), endpt_.y()) -
MAX(other.startpt_.y(), startpt_.y());
}

int tesseract::TabVector::VOverlap | ( | int | top_y,
|  | int | bottom_y
| ) |  | const
| inline

Definition at line 204 of file tabvector.h.

{
return MIN(top_y, endpt_.y()) - MAX(bottom_y, startpt_.y());
}

int tesseract::TabVector::XAtY | ( | int | y | ) | const
| inline

Definition at line 189 of file tabvector.h.

{
int height = endpt_.y() - startpt_.y();
if (height != 0)
return (y - startpt_.y()) * (endpt_.x() - startpt_.x()) / height +
startpt_.x();
else
return startpt_.x();
}

static int tesseract::TabVector::XAtY | ( | const ICOORD & | vertical,
|  | int | sort_key,
|  | int | y
| ) |  |
| inline static

Definition at line 286 of file tabvector.h.

{
if (vertical.y() != 0)
return (vertical.x() * y + sort_key) / vertical.y();
else
return sort_key;
}

void tesseract::TabVector::XYFlip | ( |  | ) |
| inline

Definition at line 255 of file tabvector.h.

{
int x = startpt_.y();
startpt_.set_y(startpt_.x());
startpt_.set_x(x);
x = endpt_.y();
endpt_.set_y(endpt_.x());
endpt_.set_x(x);
}

The documentation for this class was generated from the following files:

- /mnt/data/src/tesseract-ocr/textord/tabvector.h
- /mnt/data/src/tesseract-ocr/textord/tabvector.cpp