Tesseract  3.02
tesseract-ocr/textord/bbgrid.cpp
Go to the documentation of this file.
00001 
00002 // File:        bbgrid.cpp
00003 // Description: Class to hold BLOBNBOXs in a grid for fast access
00004 //              to neighbours.
00005 // Author:      Ray Smith
00006 // Created:     Wed Jun 06 17:22:01 PDT 2007
00007 //
00008 // (C) Copyright 2007, Google Inc.
00009 // Licensed under the Apache License, Version 2.0 (the "License");
00010 // you may not use this file except in compliance with the License.
00011 // You may obtain a copy of the License at
00012 // http://www.apache.org/licenses/LICENSE-2.0
00013 // Unless required by applicable law or agreed to in writing, software
00014 // distributed under the License is distributed on an "AS IS" BASIS,
00015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016 // See the License for the specific language governing permissions and
00017 // limitations under the License.
00018 //
00020 
00021 #include "bbgrid.h"
00022 #include "helpers.h"
00023 #include "ocrblock.h"
00024 
00025 namespace tesseract {
00026 
00028 // BBGrid IMPLEMENTATION.
00030 GridBase::GridBase() {
00031 }
00032 
00033 GridBase::GridBase(int gridsize, const ICOORD& bleft, const ICOORD& tright) {
00034   Init(gridsize, bleft, tright);
00035 }
00036 
00037 GridBase::~GridBase() {
00038 }
00039 
00040 // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
00041 // and bleft, tright are the bounding box of everything to go in it.
00042 void GridBase::Init(int gridsize, const ICOORD& bleft, const ICOORD& tright) {
00043   gridsize_ = gridsize;
00044   bleft_ = bleft;
00045   tright_ = tright;
00046   if (gridsize_ == 0)
00047     gridsize_ = 1;
00048   gridwidth_ = (tright.x() - bleft.x() + gridsize_ - 1) / gridsize_;
00049   gridheight_ = (tright.y() - bleft.y() + gridsize_ - 1) / gridsize_;
00050   gridbuckets_ = gridwidth_ * gridheight_;
00051 }
00052 
00053 // Compute the given grid coordinates from image coords.
00054 void GridBase::GridCoords(int x, int y, int* grid_x, int* grid_y) const {
00055   *grid_x = (x - bleft_.x()) / gridsize_;
00056   *grid_y = (y - bleft_.y()) / gridsize_;
00057   ClipGridCoords(grid_x, grid_y);
00058 }
00059 
00060 // Clip the given grid coordinates to fit within the grid.
00061 void GridBase::ClipGridCoords(int* x, int* y) const {
00062   *x = ClipToRange(*x, 0, gridwidth_ - 1);
00063   *y = ClipToRange(*y, 0, gridheight_ - 1);
00064 }
00065 
00066 IntGrid::IntGrid() {
00067   grid_ = NULL;
00068 }
00069 
00070 IntGrid::IntGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright)
00071   : grid_(NULL) {
00072   Init(gridsize, bleft, tright);
00073 }
00074 
00075 IntGrid::~IntGrid() {
00076   if (grid_ != NULL)
00077     delete [] grid_;
00078 }
00079 
00080 // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
00081 // and bleft, tright are the bounding box of everything to go in it.
00082 void IntGrid::Init(int gridsize, const ICOORD& bleft, const ICOORD& tright) {
00083   GridBase::Init(gridsize, bleft, tright);
00084   if (grid_ != NULL)
00085     delete [] grid_;
00086   grid_ = new int[gridbuckets_];
00087   Clear();
00088 }
00089 
00090 // Clear all the ints in the grid to zero.
00091 void IntGrid::Clear() {
00092   for (int i = 0; i < gridbuckets_; ++i) {
00093     grid_[i] = 0;
00094   }
00095 }
00096 
00097 // Rotate the grid by rotation, keeping cell contents.
00098 // rotation must be a multiple of 90 degrees.
00099 // NOTE: due to partial cells, cell coverage in the rotated grid will be
00100 // inexact. This is why there is no Rotate for the generic BBGrid.
00101 // TODO(rays) investigate fixing this inaccuracy by moving the origin after
00102 // rotation.
00103 void IntGrid::Rotate(const FCOORD& rotation) {
00104   ASSERT_HOST(rotation.x() == 0.0f || rotation.y() == 0.0f);
00105   ICOORD old_bleft(bleft());
00106   ICOORD old_tright(tright());
00107   int old_width = gridwidth();
00108   int old_height = gridheight();
00109   TBOX box(bleft(), tright());
00110   box.rotate(rotation);
00111   int* old_grid = grid_;
00112   grid_ = NULL;
00113   Init(gridsize(), box.botleft(), box.topright());
00114   // Iterate over the old grid, copying data to the rotated position in the new.
00115   int oldi = 0;
00116   FCOORD x_step(rotation);
00117   x_step *= gridsize();
00118   for (int oldy = 0; oldy < old_height; ++oldy) {
00119     FCOORD line_pos(old_bleft.x(), old_bleft.y() + gridsize() * oldy);
00120     line_pos.rotate(rotation);
00121     for (int oldx = 0; oldx < old_width; ++oldx, line_pos += x_step, ++oldi) {
00122       int grid_x, grid_y;
00123       GridCoords(static_cast<int>(line_pos.x() + 0.5),
00124                  static_cast<int>(line_pos.y() + 0.5),
00125                  &grid_x, &grid_y);
00126       grid_[grid_y * gridwidth() + grid_x] = old_grid[oldi];
00127     }
00128   }
00129   delete [] old_grid;
00130 }
00131 
00132 // Returns a new IntGrid containing values equal to the sum of all the
00133 // neighbouring cells. The returned grid must be deleted after use.
00134 // For ease of implementation, edge cells are double counted, to make them
00135 // have the same range as the non-edge cells.
00136 IntGrid* IntGrid::NeighbourhoodSum() const {
00137   IntGrid* sumgrid = new IntGrid(gridsize(), bleft(), tright());
00138   for (int y = 0; y < gridheight(); ++y) {
00139     for (int x = 0; x < gridwidth(); ++x) {
00140       int cell_count = 0;
00141       for (int yoffset = -1; yoffset <= 1; ++yoffset) {
00142         for (int xoffset = -1; xoffset <= 1; ++xoffset) {
00143           int grid_x = x + xoffset;
00144           int grid_y = y + yoffset;
00145           ClipGridCoords(&grid_x, &grid_y);
00146           cell_count += GridCellValue(grid_x, grid_y);
00147         }
00148       }
00149       if (GridCellValue(x, y) > 1)
00150         sumgrid->SetGridCell(x, y, cell_count);
00151     }
00152   }
00153   return sumgrid;
00154 }
00155 
00156 // Returns true if more than half the area of the rect is covered by grid
00157 // cells that are over the theshold.
00158 bool IntGrid::RectMostlyOverThreshold(const TBOX& rect, int threshold) const {
00159   int min_x, min_y, max_x, max_y;
00160   GridCoords(rect.left(), rect.bottom(), &min_x, &min_y);
00161   GridCoords(rect.right(), rect.top(), &max_x, &max_y);
00162   int total_area = 0;
00163   for (int y = min_y; y <= max_y; ++y) {
00164     for (int x = min_x; x <= max_x; ++x) {
00165       int value = GridCellValue(x, y);
00166       if (value > threshold) {
00167         TBOX cell_box(x * gridsize_, y * gridsize_,
00168                       (x + 1) * gridsize_, (y + 1) * gridsize_);
00169         cell_box &= rect;  // This is in-place box intersection.
00170         total_area += cell_box.area();
00171       }
00172     }
00173   }
00174   return total_area * 2 > rect.area();
00175 }
00176 
00177 // Returns true if any cell value in the given rectangle is zero.
00178 bool IntGrid::AnyZeroInRect(const TBOX& rect) const {
00179   int min_x, min_y, max_x, max_y;
00180   GridCoords(rect.left(), rect.bottom(), &min_x, &min_y);
00181   GridCoords(rect.right(), rect.top(), &max_x, &max_y);
00182   for (int y = min_y; y <= max_y; ++y) {
00183     for (int x = min_x; x <= max_x; ++x) {
00184       if (GridCellValue(x, y) == 0)
00185         return true;
00186     }
00187   }
00188   return false;
00189 }
00190 
00191 // Returns a full-resolution binary pix in which each cell over the given
00192 // threshold is filled as a black square. pixDestroy after use.
00193 // Edge cells, which have a zero 4-neighbour, are not marked.
00194 Pix* IntGrid::ThresholdToPix(int threshold) const {
00195   Pix* pix = pixCreate(tright().x() - bleft().x(),
00196                        tright().y() - bleft().y(), 1);
00197   int cellsize = gridsize();
00198   for (int y = 0; y < gridheight(); ++y) {
00199     for (int x = 0; x < gridwidth(); ++x) {
00200       if (GridCellValue(x, y) > threshold &&
00201           GridCellValue(x - 1, y) > 0 && GridCellValue(x + 1, y) > 0 &&
00202               GridCellValue(x, y - 1) > 0 && GridCellValue(x, y + 1) > 0) {
00203         pixRasterop(pix, x * cellsize, tright().y() - ((y + 1) * cellsize),
00204                     cellsize, cellsize, PIX_SET, NULL, 0, 0);
00205       }
00206     }
00207   }
00208   return pix;
00209 }
00210 
00211 // Make a Pix of the correct scaled size for the TraceOutline functions.
00212 Pix* GridReducedPix(const TBOX& box, int gridsize,
00213                     ICOORD bleft, int* left, int* bottom) {
00214   // Compute grid bounds of the outline and pad all round by 1.
00215   int grid_left = (box.left() - bleft.x()) / gridsize - 1;
00216   int grid_bottom = (box.bottom() - bleft.y()) / gridsize - 1;
00217   int grid_right = (box.right() - bleft.x()) / gridsize + 1;
00218   int grid_top = (box.top() - bleft.y()) / gridsize + 1;
00219   *left = grid_left;
00220   *bottom = grid_bottom;
00221   return pixCreate(grid_right - grid_left + 1,
00222                    grid_top - grid_bottom + 1,
00223                    1);
00224 }
00225 
00226 // Helper function to return a scaled Pix with one pixel per grid cell,
00227 // set (black) where the given outline enters the corresponding grid cell,
00228 // and clear where the outline does not touch the grid cell.
00229 // Also returns the grid coords of the bottom-left of the Pix, in *left
00230 // and *bottom, which corresponds to (0, 0) on the Pix.
00231 // Note that the Pix is used upside-down, with (0, 0) being the bottom-left.
00232 Pix* TraceOutlineOnReducedPix(C_OUTLINE* outline, int gridsize,
00233                               ICOORD bleft, int* left, int* bottom) {
00234   TBOX box = outline->bounding_box();
00235   Pix* pix = GridReducedPix(box, gridsize, bleft, left, bottom);
00236   int wpl = pixGetWpl(pix);
00237   l_uint32* data = pixGetData(pix);
00238   int length = outline->pathlength();
00239   ICOORD pos = outline->start_pos();
00240   for (int i = 0; i < length; ++i) {
00241     int grid_x = (pos.x() - bleft.x()) / gridsize - *left;
00242     int grid_y = (pos.y() - bleft.y()) / gridsize - *bottom;
00243     SET_DATA_BIT(data + grid_y * wpl, grid_x);
00244     pos += outline->step(i);
00245   }
00246   return pix;
00247 }
00248 #if 0  // Example code shows how to use TraceOutlineOnReducedPix.
00249   C_OUTLINE_IT ol_it(blob->cblob()->out_list());
00250   int grid_left, grid_bottom;
00251   Pix* pix = TraceOutlineOnReducedPix(ol_it.data(), gridsize_, bleft_,
00252                                       &grid_left, &grid_bottom);
00253   grid->InsertPixPtBBox(grid_left, grid_bottom, pix, blob);
00254   pixDestroy(&pix);
00255 #endif
00256 
00257 // As TraceOutlineOnReducedPix above, but on a BLOCK instead of a C_OUTLINE.
00258 Pix* TraceBlockOnReducedPix(BLOCK* block, int gridsize,
00259                             ICOORD bleft, int* left, int* bottom) {
00260   TBOX box = block->bounding_box();
00261   Pix* pix = GridReducedPix(box, gridsize, bleft, left, bottom);
00262   int wpl = pixGetWpl(pix);
00263   l_uint32* data = pixGetData(pix);
00264   ICOORDELT_IT it(block->poly_block()->points());
00265   for (it.mark_cycle_pt(); !it.cycled_list();) {
00266     ICOORD pos = *it.data();
00267     it.forward();
00268     ICOORD next_pos = *it.data();
00269     ICOORD line_vector = next_pos - pos;
00270     int major, minor;
00271     ICOORD major_step, minor_step;
00272     line_vector.setup_render(&major_step, &minor_step, &major, &minor);
00273     int accumulator = major / 2;
00274     while (pos != next_pos) {
00275       int grid_x = (pos.x() - bleft.x()) / gridsize - *left;
00276       int grid_y = (pos.y() - bleft.y()) / gridsize - *bottom;
00277       SET_DATA_BIT(data + grid_y * wpl, grid_x);
00278       pos += major_step;
00279       accumulator += minor;
00280       if (accumulator >= major) {
00281         accumulator -= major;
00282         pos += minor_step;
00283       }
00284     }
00285   }
00286   return pix;
00287 }
00288 
00289 }  // namespace tesseract.