本文整理汇总了C++中BLOBNBOX::bounding_box方法的典型用法代码示例。如果您正苦于以下问题:C++ BLOBNBOX::bounding_box方法的具体用法?C++ BLOBNBOX::bounding_box怎么用?C++ BLOBNBOX::bounding_box使用的例子?那么, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类BLOBNBOX
的用法示例。
在下文中一共展示了BLOBNBOX::bounding_box方法的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。
示例1: FindLineVectors
// Finds vertical lines in the given list of BLOBNBOXes. bleft and tright
// are the bounds of the image on which the input line_bblobs were found.
// The input line_bblobs list is const really.
// The output vertical_x and vertical_y are the total of all the vectors.
// The output list of TabVector makes no reference to the input BLOBNBOXes.
void LineFinder::FindLineVectors(const ICOORD& bleft, const ICOORD& tright,
BLOBNBOX_LIST* line_bblobs,
int* vertical_x, int* vertical_y,
TabVector_LIST* vectors) {
BLOBNBOX_IT bbox_it(line_bblobs);
int b_count = 0;
// Put all the blobs into the grid to find the lines, and move the blobs
// to the output lists.
AlignedBlob blob_grid(kLineFindGridSize, bleft, tright);
for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) {
BLOBNBOX* bblob = bbox_it.data();
bblob->set_left_tab_type(TT_UNCONFIRMED);
bblob->set_left_rule(bleft.x());
bblob->set_right_rule(tright.x());
bblob->set_left_crossing_rule(bleft.x());
bblob->set_right_crossing_rule(tright.x());
blob_grid.InsertBBox(false, true, bblob);
++b_count;
}
if (textord_debug_tabfind)
tprintf("Inserted %d line blobs into grid\n", b_count);
if (b_count == 0)
return;
// Search the entire grid, looking for vertical line vectors.
GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> lsearch(&blob_grid);
BLOBNBOX* bbox;
TabVector_IT vector_it(vectors);
*vertical_x = 0;
*vertical_y = 1;
lsearch.StartFullSearch();
while ((bbox = lsearch.NextFullSearch()) != NULL) {
if (bbox->left_tab_type() == TT_UNCONFIRMED) {
const TBOX& box = bbox->bounding_box();
if (AlignedBlob::WithinTestRegion(2, box.left(), box.bottom()))
tprintf("Finding line vector starting at bbox (%d,%d)\n",
box.left(), box.bottom());
AlignedBlobParams align_params(*vertical_x, *vertical_y, box.width());
TabVector* vector = blob_grid.FindVerticalAlignment(align_params, bbox,
vertical_x,
vertical_y);
if (vector != NULL) {
vector->Freeze();
vector_it.add_to_end(vector);
}
}
}
ScrollView* line_win = NULL;
if (textord_tabfind_show_vlines) {
line_win = blob_grid.MakeWindow(0, 50, "Vlines");
blob_grid.DisplayBoxes(line_win);
line_win = blob_grid.DisplayTabs("Vlines", line_win);
}
}
示例2: MarkAndDeleteNonTextBlobs
// Tests each blob in the list to see if it is certain non-text using 2
// conditions:
// 1. blob overlaps a cell with high value in noise_density_ (previously set
// by ComputeNoiseDensity).
// OR 2. The blob overlaps more than max_blob_overlaps in *this grid. This
// condition is disabled with max_blob_overlaps == -1.
// If it does, the blob is declared non-text, and is used to mark up the
// nontext_mask. Such blobs are fully deleted, and non-noise blobs have their
// neighbours reset, as they may now point to deleted data.
// WARNING: The blobs list blobs may be in the *this grid, but they are
// not removed. If any deleted blobs might be in *this, then this must be
// Clear()ed immediately after MarkAndDeleteNonTextBlobs is called.
// If the win is not NULL, deleted blobs are drawn on it in red, and kept
// blobs are drawn on it in ok_color.
void CCNonTextDetect::MarkAndDeleteNonTextBlobs(BLOBNBOX_LIST* blobs,
int max_blob_overlaps,
ScrollView* win,
ScrollView::Color ok_color,
Pix* nontext_mask) {
int imageheight = tright().y() - bleft().x();
BLOBNBOX_IT blob_it(blobs);
BLOBNBOX_LIST dead_blobs;
BLOBNBOX_IT dead_it(&dead_blobs);
for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
BLOBNBOX* blob = blob_it.data();
TBOX box = blob->bounding_box();
if (!noise_density_->RectMostlyOverThreshold(box, max_noise_count_) &&
(max_blob_overlaps < 0 ||
!BlobOverlapsTooMuch(blob, max_blob_overlaps))) {
blob->ClearNeighbours();
#ifndef GRAPHICS_DISABLED
if (win != NULL)
blob->plot(win, ok_color, ok_color);
#endif // GRAPHICS_DISABLED
} else {
if (noise_density_->AnyZeroInRect(box)) {
// There is a danger that the bounding box may overlap real text, so
// we need to render the outline.
Pix* blob_pix = blob->cblob()->render_outline();
pixRasterop(nontext_mask, box.left(), imageheight - box.top(),
box.width(), box.height(), PIX_SRC | PIX_DST,
blob_pix, 0, 0);
pixDestroy(&blob_pix);
} else {
if (box.area() < gridsize() * gridsize()) {
// It is a really bad idea to make lots of small components in the
// photo mask, so try to join it to a bigger area by expanding the
// box in a way that does not touch any zero noise density cell.
box = AttemptBoxExpansion(box, *noise_density_, gridsize());
}
// All overlapped cells are non-zero, so just mark the rectangle.
pixRasterop(nontext_mask, box.left(), imageheight - box.top(),
box.width(), box.height(), PIX_SET, NULL, 0, 0);
}
#ifndef GRAPHICS_DISABLED
if (win != NULL)
blob->plot(win, ScrollView::RED, ScrollView::RED);
#endif // GRAPHICS_DISABLED
// It is safe to delete the cblob now, as it isn't used by the grid
// or BlobOverlapsTooMuch, and the BLOBNBOXes will go away with the
// dead_blobs list.
// TODO(rays) delete the delete when the BLOBNBOX destructor deletes
// the cblob.
delete blob->cblob();
dead_it.add_to_end(blob_it.extract());
}
}
}
示例3: ExtendToBox
// Extend this vector to include the supplied blob if it doesn't
// already have it.
void TabVector::ExtendToBox(BLOBNBOX* new_blob) {
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);
}
示例4: NeighbourGaps
// Returns the box gaps between this and its neighbours_ in an array
// indexed by BlobNeighbourDir.
void BLOBNBOX::NeighbourGaps(int gaps[BND_COUNT]) const {
for (int dir = 0; dir < BND_COUNT; ++dir) {
gaps[dir] = MAX_INT16;
BLOBNBOX* neighbour = neighbours_[dir];
if (neighbour != NULL) {
TBOX n_box = neighbour->bounding_box();
if (dir == BND_LEFT || dir == BND_RIGHT) {
gaps[dir] = box.x_gap(n_box);
} else {
gaps[dir] = box.y_gap(n_box);
}
}
}
}
示例5: MoveNonTextlineBlobs
// Moves blobs that look like they don't sit well on a textline from the
// input blobs list to the output small_blobs list.
// This gets them away from initial textline finding to stop diacritics
// from forming incorrect textlines. (Introduced mainly to fix Thai.)
void TextlineProjection::MoveNonTextlineBlobs(
BLOBNBOX_LIST* blobs, BLOBNBOX_LIST* small_blobs) const {
BLOBNBOX_IT it(blobs);
BLOBNBOX_IT small_it(small_blobs);
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* blob = it.data();
const TBOX& box = blob->bounding_box();
bool debug = AlignedBlob::WithinTestRegion(2, box.left(),
box.bottom());
if (BoxOutOfHTextline(box, NULL, debug) && !blob->UniquelyVertical()) {
blob->ClearNeighbours();
small_it.add_to_end(it.extract());
}
}
}
示例6: box_next_pre_chopped
TBOX box_next_pre_chopped( //get bounding box
BLOBNBOX_IT *it //iterator to blobds
) {
BLOBNBOX *blob; //current blob
TBOX result; //total box
blob = it->data ();
result = blob->bounding_box ();
do {
it->forward ();
blob = it->data ();
}
//until next real blob
while (blob->joined_to_prev ());
return result;
}
示例7: FindVerticalLines
// Finds vertical line objects in the given pix.
// Uses the given resolution to determine size thresholds instead of any
// that may be present in the pix.
// The output vertical_x and vertical_y contain a sum of the output vectors,
// thereby giving the mean vertical direction.
// The output vectors are owned by the list and Frozen (cannot refit) by
// having no boxes, as there is no need to refit or merge separator lines.
void LineFinder::FindVerticalLines(int resolution, Pix* pix,
int* vertical_x, int* vertical_y,
TabVector_LIST* vectors) {
#ifdef HAVE_LIBLEPT
Pix* line_pix;
Boxa* boxes = GetVLineBoxes(resolution, pix, &line_pix);
C_BLOB_LIST line_cblobs;
int width = pixGetWidth(pix);
int height = pixGetHeight(pix);
ConvertBoxaToBlobs(width, height, &boxes, &line_cblobs);
// Make the BLOBNBOXes from the C_BLOBs.
BLOBNBOX_LIST line_bblobs;
C_BLOB_IT blob_it(&line_cblobs);
BLOBNBOX_IT bbox_it(&line_bblobs);
for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
C_BLOB* cblob = blob_it.data();
BLOBNBOX* bblob = new BLOBNBOX(cblob);
bbox_it.add_to_end(bblob);
}
ICOORD bleft(0, 0);
ICOORD tright(width, height);
FindLineVectors(bleft, tright, &line_bblobs, vertical_x, vertical_y, vectors);
if (!vectors->empty()) {
// Some lines were found, so erase the unused blobs from the line image
// and then subtract the line image from the source.
bbox_it.move_to_first();
for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) {
BLOBNBOX* blob = bbox_it.data();
if (blob->left_tab_type() == TT_UNCONFIRMED) {
const TBOX& box = blob->bounding_box();
Box* pixbox = boxCreate(box.left(), height - box.top(),
box.width(), box.height());
pixClearInRect(line_pix, pixbox);
boxDestroy(&pixbox);
}
}
pixDilateBrick(line_pix, line_pix, 1, 3);
pixSubtract(pix, pix, line_pix);
if (textord_tabfind_show_vlines)
pixWrite("vlinesclean.png", line_pix, IFF_PNG);
ICOORD vertical;
vertical.set_with_shrink(*vertical_x, *vertical_y);
TabVector::MergeSimilarTabVectors(vertical, vectors, NULL);
}
pixDestroy(&line_pix);
#endif
}
示例8: PlotGradedBlobs
// Display the blobs in the window colored according to textline quality.
void TextlineProjection::PlotGradedBlobs(BLOBNBOX_LIST* blobs,
ScrollView* win) {
#ifndef GRAPHICS_DISABLED
BLOBNBOX_IT it(blobs);
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
BLOBNBOX* blob = it.data();
const TBOX& box = blob->bounding_box();
bool bad_box = BoxOutOfHTextline(box, NULL, false);
if (blob->UniquelyVertical())
win->Pen(ScrollView::YELLOW);
else
win->Pen(bad_box ? ScrollView::RED : ScrollView::BLUE);
win->Rectangle(box.left(), box.bottom(), box.right(), box.top());
}
win->Update();
#endif // GRAPHICS_DISABLED
}
示例9: DisplayTabs
// Display the tab codes of the BLOBNBOXes in this grid.
ScrollView* AlignedBlob::DisplayTabs(const char* window_name,
ScrollView* tab_win) {
#ifndef GRAPHICS_DISABLED
if (tab_win == nullptr)
tab_win = MakeWindow(0, 50, window_name);
// For every tab in the grid, display it.
GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> gsearch(this);
gsearch.StartFullSearch();
BLOBNBOX* bbox;
while ((bbox = gsearch.NextFullSearch()) != nullptr) {
const TBOX& box = bbox->bounding_box();
int left_x = box.left();
int right_x = box.right();
int top_y = box.top();
int bottom_y = box.bottom();
TabType tabtype = bbox->left_tab_type();
if (tabtype != TT_NONE) {
if (tabtype == TT_MAYBE_ALIGNED)
tab_win->Pen(ScrollView::BLUE);
else if (tabtype == TT_MAYBE_RAGGED)
tab_win->Pen(ScrollView::YELLOW);
else if (tabtype == TT_CONFIRMED)
tab_win->Pen(ScrollView::GREEN);
else
tab_win->Pen(ScrollView::GREY);
tab_win->Line(left_x, top_y, left_x, bottom_y);
}
tabtype = bbox->right_tab_type();
if (tabtype != TT_NONE) {
if (tabtype == TT_MAYBE_ALIGNED)
tab_win->Pen(ScrollView::MAGENTA);
else if (tabtype == TT_MAYBE_RAGGED)
tab_win->Pen(ScrollView::ORANGE);
else if (tabtype == TT_CONFIRMED)
tab_win->Pen(ScrollView::RED);
else
tab_win->Pen(ScrollView::GREY);
tab_win->Line(right_x, top_y, right_x, bottom_y);
}
}
tab_win->Update();
#endif
return tab_win;
}
示例10: BlobOverlapsTooMuch
// Returns true if the given blob overlaps more than max_overlaps blobs
// in the current grid.
bool CCNonTextDetect::BlobOverlapsTooMuch(BLOBNBOX* blob, int max_overlaps) {
// Search the grid to see what intersects it.
// Setup a Rectangle search for overlapping this blob.
BlobGridSearch rsearch(this);
TBOX box = blob->bounding_box();
rsearch.StartRectSearch(box);
rsearch.SetUniqueMode(true);
BLOBNBOX* neighbour;
int overlap_count = 0;
while (overlap_count <= max_overlaps &&
(neighbour = rsearch.NextRectSearch()) != NULL) {
if (box.major_overlap(neighbour->bounding_box())) {
++overlap_count;
if (overlap_count > max_overlaps)
return true;
}
}
return false;
}
示例11: SetupBlobDisplacements
// Sets up displacement_modes_ with the top few modes of the perpendicular
// distance of each blob from the given direction vector, after rounding.
void BaselineRow::SetupBlobDisplacements(const FCOORD& direction) {
// Set of perpendicular displacements of the blob bottoms from the required
// baseline direction.
GenericVector<double> perp_blob_dists;
displacement_modes_.truncate(0);
// Gather the skew-corrected position of every blob.
double min_dist = MAX_FLOAT32;
double max_dist = -MAX_FLOAT32;
BLOBNBOX_IT blob_it(blobs_);
bool debug = false;
for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
BLOBNBOX* blob = blob_it.data();
const TBOX& box = blob->bounding_box();
#ifdef kDebugYCoord
if (box.bottom() < kDebugYCoord && box.top() > kDebugYCoord) debug = true;
#endif
FCOORD blob_pos((box.left() + box.right()) / 2.0f,
blob->baseline_position());
double offset = direction * blob_pos;
perp_blob_dists.push_back(offset);
if (debug) {
tprintf("Displacement %g for blob at:", offset);
box.print();
}
UpdateRange(offset, &min_dist, &max_dist);
}
// Set up a histogram using disp_quant_factor_ as the bucket size.
STATS dist_stats(IntCastRounded(min_dist / disp_quant_factor_),
IntCastRounded(max_dist / disp_quant_factor_) + 1);
for (int i = 0; i < perp_blob_dists.size(); ++i) {
dist_stats.add(IntCastRounded(perp_blob_dists[i] / disp_quant_factor_), 1);
}
GenericVector<KDPairInc<float, int> > scaled_modes;
dist_stats.top_n_modes(kMaxDisplacementsModes, &scaled_modes);
if (debug) {
for (int i = 0; i < scaled_modes.size(); ++i) {
tprintf("Top mode = %g * %d\n",
scaled_modes[i].key * disp_quant_factor_, scaled_modes[i].data);
}
}
for (int i = 0; i < scaled_modes.size(); ++i)
displacement_modes_.push_back(disp_quant_factor_ * scaled_modes[i].key);
}
示例12: TransferDiacriticsToWords
// Places a copy of blobs that are near a word (after applying rotation to the
// blob) in the most appropriate word, unless there is doubt, in which case a
// blob can end up in two words. Source blobs are not touched.
void Textord::TransferDiacriticsToWords(BLOBNBOX_LIST* diacritic_blobs,
const FCOORD& rotation,
WordGrid* word_grid) {
WordSearch ws(word_grid);
BLOBNBOX_IT b_it(diacritic_blobs);
// Apply rotation to each blob before finding the nearest words. The rotation
// allows us to only consider above/below placement and not left/right on
// vertical text, because all text is horizontal here.
for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
BLOBNBOX* blobnbox = b_it.data();
TBOX blob_box = blobnbox->bounding_box();
blob_box.rotate(rotation);
ws.StartRectSearch(blob_box);
// Above/below refer to word position relative to diacritic. Since some
// scripts eg Kannada/Telugu habitually put diacritics below words, and
// others eg Thai/Vietnamese/Latin put most diacritics above words, try
// for both if there isn't much in it.
WordWithBox* best_above_word = nullptr;
WordWithBox* best_below_word = nullptr;
int best_above_distance = 0;
int best_below_distance = 0;
for (WordWithBox* word = ws.NextRectSearch(); word != nullptr;
word = ws.NextRectSearch()) {
if (word->word()->flag(W_REP_CHAR)) continue;
TBOX word_box = word->true_bounding_box();
int x_distance = blob_box.x_gap(word_box);
int y_distance = blob_box.y_gap(word_box);
if (x_distance > 0) {
// Arbitrarily divide x-distance by 2 if there is a major y overlap,
// and the word is to the left of the diacritic. If the
// diacritic is a dropped broken character between two words, this will
// help send all the pieces to a single word, instead of splitting them
// over the 2 words.
if (word_box.major_y_overlap(blob_box) &&
blob_box.left() > word_box.right()) {
x_distance /= 2;
}
y_distance += x_distance;
}
if (word_box.y_middle() > blob_box.y_middle() &&
(best_above_word == nullptr || y_distance < best_above_distance)) {
best_above_word = word;
best_above_distance = y_distance;
}
if (word_box.y_middle() <= blob_box.y_middle() &&
(best_below_word == nullptr || y_distance < best_below_distance)) {
best_below_word = word;
best_below_distance = y_distance;
}
}
bool above_good =
best_above_word != nullptr &&
(best_below_word == nullptr ||
best_above_distance < best_below_distance + blob_box.height());
bool below_good =
best_below_word != nullptr && best_below_word != best_above_word &&
(best_above_word == nullptr ||
best_below_distance < best_above_distance + blob_box.height());
if (below_good) {
C_BLOB* copied_blob = C_BLOB::deep_copy(blobnbox->cblob());
copied_blob->rotate(rotation);
// Put the blob into the word's reject blobs list.
C_BLOB_IT blob_it(best_below_word->RejBlobs());
blob_it.add_to_end(copied_blob);
}
if (above_good) {
C_BLOB* copied_blob = C_BLOB::deep_copy(blobnbox->cblob());
copied_blob->rotate(rotation);
// Put the blob into the word's reject blobs list.
C_BLOB_IT blob_it(best_above_word->RejBlobs());
blob_it.add_to_end(copied_blob);
}
}
}
示例13: Fit
// (Re)Fit a line to the stored points. Returns false if the line
// is degenerate. Althougth the TabVector code mostly doesn't care about the
// direction of lines, XAtY would give silly results for a horizontal line.
// The class is mostly aimed at use for vertical lines representing
// horizontal tab stops.
bool TabVector::Fit(ICOORD vertical, bool force_parallel) {
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;
}
示例14: FitBaseline
// Fits a straight baseline to the points. Returns true if it had enough
// points to be reasonably sure of the fitted baseline.
// If use_box_bottoms is false, baselines positions are formed by
// considering the outlines of the blobs.
bool BaselineRow::FitBaseline(bool use_box_bottoms) {
// 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_;
}
示例15: SimilarTo
// Return true if this vector is the same side, overlaps, and close
// enough to the other to be merged.
bool TabVector::SimilarTo(const ICOORD& vertical,
const TabVector& other, BlobGrid* grid) const {
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;
}