Tesseract  3.02
tesseract-ocr/ccstruct/boxword.cpp
Go to the documentation of this file.
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