2009-07-11 10:39:56 +08:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// File: tabfind.h
|
|
|
|
// Description: Subclass of BBGrid to find tabstops.
|
|
|
|
// Author: Ray Smith
|
|
|
|
// Created: Fri Mar 21 15:03:01 PST 2008
|
|
|
|
//
|
|
|
|
// (C) Copyright 2008, Google Inc.
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#ifndef TESSERACT_TEXTORD_TABFIND_H__
|
|
|
|
#define TESSERACT_TEXTORD_TABFIND_H__
|
|
|
|
|
|
|
|
#include "alignedblob.h"
|
|
|
|
#include "callback.h"
|
|
|
|
#include "tabvector.h"
|
|
|
|
#include "linefind.h"
|
|
|
|
|
|
|
|
class BLOBNBOX;
|
|
|
|
class BLOBNBOX_LIST;
|
|
|
|
class TO_BLOCK;
|
|
|
|
class ScrollView;
|
|
|
|
struct Pix;
|
|
|
|
|
|
|
|
namespace tesseract {
|
|
|
|
|
|
|
|
typedef ResultCallback1<bool, int> WidthCallback;
|
|
|
|
|
|
|
|
struct AlignedBlobParams;
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/** Pixel resolution of column width estimates. */
|
2009-07-11 10:39:56 +08:00
|
|
|
const int kColumnWidthFactor = 20;
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* The TabFind class contains code to find tab-stops and maintain the
|
|
|
|
* vectors_ list of tab vectors.
|
|
|
|
* Also provides an interface to find neighbouring blobs
|
|
|
|
* in the grid of BLOBNBOXes that is used by multiple subclasses.
|
|
|
|
* Searching is a complex operation because of the need to enforce
|
|
|
|
* rule/separator lines, and tabstop boundaries, (when available), so
|
|
|
|
* as the holder of the list of TabVectors this class provides the functions.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
class TabFind : public AlignedBlob {
|
|
|
|
public:
|
|
|
|
TabFind(int gridsize, const ICOORD& bleft, const ICOORD& tright,
|
|
|
|
TabVector_LIST* vlines, int vertical_x, int vertical_y);
|
|
|
|
virtual ~TabFind();
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Insert a list of blobs into the given grid (not necessarily this).
|
|
|
|
* If take_ownership is true, then the blobs are removed from the source list.
|
|
|
|
* See InsertBlob for the other arguments.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void InsertBlobList(bool h_spread, bool v_spread, bool large,
|
|
|
|
BLOBNBOX_LIST* blobs, bool take_ownership,
|
|
|
|
BBGrid<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT>* grid);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Insert a single blob into the given grid (not necessarily this).
|
|
|
|
* If h_spread, then all cells covered horizontally by the box are
|
|
|
|
* used, otherwise, just the bottom-left. Similarly for v_spread.
|
|
|
|
* If large, then insert only if the bounding box doesn't intersect
|
|
|
|
* anything else already in the grid. Returns true if the blob was inserted.
|
|
|
|
* A side effect is that the left and right rule edges of the blob are
|
|
|
|
* set according to the tab vectors in this (not grid).
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
bool InsertBlob(bool h_spread, bool v_spread, bool large, BLOBNBOX* blob,
|
|
|
|
BBGrid<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT>* grid);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Find the gutter width and distance to inner neighbour for the given blob.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void GutterWidthAndNeighbourGap(int tab_x, int mean_height,
|
|
|
|
int max_gutter, bool left,
|
|
|
|
BLOBNBOX* bbox, int* gutter_width,
|
|
|
|
int* neighbour_gap);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Find the next adjacent (to left or right) blob on this text line,
|
|
|
|
* with the constraint that it must vertically significantly overlap
|
|
|
|
* the input box.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
BLOBNBOX* AdjacentBlob(const BLOBNBOX* bbox,
|
|
|
|
bool right_to_left, int gap_limit);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Compute and return, but do not set the type as being BRT_TEXT or
|
|
|
|
* BRT_UNKNOWN according to how well it forms a text line.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
BlobRegionType ComputeBlobType(BLOBNBOX* blob);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Return the x-coord that corresponds to the right edge for the given
|
|
|
|
* box. If there is a rule line to the right that vertically overlaps it,
|
|
|
|
* then return the x-coord of the rule line, otherwise return the right
|
|
|
|
* edge of the page. For details see RightTabForBox below.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
int RightEdgeForBox(const TBOX& box, bool crossing, bool extended);
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* As RightEdgeForBox, but finds the left Edge instead.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
int LeftEdgeForBox(const TBOX& box, bool crossing, bool extended);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Compute the rotation required to deskew, and its inverse rotation.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void ComputeDeskewVectors(FCOORD* deskew, FCOORD* reskew);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Return true if the given width is close to one of the common
|
|
|
|
* widths in column_widths_.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
bool CommonWidth(int width);
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Return true if the sizes are more than a
|
|
|
|
* factor of 2 different.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
static bool DifferentSizes(int size1, int size2);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Return a callback for testing CommonWidth.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
WidthCallback* WidthCB() {
|
|
|
|
return width_cb_;
|
|
|
|
}
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Return the coords at which to draw the image backdrop.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
const ICOORD& image_origin() const {
|
|
|
|
return image_origin_;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
2009-07-11 10:39:56 +08:00
|
|
|
// Accessors
|
2010-08-11 03:20:11 +08:00
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
TabVector_LIST* get_vectors() {
|
|
|
|
return &vectors_;
|
|
|
|
}
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
2009-07-11 10:39:56 +08:00
|
|
|
// Top-level function to find TabVectors in an input page block.
|
2010-08-11 03:20:11 +08:00
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void FindTabVectors(int resolution, TabVector_LIST* hlines,
|
|
|
|
BLOBNBOX_LIST* image_blobs, TO_BLOCK* block,
|
|
|
|
FCOORD* reskew, FCOORD* rerotate);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
2009-07-11 10:39:56 +08:00
|
|
|
// Top-level function to not find TabVectors in an input page block,
|
|
|
|
// but setup for single column mode.
|
2010-08-11 03:20:11 +08:00
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void DontFindTabVectors(int resolution, BLOBNBOX_LIST* image_blobs,
|
|
|
|
TO_BLOCK* block, FCOORD* reskew);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Return the TabVector that corresponds to the right edge for the given
|
|
|
|
* box. If there is a TabVector to the right that vertically overlaps it,
|
|
|
|
* then return it, otherwise return NULL. Note that Right and Left refer
|
|
|
|
* to the position of the TabVector, not its type, ie RightTabForBox
|
|
|
|
* returns the nearest TabVector to the right of the box, regardless of
|
|
|
|
* its type.
|
|
|
|
* If a TabVector crosses right through the box (as opposed to grazing one
|
|
|
|
* edge or missing entirely), then crossing false will ignore such a line.
|
|
|
|
* Crossing true will return the line for BOTH left and right edges.
|
|
|
|
* If extended is true, then TabVectors are considered to extend to their
|
|
|
|
* extended_start/end_y, otherwise, just the startpt_ and endpt_.
|
|
|
|
* These functions make use of an internal iterator to the vectors_ list
|
|
|
|
* for speed when used repeatedly on neighbouring boxes. The caveat is
|
|
|
|
* that the iterator must be updated whenever the list is modified.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
TabVector* RightTabForBox(const TBOX& box, bool crossing, bool extended);
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* As RightTabForBox, but finds the left TabVector instead.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
TabVector* LeftTabForBox(const TBOX& box, bool crossing, bool extended);
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Helper function to setup search limits for *TabForBox.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void SetupTabSearch(int x, int y, int* min_key, int* max_key);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Display the tab vectors found in this grid.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
ScrollView* DisplayTabVectors(ScrollView* tab_win);
|
|
|
|
|
|
|
|
private:
|
|
|
|
// First part of FindTabVectors, which may be used twice if the text
|
|
|
|
// is mostly of vertical alignment.
|
|
|
|
void FindInitialTabVectors(BLOBNBOX_LIST* image_blobs, TO_BLOCK* block);
|
|
|
|
|
|
|
|
// For each box in the grid, decide whether it is a candidate tab-stop,
|
|
|
|
// and if so add it to the tab_grid_.
|
|
|
|
ScrollView* FindTabBoxes();
|
|
|
|
|
|
|
|
// Return true if this box looks like a candidate tab stop, and set
|
|
|
|
// the appropriate tab type(s) to TT_UNCONFIRMED.
|
|
|
|
bool TestBoxForTabs(BLOBNBOX* bbox);
|
|
|
|
|
|
|
|
// Fills the list of TabVector with the tabstops found in the grid,
|
|
|
|
// and estimates the logical vertical direction.
|
|
|
|
void FindAllTabVectors();
|
|
|
|
// Helper for FindAllTabVectors finds the vectors of a particular type.
|
|
|
|
int FindTabVectors(int search_size_multiple,
|
|
|
|
TabAlignment alignment,
|
|
|
|
TabVector_LIST* vectors,
|
|
|
|
int* vertical_x, int* vertical_y);
|
|
|
|
// Finds a vector corresponding to a tabstop running through the
|
|
|
|
// given box of the given alignment type.
|
|
|
|
// search_size_multiple is a multiple of height used to control
|
|
|
|
// the size of the search.
|
|
|
|
// vertical_x and y are updated with an estimate of the real
|
|
|
|
// vertical direction. (skew finding.)
|
|
|
|
// Returns NULL if no decent tabstop can be found.
|
|
|
|
TabVector* FindTabVector(int search_size_multiple,
|
|
|
|
TabAlignment alignment,
|
|
|
|
BLOBNBOX* bbox,
|
|
|
|
int* vertical_x, int* vertical_y);
|
|
|
|
|
|
|
|
// Set the vertical_skew_ member from the given vector and refit
|
|
|
|
// all vectors parallel to the skew vector.
|
|
|
|
void SetVerticalSkewAndParellelize(int vertical_x, int vertical_y);
|
|
|
|
|
|
|
|
// Sort all the current vectors using the vertical_skew_ vector.
|
|
|
|
void SortVectors();
|
|
|
|
|
|
|
|
// Evaluate all the current tab vectors.
|
|
|
|
void EvaluateTabs();
|
|
|
|
|
|
|
|
// Trace textlines from one side to the other of each tab vector, saving
|
|
|
|
// the most frequent column widths found in a list so that a given width
|
|
|
|
// can be tested for being a common width with a simple callback function.
|
|
|
|
void ComputeColumnWidths(ScrollView* tab_win);
|
|
|
|
|
|
|
|
// Set the region_type_ member for all the blobs in the grid.
|
|
|
|
void ComputeBlobGoodness();
|
|
|
|
|
|
|
|
// Set the region_type_ member of the blob, if not already known.
|
|
|
|
void SetBlobRegionType(BLOBNBOX* blob);
|
|
|
|
|
|
|
|
// Mark blobs as being in a vertical text line where that is the case.
|
|
|
|
void MarkVerticalText();
|
|
|
|
|
|
|
|
// Returns true if the majority of the image is vertical text lines.
|
|
|
|
bool TextMostlyVertical();
|
|
|
|
|
|
|
|
// If this box looks like it is on a textline in the given direction,
|
|
|
|
// return the width of the textline-like group of blobs, and the number
|
|
|
|
// of blobs found.
|
|
|
|
// For more detail see FindTextlineSegment below.
|
|
|
|
int FindTextlineWidth(bool right_to_left, BLOBNBOX* bbox, int* blob_count);
|
|
|
|
|
|
|
|
// Search from the given tabstop bbox to the next opposite
|
|
|
|
// tabstop bbox on the same text line, which may be itself.
|
|
|
|
// Returns true if the search is successful, and sets
|
|
|
|
// start_pt, end_pt to the fitted baseline, width to the measured
|
|
|
|
// width of the text line (column width estimate.)
|
|
|
|
bool TraceTextline(BLOBNBOX* bbox, ICOORD* start_pt, ICOORD* end_pt,
|
|
|
|
int* left_edge, int* right_edge);
|
|
|
|
|
|
|
|
// Search from the given bbox in the given direction until the next tab
|
|
|
|
// vector is found or a significant horizontal gap is found.
|
|
|
|
// Returns the width of the line if the search is successful, (defined
|
|
|
|
// as good coverage of the width and a good fitting baseline) and sets
|
|
|
|
// start_pt, end_pt to the fitted baseline, left_blob, right_blob to
|
|
|
|
// the ends of the line. Returns zero otherwise.
|
|
|
|
// Sets blob_count to the number of blobs found on the line.
|
|
|
|
// On input, either both left_vector and right_vector should be NULL,
|
|
|
|
// indicating a basic search, or both left_vector and right_vector should
|
|
|
|
// be not NULL and one of *left_vector and *right_vector should be not NULL,
|
|
|
|
// in which case the search is strictly between tab vectors and will return
|
|
|
|
// zero if a gap is found before the opposite tab vector is reached, or a
|
|
|
|
// conflicting tab vector is found.
|
|
|
|
// If ignore_images is true, then blobs with aligned_text() < 0 are treated
|
|
|
|
// as if they do not exist.
|
|
|
|
int FindTextlineSegment(bool right_to_lefts, bool ignore_images,
|
|
|
|
BLOBNBOX* bbox, int* blob_count,
|
|
|
|
ICOORD* start_pt, ICOORD* end_pt,
|
|
|
|
TabVector** left_vector, TabVector** right_vector,
|
|
|
|
BLOBNBOX** left_blob, BLOBNBOX** right_blob);
|
|
|
|
|
|
|
|
// Find the next adjacent (to left or right) blob on this text line,
|
|
|
|
// with the constraint that it must vertically significantly overlap
|
|
|
|
// the [top_y, bottom_y] range.
|
|
|
|
// If ignore_images is true, then blobs with aligned_text() < 0 are treated
|
|
|
|
// as if they do not exist.
|
|
|
|
BLOBNBOX* AdjacentBlob(const BLOBNBOX* bbox,
|
|
|
|
bool right_to_left, bool ignore_images,
|
|
|
|
int gap_limit, int top_y, int bottom_y);
|
|
|
|
|
|
|
|
// Add a bi-directional partner relationship between the left
|
|
|
|
// and the right. If one (or both) of the vectors is a separator,
|
|
|
|
// extend a nearby extendable vector or create a new one of the
|
|
|
|
// correct type, using the given left or right blob as a guide.
|
|
|
|
void AddPartnerVector(BLOBNBOX* left_blob, BLOBNBOX* right_blob,
|
|
|
|
TabVector* left, TabVector* right);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Remove separators and unused tabs from the main vectors_ list
|
|
|
|
* to the dead_vectors_ list.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void CleanupTabs();
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Deskew the tab vectors and blobs, computing the rotation and resetting
|
|
|
|
* the storked vertical_skew_. The deskew inverse is returned in reskew.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void Deskew(TabVector_LIST* hlines, BLOBNBOX_LIST* image_blobs,
|
|
|
|
TO_BLOCK* block, FCOORD* reskew);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Restart everything and rotate the input blobs ready for vertical text.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void ResetForVerticalText(TabVector_LIST* hlines, BLOBNBOX_LIST* image_blobs,
|
|
|
|
TO_BLOCK* block, FCOORD* rerotate);
|
|
|
|
|
2010-08-11 03:20:11 +08:00
|
|
|
/**
|
|
|
|
* Compute and apply constraints to the end positions of TabVectors so
|
|
|
|
* that where possible partners end at the same y coordinate.
|
|
|
|
*/
|
2009-07-11 10:39:56 +08:00
|
|
|
void ApplyTabConstraints();
|
|
|
|
|
|
|
|
protected:
|
2010-08-11 03:20:11 +08:00
|
|
|
ICOORD vertical_skew_; //< Estimate of true vertical in this image.
|
|
|
|
int resolution_; //< Of source image in pixels per inch.
|
2009-07-11 10:39:56 +08:00
|
|
|
private:
|
|
|
|
ICOORD image_origin_; // Top-left of image in deskewed coords
|
2010-08-11 03:20:11 +08:00
|
|
|
TabVector_LIST vectors_; //< List of rule line and tabstops.
|
|
|
|
TabVector_IT v_it_; //< Iterator for searching vectors_.
|
|
|
|
TabVector_LIST dead_vectors_; //< Separators and unpartnered tab vectors.
|
|
|
|
ICOORDELT_LIST column_widths_; //< List of commonly occurring widths.
|
|
|
|
/** Callback to test an int for being a common width. */
|
2009-07-11 10:39:56 +08:00
|
|
|
WidthCallback* width_cb_;
|
2010-08-11 03:20:11 +08:00
|
|
|
/** Instance of the base class that contains only candidate tab stops. */
|
2009-07-11 10:39:56 +08:00
|
|
|
BBGrid<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT>* tab_grid_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace tesseract.
|
|
|
|
|
|
|
|
#endif // TESSERACT_TEXTORD_TABFIND_H__
|
|
|
|
|