Tesseract
3.02
|
00001 00002 // File: boxword.h 00003 // Description: Class to represent the bounding boxes of the output. 00004 // Author: Ray Smith 00005 // Created: Tue May 25 14:18:14 PDT 2010 00006 // 00007 // (C) Copyright 2010, Google Inc. 00008 // Licensed under the Apache License, Version 2.0 (the "License"); 00009 // you may not use this file except in compliance with the License. 00010 // You may obtain a copy of the License at 00011 // http://www.apache.org/licenses/LICENSE-2.0 00012 // Unless required by applicable law or agreed to in writing, software 00013 // distributed under the License is distributed on an "AS IS" BASIS, 00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 // See the License for the specific language governing permissions and 00016 // limitations under the License. 00017 // 00019 00020 #include "blobs.h" 00021 #include "boxword.h" 00022 #include "normalis.h" 00023 #include "ocrblock.h" 00024 #include "pageres.h" 00025 00026 namespace tesseract { 00027 00028 // Clip output boxes to input blob boxes for bounds that are within this 00029 // tolerance. Otherwise, the blob may be chopped and we have to just use 00030 // the word bounding box. 00031 const int kBoxClipTolerance = 2; 00032 // Min offset in baseline-normalized coords to make a character a subscript. 00033 const int kMinSubscriptOffset = 20; 00034 // Min offset in baseline-normalized coords to make a character a superscript. 00035 const int kMinSuperscriptOffset = 20; 00036 // Max y of bottom of a drop-cap blob. 00037 const int kMaxDropCapBottom = -128; 00038 00039 BoxWord::BoxWord() : length_(0) { 00040 } 00041 00042 BoxWord::BoxWord(const BoxWord& src) { 00043 CopyFrom(src); 00044 } 00045 00046 BoxWord::~BoxWord() { 00047 } 00048 00049 BoxWord& BoxWord::operator=(const BoxWord& src) { 00050 CopyFrom(src); 00051 return *this; 00052 } 00053 00054 void BoxWord::CopyFrom(const BoxWord& src) { 00055 bbox_ = src.bbox_; 00056 length_ = src.length_; 00057 boxes_.clear(); 00058 boxes_.reserve(length_); 00059 for (int i = 0; i < length_; ++i) 00060 boxes_.push_back(src.boxes_[i]); 00061 } 00062 00063 // Factory to build a BoxWord from a TWERD and the DENORM to switch 00064 // back to original image coordinates. 00065 // If the denorm is not NULL, then the output is denormalized and rotated 00066 // back to the original image coordinates. 00067 BoxWord* BoxWord::CopyFromNormalized(const DENORM* denorm, 00068 TWERD* tessword) { 00069 BoxWord* boxword = new BoxWord(); 00070 // Count the blobs. 00071 boxword->length_ = 0; 00072 for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) 00073 ++boxword->length_; 00074 // Allocate memory. 00075 boxword->boxes_.reserve(boxword->length_); 00076 00077 for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) { 00078 TBOX blob_box; 00079 for (TESSLINE* outline = tblob->outlines; outline != NULL; 00080 outline = outline->next) { 00081 EDGEPT* edgept = outline->loop; 00082 // Iterate over the edges. 00083 do { 00084 if (!edgept->IsHidden() || !edgept->prev->IsHidden()) { 00085 ICOORD pos(edgept->pos.x, edgept->pos.y); 00086 if (denorm != NULL) { 00087 TPOINT denormed; 00088 denorm->DenormTransform(edgept->pos, &denormed); 00089 pos.set_x(denormed.x); 00090 pos.set_y(denormed.y); 00091 } 00092 TBOX pt_box(pos, pos); 00093 blob_box += pt_box; 00094 } 00095 edgept = edgept->next; 00096 } while (edgept != outline->loop); 00097 } 00098 boxword->boxes_.push_back(blob_box); 00099 } 00100 boxword->ComputeBoundingBox(); 00101 return boxword; 00102 } 00103 00104 // Sets up the script_pos_ member using the tessword to get the bln 00105 // bounding boxes, the best_choice to get the unichars, and the unicharset 00106 // to get the target positions. If small_caps is true, sub/super are not 00107 // considered, but dropcaps are. 00108 void BoxWord::SetScriptPositions(const UNICHARSET& unicharset, bool small_caps, 00109 TWERD* tessword, WERD_CHOICE* best_choice) { 00110 // Allocate memory. 00111 script_pos_.init_to_size(length_, SP_NORMAL); 00112 00113 int blob_index = 0; 00114 for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next, 00115 ++blob_index) { 00116 int class_id = best_choice->unichar_id(blob_index); 00117 TBOX blob_box = tblob->bounding_box(); 00118 int top = blob_box.top(); 00119 int bottom = blob_box.bottom(); 00120 int min_bottom, max_bottom, min_top, max_top; 00121 unicharset.get_top_bottom(class_id, &min_bottom, &max_bottom, 00122 &min_top, &max_top); 00123 if (bottom <= kMaxDropCapBottom) { 00124 script_pos_[blob_index] = SP_DROPCAP; 00125 } else if (!small_caps) { 00126 if (top + kMinSubscriptOffset < min_top) { 00127 script_pos_[blob_index] = SP_SUBSCRIPT; 00128 } else if (bottom - kMinSuperscriptOffset > max_bottom) { 00129 script_pos_[blob_index] = SP_SUPERSCRIPT; 00130 } 00131 } 00132 } 00133 } 00134 00135 // Clean up the bounding boxes from the polygonal approximation by 00136 // expanding slightly, then clipping to the blobs from the original_word 00137 // that overlap. If not null, the block provides the inverse rotation. 00138 void BoxWord::ClipToOriginalWord(const BLOCK* block, WERD* original_word) { 00139 for (int i = 0; i < length_; ++i) { 00140 TBOX box = boxes_[i]; 00141 // Expand by a single pixel, as the poly approximation error is 1 pixel. 00142 box = TBOX(box.left() - 1, box.bottom() - 1, 00143 box.right() + 1, box.top() + 1); 00144 // Now find the original box that matches. 00145 TBOX original_box; 00146 C_BLOB_IT b_it(original_word->cblob_list()); 00147 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { 00148 TBOX blob_box = b_it.data()->bounding_box(); 00149 if (block != NULL) 00150 blob_box.rotate(block->re_rotation()); 00151 if (blob_box.major_overlap(box)) { 00152 original_box += blob_box; 00153 } 00154 } 00155 if (!original_box.null_box()) { 00156 if (NearlyEqual<int>(original_box.left(), box.left(), kBoxClipTolerance)) 00157 box.set_left(original_box.left()); 00158 if (NearlyEqual<int>(original_box.right(), box.right(), 00159 kBoxClipTolerance)) 00160 box.set_right(original_box.right()); 00161 if (NearlyEqual<int>(original_box.top(), box.top(), kBoxClipTolerance)) 00162 box.set_top(original_box.top()); 00163 if (NearlyEqual<int>(original_box.bottom(), box.bottom(), 00164 kBoxClipTolerance)) 00165 box.set_bottom(original_box.bottom()); 00166 } 00167 original_box = original_word->bounding_box(); 00168 if (block != NULL) 00169 original_box.rotate(block->re_rotation()); 00170 boxes_[i] = box.intersection(original_box); 00171 } 00172 ComputeBoundingBox(); 00173 } 00174 00175 // Merges the boxes from start to end, not including end, and deletes 00176 // the boxes between start and end. 00177 void BoxWord::MergeBoxes(int start, int end) { 00178 start = ClipToRange(start, 0, length_); 00179 end = ClipToRange(end, 0, length_); 00180 if (end <= start + 1) 00181 return; 00182 for (int i = start + 1; i < end; ++i) { 00183 boxes_[start] += boxes_[i]; 00184 } 00185 int shrinkage = end - 1 - start; 00186 length_ -= shrinkage; 00187 for (int i = start + 1; i < length_; ++i) 00188 boxes_[i] = boxes_[i + shrinkage]; 00189 boxes_.truncate(length_); 00190 } 00191 00192 // Inserts a new box before the given index. 00193 // Recomputes the bounding box. 00194 void BoxWord::InsertBox(int index, const TBOX& box) { 00195 if (index < length_) 00196 boxes_.insert(box, index); 00197 else 00198 boxes_.push_back(box); 00199 length_ = boxes_.size(); 00200 ComputeBoundingBox(); 00201 } 00202 00203 // Deletes the box with the given index, and shuffles up the rest. 00204 // Recomputes the bounding box. 00205 void BoxWord::DeleteBox(int index) { 00206 ASSERT_HOST(0 <= index && index < length_); 00207 boxes_.remove(index); 00208 --length_; 00209 ComputeBoundingBox(); 00210 } 00211 00212 // Deletes all the boxes stored in BoxWord. 00213 void BoxWord::DeleteAllBoxes() { 00214 length_ = 0; 00215 boxes_.clear(); 00216 bbox_ = TBOX(); 00217 } 00218 00219 // Computes the bounding box of the word. 00220 void BoxWord::ComputeBoundingBox() { 00221 bbox_ = TBOX(); 00222 for (int i = 0; i < length_; ++i) 00223 bbox_ += boxes_[i]; 00224 } 00225 00226 // This and other putatively are the same, so call the (permanent) callback 00227 // for each blob index where the bounding boxes match. 00228 // The callback is deleted on completion. 00229 void BoxWord::ProcessMatchedBlobs(const TWERD& other, 00230 TessCallback1<int>* cb) const { 00231 TBLOB* blob = other.blobs; 00232 for (int i = 0; i < length_ && blob != NULL; ++i, blob = blob->next) { 00233 TBOX blob_box = blob->bounding_box(); 00234 if (blob_box == boxes_[i]) 00235 cb->Run(i); 00236 } 00237 delete cb; 00238 } 00239 00240 } // namespace tesseract. 00241 00242