• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 ///////////////////////////////////////////////////////////////////////
2 // File:        strokewidth.cpp
3 // Description: Subclass of BBGrid to find uniformity of strokewidth.
4 // Author:      Ray Smith
5 // Created:     Mon Mar 31 16:17:01 PST 2008
6 //
7 // (C) Copyright 2008, Google Inc.
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 ///////////////////////////////////////////////////////////////////////
19 
20 #include "strokewidth.h"
21 #include "blobbox.h"
22 #include "tabfind.h"
23 #include "tordmain.h"  // For SetBlobStrokeWidth.
24 
25 namespace tesseract {
26 
27 // Allowed proportional change in stroke width to be the same font.
28 const double kStrokeWidthFractionTolerance = 0.125;
29 // Allowed constant change in stroke width to be the same font.
30 // Really 1.5 pixels.
31 const double kStrokeWidthTolerance = 1.5;
32 // Maximum height in inches of the largest possible text.
33 const double kMaxTextSize = 2.0;
34 
StrokeWidth(int gridsize,const ICOORD & bleft,const ICOORD & tright)35 StrokeWidth::StrokeWidth(int gridsize,
36                          const ICOORD& bleft, const ICOORD& tright)
37   : BBGrid<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT>(gridsize, bleft, tright) {
38 }
39 
~StrokeWidth()40 StrokeWidth::~StrokeWidth() {
41 }
42 
43 // Puts the block blobs (normal and large) into the grid.
InsertBlobs(TO_BLOCK * block,TabFind * line_grid)44 void StrokeWidth::InsertBlobs(TO_BLOCK* block, TabFind* line_grid) {
45   // Insert the blobs into this grid using the separator lines in line_grid.
46   line_grid->InsertBlobList(true, false, false, &block->blobs, false, this);
47   line_grid->InsertBlobList(true, false, true, &block->large_blobs,
48                             false, this);
49 }
50 
51 // Moves the large blobs that have good stroke-width neighbours to the normal
52 // blobs list.
MoveGoodLargeBlobs(int resolution,TO_BLOCK * block)53 void StrokeWidth::MoveGoodLargeBlobs(int resolution, TO_BLOCK* block) {
54   BLOBNBOX_IT large_it = &block->large_blobs;
55   BLOBNBOX_IT blob_it = &block->blobs;
56   int max_height = static_cast<int>(resolution * kMaxTextSize);
57   int b_count = 0;
58   for (large_it.mark_cycle_pt(); !large_it.cycled_list(); large_it.forward()) {
59     BLOBNBOX* large_blob = large_it.data();
60     if (large_blob->bounding_box().height() <= max_height &&
61         GoodTextBlob(large_blob)) {
62       blob_it.add_to_end(large_it.extract());
63       ++b_count;
64     }
65   }
66   if (textord_debug_tabfind) {
67     tprintf("Moved %d large blobs to normal list\n",
68             b_count);
69   }
70 }
71 
72 // Displays the blobs green or red according to whether they are good or not.
DisplayGoodBlobs(const char * window_name,ScrollView * window)73 ScrollView* StrokeWidth::DisplayGoodBlobs(const char* window_name,
74                                           ScrollView* window) {
75 #ifndef GRAPHICS_DISABLED
76   if (window == NULL)
77     window = MakeWindow(0, 0, window_name);
78   // For every blob in the grid, display it.
79   window->Brush(ScrollView::NONE);
80 
81   // For every bbox in the grid, display it.
82   GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> gsearch(this);
83   gsearch.StartFullSearch();
84   BLOBNBOX* bbox;
85   while ((bbox = gsearch.NextFullSearch()) != NULL) {
86     TBOX box = bbox->bounding_box();
87     int left_x = box.left();
88     int right_x = box.right();
89     int top_y = box.top();
90     int bottom_y = box.bottom();
91     if (textord_debug_printable || GoodTextBlob(bbox))
92       window->Pen(ScrollView::GREEN);
93     else
94       window->Pen(ScrollView::RED);
95     window->Rectangle(left_x, bottom_y, right_x, top_y);
96   }
97   window->Update();
98 #endif
99   return window;
100 }
101 
102 // Handles a click event in a display window.
HandleClick(int x,int y)103 void StrokeWidth::HandleClick(int x, int y) {
104   BBGrid<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT>::HandleClick(x, y);
105   // Run a radial search for blobs that overlap.
106   GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> radsearch(this);
107   radsearch.StartRadSearch(x, y, 1);
108   BLOBNBOX* neighbour;
109   FCOORD click(x, y);
110   while ((neighbour = radsearch.NextRadSearch()) != NULL) {
111     TBOX nbox = neighbour->bounding_box();
112     if (nbox.contains(click) && neighbour->cblob() != NULL) {
113       SetBlobStrokeWidth(true, neighbour);
114       tprintf("Box (%d,%d)->(%d,%d): h-width=%.1f, v-width=%.1f p-width=%1.f\n",
115               nbox.left(), nbox.bottom(), nbox.right(), nbox.top(),
116               neighbour->horz_stroke_width(), neighbour->vert_stroke_width(),
117               2.0 * neighbour->cblob()->area()/neighbour->cblob()->perimeter());
118     }
119   }
120 }
121 
122 // Returns true if there is at least one side neighbour that has a similar
123 // stroke width and is not on the other side of a rule line.
GoodTextBlob(BLOBNBOX * blob)124 bool StrokeWidth::GoodTextBlob(BLOBNBOX* blob) {
125   double h_width = blob->horz_stroke_width();
126   double v_width = blob->vert_stroke_width();
127   // The perimeter-based width is used as a backup in case there is
128   // no information in the blob.
129   double p_width = 2.0f * blob->cblob()->area();
130   p_width /= blob->cblob()->perimeter();
131   double h_tolerance = h_width * kStrokeWidthFractionTolerance
132                      + kStrokeWidthTolerance;
133   double v_tolerance = v_width * kStrokeWidthFractionTolerance
134                      + kStrokeWidthTolerance;
135   double p_tolerance = p_width * kStrokeWidthFractionTolerance
136                      + kStrokeWidthTolerance;
137 
138   // Run a radial search for neighbours that overlap.
139   TBOX box = blob->bounding_box();
140   int radius = box.height() / gridsize_ + 2;
141   GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> radsearch(this);
142   radsearch.StartRadSearch((box.left() + box.right()) / 2, box.bottom(),
143                            radius);
144   int top = box.top();
145   int bottom = box.bottom();
146   int min_overlap = (top - bottom) / 2;
147   BLOBNBOX* neighbour;
148   while ((neighbour = radsearch.NextRadSearch()) != NULL) {
149     TBOX nbox = neighbour->bounding_box();
150     if (neighbour == blob) {
151       continue;
152     }
153     // In finding a suitable neighbour, do not cross rule lines.
154     if (nbox.right() > blob->right_rule() || nbox.left() < blob->left_rule()) {
155       continue;  // Can't use it.
156     }
157     int overlap = MIN(nbox.top(), top) - MAX(nbox.bottom(), bottom);
158     if (overlap >= min_overlap &&
159         !TabFind::DifferentSizes(box.height(), nbox.height())) {
160       double n_h_width = neighbour->horz_stroke_width();
161       double n_v_width = neighbour->vert_stroke_width();
162       double n_p_width = 2.0f * neighbour->cblob()->area();
163       n_p_width /= neighbour->cblob()->perimeter();
164       bool h_zero = h_width == 0.0f || n_h_width == 0.0f;
165       bool v_zero = v_width == 0.0f || n_v_width == 0.0f;
166       bool h_ok = !h_zero && NearlyEqual(h_width, n_h_width, h_tolerance);
167       bool v_ok = !v_zero && NearlyEqual(v_width, n_v_width, v_tolerance);
168       bool p_ok = h_zero && v_zero &&
169                   NearlyEqual(p_width, n_p_width, p_tolerance);
170       // For a match, at least one of the horizontal and vertical widths
171       // must match, and the other one must either match or be zero.
172       // Only if both are zero will we look at the perimeter metric.
173       if (p_ok || ((v_ok || h_ok) && (h_ok || h_zero) && (v_ok || v_zero))) {
174         return true;
175       }
176     }
177   }
178   return false;
179 }
180 
181 }  // namespace tesseract.
182 
183