1 ///////////////////////////////////////////////////////////////////////
2 // 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 //
19 ///////////////////////////////////////////////////////////////////////
20
21 #ifndef TESSERACT_TEXTORD_BBGRID_H__
22 #define TESSERACT_TEXTORD_BBGRID_H__
23
24 #include "clst.h"
25 #include "coutln.h"
26 #include "rect.h"
27 #include "scrollview.h"
28
29 // Some code is dependent upon leptonica. If you don't have it,
30 // you don't get this functionality.
31 #ifdef HAVE_CONFIG_H
32 #include "config_auto.h"
33 #endif
34 #ifdef HAVE_LIBLEPT
35 #include "allheaders.h"
36 #endif
37
38 class BLOCK;
39
40 namespace tesseract {
41
42 #ifdef HAVE_LIBLEPT
43 // Helper function to return a scaled Pix with one pixel per grid cell,
44 // set (black) where the given outline enters the corresponding grid cell,
45 // and clear where the outline does not touch the grid cell.
46 // Also returns the grid coords of the bottom-left of the Pix, in *left
47 // and *bottom, which corresponds to (0, 0) on the Pix.
48 // Note that the Pix is used upside-down, with (0, 0) being the bottom-left.
49 Pix* TraceOutlineOnReducedPix(C_OUTLINE* outline, int gridsize,
50 ICOORD bleft, int* left, int* bottom);
51 // As TraceOutlineOnReducedPix above, but on a BLOCK instead of a C_OUTLINE.
52 Pix* TraceBlockOnReducedPix(BLOCK* block, int gridsize,
53 ICOORD bleft, int* left, int* bottom);
54 #endif
55
56 template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch;
57
58 // The BBGrid class holds C_LISTs of template classes BBC (bounding box class)
59 // in a grid for fast neighbour access.
60 // The BBC class must have a member const TBOX& bounding_box() const.
61 // The BBC class must have been CLISTIZEH'ed elsewhere to make the
62 // list class BBC_CLIST and the iterator BBC_C_IT.
63 // Use of C_LISTs enables BBCs to exist in multiple cells simultaneously.
64 // As a consequence, ownership of BBCs is assumed to be elsewhere and
65 // persistent for at least the life of the BBGrid, or at least until Clear is
66 // called which removes all references to inserted objects without actually
67 // deleting them.
68 // Most uses derive a class from a specific instantiation of BBGrid,
69 // thereby making most of the ugly template notation go away.
70 // The friend class GridSearch, with the same template arguments, is
71 // used to search a grid efficiently in one of several search patterns.
72 template<class BBC, class BBC_CLIST, class BBC_C_IT> class BBGrid {
73 friend class GridSearch<BBC, BBC_CLIST, BBC_C_IT>;
74 public:
75 BBGrid();
76 BBGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright);
77 virtual ~BBGrid();
78
79 // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
80 // and bleft, tright are the bounding box of everything to go in it.
81 void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
82
83 // Empty all the lists but leave the grid itself intact.
84 void Clear();
85 // Deallocate the data in the lists but otherwise leave the lists and the grid
86 // intact.
87 void ClearGridData(void (*free_method)(BBC*));
88
89 // Simple accessors.
gridsize()90 int gridsize() const {
91 return gridsize_;
92 }
gridwidth()93 int gridwidth() const {
94 return gridwidth_;
95 }
gridheight()96 int gridheight() const {
97 return gridheight_;
98 }
bleft()99 ICOORD bleft() const {
100 return bleft_;
101 }
tright()102 ICOORD tright() const {
103 return tright_;
104 }
105
106 // Insert a bbox into the appropriate place in the grid.
107 // If h_spread, then all cells covered horizontally by the box are
108 // used, otherwise, just the bottom-left. Similarly for v_spread.
109 // WARNING: InsertBBox may invalidate an active GridSearch. Call
110 // RepositionIterator() on any GridSearches that are active on this grid.
111 void InsertBBox(bool h_spread, bool v_spread, BBC* bbox);
112 #ifdef HAVE_LIBLEPT
113 // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
114 // which each pixel corresponds to a grid cell, insert a bbox into every
115 // place in the grid where the corresponding pixel is 1. The Pix is handled
116 // upside-down to match the Tesseract coordinate system. (As created by
117 // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
118 // (0, 0) in the pix corresponds to (left, bottom) in the
119 // grid (in grid coords), and the pix works up the grid from there.
120 // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
121 // RepositionIterator() on any GridSearches that are active on this grid.
122 void InsertPixPtBBox(int left, int bottom, Pix* pix, BBC* bbox);
123 #endif
124 // Remove the bbox from the grid.
125 // WARNING: Any GridSearch operating on this grid could be invalidated!
126 // If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
127 void RemoveBBox(BBC* bbox);
128
129 // Compute the given grid coordinates from image coords.
130 void GridCoords(int x, int y, int* grid_x, int* grid_y);
131
132 // Clip the given grid coordinates to fit within the grid.
133 void ClipGridCoords(int* x, int* y);
134
135 // Make a window of an appropriate size to display things in the grid.
136 ScrollView* MakeWindow(int x, int y, const char* window_name);
137
138 // Display the bounding boxes of the BLOBNBOXes in this grid.
139 // Use of this function requires an additional member of the BBC class:
140 // ScrollView::Color BBC::BoxColor() const.
141 void DisplayBoxes(ScrollView* window);
142
143 // ASSERT_HOST that every cell contains no more than one copy of each entry.
144 void AssertNoDuplicates();
145
146 // Handle a click event in a display window.
147 virtual void HandleClick(int x, int y);
148
149 protected:
150 int gridsize_; // Pixel size of each grid cell.
151 int gridwidth_; // Size of the grid in cells.
152 int gridheight_;
153 int gridbuckets_; // Total cells in grid.
154 ICOORD bleft_; // Pixel coords of bottom-left of grid.
155 ICOORD tright_; // Pixel coords of top-right of grid.
156 BBC_CLIST* grid_; // 2-d array of CLISTS of BBC elements.
157
158 private:
159 };
160
161 // The GridSearch class enables neighbourhood searching on a BBGrid.
162 template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch {
163 public:
GridSearch(BBGrid<BBC,BBC_CLIST,BBC_C_IT> * grid)164 GridSearch(BBGrid<BBC, BBC_CLIST, BBC_C_IT>* grid)
165 : grid_(grid), previous_return_(NULL), next_return_(NULL) {
166 }
167
168 // Get the grid x, y coords of the most recently returned BBC.
GridX()169 int GridX() const {
170 return x_;
171 }
GridY()172 int GridY() const {
173 return y_;
174 }
175 // Apart from full search, all other searches return a box several
176 // times if the box is inserted with h_spread or v_spread.
177 // This method will return true for only one occurrance of each box
178 // that was inserted with both h_spread and v_spread as true.
179 // It will usually return false for boxes that were not inserted with
180 // both h_spread=true and v_spread=true
ReturnedSeedElement()181 bool ReturnedSeedElement() const {
182 TBOX box = previous_return_->bounding_box();
183 int x_center = (box.left()+box.right())/2;
184 int y_center = (box.top()+box.bottom())/2;
185 int grid_x, grid_y;
186 grid_->GridCoords(x_center, y_center, &grid_x, &grid_y);
187 return (x_ == grid_x) && (y_ == grid_y);
188 }
189
190 // Various searching iterations... Note that these iterations
191 // all share data members, so you can't run more than one iteration
192 // in parallel in a single GridSearch instance, but multiple instances
193 // can search the same BBGrid in parallel.
194 // Note that all the searches can return blobs that may not exactly
195 // match the search conditions, since they return everything in the
196 // covered grid cells. It is up to the caller to check for
197 // appropriateness.
198
199 // Start a new full search. Will iterate all stored blobs, from the top.
200 // If the blobs have been inserted using InsertBBox, (not InsertPixPtBBox)
201 // then the full search guarantees to return each blob in the grid once.
202 // Other searches may return a blob more than once if they have been
203 // inserted using h_spread or v_spread.
204 void StartFullSearch();
205 // Return the next bbox in the search or NULL if done.
206 BBC* NextFullSearch();
207
208 // Start a new radius search. Will search in a spiral upto a
209 // given maximum radius in grid cells from the given center in pixels.
210 void StartRadSearch(int x, int y, int max_radius);
211 // Return the next bbox in the radius search or NULL if the
212 // maximum radius has been reached.
213 BBC* NextRadSearch();
214
215 // Start a new left or right-looking search. Will search to the side
216 // for a box that vertically overlaps the given vertical line segment.
217 // CAVEAT: This search returns all blobs from the cells to the side
218 // of the start, and somewhat below, since there is no guarantee
219 // that there may not be a taller object in a lower cell. The
220 // blobs returned will include all those that vertically overlap and
221 // are no more than twice as high, but may also include some that do
222 // not overlap and some that are more than twice as high.
223 void StartSideSearch(int x, int ymin, int ymax);
224 // Return the next bbox in the side search or NULL if the
225 // edge has been reached. Searches left to right or right to left
226 // according to the flag.
227 BBC* NextSideSearch(bool right_to_left);
228
229 // Start a vertical-looking search. Will search up or down
230 // for a box that horizontally overlaps the given line segment.
231 void StartVerticalSearch(int xmin, int xmax, int y);
232 // Return the next bbox in the vertical search or NULL if the
233 // edge has been reached. Searches top to bottom or bottom to top
234 // according to the flag.
235 BBC* NextVerticalSearch(bool top_to_bottom);
236
237 // Start a rectangular search. Will search for a box that overlaps the
238 // given rectangle.
239 void StartRectSearch(const TBOX& rect);
240 // Return the next bbox in the rectangular search or NULL if complete.
241 BBC* NextRectSearch();
242
243 // Remove the last returned BBC. Will not invalidate this. May invalidate
244 // any other concurrent GridSearch on the same grid. If any others are
245 // in use, call RepositionIterator on those, to continue without harm.
246 void RemoveBBox();
247 void RepositionIterator();
248
249 private:
250 // Factored out helper to start a search.
251 void CommonStart(int x, int y);
252 // Factored out helper to complete a next search.
253 BBC* CommonNext();
254 // Factored out final return when search is exhausted.
255 BBC* CommonEnd();
256 // Factored out function to set the iterator to the current x_, y_
257 // grid coords and mark the cycle pt.
258 void SetIterator();
259
260 private:
261 // The grid we are searching.
262 BBGrid<BBC, BBC_CLIST, BBC_C_IT>* grid_;
263 // For executing a search. The different search algorithms use these in
264 // different ways, but most use x_origin_ and y_origin_ as the start position.
265 int x_origin_;
266 int y_origin_;
267 int max_radius_;
268 int radius_;
269 int rad_index_;
270 int rad_dir_;
271 TBOX rect_;
272 int x_; // The current location in grid coords, of the current search.
273 int y_;
274 BBC* previous_return_; // Previous return from Next*.
275 BBC* next_return_; // Current value of it_.data() used for repositioning.
276 // An iterator over the list at (x_, y_) in the grid_.
277 BBC_C_IT it_;
278 };
279
280 // Sort function to sort a BBC by bounding_box().left().
281 template<class BBC>
SortByBoxLeft(const void * void1,const void * void2)282 int SortByBoxLeft(const void* void1, const void* void2) {
283 // The void*s are actually doubly indirected, so get rid of one level.
284 const BBC* p1 = *reinterpret_cast<const BBC* const *>(void1);
285 const BBC* p2 = *reinterpret_cast<const BBC* const *>(void2);
286 return p1->bounding_box().left() - p2->bounding_box().left();
287 }
288
289 ///////////////////////////////////////////////////////////////////////
290 // BBGrid IMPLEMENTATION.
291 ///////////////////////////////////////////////////////////////////////
292 template<class BBC, class BBC_CLIST, class BBC_C_IT>
BBGrid()293 BBGrid<BBC, BBC_CLIST, BBC_C_IT>::BBGrid() : grid_(NULL) {
294 }
295
296 template<class BBC, class BBC_CLIST, class BBC_C_IT>
BBGrid(int gridsize,const ICOORD & bleft,const ICOORD & tright)297 BBGrid<BBC, BBC_CLIST, BBC_C_IT>::BBGrid(
298 int gridsize, const ICOORD& bleft, const ICOORD& tright)
299 : grid_(NULL) {
300 Init(gridsize, bleft, tright);
301 }
302
303 template<class BBC, class BBC_CLIST, class BBC_C_IT>
~BBGrid()304 BBGrid<BBC, BBC_CLIST, BBC_C_IT>::~BBGrid() {
305 if (grid_ != NULL)
306 delete [] grid_;
307 }
308
309 // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
310 // and bleft, tright are the bounding box of everything to go in it.
311 template<class BBC, class BBC_CLIST, class BBC_C_IT>
Init(int gridsize,const ICOORD & bleft,const ICOORD & tright)312 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::Init(int gridsize,
313 const ICOORD& bleft,
314 const ICOORD& tright) {
315 gridsize_ = gridsize;
316 bleft_ = bleft;
317 tright_ = tright;
318 if (grid_ != NULL)
319 delete [] grid_;
320 if (gridsize_ == 0)
321 gridsize_ = 1;
322 gridwidth_ = (tright.x() - bleft.x() + gridsize_ - 1) / gridsize_;
323 gridheight_ = (tright.y() - bleft.y() + gridsize_ - 1) / gridsize_;
324 gridbuckets_ = gridwidth_ * gridheight_;
325 grid_ = new BBC_CLIST[gridbuckets_];
326 }
327
328 // Clear all lists, but leave the array of lists present.
329 template<class BBC, class BBC_CLIST, class BBC_C_IT>
Clear()330 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::Clear() {
331 for (int i = 0; i < gridbuckets_; ++i) {
332 grid_[i].shallow_clear();
333 }
334 }
335
336 // Deallocate the data in the lists but otherwise leave the lists and the grid
337 // intact.
338 template<class BBC, class BBC_CLIST, class BBC_C_IT>
ClearGridData(void (* free_method)(BBC *))339 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::ClearGridData(
340 void (*free_method)(BBC*)) {
341 if (grid_ == NULL) return;
342 GridSearch<BBC, BBC_CLIST, BBC_C_IT> search(this);
343 search.StartFullSearch();
344 BBC* bb;
345 BBC_CLIST bb_list;
346 BBC_C_IT it(&bb_list);
347 while ((bb = search.NextFullSearch()) != NULL) {
348 it.add_after_then_move(bb);
349 }
350 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
351 free_method(it.data());
352 }
353 }
354
355 // Insert a bbox into the appropriate place in the grid.
356 // If h_spread, then all cells covered horizontally by the box are
357 // used, otherwise, just the bottom-left. Similarly for v_spread.
358 // WARNING: InsertBBox may invalidate an active GridSearch. Call
359 // RepositionIterator() on any GridSearches that are active on this grid.
360 template<class BBC, class BBC_CLIST, class BBC_C_IT>
InsertBBox(bool h_spread,bool v_spread,BBC * bbox)361 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::InsertBBox(bool h_spread, bool v_spread,
362 BBC* bbox) {
363 TBOX box = bbox->bounding_box();
364 int start_x, start_y, end_x, end_y;
365 GridCoords(box.left(), box.bottom(), &start_x, &start_y);
366 GridCoords(box.right(), box.top(), &end_x, &end_y);
367 if (!h_spread)
368 end_x = start_x;
369 if (!v_spread)
370 end_y = start_y;
371 int grid_index = start_y * gridwidth_;
372 for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
373 for (int x = start_x; x <= end_x; ++x) {
374 grid_[grid_index + x].add_sorted(SortByBoxLeft<BBC>, true, bbox);
375 }
376 }
377 }
378
379 #ifdef HAVE_LIBLEPT
380 // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
381 // which each pixel corresponds to a grid cell, insert a bbox into every
382 // place in the grid where the corresponding pixel is 1. The Pix is handled
383 // upside-down to match the Tesseract coordinate system. (As created by
384 // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
385 // (0, 0) in the pix corresponds to (left, bottom) in the
386 // grid (in grid coords), and the pix works up the grid from there.
387 // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
388 // RepositionIterator() on any GridSearches that are active on this grid.
389 template<class BBC, class BBC_CLIST, class BBC_C_IT>
InsertPixPtBBox(int left,int bottom,Pix * pix,BBC * bbox)390 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::InsertPixPtBBox(int left, int bottom,
391 Pix* pix, BBC* bbox) {
392 int width = pixGetWidth(pix);
393 int height = pixGetHeight(pix);
394 for (int y = 0; y < height; ++y) {
395 l_uint32* data = pixGetData(pix) + y * pixGetWpl(pix);
396 for (int x = 0; x < width; ++x) {
397 if (GET_DATA_BIT(data, x)) {
398 grid_[(bottom + y) * gridwidth_ + x + left].
399 add_sorted(SortByBoxLeft<BBC>, true, bbox);
400 }
401 }
402 }
403 }
404 #endif
405
406 // Remove the bbox from the grid.
407 // WARNING: Any GridSearch operating on this grid could be invalidated!
408 // If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
409 template<class BBC, class BBC_CLIST, class BBC_C_IT>
RemoveBBox(BBC * bbox)410 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::RemoveBBox(BBC* bbox) {
411 TBOX box = bbox->bounding_box();
412 int start_x, start_y, end_x, end_y;
413 GridCoords(box.left(), box.bottom(), &start_x, &start_y);
414 GridCoords(box.right(), box.top(), &end_x, &end_y);
415 int grid_index = start_y * gridwidth_;
416 for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
417 for (int x = start_x; x <= end_x; ++x) {
418 BBC_C_IT it(&grid_[grid_index + x]);
419 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
420 if (it.data() == bbox)
421 it.extract();
422 }
423 }
424 }
425 }
426
427 // Compute the given grid coordinates from image coords.
428 template<class BBC, class BBC_CLIST, class BBC_C_IT>
GridCoords(int x,int y,int * grid_x,int * grid_y)429 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::GridCoords(int x, int y,
430 int* grid_x, int* grid_y) {
431 *grid_x = (x - bleft_.x()) / gridsize_;
432 *grid_y = (y - bleft_.y()) / gridsize_;
433 ClipGridCoords(grid_x, grid_y);
434 }
435
436 // Clip the given grid coordinates to fit within the grid.
437 template<class BBC, class BBC_CLIST, class BBC_C_IT>
ClipGridCoords(int * x,int * y)438 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::ClipGridCoords(int* x, int* y) {
439 if (*x < 0) *x = 0;
440 if (*x >= gridwidth_) *x = gridwidth_ - 1;
441 if (*y < 0) *y = 0;
442 if (*y >= gridheight_) *y = gridheight_ - 1;
443 }
444
445 template<class G> class TabEventHandler : public SVEventHandler {
446 public:
TabEventHandler(G * grid)447 explicit TabEventHandler(G* grid) : grid_(grid) {
448 }
Notify(const SVEvent * sv_event)449 void Notify(const SVEvent* sv_event) {
450 if (sv_event->type == SVET_CLICK) {
451 grid_->HandleClick(sv_event->x, sv_event->y);
452 }
453 }
454 private:
455 G* grid_;
456 };
457
458 // Make a window of an appropriate size to display things in the grid.
459 // Position the window at the given x,y.
460 template<class BBC, class BBC_CLIST, class BBC_C_IT>
MakeWindow(int x,int y,const char * window_name)461 ScrollView* BBGrid<BBC, BBC_CLIST, BBC_C_IT>::MakeWindow(
462 int x, int y, const char* window_name) {
463 ScrollView* tab_win = NULL;
464 #ifndef GRAPHICS_DISABLED
465 tab_win = new ScrollView(window_name, x, y,
466 tright_.x() - bleft_.x(),
467 tright_.y() - bleft_.y(),
468 tright_.x() - bleft_.x(),
469 tright_.y() - bleft_.y(),
470 true);
471 TabEventHandler<BBGrid<BBC, BBC_CLIST, BBC_C_IT> >* handler =
472 new TabEventHandler<BBGrid<BBC, BBC_CLIST, BBC_C_IT> >(this);
473 tab_win->AddEventHandler(handler);
474 tab_win->Pen(ScrollView::GREY);
475 tab_win->Rectangle(0, 0, tright_.x(), tright_.y());
476 #endif
477 return tab_win;
478 }
479
480 // Create a window at (x,y) and display the bounding boxes of the
481 // BLOBNBOXes in this grid.
482 // Use of this function requires an additional member of the BBC class:
483 // ScrollView::Color BBC::BoxColor() const.
484 template<class BBC, class BBC_CLIST, class BBC_C_IT>
DisplayBoxes(ScrollView * tab_win)485 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::DisplayBoxes(ScrollView* tab_win) {
486 #ifndef GRAPHICS_DISABLED
487 tab_win->Pen(ScrollView::BLUE);
488 tab_win->Brush(ScrollView::NONE);
489
490 // For every bbox in the grid, display it.
491 GridSearch<BBC, BBC_CLIST, BBC_C_IT> gsearch(this);
492 gsearch.StartFullSearch();
493 BBC* bbox;
494 while ((bbox = gsearch.NextFullSearch()) != NULL) {
495 TBOX box = bbox->bounding_box();
496 int left_x = box.left();
497 int right_x = box.right();
498 int top_y = box.top();
499 int bottom_y = box.bottom();
500 ScrollView::Color box_color = bbox->BoxColor();
501 tab_win->Pen(box_color);
502 tab_win->Rectangle(left_x, bottom_y, right_x, top_y);
503 }
504 tab_win->Update();
505 #endif
506 }
507
508 // ASSERT_HOST that every cell contains no more than one copy of each entry.
509 template<class BBC, class BBC_CLIST, class BBC_C_IT>
AssertNoDuplicates()510 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::AssertNoDuplicates() {
511 // Process all grid cells.
512 for (int i = gridwidth_ * gridheight_ - 1; i >= 0; --i) {
513 // Iterate over all elements excent the last.
514 for (BBC_C_IT it(&grid_[i]); !it.at_last(); it.forward()) {
515 BBC* ptr = it.data();
516 BBC_C_IT it2(it);
517 // None of the rest of the elements in the list should equal ptr.
518 for (it2.forward(); !it2.at_first(); it2.forward()) {
519 ASSERT_HOST(it2.data() != ptr);
520 }
521 }
522 }
523 }
524
525 // Handle a click event in a display window.
526 template<class BBC, class BBC_CLIST, class BBC_C_IT>
HandleClick(int x,int y)527 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::HandleClick(int x, int y) {
528 tprintf("Click at (%d, %d)\n", x, y);
529 }
530
531 ///////////////////////////////////////////////////////////////////////
532 // GridSearch IMPLEMENTATION.
533 ///////////////////////////////////////////////////////////////////////
534
535 // Start a new full search. Will iterate all stored blobs.
536 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartFullSearch()537 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartFullSearch() {
538 // Full search uses x_ and y_ as the current grid
539 // cell being searched.
540 CommonStart(grid_->bleft_.x(), grid_->tright_.y());
541 }
542
543 // Return the next bbox in the search or NULL if done.
544 // The other searches will return a box that overlaps the grid cell
545 // thereby duplicating boxes, but NextFullSearch only returns each box once.
546 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextFullSearch()547 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextFullSearch() {
548 int x;
549 int y;
550 do {
551 while (it_.cycled_list()) {
552 ++x_;
553 if (x_ >= grid_->gridwidth_) {
554 --y_;
555 if (y_ < 0)
556 return CommonEnd();
557 x_ = 0;
558 }
559 SetIterator();
560 }
561 CommonNext();
562 TBOX box = previous_return_->bounding_box();
563 grid_->GridCoords(box.left(), box.bottom(), &x, &y);
564 } while (x != x_ || y != y_);
565 return previous_return_;
566 }
567
568 // Start a new radius search.
569 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartRadSearch(int x,int y,int max_radius)570 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartRadSearch(int x, int y,
571 int max_radius) {
572 // Rad search uses x_origin_ and y_origin_ as the center of the circle.
573 // The radius_ is the radius of the (diamond-shaped) circle and
574 // rad_index/rad_dir_ combine to determine the position around it.
575 max_radius_ = max_radius;
576 radius_ = 0;
577 rad_index_ = 0;
578 rad_dir_ = 3;
579 CommonStart(x, y);
580 }
581
582 // Return the next bbox in the radius search or NULL if the
583 // maximum radius has been reached.
584 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextRadSearch()585 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextRadSearch() {
586 while (it_.cycled_list()) {
587 ++rad_index_;
588 if (rad_index_ >= radius_) {
589 ++rad_dir_;
590 rad_index_ = 0;
591 if (rad_dir_ >= 4) {
592 ++radius_;
593 if (radius_ > max_radius_)
594 return CommonEnd();
595 rad_dir_ = 0;
596 }
597 }
598 ICOORD offset = C_OUTLINE::chain_step(rad_dir_);
599 offset *= radius_ - rad_index_;
600 offset += C_OUTLINE::chain_step(rad_dir_ + 1) * rad_index_;
601 x_ = x_origin_ + offset.x();
602 y_ = y_origin_ + offset.y();
603 if (x_ >= 0 && x_ < grid_->gridwidth_ &&
604 y_ >= 0 && y_ < grid_->gridheight_)
605 SetIterator();
606 }
607 return CommonNext();
608 }
609
610 // Start a new left or right-looking search. Will search to the side
611 // for a box that vertically overlaps the given vertical line segment.
612 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartSideSearch(int x,int ymin,int ymax)613 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartSideSearch(int x,
614 int ymin, int ymax) {
615 // Right search records the x in x_origin_, the ymax in y_origin_
616 // and the size of the vertical strip to search in radius_.
617 // To guarantee finding overlapping objects of upto twice the
618 // given size, double the height.
619 radius_ = ((ymax - ymin) * 2 + grid_->gridsize_ - 1) / grid_->gridsize_;
620 rad_index_ = 0;
621 CommonStart(x, ymax);
622 }
623
624 // Return the next bbox in the side search or NULL if the
625 // edge has been reached. Searches left to right or right to left
626 // according to the flag.
627 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextSideSearch(bool right_to_left)628 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextSideSearch(bool right_to_left) {
629 while (it_.cycled_list()) {
630 ++rad_index_;
631 if (rad_index_ > radius_) {
632 if (right_to_left)
633 --x_;
634 else
635 ++x_;
636 rad_index_ = 0;
637 if (x_ < 0 || x_ >= grid_->gridwidth_)
638 return CommonEnd();
639 }
640 y_ = y_origin_ - rad_index_;
641 if (y_ >= 0 && y_ < grid_->gridheight_)
642 SetIterator();
643 }
644 return CommonNext();
645 }
646
647 // Start a vertical-looking search. Will search up or down
648 // for a box that horizontally overlaps the given line segment.
649 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartVerticalSearch(int xmin,int xmax,int y)650 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartVerticalSearch(int xmin,
651 int xmax,
652 int y) {
653 // Right search records the xmin in x_origin_, the y in y_origin_
654 // and the size of the horizontal strip to search in radius_.
655 radius_ = (xmax - xmin + grid_->gridsize_ - 1) / grid_->gridsize_;
656 rad_index_ = 0;
657 CommonStart(xmin, y);
658 }
659
660 // Return the next bbox in the vertical search or NULL if the
661 // edge has been reached. Searches top to bottom or bottom to top
662 // according to the flag.
663 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextVerticalSearch(bool top_to_bottom)664 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextVerticalSearch(
665 bool top_to_bottom) {
666 while (it_.cycled_list()) {
667 ++rad_index_;
668 if (rad_index_ > radius_) {
669 if (top_to_bottom)
670 --y_;
671 else
672 ++y_;
673 rad_index_ = 0;
674 if (y_ < 0 || y_ >= grid_->gridheight_)
675 return CommonEnd();
676 }
677 x_ = x_origin_ + rad_index_;
678 if (x_ >= 0 && x_ < grid_->gridwidth_)
679 SetIterator();
680 }
681 return CommonNext();
682 }
683
684 // Start a rectangular search. Will search for a box that overlaps the
685 // given rectangle.
686 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartRectSearch(const TBOX & rect)687 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartRectSearch(const TBOX& rect) {
688 // Rect search records the xmin in x_origin_, the ymin in y_origin_
689 // and the xmax in max_radius_.
690 // The search proceeds left to right, top to bottom.
691 rect_ = rect;
692 CommonStart(rect.left(), rect.top());
693 grid_->GridCoords(rect.right(), rect.bottom(), // - rect.height(),
694 &max_radius_, &y_origin_);
695 }
696
697 // Return the next bbox in the rectangular search or NULL if complete.
698 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextRectSearch()699 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextRectSearch() {
700 while (it_.cycled_list()) {
701 ++x_;
702 if (x_ > max_radius_) {
703 --y_;
704 x_ = x_origin_;
705 if (y_ < y_origin_)
706 return CommonEnd();
707 }
708 SetIterator();
709 }
710 return CommonNext();
711 }
712
713 // Remove the last returned BBC. Will not invalidate this. May invalidate
714 // any other concurrent GridSearch on the same grid. If any others are
715 // in use, call RepositionIterator on those, to continue without harm.
716 template<class BBC, class BBC_CLIST, class BBC_C_IT>
RemoveBBox()717 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::RemoveBBox() {
718 if (previous_return_ != NULL) {
719 // Remove all instances of previous_return_ from the list, so the iterator
720 // remains valid after removal from the rest of the grid cells.
721 // if previous_return_ is not on the list, then it has been removed already.
722 BBC* prev_data = NULL;
723 BBC* new_previous_return = NULL;
724 it_.move_to_first();
725 for (it_.mark_cycle_pt(); !it_.cycled_list();) {
726 if (it_.data() == previous_return_) {
727 new_previous_return = prev_data;
728 it_.extract();
729 it_.forward();
730 next_return_ = it_.cycled_list() ? NULL : it_.data();
731 } else {
732 prev_data = it_.data();
733 it_.forward();
734 }
735 }
736 grid_->RemoveBBox(previous_return_);
737 previous_return_ = new_previous_return;
738 RepositionIterator();
739 }
740 }
741
742 template<class BBC, class BBC_CLIST, class BBC_C_IT>
RepositionIterator()743 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::RepositionIterator() {
744 // Reset the iterator back to one past the previous return.
745 // If the previous_return_ is no longer in the list, then
746 // next_return_ serves as a backup.
747 it_.move_to_first();
748 for (it_.mark_cycle_pt(); !it_.cycled_list(); it_.forward()) {
749 if (it_.data() == previous_return_ ||
750 it_.data_relative(1) == next_return_) {
751 CommonNext();
752 return;
753 }
754 }
755 // We ran off the end of the list. Move to a new cell next time.
756 previous_return_ = NULL;
757 next_return_ = NULL;
758 }
759
760 // Factored out helper to start a search.
761 template<class BBC, class BBC_CLIST, class BBC_C_IT>
CommonStart(int x,int y)762 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonStart(int x, int y) {
763 grid_->GridCoords(x, y, &x_origin_, &y_origin_);
764 x_ = x_origin_;
765 y_ = y_origin_;
766 SetIterator();
767 previous_return_ = NULL;
768 next_return_ = it_.empty() ? NULL : it_.data();
769 }
770
771 // Factored out helper to complete a next search.
772 template<class BBC, class BBC_CLIST, class BBC_C_IT>
CommonNext()773 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonNext() {
774 previous_return_ = it_.data();
775 it_.forward();
776 next_return_ = it_.cycled_list() ? NULL : it_.data();
777 return previous_return_;
778 }
779
780 // Factored out final return when search is exhausted.
781 template<class BBC, class BBC_CLIST, class BBC_C_IT>
CommonEnd()782 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonEnd() {
783 previous_return_ = NULL;
784 next_return_ = NULL;
785 return NULL;
786 }
787
788 // Factored out function to set the iterator to the current x_, y_
789 // grid coords and mark the cycle pt.
790 template<class BBC, class BBC_CLIST, class BBC_C_IT>
SetIterator()791 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::SetIterator() {
792 it_= &(grid_->grid_[y_ * grid_->gridwidth_ + x_]);
793 it_.mark_cycle_pt();
794 }
795
796 } // namespace tesseract.
797
798 #endif // TESSERACT_TEXTORD_BBGRID_H__
799