Tesseract
3.02
|
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.