tesseract  3.03
/usr/local/google/home/jbreiden/tesseract-ocr-read-only/ccstruct/blobs.cpp
Go to the documentation of this file.
00001 /* -*-C-*-
00002  ********************************************************************************
00003  *
00004  * File:        blobs.c  (Formerly blobs.c)
00005  * Description:  Blob definition
00006  * Author:       Mark Seaman, OCR Technology
00007  * Created:      Fri Oct 27 15:39:52 1989
00008  * Modified:     Thu Mar 28 15:33:26 1991 (Mark Seaman) marks@hpgrlt
00009  * Language:     C
00010  * Package:      N/A
00011  * Status:       Experimental (Do Not Distribute)
00012  *
00013  * (c) Copyright 1989, Hewlett-Packard Company.
00014  ** Licensed under the Apache License, Version 2.0 (the "License");
00015  ** you may not use this file except in compliance with the License.
00016  ** You may obtain a copy of the License at
00017  ** http://www.apache.org/licenses/LICENSE-2.0
00018  ** Unless required by applicable law or agreed to in writing, software
00019  ** distributed under the License is distributed on an "AS IS" BASIS,
00020  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00021  ** See the License for the specific language governing permissions and
00022  ** limitations under the License.
00023  *
00024  *********************************************************************************/
00025 
00026 /*----------------------------------------------------------------------
00027               I n c l u d e s
00028 ----------------------------------------------------------------------*/
00029 // Include automatically generated configuration file if running autoconf.
00030 #ifdef HAVE_CONFIG_H
00031 #include "config_auto.h"
00032 #endif
00033 
00034 #include "blobs.h"
00035 #include "ccstruct.h"
00036 #include "clst.h"
00037 #include "cutil.h"
00038 #include "emalloc.h"
00039 #include "helpers.h"
00040 #include "linlsq.h"
00041 #include "ndminx.h"
00042 #include "normalis.h"
00043 #include "ocrblock.h"
00044 #include "ocrrow.h"
00045 #include "points.h"
00046 #include "polyaprx.h"
00047 #include "structures.h"
00048 #include "werd.h"
00049 
00050 using tesseract::CCStruct;
00051 
00052 // A Vector representing the "vertical" direction when measuring the
00053 // divisiblity of blobs into multiple blobs just by separating outlines.
00054 // See divisible_blob below for the use.
00055 const TPOINT kDivisibleVerticalUpright(0, 1);
00056 // A vector representing the "vertical" direction for italic text for use
00057 // when separating outlines. Using it actually deteriorates final accuracy,
00058 // so it is only used for ApplyBoxes chopping to get a better segmentation.
00059 const TPOINT kDivisibleVerticalItalic(1, 5);
00060 
00061 /*----------------------------------------------------------------------
00062               F u n c t i o n s
00063 ----------------------------------------------------------------------*/
00064 
00065 CLISTIZE(EDGEPT);
00066 
00067 // Consume the circular list of EDGEPTs to make a TESSLINE.
00068 TESSLINE* TESSLINE::BuildFromOutlineList(EDGEPT* outline) {
00069   TESSLINE* result = new TESSLINE;
00070   result->loop = outline;
00071   if (outline->src_outline != NULL) {
00072     // ASSUMPTION: This function is only ever called from ApproximateOutline
00073     // and therefore either all points have a src_outline or all do not.
00074                 // Just as SetupFromPos sets the vectors from the vertices, setup the
00075                 // step_count members to indicate the (positive) number of original
00076                 // C_OUTLINE steps to the next vertex.
00077                 EDGEPT* pt = outline;
00078                 do {
00079                   pt->step_count = pt->next->start_step - pt->start_step;
00080                   if (pt->step_count < 0)
00081                     pt->step_count += pt->src_outline->pathlength();
00082                   pt = pt->next;
00083                 } while (pt != outline);
00084   }
00085   result->SetupFromPos();
00086   return result;
00087 }
00088 
00089 // Copies the data and the outline, but leaves next untouched.
00090 void TESSLINE::CopyFrom(const TESSLINE& src) {
00091   Clear();
00092   topleft = src.topleft;
00093   botright = src.botright;
00094   start = src.start;
00095   is_hole = src.is_hole;
00096   if (src.loop != NULL) {
00097     EDGEPT* prevpt = NULL;
00098     EDGEPT* newpt = NULL;
00099     EDGEPT* srcpt = src.loop;
00100     do {
00101       newpt = new EDGEPT(*srcpt);
00102       if (prevpt == NULL) {
00103         loop = newpt;
00104       } else {
00105         newpt->prev = prevpt;
00106         prevpt->next = newpt;
00107       }
00108       prevpt = newpt;
00109       srcpt = srcpt->next;
00110     } while (srcpt != src.loop);
00111     loop->prev = newpt;
00112     newpt->next = loop;
00113   }
00114 }
00115 
00116 // Deletes owned data.
00117 void TESSLINE::Clear() {
00118   if (loop == NULL)
00119     return;
00120 
00121   EDGEPT* this_edge = loop;
00122   do {
00123     EDGEPT* next_edge = this_edge->next;
00124     delete this_edge;
00125     this_edge = next_edge;
00126   } while (this_edge != loop);
00127   loop = NULL;
00128 }
00129 
00130 // Normalize in-place using the DENORM.
00131 void TESSLINE::Normalize(const DENORM& denorm) {
00132   EDGEPT* pt = loop;
00133   do {
00134     denorm.LocalNormTransform(pt->pos, &pt->pos);
00135     pt = pt->next;
00136   } while (pt != loop);
00137   SetupFromPos();
00138 }
00139 
00140 // Rotates by the given rotation in place.
00141 void TESSLINE::Rotate(const FCOORD rot) {
00142   EDGEPT* pt = loop;
00143   do {
00144     int tmp = static_cast<int>(floor(pt->pos.x * rot.x() -
00145                                      pt->pos.y * rot.y() + 0.5));
00146     pt->pos.y = static_cast<int>(floor(pt->pos.y * rot.x() +
00147                                        pt->pos.x * rot.y() + 0.5));
00148     pt->pos.x = tmp;
00149     pt = pt->next;
00150   } while (pt != loop);
00151   SetupFromPos();
00152 }
00153 
00154 // Moves by the given vec in place.
00155 void TESSLINE::Move(const ICOORD vec) {
00156   EDGEPT* pt = loop;
00157   do {
00158     pt->pos.x += vec.x();
00159     pt->pos.y += vec.y();
00160     pt = pt->next;
00161   } while (pt != loop);
00162   SetupFromPos();
00163 }
00164 
00165 // Scales by the given factor in place.
00166 void TESSLINE::Scale(float factor) {
00167   EDGEPT* pt = loop;
00168   do {
00169     pt->pos.x = static_cast<int>(floor(pt->pos.x * factor + 0.5));
00170     pt->pos.y = static_cast<int>(floor(pt->pos.y * factor + 0.5));
00171     pt = pt->next;
00172   } while (pt != loop);
00173   SetupFromPos();
00174 }
00175 
00176 // Sets up the start and vec members of the loop from the pos members.
00177 void TESSLINE::SetupFromPos() {
00178   EDGEPT* pt = loop;
00179   do {
00180     pt->vec.x = pt->next->pos.x - pt->pos.x;
00181     pt->vec.y = pt->next->pos.y - pt->pos.y;
00182     pt = pt->next;
00183   } while (pt != loop);
00184   start = pt->pos;
00185   ComputeBoundingBox();
00186 }
00187 
00188 // Recomputes the bounding box from the points in the loop.
00189 void TESSLINE::ComputeBoundingBox() {
00190   int minx = MAX_INT32;
00191   int miny = MAX_INT32;
00192   int maxx = -MAX_INT32;
00193   int maxy = -MAX_INT32;
00194 
00195   // Find boundaries.
00196   start = loop->pos;
00197   EDGEPT* this_edge = loop;
00198   do {
00199     if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) {
00200       if (this_edge->pos.x < minx)
00201         minx = this_edge->pos.x;
00202       if (this_edge->pos.y < miny)
00203         miny = this_edge->pos.y;
00204       if (this_edge->pos.x > maxx)
00205         maxx = this_edge->pos.x;
00206       if (this_edge->pos.y > maxy)
00207         maxy = this_edge->pos.y;
00208     }
00209     this_edge = this_edge->next;
00210   } while (this_edge != loop);
00211   // Reset bounds.
00212   topleft.x = minx;
00213   topleft.y = maxy;
00214   botright.x = maxx;
00215   botright.y = miny;
00216 }
00217 
00218 // Computes the min and max cross product of the outline points with the
00219 // given vec and returns the results in min_xp and max_xp. Geometrically
00220 // this is the left and right edge of the outline perpendicular to the
00221 // given direction, but to get the distance units correct, you would
00222 // have to divide by the modulus of vec.
00223 void TESSLINE::MinMaxCrossProduct(const TPOINT vec,
00224                                   int* min_xp, int* max_xp) const {
00225   *min_xp = MAX_INT32;
00226   *max_xp = MIN_INT32;
00227   EDGEPT* this_edge = loop;
00228   do {
00229     if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) {
00230       int product = CROSS(this_edge->pos, vec);
00231       UpdateRange(product, min_xp, max_xp);
00232     }
00233     this_edge = this_edge->next;
00234   } while (this_edge != loop);
00235 }
00236 
00237 TBOX TESSLINE::bounding_box() const {
00238   return TBOX(topleft.x, botright.y, botright.x, topleft.y);
00239 }
00240 
00241 #ifndef GRAPHICS_DISABLED
00242 void TESSLINE::plot(ScrollView* window, ScrollView::Color color,
00243                     ScrollView::Color child_color) {
00244   if (is_hole)
00245     window->Pen(child_color);
00246   else
00247     window->Pen(color);
00248   window->SetCursor(start.x, start.y);
00249   EDGEPT* pt = loop;
00250   do {
00251     bool prev_hidden = pt->IsHidden();
00252     pt = pt->next;
00253     if (prev_hidden)
00254       window->SetCursor(pt->pos.x, pt->pos.y);
00255     else
00256       window->DrawTo(pt->pos.x, pt->pos.y);
00257   } while (pt != loop);
00258 }
00259 #endif  // GRAPHICS_DISABLED
00260 
00261 // Returns the first non-hidden EDGEPT that has a different src_outline to
00262 // its predecessor, or, if all the same, the lowest indexed point.
00263 EDGEPT* TESSLINE::FindBestStartPt() const {
00264   EDGEPT* best_start = loop;
00265   int best_step = loop->start_step;
00266   // Iterate the polygon.
00267   EDGEPT* pt = loop;
00268   do {
00269     if (pt->IsHidden()) continue;
00270     if (pt->prev->IsHidden() || pt->prev->src_outline != pt->src_outline)
00271       return pt;  // Qualifies as the best.
00272     if (pt->start_step < best_step) {
00273       best_step = pt->start_step;
00274       best_start = pt;
00275     }
00276   } while ((pt = pt->next) != loop);
00277   return best_start;
00278 }
00279 
00280 // Iterate the given list of outlines, converting to TESSLINE by polygonal
00281 // approximation and recursively any children, returning the current tail
00282 // of the resulting list of TESSLINEs.
00283 static TESSLINE** ApproximateOutlineList(bool allow_detailed_fx,
00284                                          C_OUTLINE_LIST* outlines,
00285                                          bool children,
00286                                          TESSLINE** tail) {
00287   C_OUTLINE_IT ol_it(outlines);
00288   for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) {
00289     C_OUTLINE* outline = ol_it.data();
00290     if (outline->pathlength() > 0) {
00291       TESSLINE* tessline = ApproximateOutline(allow_detailed_fx, outline);
00292       tessline->is_hole = children;
00293       *tail = tessline;
00294       tail = &tessline->next;
00295     }
00296     if (!outline->child()->empty()) {
00297       tail = ApproximateOutlineList(allow_detailed_fx, outline->child(), true,
00298                                     tail);
00299     }
00300   }
00301   return tail;
00302 }
00303 
00304 // Factory to build a TBLOB from a C_BLOB with polygonal approximation along
00305 // the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB
00306 // contain pointers to the input C_OUTLINEs that enable higher-resolution
00307 // feature extraction that does not use the polygonal approximation.
00308 TBLOB* TBLOB::PolygonalCopy(bool allow_detailed_fx, C_BLOB* src) {
00309   TBLOB* tblob = new TBLOB;
00310   ApproximateOutlineList(allow_detailed_fx, src->out_list(), false,
00311                          &tblob->outlines);
00312   return tblob;
00313 }
00314 
00315 // Factory builds a blob with no outlines, but copies the other member data.
00316 TBLOB* TBLOB::ShallowCopy(const TBLOB& src) {
00317   TBLOB* blob = new TBLOB;
00318   blob->denorm_ = src.denorm_;
00319   return blob;
00320 }
00321 
00322 // Normalizes the blob for classification only if needed.
00323 // (Normally this means a non-zero classify rotation.)
00324 // If no Normalization is needed, then NULL is returned, and the input blob
00325 // can be used directly. Otherwise a new TBLOB is returned which must be
00326 // deleted after use.
00327 TBLOB* TBLOB::ClassifyNormalizeIfNeeded() const {
00328   TBLOB* rotated_blob = NULL;
00329   // If necessary, copy the blob and rotate it. The rotation is always
00330   // +/- 90 degrees, as 180 was already taken care of.
00331   if (denorm_.block() != NULL &&
00332       denorm_.block()->classify_rotation().y() != 0.0) {
00333     TBOX box = bounding_box();
00334     int x_middle = (box.left() + box.right()) / 2;
00335     int y_middle = (box.top() + box.bottom()) / 2;
00336     rotated_blob = new TBLOB(*this);
00337     const FCOORD& rotation = denorm_.block()->classify_rotation();
00338     // Move the rotated blob back to the same y-position so that we
00339     // can still distinguish similar glyphs with differeny y-position.
00340     float target_y = kBlnBaselineOffset +
00341         (rotation.y() > 0 ? x_middle - box.left() : box.right() - x_middle);
00342     rotated_blob->Normalize(NULL, &rotation, &denorm_, x_middle, y_middle,
00343                             1.0f, 1.0f, 0.0f, target_y,
00344                             denorm_.inverse(), denorm_.pix());
00345   }
00346   return rotated_blob;
00347 }
00348 
00349 // Copies the data and the outline, but leaves next untouched.
00350 void TBLOB::CopyFrom(const TBLOB& src) {
00351   Clear();
00352   TESSLINE* prev_outline = NULL;
00353   for (TESSLINE* srcline = src.outlines; srcline != NULL;
00354        srcline = srcline->next) {
00355     TESSLINE* new_outline = new TESSLINE(*srcline);
00356     if (outlines == NULL)
00357       outlines = new_outline;
00358     else
00359       prev_outline->next = new_outline;
00360     prev_outline = new_outline;
00361   }
00362   denorm_ = src.denorm_;
00363 }
00364 
00365 // Deletes owned data.
00366 void TBLOB::Clear() {
00367   for (TESSLINE* next_outline = NULL; outlines != NULL;
00368        outlines = next_outline) {
00369     next_outline = outlines->next;
00370     delete outlines;
00371   }
00372 }
00373 
00374 // Sets up the built-in DENORM and normalizes the blob in-place.
00375 // For parameters see DENORM::SetupNormalization, plus the inverse flag for
00376 // this blob and the Pix for the full image.
00377 void TBLOB::Normalize(const BLOCK* block,
00378                       const FCOORD* rotation,
00379                       const DENORM* predecessor,
00380                       float x_origin, float y_origin,
00381                       float x_scale, float y_scale,
00382                       float final_xshift, float final_yshift,
00383                       bool inverse, Pix* pix) {
00384   denorm_.SetupNormalization(block, rotation, predecessor, x_origin, y_origin,
00385                              x_scale, y_scale, final_xshift, final_yshift);
00386   denorm_.set_inverse(inverse);
00387   denorm_.set_pix(pix);
00388   // TODO(rays) outline->Normalize is more accurate, but breaks tests due
00389   // the changes it makes. Reinstate this code with a retraining.
00390   // The reason this change is troublesome is that it normalizes for the
00391   // baseline value computed independently at each x-coord. If the baseline
00392   // is not horizontal, this introduces shear into the normalized blob, which
00393   // is useful on the rare occasions that the baseline is really curved, but
00394   // the baselines need to be stabilized the rest of the time.
00395 #if 0
00396   for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
00397     outline->Normalize(denorm_);
00398   }
00399 #else
00400   denorm_.LocalNormBlob(this);
00401 #endif
00402 }
00403 
00404 // Rotates by the given rotation in place.
00405 void TBLOB::Rotate(const FCOORD rotation) {
00406   for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
00407     outline->Rotate(rotation);
00408   }
00409 }
00410 
00411 // Moves by the given vec in place.
00412 void TBLOB::Move(const ICOORD vec) {
00413   for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
00414     outline->Move(vec);
00415   }
00416 }
00417 
00418 // Scales by the given factor in place.
00419 void TBLOB::Scale(float factor) {
00420   for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
00421     outline->Scale(factor);
00422   }
00423 }
00424 
00425 // Recomputes the bounding boxes of the outlines.
00426 void TBLOB::ComputeBoundingBoxes() {
00427   for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
00428     outline->ComputeBoundingBox();
00429   }
00430 }
00431 
00432 // Returns the number of outlines.
00433 int TBLOB::NumOutlines() const {
00434   int result = 0;
00435   for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next)
00436     ++result;
00437   return result;
00438 }
00439 
00440 /**********************************************************************
00441  * TBLOB::bounding_box()
00442  *
00443  * Compute the bounding_box of a compound blob, defined to be the
00444  * bounding box of the union of all top-level outlines in the blob.
00445  **********************************************************************/
00446 TBOX TBLOB::bounding_box() const {
00447   if (outlines == NULL)
00448     return TBOX(0, 0, 0, 0);
00449   TESSLINE *outline = outlines;
00450   TBOX box = outline->bounding_box();
00451   for (outline = outline->next; outline != NULL; outline = outline->next) {
00452     box += outline->bounding_box();
00453   }
00454   return box;
00455 }
00456 
00457 #ifndef GRAPHICS_DISABLED
00458 void TBLOB::plot(ScrollView* window, ScrollView::Color color,
00459                  ScrollView::Color child_color) {
00460   for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next)
00461     outline->plot(window, color, child_color);
00462 }
00463 #endif  // GRAPHICS_DISABLED
00464 
00465 // Computes the center of mass and second moments for the old baseline and
00466 // 2nd moment normalizations. Returns the outline length.
00467 // The input denorm should be the normalizations that have been applied from
00468 // the image to the current state of this TBLOB.
00469 int TBLOB::ComputeMoments(FCOORD* center, FCOORD* second_moments) const {
00470   // Compute 1st and 2nd moments of the original outline.
00471   LLSQ accumulator;
00472   TBOX box = bounding_box();
00473   // Iterate the outlines, accumulating edges relative the box.botleft().
00474   CollectEdges(box, NULL, &accumulator, NULL, NULL);
00475   *center = accumulator.mean_point() + box.botleft();
00476   // The 2nd moments are just the standard deviation of the point positions.
00477   double x2nd = sqrt(accumulator.x_variance());
00478   double y2nd = sqrt(accumulator.y_variance());
00479   if (x2nd < 1.0) x2nd = 1.0;
00480   if (y2nd < 1.0) y2nd = 1.0;
00481   second_moments->set_x(x2nd);
00482   second_moments->set_y(y2nd);
00483   return accumulator.count();
00484 }
00485 
00486 // Computes the precise bounding box of the coords that are generated by
00487 // GetEdgeCoords. This may be different from the bounding box of the polygon.
00488 void TBLOB::GetPreciseBoundingBox(TBOX* precise_box) const {
00489   TBOX box = bounding_box();
00490   *precise_box = TBOX();
00491   CollectEdges(box, precise_box, NULL, NULL, NULL);
00492   precise_box->move(box.botleft());
00493 }
00494 
00495 // Adds edges to the given vectors.
00496 // For all the edge steps in all the outlines, or polygonal approximation
00497 // where there are no edge steps, collects the steps into x_coords/y_coords.
00498 // x_coords is a collection of the x-coords of vertical edges for each
00499 // y-coord starting at box.bottom().
00500 // y_coords is a collection of the y-coords of horizontal edges for each
00501 // x-coord starting at box.left().
00502 // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom.
00503 // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1.
00504 void TBLOB::GetEdgeCoords(const TBOX& box,
00505                           GenericVector<GenericVector<int> >* x_coords,
00506                           GenericVector<GenericVector<int> >* y_coords) const {
00507   GenericVector<int> empty;
00508   x_coords->init_to_size(box.height(), empty);
00509   y_coords->init_to_size(box.width(), empty);
00510   CollectEdges(box, NULL, NULL, x_coords, y_coords);
00511   // Sort the output vectors.
00512   for (int i = 0; i < x_coords->size(); ++i)
00513     (*x_coords)[i].sort();
00514   for (int i = 0; i < y_coords->size(); ++i)
00515     (*y_coords)[i].sort();
00516 }
00517 
00518 // Accumulates the segment between pt1 and pt2 in the LLSQ, quantizing over
00519 // the integer coordinate grid to properly weight long vectors.
00520 static void SegmentLLSQ(const FCOORD& pt1, const FCOORD& pt2,
00521                         LLSQ* accumulator) {
00522   FCOORD step(pt2);
00523   step -= pt1;
00524   int xstart = IntCastRounded(MIN(pt1.x(), pt2.x()));
00525   int xend = IntCastRounded(MAX(pt1.x(), pt2.x()));
00526   int ystart = IntCastRounded(MIN(pt1.y(), pt2.y()));
00527   int yend = IntCastRounded(MAX(pt1.y(), pt2.y()));
00528   if (xstart == xend && ystart == yend) return;  // Nothing to do.
00529   double weight = step.length() / (xend - xstart + yend - ystart);
00530   // Compute and save the y-position at the middle of each x-step.
00531   for (int x = xstart; x < xend; ++x) {
00532     double y = pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x();
00533     accumulator->add(x + 0.5, y, weight);
00534   }
00535   // Compute and save the x-position at the middle of each y-step.
00536   for (int y = ystart; y < yend; ++y) {
00537     double x = pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y();
00538     accumulator->add(x, y + 0.5, weight);
00539   }
00540 }
00541 
00542 // Adds any edges from a single segment of outline between pt1 and pt2 to
00543 // the x_coords, y_coords vectors. pt1 and pt2 should be relative to the
00544 // bottom-left of the bounding box, hence indices to x_coords, y_coords
00545 // are clipped to ([0,x_limit], [0,y_limit]).
00546 // See GetEdgeCoords above for a description of x_coords, y_coords.
00547 static void SegmentCoords(const FCOORD& pt1, const FCOORD& pt2,
00548                           int x_limit, int y_limit,
00549                           GenericVector<GenericVector<int> >* x_coords,
00550                           GenericVector<GenericVector<int> >* y_coords) {
00551   FCOORD step(pt2);
00552   step -= pt1;
00553   int start = ClipToRange(IntCastRounded(MIN(pt1.x(), pt2.x())), 0, x_limit);
00554   int end = ClipToRange(IntCastRounded(MAX(pt1.x(), pt2.x())), 0, x_limit);
00555   for (int x = start; x < end; ++x) {
00556     int y = IntCastRounded(pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x());
00557     (*y_coords)[x].push_back(y);
00558   }
00559   start = ClipToRange(IntCastRounded(MIN(pt1.y(), pt2.y())), 0, y_limit);
00560   end = ClipToRange(IntCastRounded(MAX(pt1.y(), pt2.y())), 0, y_limit);
00561   for (int y = start; y < end; ++y) {
00562     int x = IntCastRounded(pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y());
00563     (*x_coords)[y].push_back(x);
00564   }
00565 }
00566 
00567 // Adds any edges from a single segment of outline between pt1 and pt2 to
00568 // the bbox such that it guarantees to contain anything produced by
00569 // SegmentCoords.
00570 static void SegmentBBox(const FCOORD& pt1, const FCOORD& pt2, TBOX* bbox) {
00571   FCOORD step(pt2);
00572   step -= pt1;
00573   int x1 = IntCastRounded(MIN(pt1.x(), pt2.x()));
00574   int x2 = IntCastRounded(MAX(pt1.x(), pt2.x()));
00575   if (x2 > x1) {
00576     int y1 = IntCastRounded(pt1.y() + step.y() * (x1 + 0.5 - pt1.x()) /
00577                             step.x());
00578     int y2 = IntCastRounded(pt1.y() + step.y() * (x2 - 0.5 - pt1.x()) /
00579                             step.x());
00580     TBOX point(x1, MIN(y1, y2), x2, MAX(y1, y2));
00581     *bbox += point;
00582   }
00583   int y1 = IntCastRounded(MIN(pt1.y(), pt2.y()));
00584   int y2 = IntCastRounded(MAX(pt1.y(), pt2.y()));
00585   if (y2 > y1) {
00586     int x1 = IntCastRounded(pt1.x() + step.x() * (y1 + 0.5 - pt1.y()) /
00587                             step.y());
00588     int x2 = IntCastRounded(pt1.x() + step.x() * (y2 - 0.5 - pt1.y()) /
00589                             step.y());
00590     TBOX point(MIN(x1, x2), y1, MAX(x1, x2), y2);
00591     *bbox += point;
00592   }
00593 }
00594 
00595 // Collects edges into the given bounding box, LLSQ accumulator and/or x_coords,
00596 // y_coords vectors.
00597 // For a description of x_coords/y_coords, see GetEdgeCoords above.
00598 // Startpt to lastpt, inclusive, MUST have the same src_outline member,
00599 // which may be NULL. The vector from lastpt to its next is included in
00600 // the accumulation. Hidden edges should be excluded by the caller.
00601 // The input denorm should be the normalizations that have been applied from
00602 // the image to the current state of the TBLOB from which startpt, lastpt come.
00603 // box is the bounding box of the blob from which the EDGEPTs are taken and
00604 // indices into x_coords, y_coords are offset by box.botleft().
00605 static void CollectEdgesOfRun(const EDGEPT* startpt, const EDGEPT* lastpt,
00606                               const DENORM& denorm, const TBOX& box,
00607                               TBOX* bounding_box,
00608                               LLSQ* accumulator,
00609                               GenericVector<GenericVector<int> > *x_coords,
00610                               GenericVector<GenericVector<int> > *y_coords) {
00611   const C_OUTLINE* outline = startpt->src_outline;
00612   int x_limit = box.width() - 1;
00613   int y_limit = box.height() - 1;
00614   if (outline != NULL) {
00615     // Use higher-resolution edge points stored on the outline.
00616     // The outline coordinates may not match the binary image because of the
00617     // rotation for vertical text lines, but the root_denorm IS the matching
00618     // start of the DENORM chain.
00619     const DENORM* root_denorm = denorm.RootDenorm();
00620     int step_length = outline->pathlength();
00621     int start_index = startpt->start_step;
00622     // Note that if this run straddles the wrap-around point of the outline,
00623     // that lastpt->start_step may have a lower index than startpt->start_step,
00624     // and we want to use an end_index that allows us to use a positive
00625     // increment, so we add step_length if necessary, but that may be beyond the
00626     // bounds of the outline steps/ due to wrap-around, so we use % step_length
00627     // everywhere, except for start_index.
00628     int end_index = lastpt->start_step + lastpt->step_count;
00629     if (end_index <= start_index)
00630       end_index += step_length;
00631     // pos is the integer coordinates of the binary image steps.
00632     ICOORD pos = outline->position_at_index(start_index);
00633     FCOORD origin(box.left(), box.bottom());
00634     // f_pos is a floating-point version of pos that offers improved edge
00635     // positioning using greyscale information or smoothing of edge steps.
00636     FCOORD f_pos = outline->sub_pixel_pos_at_index(pos, start_index);
00637     // pos_normed is f_pos after the appropriate normalization, and relative
00638     // to origin.
00639     // prev_normed is the previous value of pos_normed.
00640     FCOORD prev_normed;
00641     denorm.NormTransform(root_denorm, f_pos, &prev_normed);
00642     prev_normed -= origin;
00643     for (int index = start_index; index < end_index; ++index) {
00644       ICOORD step = outline->step(index % step_length);
00645       // Only use the point if its edge strength is positive. This excludes
00646       // points that don't provide useful information, eg
00647       // ___________
00648       //            |___________
00649       // The vertical step provides only noisy, damaging information, as even
00650       // with a greyscale image, the positioning of the edge there may be a
00651       // fictitious extrapolation, so previous processing has eliminated it.
00652       if (outline->edge_strength_at_index(index % step_length) > 0) {
00653         FCOORD f_pos = outline->sub_pixel_pos_at_index(pos,
00654                                                        index % step_length);
00655         FCOORD pos_normed;
00656         denorm.NormTransform(root_denorm, f_pos, &pos_normed);
00657         pos_normed -= origin;
00658         // Accumulate the information that is selected by the caller.
00659         if (bounding_box != NULL) {
00660           SegmentBBox(pos_normed, prev_normed, bounding_box);
00661         }
00662         if (accumulator != NULL) {
00663           SegmentLLSQ(pos_normed, prev_normed, accumulator);
00664         }
00665         if (x_coords != NULL && y_coords != NULL) {
00666           SegmentCoords(pos_normed, prev_normed, x_limit, y_limit,
00667                         x_coords, y_coords);
00668         }
00669         prev_normed = pos_normed;
00670       }
00671       pos += step;
00672     }
00673   } else {
00674     // There is no outline, so we are forced to use the polygonal approximation.
00675     const EDGEPT* endpt = lastpt->next;
00676     const EDGEPT* pt = startpt;
00677     do {
00678       FCOORD next_pos(pt->next->pos.x - box.left(),
00679                       pt->next->pos.y - box.bottom());
00680       FCOORD pos(pt->pos.x - box.left(), pt->pos.y - box.bottom());
00681       if (bounding_box != NULL) {
00682         SegmentBBox(next_pos, pos, bounding_box);
00683       }
00684       if (accumulator != NULL) {
00685         SegmentLLSQ(next_pos, pos, accumulator);
00686       }
00687       if (x_coords != NULL && y_coords != NULL) {
00688         SegmentCoords(next_pos, pos, x_limit, y_limit, x_coords, y_coords);
00689       }
00690     } while ((pt = pt->next) != endpt);
00691   }
00692 }
00693 
00694 // For all the edge steps in all the outlines, or polygonal approximation
00695 // where there are no edge steps, collects the steps into the bounding_box,
00696 // llsq and/or the x_coords/y_coords. Both are used in different kinds of
00697 // normalization.
00698 // For a description of x_coords, y_coords, see GetEdgeCoords above.
00699 void TBLOB::CollectEdges(const TBOX& box,
00700                          TBOX* bounding_box, LLSQ* llsq,
00701                          GenericVector<GenericVector<int> >* x_coords,
00702                          GenericVector<GenericVector<int> >* y_coords) const {
00703   // Iterate the outlines.
00704   for (const TESSLINE* ol = outlines; ol != NULL; ol = ol->next) {
00705     // Iterate the polygon.
00706     EDGEPT* loop_pt = ol->FindBestStartPt();
00707     EDGEPT* pt = loop_pt;
00708     if (pt == NULL) continue;
00709     do {
00710       if (pt->IsHidden()) continue;
00711       // Find a run of equal src_outline.
00712       EDGEPT* last_pt = pt;
00713       do {
00714         last_pt = last_pt->next;
00715       } while (last_pt != loop_pt && !last_pt->IsHidden() &&
00716                last_pt->src_outline == pt->src_outline);
00717       last_pt = last_pt->prev;
00718       CollectEdgesOfRun(pt, last_pt, denorm_, box,
00719                         bounding_box, llsq, x_coords, y_coords);
00720       pt = last_pt;
00721     } while ((pt = pt->next) != loop_pt);
00722   }
00723 }
00724 
00725 // Factory to build a TWERD from a (C_BLOB) WERD, with polygonal
00726 // approximation along the way.
00727 TWERD* TWERD::PolygonalCopy(bool allow_detailed_fx, WERD* src) {
00728   TWERD* tessword = new TWERD;
00729   tessword->latin_script = src->flag(W_SCRIPT_IS_LATIN);
00730   C_BLOB_IT b_it(src->cblob_list());
00731   for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
00732     C_BLOB* blob = b_it.data();
00733     TBLOB* tblob = TBLOB::PolygonalCopy(allow_detailed_fx, blob);
00734     tessword->blobs.push_back(tblob);
00735   }
00736   return tessword;
00737 }
00738 
00739 // Baseline normalizes the blobs in-place, recording the normalization in the
00740 // DENORMs in the blobs.
00741 void TWERD::BLNormalize(const BLOCK* block, const ROW* row, Pix* pix,
00742                         bool inverse, float x_height, bool numeric_mode,
00743                         tesseract::OcrEngineMode hint,
00744                         const TBOX* norm_box,
00745                         DENORM* word_denorm) {
00746   TBOX word_box = bounding_box();
00747   if (norm_box != NULL) word_box = *norm_box;
00748   float word_middle = (word_box.left() + word_box.right()) / 2.0f;
00749   float input_y_offset = 0.0f;
00750   float final_y_offset = static_cast<float>(kBlnBaselineOffset);
00751   float scale = kBlnXHeight / x_height;
00752   if (hint == tesseract::OEM_CUBE_ONLY || row == NULL) {
00753     word_middle = word_box.left();
00754     input_y_offset = word_box.bottom();
00755     final_y_offset = 0.0f;
00756     if (hint == tesseract::OEM_CUBE_ONLY)
00757       scale = 1.0f;
00758   } else {
00759     input_y_offset = row->base_line(word_middle);
00760   }
00761   for (int b = 0; b < blobs.size(); ++b) {
00762     TBLOB* blob = blobs[b];
00763     TBOX blob_box = blob->bounding_box();
00764     float mid_x = (blob_box.left() + blob_box.right()) / 2.0f;
00765     float baseline = input_y_offset;
00766     float blob_scale = scale;
00767     if (numeric_mode) {
00768       baseline = blob_box.bottom();
00769       blob_scale = ClipToRange(kBlnXHeight * 4.0f / (3 * blob_box.height()),
00770                                scale, scale * 1.5f);
00771     } else if (row != NULL && hint != tesseract::OEM_CUBE_ONLY) {
00772       baseline = row->base_line(mid_x);
00773     }
00774     // The image will be 8-bit grey if the input was grey or color. Note that in
00775     // a grey image 0 is black and 255 is white. If the input was binary, then
00776     // the pix will be binary and 0 is white, with 1 being black.
00777     // To tell the difference pixGetDepth() will return 8 or 1.
00778     // The inverse flag will be true iff the word has been determined to be
00779     // white on black, and is independent of whether the pix is 8 bit or 1 bit.
00780     blob->Normalize(block, NULL, NULL, word_middle, baseline, blob_scale,
00781                     blob_scale, 0.0f, final_y_offset, inverse, pix);
00782   }
00783   if (word_denorm != NULL) {
00784     word_denorm->SetupNormalization(block, NULL, NULL, word_middle,
00785                                     input_y_offset, scale, scale,
00786                                     0.0f, final_y_offset);
00787     word_denorm->set_inverse(inverse);
00788     word_denorm->set_pix(pix);
00789   }
00790 }
00791 
00792 // Copies the data and the blobs, but leaves next untouched.
00793 void TWERD::CopyFrom(const TWERD& src) {
00794   Clear();
00795   latin_script = src.latin_script;
00796   for (int b = 0; b < src.blobs.size(); ++b) {
00797     TBLOB* new_blob = new TBLOB(*src.blobs[b]);
00798     blobs.push_back(new_blob);
00799   }
00800 }
00801 
00802 // Deletes owned data.
00803 void TWERD::Clear() {
00804   blobs.delete_data_pointers();
00805   blobs.clear();
00806 }
00807 
00808 // Recomputes the bounding boxes of the blobs.
00809 void TWERD::ComputeBoundingBoxes() {
00810   for (int b = 0; b < blobs.size(); ++b) {
00811     blobs[b]->ComputeBoundingBoxes();
00812   }
00813 }
00814 
00815 TBOX TWERD::bounding_box() const {
00816   TBOX result;
00817   for (int b = 0; b < blobs.size(); ++b) {
00818     TBOX box = blobs[b]->bounding_box();
00819     result += box;
00820   }
00821   return result;
00822 }
00823 
00824 // Merges the blobs from start to end, not including end, and deletes
00825 // the blobs between start and end.
00826 void TWERD::MergeBlobs(int start, int end) {
00827   if (start >= blobs.size() - 1)  return;  // Nothing to do.
00828   TESSLINE* outline = blobs[start]->outlines;
00829   for (int i = start + 1; i < end && i < blobs.size(); ++i) {
00830     TBLOB* next_blob = blobs[i];
00831     // Take the outlines from the next blob.
00832     if (outline == NULL) {
00833       blobs[start]->outlines = next_blob->outlines;
00834       outline = blobs[start]->outlines;
00835     } else {
00836       while (outline->next != NULL)
00837         outline = outline->next;
00838       outline->next = next_blob->outlines;
00839       next_blob->outlines = NULL;
00840     }
00841     // Delete the next blob and move on.
00842     delete next_blob;
00843     blobs[i] = NULL;
00844   }
00845   // Remove dead blobs from the vector.
00846   for (int i = start + 1; i < end && start + 1 < blobs.size(); ++i) {
00847     blobs.remove(start + 1);
00848   }
00849 }
00850 
00851 #ifndef GRAPHICS_DISABLED
00852 void TWERD::plot(ScrollView* window) {
00853   ScrollView::Color color = WERD::NextColor(ScrollView::BLACK);
00854   for (int b = 0; b < blobs.size(); ++b) {
00855     blobs[b]->plot(window, color, ScrollView::BROWN);
00856     color = WERD::NextColor(color);
00857   }
00858 }
00859 #endif  // GRAPHICS_DISABLED
00860 
00861 /**********************************************************************
00862  * blob_origin
00863  *
00864  * Compute the origin of a compound blob, define to be the centre
00865  * of the bounding box.
00866  **********************************************************************/
00867 void blob_origin(TBLOB *blob,       /*blob to compute on */
00868                  TPOINT *origin) {  /*return value */
00869   TBOX bbox = blob->bounding_box();
00870   *origin = (bbox.topleft() + bbox.botright()) / 2;
00871 }
00872 
00873 /**********************************************************************
00874  * divisible_blob
00875  *
00876  * Returns true if the blob contains multiple outlines than can be
00877  * separated using divide_blobs. Sets the location to be used in the
00878  * call to divide_blobs.
00879  **********************************************************************/
00880 bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT* location) {
00881   if (blob->outlines == NULL || blob->outlines->next == NULL)
00882     return false;  // Need at least 2 outlines for it to be possible.
00883   int max_gap = 0;
00884   TPOINT vertical = italic_blob ? kDivisibleVerticalItalic
00885                                 : kDivisibleVerticalUpright;
00886   for (TESSLINE* outline1 = blob->outlines; outline1 != NULL;
00887        outline1 = outline1->next) {
00888     if (outline1->is_hole)
00889       continue;  // Holes do not count as separable.
00890     TPOINT mid_pt1(
00891       static_cast<inT16>((outline1->topleft.x + outline1->botright.x) / 2),
00892       static_cast<inT16>((outline1->topleft.y + outline1->botright.y) / 2));
00893     int mid_prod1 = CROSS(mid_pt1, vertical);
00894     int min_prod1, max_prod1;
00895     outline1->MinMaxCrossProduct(vertical, &min_prod1, &max_prod1);
00896     for (TESSLINE* outline2 = outline1->next; outline2 != NULL;
00897          outline2 = outline2->next) {
00898       if (outline2->is_hole)
00899         continue;  // Holes do not count as separable.
00900       TPOINT mid_pt2(
00901         static_cast<inT16>((outline2->topleft.x + outline2->botright.x) / 2),
00902         static_cast<inT16>((outline2->topleft.y + outline2->botright.y) / 2));
00903       int mid_prod2 = CROSS(mid_pt2, vertical);
00904       int min_prod2, max_prod2;
00905       outline2->MinMaxCrossProduct(vertical, &min_prod2, &max_prod2);
00906       int mid_gap = abs(mid_prod2 - mid_prod1);
00907       int overlap = MIN(max_prod1, max_prod2) - MAX(min_prod1, min_prod2);
00908       if (mid_gap - overlap / 4 > max_gap) {
00909         max_gap = mid_gap - overlap / 4;
00910         *location = mid_pt1;
00911         *location += mid_pt2;
00912         *location /= 2;
00913       }
00914     }
00915   }
00916   // Use the y component of the vertical vector as an approximation to its
00917   // length.
00918   return max_gap > vertical.y;
00919 }
00920 
00921 /**********************************************************************
00922  * divide_blobs
00923  *
00924  * Create two blobs by grouping the outlines in the appropriate blob.
00925  * The outlines that are beyond the location point are moved to the
00926  * other blob.  The ones whose x location is less than that point are
00927  * retained in the original blob.
00928  **********************************************************************/
00929 void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob,
00930                   const TPOINT& location) {
00931   TPOINT vertical = italic_blob ? kDivisibleVerticalItalic
00932                                 : kDivisibleVerticalUpright;
00933   TESSLINE *outline1 = NULL;
00934   TESSLINE *outline2 = NULL;
00935 
00936   TESSLINE *outline = blob->outlines;
00937   blob->outlines = NULL;
00938   int location_prod = CROSS(location, vertical);
00939 
00940   while (outline != NULL) {
00941     TPOINT mid_pt(
00942       static_cast<inT16>((outline->topleft.x + outline->botright.x) / 2),
00943       static_cast<inT16>((outline->topleft.y + outline->botright.y) / 2));
00944     int mid_prod = CROSS(mid_pt, vertical);
00945     if (mid_prod < location_prod) {
00946       // Outline is in left blob.
00947       if (outline1)
00948         outline1->next = outline;
00949       else
00950         blob->outlines = outline;
00951       outline1 = outline;
00952     } else {
00953       // Outline is in right blob.
00954       if (outline2)
00955         outline2->next = outline;
00956       else
00957         other_blob->outlines = outline;
00958       outline2 = outline;
00959     }
00960     outline = outline->next;
00961   }
00962 
00963   if (outline1)
00964     outline1->next = NULL;
00965   if (outline2)
00966     outline2->next = NULL;
00967 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines