tesseract
3.03
|
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 00033 BoxWord::BoxWord() : length_(0) { 00034 } 00035 00036 BoxWord::BoxWord(const BoxWord& src) { 00037 CopyFrom(src); 00038 } 00039 00040 BoxWord::~BoxWord() { 00041 } 00042 00043 BoxWord& BoxWord::operator=(const BoxWord& src) { 00044 CopyFrom(src); 00045 return *this; 00046 } 00047 00048 void BoxWord::CopyFrom(const BoxWord& src) { 00049 bbox_ = src.bbox_; 00050 length_ = src.length_; 00051 boxes_.clear(); 00052 boxes_.reserve(length_); 00053 for (int i = 0; i < length_; ++i) 00054 boxes_.push_back(src.boxes_[i]); 00055 } 00056 00057 // Factory to build a BoxWord from a TWERD using the DENORMs on each blob to 00058 // switch back to original image coordinates. 00059 BoxWord* BoxWord::CopyFromNormalized(TWERD* tessword) { 00060 BoxWord* boxword = new BoxWord(); 00061 // Count the blobs. 00062 boxword->length_ = tessword->NumBlobs(); 00063 // Allocate memory. 00064 boxword->boxes_.reserve(boxword->length_); 00065 00066 for (int b = 0; b < boxword->length_; ++b) { 00067 TBLOB* tblob = tessword->blobs[b]; 00068 TBOX blob_box; 00069 for (TESSLINE* outline = tblob->outlines; outline != NULL; 00070 outline = outline->next) { 00071 EDGEPT* edgept = outline->loop; 00072 // Iterate over the edges. 00073 do { 00074 if (!edgept->IsHidden() || !edgept->prev->IsHidden()) { 00075 ICOORD pos(edgept->pos.x, edgept->pos.y); 00076 TPOINT denormed; 00077 tblob->denorm().DenormTransform(NULL, edgept->pos, &denormed); 00078 pos.set_x(denormed.x); 00079 pos.set_y(denormed.y); 00080 TBOX pt_box(pos, pos); 00081 blob_box += pt_box; 00082 } 00083 edgept = edgept->next; 00084 } while (edgept != outline->loop); 00085 } 00086 boxword->boxes_.push_back(blob_box); 00087 } 00088 boxword->ComputeBoundingBox(); 00089 return boxword; 00090 } 00091 00092 // Clean up the bounding boxes from the polygonal approximation by 00093 // expanding slightly, then clipping to the blobs from the original_word 00094 // that overlap. If not null, the block provides the inverse rotation. 00095 void BoxWord::ClipToOriginalWord(const BLOCK* block, WERD* original_word) { 00096 for (int i = 0; i < length_; ++i) { 00097 TBOX box = boxes_[i]; 00098 // Expand by a single pixel, as the poly approximation error is 1 pixel. 00099 box = TBOX(box.left() - 1, box.bottom() - 1, 00100 box.right() + 1, box.top() + 1); 00101 // Now find the original box that matches. 00102 TBOX original_box; 00103 C_BLOB_IT b_it(original_word->cblob_list()); 00104 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { 00105 TBOX blob_box = b_it.data()->bounding_box(); 00106 if (block != NULL) 00107 blob_box.rotate(block->re_rotation()); 00108 if (blob_box.major_overlap(box)) { 00109 original_box += blob_box; 00110 } 00111 } 00112 if (!original_box.null_box()) { 00113 if (NearlyEqual<int>(original_box.left(), box.left(), kBoxClipTolerance)) 00114 box.set_left(original_box.left()); 00115 if (NearlyEqual<int>(original_box.right(), box.right(), 00116 kBoxClipTolerance)) 00117 box.set_right(original_box.right()); 00118 if (NearlyEqual<int>(original_box.top(), box.top(), kBoxClipTolerance)) 00119 box.set_top(original_box.top()); 00120 if (NearlyEqual<int>(original_box.bottom(), box.bottom(), 00121 kBoxClipTolerance)) 00122 box.set_bottom(original_box.bottom()); 00123 } 00124 original_box = original_word->bounding_box(); 00125 if (block != NULL) 00126 original_box.rotate(block->re_rotation()); 00127 boxes_[i] = box.intersection(original_box); 00128 } 00129 ComputeBoundingBox(); 00130 } 00131 00132 // Merges the boxes from start to end, not including end, and deletes 00133 // the boxes between start and end. 00134 void BoxWord::MergeBoxes(int start, int end) { 00135 start = ClipToRange(start, 0, length_); 00136 end = ClipToRange(end, 0, length_); 00137 if (end <= start + 1) 00138 return; 00139 for (int i = start + 1; i < end; ++i) { 00140 boxes_[start] += boxes_[i]; 00141 } 00142 int shrinkage = end - 1 - start; 00143 length_ -= shrinkage; 00144 for (int i = start + 1; i < length_; ++i) 00145 boxes_[i] = boxes_[i + shrinkage]; 00146 boxes_.truncate(length_); 00147 } 00148 00149 // Inserts a new box before the given index. 00150 // Recomputes the bounding box. 00151 void BoxWord::InsertBox(int index, const TBOX& box) { 00152 if (index < length_) 00153 boxes_.insert(box, index); 00154 else 00155 boxes_.push_back(box); 00156 length_ = boxes_.size(); 00157 ComputeBoundingBox(); 00158 } 00159 00160 // Deletes the box with the given index, and shuffles up the rest. 00161 // Recomputes the bounding box. 00162 void BoxWord::DeleteBox(int index) { 00163 ASSERT_HOST(0 <= index && index < length_); 00164 boxes_.remove(index); 00165 --length_; 00166 ComputeBoundingBox(); 00167 } 00168 00169 // Deletes all the boxes stored in BoxWord. 00170 void BoxWord::DeleteAllBoxes() { 00171 length_ = 0; 00172 boxes_.clear(); 00173 bbox_ = TBOX(); 00174 } 00175 00176 // Computes the bounding box of the word. 00177 void BoxWord::ComputeBoundingBox() { 00178 bbox_ = TBOX(); 00179 for (int i = 0; i < length_; ++i) 00180 bbox_ += boxes_[i]; 00181 } 00182 00183 // This and other putatively are the same, so call the (permanent) callback 00184 // for each blob index where the bounding boxes match. 00185 // The callback is deleted on completion. 00186 void BoxWord::ProcessMatchedBlobs(const TWERD& other, 00187 TessCallback1<int>* cb) const { 00188 for (int i = 0; i < length_ && i < other.NumBlobs(); ++i) { 00189 TBOX blob_box = other.blobs[i]->bounding_box(); 00190 if (blob_box == boxes_[i]) 00191 cb->Run(i); 00192 } 00193 delete cb; 00194 } 00195 00196 } // namespace tesseract.