Tesseract 3.02

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

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
boxword.cpp
Go to the documentation of this file.
1
2 // File: boxword.h
3 // Description: Class to represent the bounding boxes of the output.
4 // Author: Ray Smith
5 // Created: Tue May 25 14:18:14 PDT 2010
6 //
7 // (C) Copyright 2010, 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 //
19
20 #include "blobs.h"
21 #include "boxword.h"
22 #include "normalis.h"
23 #include "ocrblock.h"
24 #include "pageres.h"
25
26 namespace tesseract {
27
28 // Clip output boxes to input blob boxes for bounds that are within this
29 // tolerance. Otherwise, the blob may be chopped and we have to just use
30 // the word bounding box.
31 const int kBoxClipTolerance = 2;
32 // Min offset in baseline-normalized coords to make a character a subscript.
33 const int kMinSubscriptOffset = 20;
34 // Min offset in baseline-normalized coords to make a character a superscript.
35 const int kMinSuperscriptOffset = 20;
36 // Max y of bottom of a drop-cap blob.
37 const int kMaxDropCapBottom = -128;
38
39 BoxWord::BoxWord() : length_(0) {
40 }
41
42 BoxWord::BoxWord(const BoxWord& src) {
43 CopyFrom(src);
44 }
45
46 BoxWord::~BoxWord() {
47 }
48
49 BoxWord& BoxWord::operator=(const BoxWord& src) {
50 CopyFrom(src);
51 return *this;
52 }
53
54 void BoxWord::CopyFrom(const BoxWord& src) {
55 bbox_ = src.bbox_;
56 length_ = src.length_;
57 boxes_.clear();
58 boxes_.reserve(length_);
59 for (int i = 0; i < length_; ++i)
60 boxes_.push_back(src.boxes_[i]);
61 }
62
63 // Factory to build a BoxWord from a TWERD and the DENORM to switch
64 // back to original image coordinates.
65 // If the denorm is not NULL, then the output is denormalized and rotated
66 // back to the original image coordinates.
67 BoxWord* BoxWord::CopyFromNormalized(const DENORM* denorm,
68 TWERD* tessword) {
69 BoxWord* boxword = new BoxWord();
70 // Count the blobs.
71 boxword->length_ = 0;
72 for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next)
73 ++boxword->length_;
74 // Allocate memory.
75 boxword->boxes_.reserve(boxword->length_);
76
77 for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) {
78 TBOX blob_box;
79 for (TESSLINE* outline = tblob->outlines; outline != NULL;
80 outline = outline->next) {
81 EDGEPT* edgept = outline->loop;
82 // Iterate over the edges.
83 do {
84 if (!edgept->IsHidden() || !edgept->prev->IsHidden()) {
85 ICOORD pos(edgept->pos.x, edgept->pos.y);
86 if (denorm != NULL) {
87 TPOINT denormed;
88 denorm->DenormTransform(edgept->pos, &denormed);
89 pos.set_x(denormed.x);
90 pos.set_y(denormed.y);
91 }
92 TBOX pt_box(pos, pos);
93 blob_box += pt_box;
94 }
95 edgept = edgept->next;
96 } while (edgept != outline->loop);
97 }
98 boxword->boxes_.push_back(blob_box);
99 }
100 boxword->ComputeBoundingBox();
101 return boxword;
102 }
103
104 // Sets up the script_pos_ member using the tessword to get the bln
105 // bounding boxes, the best_choice to get the unichars, and the unicharset
106 // to get the target positions. If small_caps is true, sub/super are not
107 // considered, but dropcaps are.
108 void BoxWord::SetScriptPositions(const UNICHARSET& unicharset, bool small_caps,
109 TWERD* tessword, WERD_CHOICE* best_choice) {
110 // Allocate memory.
111 script_pos_.init_to_size(length_, SP_NORMAL);
112
113 int blob_index = 0;
114 for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next,
115 ++blob_index) {
116 int class_id = best_choice->unichar_id(blob_index);
117 TBOX blob_box = tblob->bounding_box();
118 int top = blob_box.top();
119 int bottom = blob_box.bottom();
120 int min_bottom, max_bottom, min_top, max_top;
121 unicharset.get_top_bottom(class_id, &min_bottom, &max_bottom,
122 &min_top, &max_top);
123 if (bottom <= kMaxDropCapBottom) {
124 script_pos_[blob_index] = SP_DROPCAP;
125 } else if (!small_caps) {
126 if (top + kMinSubscriptOffset < min_top) {
127 script_pos_[blob_index] = SP_SUBSCRIPT;
128 } else if (bottom - kMinSuperscriptOffset > max_bottom) {
129 script_pos_[blob_index] = SP_SUPERSCRIPT;
130 }
131 }
132 }
133 }
134
135 // Clean up the bounding boxes from the polygonal approximation by
136 // expanding slightly, then clipping to the blobs from the original_word
137 // that overlap. If not null, the block provides the inverse rotation.
138 void BoxWord::ClipToOriginalWord(const BLOCK* block, WERD* original_word) {
139 for (int i = 0; i < length_; ++i) {
140 TBOX box = boxes_[i];
141 // Expand by a single pixel, as the poly approximation error is 1 pixel.
142 box = TBOX(box.left() - 1, box.bottom() - 1,
143 box.right() + 1, box.top() + 1);
144 // Now find the original box that matches.
145 TBOX original_box;
146 C_BLOB_IT b_it(original_word->cblob_list());
147 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
148 TBOX blob_box = b_it.data()->bounding_box();
149 if (block != NULL)
150 blob_box.rotate(block->re_rotation());
151 if (blob_box.major_overlap(box)) {
152 original_box += blob_box;
153 }
154 }
155 if (!original_box.null_box()) {
156 if (NearlyEqual<int>(original_box.left(), box.left(), kBoxClipTolerance))
157 box.set_left(original_box.left());
158 if (NearlyEqual<int>(original_box.right(), box.right(),
159 kBoxClipTolerance))
160 box.set_right(original_box.right());
161 if (NearlyEqual<int>(original_box.top(), box.top(), kBoxClipTolerance))
162 box.set_top(original_box.top());
163 if (NearlyEqual<int>(original_box.bottom(), box.bottom(),
164 kBoxClipTolerance))
165 box.set_bottom(original_box.bottom());
166 }
167 original_box = original_word->bounding_box();
168 if (block != NULL)
169 original_box.rotate(block->re_rotation());
170 boxes_[i] = box.intersection(original_box);
171 }
172 ComputeBoundingBox();
173 }
174
175 // Merges the boxes from start to end, not including end, and deletes
176 // the boxes between start and end.
177 void BoxWord::MergeBoxes(int start, int end) {
178 start = ClipToRange(start, 0, length_);
179 end = ClipToRange(end, 0, length_);
180 if (end <= start + 1)
181 return;
182 for (int i = start + 1; i < end; ++i) {
183 boxes_[start] += boxes_[i];
184 }
185 int shrinkage = end - 1 - start;
186 length_ -= shrinkage;
187 for (int i = start + 1; i < length_; ++i)
188 boxes_[i] = boxes_[i + shrinkage];
189 boxes_.truncate(length_);
190 }
191
192 // Inserts a new box before the given index.
193 // Recomputes the bounding box.
194 void BoxWord::InsertBox(int index, const TBOX& box) {
195 if (index < length_)
196 boxes_.insert(box, index);
197 else
198 boxes_.push_back(box);
199 length_ = boxes_.size();
200 ComputeBoundingBox();
201 }
202
203 // Deletes the box with the given index, and shuffles up the rest.
204 // Recomputes the bounding box.
205 void BoxWord::DeleteBox(int index) {
206 ASSERT_HOST(0 <= index && index < length_);
207 boxes_.remove(index);
208 --length_;
209 ComputeBoundingBox();
210 }
211
212 // Deletes all the boxes stored in BoxWord.
213 void BoxWord::DeleteAllBoxes() {
214 length_ = 0;
215 boxes_.clear();
216 bbox_ = TBOX();
217 }
218
219 // Computes the bounding box of the word.
220 void BoxWord::ComputeBoundingBox() {
221 bbox_ = TBOX();
222 for (int i = 0; i < length_; ++i)
223 bbox_ += boxes_[i];
224 }
225
226 // This and other putatively are the same, so call the (permanent) callback
227 // for each blob index where the bounding boxes match.
228 // The callback is deleted on completion.
229 void BoxWord::ProcessMatchedBlobs(const TWERD& other,
230 TessCallback1<int>* cb) const {
231 TBLOB* blob = other.blobs;
232 for (int i = 0; i < length_ && blob != NULL; ++i, blob = blob->next) {
233 TBOX blob_box = blob->bounding_box();
234 if (blob_box == boxes_[i])
235 cb->Run(i);
236 }
237 delete cb;
238 }
239
240 } // namespace tesseract.
241
242