/////////////////////////////////////////////////////////////////////// // 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 WidthCallback; struct AlignedBlobParams; // Pixel resolution of column width estimates. const int kColumnWidthFactor = 20; // 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. 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(); // 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. void InsertBlobList(bool h_spread, bool v_spread, bool large, BLOBNBOX_LIST* blobs, bool take_ownership, BBGrid* grid); // 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). bool InsertBlob(bool h_spread, bool v_spread, bool large, BLOBNBOX* blob, BBGrid* grid); // Find the gutter width and distance to inner neighbour for the given blob. void GutterWidthAndNeighbourGap(int tab_x, int mean_height, int max_gutter, bool left, BLOBNBOX* bbox, int* gutter_width, int* neighbour_gap); // 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. BLOBNBOX* AdjacentBlob(const BLOBNBOX* bbox, bool right_to_left, int gap_limit); // 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. BlobRegionType ComputeBlobType(BLOBNBOX* blob); // 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. int RightEdgeForBox(const TBOX& box, bool crossing, bool extended); // As RightEdgeForBox, but finds the left Edge instead. int LeftEdgeForBox(const TBOX& box, bool crossing, bool extended); // Compute the rotation required to deskew, and its inverse rotation. void ComputeDeskewVectors(FCOORD* deskew, FCOORD* reskew); // Return true if the given width is close to one of the common // widths in column_widths_. bool CommonWidth(int width); // Return true if the sizes are more than a // factor of 2 different. static bool DifferentSizes(int size1, int size2); // Return a callback for testing CommonWidth. WidthCallback* WidthCB() { return width_cb_; } // Return the coords at which to draw the image backdrop. const ICOORD& image_origin() const { return image_origin_; } protected: // Accessors TabVector_LIST* get_vectors() { return &vectors_; } // Top-level function to find TabVectors in an input page block. void FindTabVectors(int resolution, TabVector_LIST* hlines, BLOBNBOX_LIST* image_blobs, TO_BLOCK* block, FCOORD* reskew, FCOORD* rerotate); // Top-level function to not find TabVectors in an input page block, // but setup for single column mode. void DontFindTabVectors(int resolution, BLOBNBOX_LIST* image_blobs, TO_BLOCK* block, FCOORD* reskew); // 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. TabVector* RightTabForBox(const TBOX& box, bool crossing, bool extended); // As RightTabForBox, but finds the left TabVector instead. TabVector* LeftTabForBox(const TBOX& box, bool crossing, bool extended); // Helper function to setup search limits for *TabForBox. void SetupTabSearch(int x, int y, int* min_key, int* max_key); // Display the tab vectors found in this grid. 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); // Remove separators and unused tabs from the main vectors_ list // to the dead_vectors_ list. void CleanupTabs(); // Deskew the tab vectors and blobs, computing the rotation and resetting // the storked vertical_skew_. The deskew inverse is returned in reskew. void Deskew(TabVector_LIST* hlines, BLOBNBOX_LIST* image_blobs, TO_BLOCK* block, FCOORD* reskew); // Restart everything and rotate the input blobs ready for vertical text. void ResetForVerticalText(TabVector_LIST* hlines, BLOBNBOX_LIST* image_blobs, TO_BLOCK* block, FCOORD* rerotate); // Compute and apply constraints to the end positions of TabVectors so // that where possible partners end at the same y coordinate. void ApplyTabConstraints(); protected: ICOORD vertical_skew_; // Estimate of true vertical in this image. int resolution_; // Of source image in pixels per inch. private: ICOORD image_origin_; // Top-left of image in deskewed coords 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. WidthCallback* width_cb_; // Instance of the base class that contains only candidate tab stops. BBGrid* tab_grid_; }; } // namespace tesseract. #endif // TESSERACT_TEXTORD_TABFIND_H__