tesseract  4.00.00dev
bbgrid.h
Go to the documentation of this file.
1 // File: bbgrid.h
3 // Description: Class to hold BLOBNBOXs in a grid for fast access
4 // to neighbours.
5 // Author: Ray Smith
6 // Created: Wed Jun 06 17:22:01 PDT 2007
7 //
8 // (C) Copyright 2007, Google Inc.
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
20 
21 #ifndef TESSERACT_TEXTORD_BBGRID_H_
22 #define TESSERACT_TEXTORD_BBGRID_H_
23 
24 #include <unordered_set>
25 
26 #include "clst.h"
27 #include "coutln.h"
28 #include "rect.h"
29 #include "scrollview.h"
30 
31 #include "allheaders.h"
32 
33 class BLOCK;
34 
35 namespace tesseract {
36 
37 // Helper function to return a scaled Pix with one pixel per grid cell,
38 // set (black) where the given outline enters the corresponding grid cell,
39 // and clear where the outline does not touch the grid cell.
40 // Also returns the grid coords of the bottom-left of the Pix, in *left
41 // and *bottom, which corresponds to (0, 0) on the Pix.
42 // Note that the Pix is used upside-down, with (0, 0) being the bottom-left.
43 Pix* TraceOutlineOnReducedPix(C_OUTLINE* outline, int gridsize,
44  ICOORD bleft, int* left, int* bottom);
45 // As TraceOutlineOnReducedPix above, but on a BLOCK instead of a C_OUTLINE.
46 Pix* TraceBlockOnReducedPix(BLOCK* block, int gridsize,
47  ICOORD bleft, int* left, int* bottom);
48 
49 template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch;
50 
51 // The GridBase class is the base class for BBGrid and IntGrid.
52 // It holds the geometry and scale of the grid.
53 class GridBase {
54  public:
55  GridBase();
56  GridBase(int gridsize, const ICOORD& bleft, const ICOORD& tright);
57  virtual ~GridBase();
58 
59  // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
60  // and bleft, tright are the bounding box of everything to go in it.
61  void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
62 
63  // Simple accessors.
64  int gridsize() const {
65  return gridsize_;
66  }
67  int gridwidth() const {
68  return gridwidth_;
69  }
70  int gridheight() const {
71  return gridheight_;
72  }
73  const ICOORD& bleft() const {
74  return bleft_;
75  }
76  const ICOORD& tright() const {
77  return tright_;
78  }
79  // Compute the given grid coordinates from image coords.
80  void GridCoords(int x, int y, int* grid_x, int* grid_y) const;
81 
82  // Clip the given grid coordinates to fit within the grid.
83  void ClipGridCoords(int* x, int* y) const;
84 
85  protected:
86  // TODO(rays) Make these private and migrate to the accessors in subclasses.
87  int gridsize_; // Pixel size of each grid cell.
88  int gridwidth_; // Size of the grid in cells.
90  int gridbuckets_; // Total cells in grid.
91  ICOORD bleft_; // Pixel coords of bottom-left of grid.
92  ICOORD tright_; // Pixel coords of top-right of grid.
93 
94  private:
95 };
96 
97 // The IntGrid maintains a single int for each cell in a grid.
98 class IntGrid : public GridBase {
99  public:
100  IntGrid();
101  IntGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright);
102  virtual ~IntGrid();
103 
104  // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
105  // and bleft, tright are the bounding box of everything to go in it.
106  void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
107 
108  // Clear all the ints in the grid to zero.
109  void Clear();
110 
111  // Rotate the grid by rotation, keeping cell contents.
112  // rotation must be a multiple of 90 degrees.
113  // NOTE: due to partial cells, cell coverage in the rotated grid will be
114  // inexact. This is why there is no Rotate for the generic BBGrid.
115  void Rotate(const FCOORD& rotation);
116 
117  // Returns a new IntGrid containing values equal to the sum of all the
118  // neighbouring cells. The returned grid must be deleted after use.
119  IntGrid* NeighbourhoodSum() const;
120 
121  int GridCellValue(int grid_x, int grid_y) const {
122  ClipGridCoords(&grid_x, &grid_y);
123  return grid_[grid_y * gridwidth_ + grid_x];
124  }
125  void SetGridCell(int grid_x, int grid_y, int value) {
126  ASSERT_HOST(grid_x >= 0 && grid_x < gridwidth());
127  ASSERT_HOST(grid_y >= 0 && grid_y < gridheight());
128  grid_[grid_y * gridwidth_ + grid_x] = value;
129  }
130  // Returns true if more than half the area of the rect is covered by grid
131  // cells that are over the threshold.
132  bool RectMostlyOverThreshold(const TBOX& rect, int threshold) const;
133 
134  // Returns true if any cell value in the given rectangle is zero.
135  bool AnyZeroInRect(const TBOX& rect) const;
136 
137  // Returns a full-resolution binary pix in which each cell over the given
138  // threshold is filled as a black square. pixDestroy after use.
139  Pix* ThresholdToPix(int threshold) const;
140 
141  private:
142  int* grid_; // 2-d array of ints.
143 };
144 
145 // The BBGrid class holds C_LISTs of template classes BBC (bounding box class)
146 // in a grid for fast neighbour access.
147 // The BBC class must have a member const TBOX& bounding_box() const.
148 // The BBC class must have been CLISTIZEH'ed elsewhere to make the
149 // list class BBC_CLIST and the iterator BBC_C_IT.
150 // Use of C_LISTs enables BBCs to exist in multiple cells simultaneously.
151 // As a consequence, ownership of BBCs is assumed to be elsewhere and
152 // persistent for at least the life of the BBGrid, or at least until Clear is
153 // called which removes all references to inserted objects without actually
154 // deleting them.
155 // Most uses derive a class from a specific instantiation of BBGrid,
156 // thereby making most of the ugly template notation go away.
157 // The friend class GridSearch, with the same template arguments, is
158 // used to search a grid efficiently in one of several search patterns.
159 template<class BBC, class BBC_CLIST, class BBC_C_IT> class BBGrid
160  : public GridBase {
161  friend class GridSearch<BBC, BBC_CLIST, BBC_C_IT>;
162  public:
163  BBGrid();
164  BBGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright);
165  virtual ~BBGrid();
166 
167  // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
168  // and bleft, tright are the bounding box of everything to go in it.
169  void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
170 
171  // Empty all the lists but leave the grid itself intact.
172  void Clear();
173  // Deallocate the data in the lists but otherwise leave the lists and the grid
174  // intact.
175  void ClearGridData(void (*free_method)(BBC*));
176 
177  // Insert a bbox into the appropriate place in the grid.
178  // If h_spread, then all cells covered horizontally by the box are
179  // used, otherwise, just the bottom-left. Similarly for v_spread.
180  // WARNING: InsertBBox may invalidate an active GridSearch. Call
181  // RepositionIterator() on any GridSearches that are active on this grid.
182  void InsertBBox(bool h_spread, bool v_spread, BBC* bbox);
183 
184  // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
185  // which each pixel corresponds to a grid cell, insert a bbox into every
186  // place in the grid where the corresponding pixel is 1. The Pix is handled
187  // upside-down to match the Tesseract coordinate system. (As created by
188  // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
189  // (0, 0) in the pix corresponds to (left, bottom) in the
190  // grid (in grid coords), and the pix works up the grid from there.
191  // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
192  // RepositionIterator() on any GridSearches that are active on this grid.
193  void InsertPixPtBBox(int left, int bottom, Pix* pix, BBC* bbox);
194 
195  // Remove the bbox from the grid.
196  // WARNING: Any GridSearch operating on this grid could be invalidated!
197  // If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
198  void RemoveBBox(BBC* bbox);
199 
200  // Returns true if the given rectangle has no overlapping elements.
201  bool RectangleEmpty(const TBOX& rect);
202 
203  // Returns an IntGrid showing the number of elements in each cell.
204  // Returned IntGrid must be deleted after use.
205  IntGrid* CountCellElements();
206 
207  // Make a window of an appropriate size to display things in the grid.
208  ScrollView* MakeWindow(int x, int y, const char* window_name);
209 
210  // Display the bounding boxes of the BLOBNBOXes in this grid.
211  // Use of this function requires an additional member of the BBC class:
212  // ScrollView::Color BBC::BoxColor() const.
213  void DisplayBoxes(ScrollView* window);
214 
215  // ASSERT_HOST that every cell contains no more than one copy of each entry.
216  void AssertNoDuplicates();
217 
218  // Handle a click event in a display window.
219  virtual void HandleClick(int x, int y);
220 
221  protected:
222  BBC_CLIST* grid_; // 2-d array of CLISTS of BBC elements.
223 
224  private:
225 };
226 
227 // Hash functor for generic pointers.
228 template<typename T> struct PtrHash {
229  size_t operator()(const T* ptr) const {
230  return reinterpret_cast<size_t>(ptr) / sizeof(T);
231  }
232 };
233 
234 
235 // The GridSearch class enables neighbourhood searching on a BBGrid.
236 template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch {
237  public:
239  : grid_(grid), unique_mode_(false),
240  previous_return_(NULL), next_return_(NULL) {
241  }
242 
243  // Get the grid x, y coords of the most recently returned BBC.
244  int GridX() const {
245  return x_;
246  }
247  int GridY() const {
248  return y_;
249  }
250 
251  // Sets the search mode to return a box only once.
252  // Efficiency warning: Implementation currently uses a squared-order
253  // search in the number of returned elements. Use only where a small
254  // number of elements are spread over a wide area, eg ColPartitions.
255  void SetUniqueMode(bool mode) {
256  unique_mode_ = mode;
257  }
258  // TODO(rays) Replace calls to ReturnedSeedElement with SetUniqueMode.
259  // It only works if the search includes the bottom-left corner.
260  // Apart from full search, all other searches return a box several
261  // times if the box is inserted with h_spread or v_spread.
262  // This method will return true for only one occurrence of each box
263  // that was inserted with both h_spread and v_spread as true.
264  // It will usually return false for boxes that were not inserted with
265  // both h_spread=true and v_spread=true
266  bool ReturnedSeedElement() const {
267  TBOX box = previous_return_->bounding_box();
268  int x_center = (box.left()+box.right())/2;
269  int y_center = (box.top()+box.bottom())/2;
270  int grid_x, grid_y;
271  grid_->GridCoords(x_center, y_center, &grid_x, &grid_y);
272  return (x_ == grid_x) && (y_ == grid_y);
273  }
274 
275  // Various searching iterations... Note that these iterations
276  // all share data members, so you can't run more than one iteration
277  // in parallel in a single GridSearch instance, but multiple instances
278  // can search the same BBGrid in parallel.
279  // Note that all the searches can return blobs that may not exactly
280  // match the search conditions, since they return everything in the
281  // covered grid cells. It is up to the caller to check for
282  // appropriateness.
283  // TODO(rays) NextRectSearch only returns valid elements. Make the other
284  // searches test before return also and remove the tests from code
285  // that uses GridSearch.
286 
287  // Start a new full search. Will iterate all stored blobs, from the top.
288  // If the blobs have been inserted using InsertBBox, (not InsertPixPtBBox)
289  // then the full search guarantees to return each blob in the grid once.
290  // Other searches may return a blob more than once if they have been
291  // inserted using h_spread or v_spread.
292  void StartFullSearch();
293  // Return the next bbox in the search or NULL if done.
294  BBC* NextFullSearch();
295 
296  // Start a new radius search. Will search in a spiral up to a
297  // given maximum radius in grid cells from the given center in pixels.
298  void StartRadSearch(int x, int y, int max_radius);
299  // Return the next bbox in the radius search or NULL if the
300  // maximum radius has been reached.
301  BBC* NextRadSearch();
302 
303  // Start a new left or right-looking search. Will search to the side
304  // for a box that vertically overlaps the given vertical line segment.
305  // CAVEAT: This search returns all blobs from the cells to the side
306  // of the start, and somewhat below, since there is no guarantee
307  // that there may not be a taller object in a lower cell. The
308  // blobs returned will include all those that vertically overlap and
309  // are no more than twice as high, but may also include some that do
310  // not overlap and some that are more than twice as high.
311  void StartSideSearch(int x, int ymin, int ymax);
312  // Return the next bbox in the side search or NULL if the
313  // edge has been reached. Searches left to right or right to left
314  // according to the flag.
315  BBC* NextSideSearch(bool right_to_left);
316 
317  // Start a vertical-looking search. Will search up or down
318  // for a box that horizontally overlaps the given line segment.
319  void StartVerticalSearch(int xmin, int xmax, int y);
320  // Return the next bbox in the vertical search or NULL if the
321  // edge has been reached. Searches top to bottom or bottom to top
322  // according to the flag.
323  BBC* NextVerticalSearch(bool top_to_bottom);
324 
325  // Start a rectangular search. Will search for a box that overlaps the
326  // given rectangle.
327  void StartRectSearch(const TBOX& rect);
328  // Return the next bbox in the rectangular search or NULL if complete.
329  BBC* NextRectSearch();
330 
331  // Remove the last returned BBC. Will not invalidate this. May invalidate
332  // any other concurrent GridSearch on the same grid. If any others are
333  // in use, call RepositionIterator on those, to continue without harm.
334  void RemoveBBox();
335  void RepositionIterator();
336 
337  private:
338  // Factored out helper to start a search.
339  void CommonStart(int x, int y);
340  // Factored out helper to complete a next search.
341  BBC* CommonNext();
342  // Factored out final return when search is exhausted.
343  BBC* CommonEnd();
344  // Factored out function to set the iterator to the current x_, y_
345  // grid coords and mark the cycle pt.
346  void SetIterator();
347 
348  private:
349  // The grid we are searching.
351  // For executing a search. The different search algorithms use these in
352  // different ways, but most use x_origin_ and y_origin_ as the start position.
353  int x_origin_;
354  int y_origin_;
355  int max_radius_;
356  int radius_;
357  int rad_index_;
358  int rad_dir_;
359  TBOX rect_;
360  int x_; // The current location in grid coords, of the current search.
361  int y_;
362  bool unique_mode_;
363  BBC* previous_return_; // Previous return from Next*.
364  BBC* next_return_; // Current value of it_.data() used for repositioning.
365  // An iterator over the list at (x_, y_) in the grid_.
366  BBC_C_IT it_;
367  // Set of unique returned elements used when unique_mode_ is true.
368  std::unordered_set<BBC*, PtrHash<BBC> > returns_;
369 };
370 
371 // Sort function to sort a BBC by bounding_box().left().
372 template<class BBC>
373 int SortByBoxLeft(const void* void1, const void* void2) {
374  // The void*s are actually doubly indirected, so get rid of one level.
375  const BBC* p1 = *static_cast<const BBC* const *>(void1);
376  const BBC* p2 = *static_cast<const BBC* const *>(void2);
377  int result = p1->bounding_box().left() - p2->bounding_box().left();
378  if (result != 0)
379  return result;
380  result = p1->bounding_box().right() - p2->bounding_box().right();
381  if (result != 0)
382  return result;
383  result = p1->bounding_box().bottom() - p2->bounding_box().bottom();
384  if (result != 0)
385  return result;
386  return p1->bounding_box().top() - p2->bounding_box().top();
387 }
388 
389 // Sort function to sort a BBC by bounding_box().right() in right-to-left order.
390 template<class BBC>
391 int SortRightToLeft(const void* void1, const void* void2) {
392  // The void*s are actually doubly indirected, so get rid of one level.
393  const BBC* p1 = *static_cast<const BBC* const *>(void1);
394  const BBC* p2 = *static_cast<const BBC* const *>(void2);
395  int result = p2->bounding_box().right() - p1->bounding_box().right();
396  if (result != 0)
397  return result;
398  result = p2->bounding_box().left() - p1->bounding_box().left();
399  if (result != 0)
400  return result;
401  result = p1->bounding_box().bottom() - p2->bounding_box().bottom();
402  if (result != 0)
403  return result;
404  return p1->bounding_box().top() - p2->bounding_box().top();
405 }
406 
407 // Sort function to sort a BBC by bounding_box().bottom().
408 template<class BBC>
409 int SortByBoxBottom(const void* void1, const void* void2) {
410  // The void*s are actually doubly indirected, so get rid of one level.
411  const BBC* p1 = *static_cast<const BBC* const *>(void1);
412  const BBC* p2 = *static_cast<const BBC* const *>(void2);
413  int result = p1->bounding_box().bottom() - p2->bounding_box().bottom();
414  if (result != 0)
415  return result;
416  result = p1->bounding_box().top() - p2->bounding_box().top();
417  if (result != 0)
418  return result;
419  result = p1->bounding_box().left() - p2->bounding_box().left();
420  if (result != 0)
421  return result;
422  return p1->bounding_box().right() - p2->bounding_box().right();
423 }
424 
426 // BBGrid IMPLEMENTATION.
428 template<class BBC, class BBC_CLIST, class BBC_C_IT>
430 }
431 
432 template<class BBC, class BBC_CLIST, class BBC_C_IT>
434  int gridsize, const ICOORD& bleft, const ICOORD& tright)
435  : grid_(NULL) {
436  Init(gridsize, bleft, tright);
437 }
438 
439 template<class BBC, class BBC_CLIST, class BBC_C_IT>
441  if (grid_ != NULL)
442  delete [] grid_;
443 }
444 
445 // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
446 // and bleft, tright are the bounding box of everything to go in it.
447 template<class BBC, class BBC_CLIST, class BBC_C_IT>
449  const ICOORD& bleft,
450  const ICOORD& tright) {
451  GridBase::Init(gridsize, bleft, tright);
452  if (grid_ != NULL)
453  delete [] grid_;
454  grid_ = new BBC_CLIST[gridbuckets_];
455 }
456 
457 // Clear all lists, but leave the array of lists present.
458 template<class BBC, class BBC_CLIST, class BBC_C_IT>
460  for (int i = 0; i < gridbuckets_; ++i) {
461  grid_[i].shallow_clear();
462  }
463 }
464 
465 // Deallocate the data in the lists but otherwise leave the lists and the grid
466 // intact.
467 template<class BBC, class BBC_CLIST, class BBC_C_IT>
469  void (*free_method)(BBC*)) {
470  if (grid_ == NULL) return;
472  search.StartFullSearch();
473  BBC* bb;
474  BBC_CLIST bb_list;
475  BBC_C_IT it(&bb_list);
476  while ((bb = search.NextFullSearch()) != NULL) {
477  it.add_after_then_move(bb);
478  }
479  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
480  free_method(it.data());
481  }
482 }
483 
484 // Insert a bbox into the appropriate place in the grid.
485 // If h_spread, then all cells covered horizontally by the box are
486 // used, otherwise, just the bottom-left. Similarly for v_spread.
487 // WARNING: InsertBBox may invalidate an active GridSearch. Call
488 // RepositionIterator() on any GridSearches that are active on this grid.
489 template<class BBC, class BBC_CLIST, class BBC_C_IT>
490 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::InsertBBox(bool h_spread, bool v_spread,
491  BBC* bbox) {
492  TBOX box = bbox->bounding_box();
493  int start_x, start_y, end_x, end_y;
494  GridCoords(box.left(), box.bottom(), &start_x, &start_y);
495  GridCoords(box.right(), box.top(), &end_x, &end_y);
496  if (!h_spread)
497  end_x = start_x;
498  if (!v_spread)
499  end_y = start_y;
500  int grid_index = start_y * gridwidth_;
501  for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
502  for (int x = start_x; x <= end_x; ++x) {
503  grid_[grid_index + x].add_sorted(SortByBoxLeft<BBC>, true, bbox);
504  }
505  }
506 }
507 
508 // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
509 // which each pixel corresponds to a grid cell, insert a bbox into every
510 // place in the grid where the corresponding pixel is 1. The Pix is handled
511 // upside-down to match the Tesseract coordinate system. (As created by
512 // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
513 // (0, 0) in the pix corresponds to (left, bottom) in the
514 // grid (in grid coords), and the pix works up the grid from there.
515 // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
516 // RepositionIterator() on any GridSearches that are active on this grid.
517 template<class BBC, class BBC_CLIST, class BBC_C_IT>
519  Pix* pix, BBC* bbox) {
520  int width = pixGetWidth(pix);
521  int height = pixGetHeight(pix);
522  for (int y = 0; y < height; ++y) {
523  l_uint32* data = pixGetData(pix) + y * pixGetWpl(pix);
524  for (int x = 0; x < width; ++x) {
525  if (GET_DATA_BIT(data, x)) {
526  grid_[(bottom + y) * gridwidth_ + x + left].
527  add_sorted(SortByBoxLeft<BBC>, true, bbox);
528  }
529  }
530  }
531 }
532 
533 // Remove the bbox from the grid.
534 // WARNING: Any GridSearch operating on this grid could be invalidated!
535 // If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
536 template<class BBC, class BBC_CLIST, class BBC_C_IT>
538  TBOX box = bbox->bounding_box();
539  int start_x, start_y, end_x, end_y;
540  GridCoords(box.left(), box.bottom(), &start_x, &start_y);
541  GridCoords(box.right(), box.top(), &end_x, &end_y);
542  int grid_index = start_y * gridwidth_;
543  for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
544  for (int x = start_x; x <= end_x; ++x) {
545  BBC_C_IT it(&grid_[grid_index + x]);
546  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
547  if (it.data() == bbox)
548  it.extract();
549  }
550  }
551  }
552 }
553 
554 // Returns true if the given rectangle has no overlapping elements.
555 template<class BBC, class BBC_CLIST, class BBC_C_IT>
558  rsearch.StartRectSearch(rect);
559  return rsearch.NextRectSearch() == NULL;
560 }
561 
562 // Returns an IntGrid showing the number of elements in each cell.
563 // Returned IntGrid must be deleted after use.
564 template<class BBC, class BBC_CLIST, class BBC_C_IT>
566  IntGrid* intgrid = new IntGrid(gridsize(), bleft(), tright());
567  for (int y = 0; y < gridheight(); ++y) {
568  for (int x = 0; x < gridwidth(); ++x) {
569  int cell_count = grid_[y * gridwidth() + x].length();
570  intgrid->SetGridCell(x, y, cell_count);
571  }
572  }
573  return intgrid;
574 }
575 
576 
577 template<class G> class TabEventHandler : public SVEventHandler {
578  public:
579  explicit TabEventHandler(G* grid) : grid_(grid) {
580  }
581  void Notify(const SVEvent* sv_event) {
582  if (sv_event->type == SVET_CLICK) {
583  grid_->HandleClick(sv_event->x, sv_event->y);
584  }
585  }
586  private:
587  G* grid_;
588 };
589 
590 // Make a window of an appropriate size to display things in the grid.
591 // Position the window at the given x,y.
592 template<class BBC, class BBC_CLIST, class BBC_C_IT>
594  int x, int y, const char* window_name) {
595  ScrollView* tab_win = NULL;
596 #ifndef GRAPHICS_DISABLED
597  tab_win = new ScrollView(window_name, x, y,
598  tright_.x() - bleft_.x(),
599  tright_.y() - bleft_.y(),
600  tright_.x() - bleft_.x(),
601  tright_.y() - bleft_.y(),
602  true);
605  tab_win->AddEventHandler(handler);
606  tab_win->Pen(ScrollView::GREY);
607  tab_win->Rectangle(0, 0, tright_.x() - bleft_.x(), tright_.y() - bleft_.y());
608 #endif
609  return tab_win;
610 }
611 
612 // Create a window at (x,y) and display the bounding boxes of the
613 // BLOBNBOXes in this grid.
614 // Use of this function requires an additional member of the BBC class:
615 // ScrollView::Color BBC::BoxColor() const.
616 template<class BBC, class BBC_CLIST, class BBC_C_IT>
618 #ifndef GRAPHICS_DISABLED
619  tab_win->Pen(ScrollView::BLUE);
620  tab_win->Brush(ScrollView::NONE);
621 
622  // For every bbox in the grid, display it.
624  gsearch.StartFullSearch();
625  BBC* bbox;
626  while ((bbox = gsearch.NextFullSearch()) != NULL) {
627  const TBOX& box = bbox->bounding_box();
628  int left_x = box.left();
629  int right_x = box.right();
630  int top_y = box.top();
631  int bottom_y = box.bottom();
632  ScrollView::Color box_color = bbox->BoxColor();
633  tab_win->Pen(box_color);
634  tab_win->Rectangle(left_x, bottom_y, right_x, top_y);
635  }
636  tab_win->Update();
637 #endif
638 }
639 
640 // ASSERT_HOST that every cell contains no more than one copy of each entry.
641 template<class BBC, class BBC_CLIST, class BBC_C_IT>
643  // Process all grid cells.
644  for (int i = gridwidth_ * gridheight_ - 1; i >= 0; --i) {
645  // Iterate over all elements excent the last.
646  for (BBC_C_IT it(&grid_[i]); !it.at_last(); it.forward()) {
647  BBC* ptr = it.data();
648  BBC_C_IT it2(it);
649  // None of the rest of the elements in the list should equal ptr.
650  for (it2.forward(); !it2.at_first(); it2.forward()) {
651  ASSERT_HOST(it2.data() != ptr);
652  }
653  }
654  }
655 }
656 
657 // Handle a click event in a display window.
658 template<class BBC, class BBC_CLIST, class BBC_C_IT>
660  tprintf("Click at (%d, %d)\n", x, y);
661 }
662 
664 // GridSearch IMPLEMENTATION.
666 
667 // Start a new full search. Will iterate all stored blobs.
668 template<class BBC, class BBC_CLIST, class BBC_C_IT>
670  // Full search uses x_ and y_ as the current grid
671  // cell being searched.
672  CommonStart(grid_->bleft_.x(), grid_->tright_.y());
673 }
674 
675 // Return the next bbox in the search or NULL if done.
676 // The other searches will return a box that overlaps the grid cell
677 // thereby duplicating boxes, but NextFullSearch only returns each box once.
678 template<class BBC, class BBC_CLIST, class BBC_C_IT>
680  int x;
681  int y;
682  do {
683  while (it_.cycled_list()) {
684  ++x_;
685  if (x_ >= grid_->gridwidth_) {
686  --y_;
687  if (y_ < 0)
688  return CommonEnd();
689  x_ = 0;
690  }
691  SetIterator();
692  }
693  CommonNext();
694  TBOX box = previous_return_->bounding_box();
695  grid_->GridCoords(box.left(), box.bottom(), &x, &y);
696  } while (x != x_ || y != y_);
697  return previous_return_;
698 }
699 
700 // Start a new radius search.
701 template<class BBC, class BBC_CLIST, class BBC_C_IT>
703  int max_radius) {
704  // Rad search uses x_origin_ and y_origin_ as the center of the circle.
705  // The radius_ is the radius of the (diamond-shaped) circle and
706  // rad_index/rad_dir_ combine to determine the position around it.
707  max_radius_ = max_radius;
708  radius_ = 0;
709  rad_index_ = 0;
710  rad_dir_ = 3;
711  CommonStart(x, y);
712 }
713 
714 // Return the next bbox in the radius search or NULL if the
715 // maximum radius has been reached.
716 template<class BBC, class BBC_CLIST, class BBC_C_IT>
718  do {
719  while (it_.cycled_list()) {
720  ++rad_index_;
721  if (rad_index_ >= radius_) {
722  ++rad_dir_;
723  rad_index_ = 0;
724  if (rad_dir_ >= 4) {
725  ++radius_;
726  if (radius_ > max_radius_)
727  return CommonEnd();
728  rad_dir_ = 0;
729  }
730  }
731  ICOORD offset = C_OUTLINE::chain_step(rad_dir_);
732  offset *= radius_ - rad_index_;
733  offset += C_OUTLINE::chain_step(rad_dir_ + 1) * rad_index_;
734  x_ = x_origin_ + offset.x();
735  y_ = y_origin_ + offset.y();
736  if (x_ >= 0 && x_ < grid_->gridwidth_ &&
737  y_ >= 0 && y_ < grid_->gridheight_)
738  SetIterator();
739  }
740  CommonNext();
741  } while (unique_mode_ && returns_.find(previous_return_) != returns_.end());
742  if (unique_mode_)
743  returns_.insert(previous_return_);
744  return previous_return_;
745 }
746 
747 // Start a new left or right-looking search. Will search to the side
748 // for a box that vertically overlaps the given vertical line segment.
749 template<class BBC, class BBC_CLIST, class BBC_C_IT>
751  int ymin, int ymax) {
752  // Right search records the x in x_origin_, the ymax in y_origin_
753  // and the size of the vertical strip to search in radius_.
754  // To guarantee finding overlapping objects of up to twice the
755  // given size, double the height.
756  radius_ = ((ymax - ymin) * 2 + grid_->gridsize_ - 1) / grid_->gridsize_;
757  rad_index_ = 0;
758  CommonStart(x, ymax);
759 }
760 
761 // Return the next bbox in the side search or NULL if the
762 // edge has been reached. Searches left to right or right to left
763 // according to the flag.
764 template<class BBC, class BBC_CLIST, class BBC_C_IT>
766  do {
767  while (it_.cycled_list()) {
768  ++rad_index_;
769  if (rad_index_ > radius_) {
770  if (right_to_left)
771  --x_;
772  else
773  ++x_;
774  rad_index_ = 0;
775  if (x_ < 0 || x_ >= grid_->gridwidth_)
776  return CommonEnd();
777  }
778  y_ = y_origin_ - rad_index_;
779  if (y_ >= 0 && y_ < grid_->gridheight_)
780  SetIterator();
781  }
782  CommonNext();
783  } while (unique_mode_ && returns_.find(previous_return_) != returns_.end());
784  if (unique_mode_)
785  returns_.insert(previous_return_);
786  return previous_return_;
787 }
788 
789 // Start a vertical-looking search. Will search up or down
790 // for a box that horizontally overlaps the given line segment.
791 template<class BBC, class BBC_CLIST, class BBC_C_IT>
793  int xmax,
794  int y) {
795  // Right search records the xmin in x_origin_, the y in y_origin_
796  // and the size of the horizontal strip to search in radius_.
797  radius_ = (xmax - xmin + grid_->gridsize_ - 1) / grid_->gridsize_;
798  rad_index_ = 0;
799  CommonStart(xmin, y);
800 }
801 
802 // Return the next bbox in the vertical search or NULL if the
803 // edge has been reached. Searches top to bottom or bottom to top
804 // according to the flag.
805 template<class BBC, class BBC_CLIST, class BBC_C_IT>
807  bool top_to_bottom) {
808  do {
809  while (it_.cycled_list()) {
810  ++rad_index_;
811  if (rad_index_ > radius_) {
812  if (top_to_bottom)
813  --y_;
814  else
815  ++y_;
816  rad_index_ = 0;
817  if (y_ < 0 || y_ >= grid_->gridheight_)
818  return CommonEnd();
819  }
820  x_ = x_origin_ + rad_index_;
821  if (x_ >= 0 && x_ < grid_->gridwidth_)
822  SetIterator();
823  }
824  CommonNext();
825  } while (unique_mode_ && returns_.find(previous_return_) != returns_.end());
826  if (unique_mode_)
827  returns_.insert(previous_return_);
828  return previous_return_;
829 }
830 
831 // Start a rectangular search. Will search for a box that overlaps the
832 // given rectangle.
833 template<class BBC, class BBC_CLIST, class BBC_C_IT>
835  // Rect search records the xmin in x_origin_, the ymin in y_origin_
836  // and the xmax in max_radius_.
837  // The search proceeds left to right, top to bottom.
838  rect_ = rect;
839  CommonStart(rect.left(), rect.top());
840  grid_->GridCoords(rect.right(), rect.bottom(), // - rect.height(),
841  &max_radius_, &y_origin_);
842 }
843 
844 // Return the next bbox in the rectangular search or NULL if complete.
845 template<class BBC, class BBC_CLIST, class BBC_C_IT>
847  do {
848  while (it_.cycled_list()) {
849  ++x_;
850  if (x_ > max_radius_) {
851  --y_;
852  x_ = x_origin_;
853  if (y_ < y_origin_)
854  return CommonEnd();
855  }
856  SetIterator();
857  }
858  CommonNext();
859  } while (!rect_.overlap(previous_return_->bounding_box()) ||
860  (unique_mode_ && returns_.find(previous_return_) != returns_.end()));
861  if (unique_mode_)
862  returns_.insert(previous_return_);
863  return previous_return_;
864 }
865 
866 // Remove the last returned BBC. Will not invalidate this. May invalidate
867 // any other concurrent GridSearch on the same grid. If any others are
868 // in use, call RepositionIterator on those, to continue without harm.
869 template<class BBC, class BBC_CLIST, class BBC_C_IT>
871  if (previous_return_ != NULL) {
872  // Remove all instances of previous_return_ from the list, so the iterator
873  // remains valid after removal from the rest of the grid cells.
874  // if previous_return_ is not on the list, then it has been removed already.
875  BBC* prev_data = NULL;
876  BBC* new_previous_return = NULL;
877  it_.move_to_first();
878  for (it_.mark_cycle_pt(); !it_.cycled_list();) {
879  if (it_.data() == previous_return_) {
880  new_previous_return = prev_data;
881  it_.extract();
882  it_.forward();
883  next_return_ = it_.cycled_list() ? NULL : it_.data();
884  } else {
885  prev_data = it_.data();
886  it_.forward();
887  }
888  }
889  grid_->RemoveBBox(previous_return_);
890  previous_return_ = new_previous_return;
891  RepositionIterator();
892  }
893 }
894 
895 template<class BBC, class BBC_CLIST, class BBC_C_IT>
897  // Something was deleted, so we have little choice but to clear the
898  // returns list.
899  returns_.clear();
900  // Reset the iterator back to one past the previous return.
901  // If the previous_return_ is no longer in the list, then
902  // next_return_ serves as a backup.
903  it_.move_to_first();
904  // Special case, the first element was removed and reposition
905  // iterator was called. In this case, the data is fine, but the
906  // cycle point is not. Detect it and return.
907  if (!it_.empty() && it_.data() == next_return_) {
908  it_.mark_cycle_pt();
909  return;
910  }
911  for (it_.mark_cycle_pt(); !it_.cycled_list(); it_.forward()) {
912  if (it_.data() == previous_return_ ||
913  it_.data_relative(1) == next_return_) {
914  CommonNext();
915  return;
916  }
917  }
918  // We ran off the end of the list. Move to a new cell next time.
919  previous_return_ = NULL;
920  next_return_ = NULL;
921 }
922 
923 // Factored out helper to start a search.
924 template<class BBC, class BBC_CLIST, class BBC_C_IT>
926  grid_->GridCoords(x, y, &x_origin_, &y_origin_);
927  x_ = x_origin_;
928  y_ = y_origin_;
929  SetIterator();
930  previous_return_ = NULL;
931  next_return_ = it_.empty() ? NULL : it_.data();
932  returns_.clear();
933 }
934 
935 // Factored out helper to complete a next search.
936 template<class BBC, class BBC_CLIST, class BBC_C_IT>
938  previous_return_ = it_.data();
939  it_.forward();
940  next_return_ = it_.cycled_list() ? NULL : it_.data();
941  return previous_return_;
942 }
943 
944 // Factored out final return when search is exhausted.
945 template<class BBC, class BBC_CLIST, class BBC_C_IT>
947  previous_return_ = NULL;
948  next_return_ = NULL;
949  return NULL;
950 }
951 
952 // Factored out function to set the iterator to the current x_, y_
953 // grid coords and mark the cycle pt.
954 template<class BBC, class BBC_CLIST, class BBC_C_IT>
956  it_= &(grid_->grid_[y_ * grid_->gridwidth_ + x_]);
957  it_.mark_cycle_pt();
958 }
959 
960 } // namespace tesseract.
961 
962 #endif // TESSERACT_TEXTORD_BBGRID_H_
SVEventType type
Definition: scrollview.h:64
size_t operator()(const T *ptr) const
Definition: bbgrid.h:229
int GridY() const
Definition: bbgrid.h:247
Definition: points.h:189
ICOORD tright_
Definition: bbgrid.h:92
bool ReturnedSeedElement() const
Definition: bbgrid.h:266
void Brush(Color color)
Definition: scrollview.cpp:732
virtual ~GridBase()
Definition: bbgrid.cpp:37
GridSearch(BBGrid< BBC, BBC_CLIST, BBC_C_IT > *grid)
Definition: bbgrid.h:238
const ICOORD & bleft() const
Definition: bbgrid.h:73
inT16 x() const
access function
Definition: points.h:52
#define tprintf(...)
Definition: tprintf.h:31
static ICOORD chain_step(int chaindir)
Definition: coutln.cpp:1064
int gridsize() const
Definition: bbgrid.h:64
voidpf uLong offset
Definition: ioapi.h:42
int gridheight() const
Definition: bbgrid.h:70
int gridwidth() const
Definition: bbgrid.h:67
int SortByBoxBottom(const void *void1, const void *void2)
Definition: bbgrid.h:409
#define ASSERT_HOST(x)
Definition: errcode.h:84
inT16 left() const
Definition: rect.h:68
void SetUniqueMode(bool mode)
Definition: bbgrid.h:255
LIST search(LIST list, void *key, int_compare is_equal)
Definition: oldlist.cpp:406
int SortByBoxLeft(const void *void1, const void *void2)
Definition: bbgrid.h:373
BBC * NextFullSearch()
Definition: bbgrid.h:679
void Rectangle(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:606
const char int mode
Definition: ioapi.h:38
Pix * TraceBlockOnReducedPix(BLOCK *block, int gridsize, ICOORD bleft, int *left, int *bottom)
Definition: bbgrid.cpp:258
static void Update()
Definition: scrollview.cpp:715
inT16 y() const
access_function
Definition: points.h:56
const ICOORD & tright() const
Definition: bbgrid.h:76
int y
Definition: scrollview.h:67
void AddEventHandler(SVEventHandler *listener)
Add an Event Listener to this ScrollView Window.
Definition: scrollview.cpp:418
int x
Definition: scrollview.h:66
Pix * TraceOutlineOnReducedPix(C_OUTLINE *outline, int gridsize, ICOORD bleft, int *left, int *bottom)
Definition: bbgrid.cpp:232
inT16 top() const
Definition: rect.h:54
Definition: rect.h:30
int GridCellValue(int grid_x, int grid_y) const
Definition: bbgrid.h:121
void Init(int gridsize, const ICOORD &bleft, const ICOORD &tright)
Definition: bbgrid.cpp:42
void Notify(const SVEvent *sv_event)
Definition: bbgrid.h:581
BBC * NextRectSearch()
Definition: bbgrid.h:846
inT16 right() const
Definition: rect.h:75
void StartFullSearch()
Definition: bbgrid.h:669
inT16 bottom() const
Definition: rect.h:61
void StartRectSearch(const TBOX &rect)
Definition: bbgrid.h:834
BBC_CLIST * grid_
Definition: bbgrid.h:222
void SetGridCell(int grid_x, int grid_y, int value)
Definition: bbgrid.h:125
int SortRightToLeft(const void *void1, const void *void2)
Definition: bbgrid.h:391
void ClipGridCoords(int *x, int *y) const
Definition: bbgrid.cpp:61
int GridX() const
Definition: bbgrid.h:244
Definition: ocrblock.h:30
void Pen(Color color)
Definition: scrollview.cpp:726
integer coordinate
Definition: points.h:30
void GridCoords(int x, int y, int *grid_x, int *grid_y) const
Definition: bbgrid.cpp:54