Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 00017 /* 00018 * maze.c 00019 * 00020 * This is a game with a pedagogical slant. A maze is represented 00021 * by a binary image. The ON pixels (fg) are walls. The goal is 00022 * to navigate on OFF pixels (bg), using Manhattan steps 00023 * (N, S, E, W), between arbitrary start and end positions. 00024 * The problem is thus to find the shortest route between two points 00025 * in a binary image that are 4-connected in the bg. This is done 00026 * with a breadth-first search, implemented with a queue. 00027 * We also use a queue of pointers to generate the maze (image). 00028 * 00029 * PIX *generateBinaryMaze() 00030 * static MAZEEL *mazeelCreate() 00031 * 00032 * PIX *pixSearchBinaryMaze() 00033 * static l_int32 localSearchForBackground() 00034 * 00035 * Generalizing a maze to a grayscale image, the search is 00036 * now for the "shortest" or least cost path, for some given 00037 * cost function. 00038 * 00039 * PIX *pixSearchGrayMaze() 00040 * 00041 * 00042 * Elegant method for finding largest white (or black) rectangle 00043 * in an image. 00044 * 00045 * l_int32 pixFindLargestRectangle() 00046 */ 00047 00048 #include <string.h> 00049 #ifdef _WIN32 00050 #include <stdlib.h> 00051 #include <time.h> 00052 #endif /* _WIN32 */ 00053 #include "allheaders.h" 00054 00055 static const l_int32 MIN_MAZE_WIDTH = 50; 00056 static const l_int32 MIN_MAZE_HEIGHT = 50; 00057 00058 static const l_float32 DEFAULT_WALL_PROBABILITY = 0.65; 00059 static const l_float32 DEFAULT_ANISOTROPY_RATIO = 0.25; 00060 00061 enum { /* direction from parent to newly created element */ 00062 START_LOC = 0, 00063 DIR_NORTH = 1, 00064 DIR_SOUTH = 2, 00065 DIR_WEST = 3, 00066 DIR_EAST = 4 00067 }; 00068 00069 struct MazeElement { 00070 l_float32 distance; 00071 l_int32 x; 00072 l_int32 y; 00073 l_uint32 val; /* value of maze pixel at this location */ 00074 l_int32 dir; /* direction from parent to child */ 00075 }; 00076 typedef struct MazeElement MAZEEL; 00077 00078 00079 static MAZEEL *mazeelCreate(l_int32 x, l_int32 y, l_int32 dir); 00080 static l_int32 localSearchForBackground(PIX *pix, l_int32 *px, 00081 l_int32 *py, l_int32 maxrad); 00082 00083 #ifndef NO_CONSOLE_IO 00084 #define DEBUG_PATH 0 00085 #define DEBUG_MAZE 0 00086 #endif /* ~NO_CONSOLE_IO */ 00087 00088 00089 /*---------------------------------------------------------------------* 00090 * Binary maze generation as cellular automaton * 00091 *---------------------------------------------------------------------*/ 00092 /*! 00093 * generateBinaryMaze() 00094 * 00095 * Input: w, h (size of maze) 00096 * xi, yi (initial location) 00097 * wallps (probability that a pixel to the side is ON) 00098 * ranis (ratio of prob that pixel in forward direction 00099 * is a wall to the probability that pixel in 00100 * side directions is a wall) 00101 * Return: pix, or null on error 00102 * 00103 * Notes: 00104 * (1) We have two input probability factors that determine the 00105 * density of walls and average length of straight passages. 00106 * When ranis < 1.0, you are more likely to generate a wall 00107 * to the side than going forward. Enter 0.0 for either if 00108 * you want to use the default values. 00109 * (2) This is a type of percolation problem, and exhibits 00110 * different phases for different parameters wallps and ranis. 00111 * For larger values of these parameters, regions in the maze 00112 * are not explored because the maze generator walls them 00113 * off and cannot get through. The boundary between the 00114 * two phases in this two-dimensional parameter space goes 00115 * near these values: 00116 * wallps ranis 00117 * 0.35 1.00 00118 * 0.40 0.85 00119 * 0.45 0.70 00120 * 0.50 0.50 00121 * 0.55 0.40 00122 * 0.60 0.30 00123 * 0.65 0.25 00124 * 0.70 0.19 00125 * 0.75 0.15 00126 * 0.80 0.11 00127 * (3) Because here is a considerable amount of overhead in calling 00128 * pixGetPixel() and pixSetPixel(), this function can be sped 00129 * up with little effort using raster line pointers and the 00130 * GET_DATA* and SET_DATA* macros. 00131 */ 00132 PIX * 00133 generateBinaryMaze(l_int32 w, 00134 l_int32 h, 00135 l_int32 xi, 00136 l_int32 yi, 00137 l_float32 wallps, 00138 l_float32 ranis) 00139 { 00140 l_int32 x, y, dir; 00141 l_uint32 val; 00142 l_float32 frand, wallpf, testp; 00143 MAZEEL *el, *elp; 00144 PIX *pixd; /* the destination maze */ 00145 PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ 00146 L_QUEUE *lq; 00147 00148 /* On Windows, seeding is apparently necessary to get decent mazes. 00149 * Windows rand() returns a value up to 2^15 - 1, whereas unix 00150 * rand() returns a value up to 2^31 - 1. Therefore the generated 00151 * mazes will differ on the two platforms. */ 00152 #ifdef _WIN32 00153 srand(28*333); 00154 #endif /* _WIN32 */ 00155 00156 if (w < MIN_MAZE_WIDTH) 00157 w = MIN_MAZE_WIDTH; 00158 if (h < MIN_MAZE_HEIGHT) 00159 h = MIN_MAZE_HEIGHT; 00160 if (xi <= 0 || xi >= w) 00161 xi = w / 6; 00162 if (yi <= 0 || yi >= h) 00163 yi = h / 5; 00164 if (wallps < 0.05 || wallps > 0.95) 00165 wallps = DEFAULT_WALL_PROBABILITY; 00166 if (ranis < 0.05 || ranis > 1.0) 00167 ranis = DEFAULT_ANISOTROPY_RATIO; 00168 wallpf = wallps * ranis; 00169 00170 #if DEBUG_MAZE 00171 fprintf(stderr, "(w, h) = (%d, %d), (xi, yi) = (%d, %d)\n", w, h, xi, yi); 00172 fprintf(stderr, "Using: prob(wall) = %7.4f, anisotropy factor = %7.4f\n", 00173 wallps, ranis); 00174 #endif /* DEBUG_MAZE */ 00175 00176 /* These are initialized to OFF */ 00177 pixd = pixCreate(w, h, 1); 00178 pixm = pixCreate(w, h, 1); 00179 00180 lq = lqueueCreate(0); 00181 00182 /* Prime the queue with the first pixel; it is OFF */ 00183 el = mazeelCreate(xi, yi, START_LOC); 00184 pixSetPixel(pixm, xi, yi, 1); /* mark visited */ 00185 lqueueAdd(lq, el); 00186 00187 /* While we're at it ... */ 00188 while (lqueueGetCount(lq) > 0) { 00189 elp = (MAZEEL *)lqueueRemove(lq); 00190 x = elp->x; 00191 y = elp->y; 00192 dir = elp->dir; 00193 if (x > 0) { /* check west */ 00194 pixGetPixel(pixm, x - 1, y, &val); 00195 if (val == 0) { /* not yet visited */ 00196 pixSetPixel(pixm, x - 1, y, 1); /* mark visited */ 00197 frand = (l_float32)rand() / (l_float32)RAND_MAX; 00198 testp = wallps; 00199 if (dir == DIR_WEST) 00200 testp = wallpf; 00201 if (frand <= testp) { /* make it a wall */ 00202 pixSetPixel(pixd, x - 1, y, 1); 00203 } 00204 else { /* not a wall */ 00205 el = mazeelCreate(x - 1, y, DIR_WEST); 00206 lqueueAdd(lq, el); 00207 } 00208 } 00209 } 00210 if (y > 0) { /* check north */ 00211 pixGetPixel(pixm, x, y - 1, &val); 00212 if (val == 0) { /* not yet visited */ 00213 pixSetPixel(pixm, x, y - 1, 1); /* mark visited */ 00214 frand = (l_float32)rand() / (l_float32)RAND_MAX; 00215 testp = wallps; 00216 if (dir == DIR_NORTH) 00217 testp = wallpf; 00218 if (frand <= testp) { /* make it a wall */ 00219 pixSetPixel(pixd, x, y - 1, 1); 00220 } 00221 else { /* not a wall */ 00222 el = mazeelCreate(x, y - 1, DIR_NORTH); 00223 lqueueAdd(lq, el); 00224 } 00225 } 00226 } 00227 if (x < w - 1) { /* check east */ 00228 pixGetPixel(pixm, x + 1, y, &val); 00229 if (val == 0) { /* not yet visited */ 00230 pixSetPixel(pixm, x + 1, y, 1); /* mark visited */ 00231 frand = (l_float32)rand() / (l_float32)RAND_MAX; 00232 testp = wallps; 00233 if (dir == DIR_EAST) 00234 testp = wallpf; 00235 if (frand <= testp) { /* make it a wall */ 00236 pixSetPixel(pixd, x + 1, y, 1); 00237 } 00238 else { /* not a wall */ 00239 el = mazeelCreate(x + 1, y, DIR_EAST); 00240 lqueueAdd(lq, el); 00241 } 00242 } 00243 } 00244 if (y < h - 1) { /* check south */ 00245 pixGetPixel(pixm, x, y + 1, &val); 00246 if (val == 0) { /* not yet visited */ 00247 pixSetPixel(pixm, x, y + 1, 1); /* mark visited */ 00248 frand = (l_float32)rand() / (l_float32)RAND_MAX; 00249 testp = wallps; 00250 if (dir == DIR_SOUTH) 00251 testp = wallpf; 00252 if (frand <= testp) { /* make it a wall */ 00253 pixSetPixel(pixd, x, y + 1, 1); 00254 } 00255 else { /* not a wall */ 00256 el = mazeelCreate(x, y + 1, DIR_SOUTH); 00257 lqueueAdd(lq, el); 00258 } 00259 } 00260 } 00261 FREE(elp); 00262 } 00263 00264 lqueueDestroy(&lq, TRUE); 00265 pixDestroy(&pixm); 00266 return pixd; 00267 } 00268 00269 00270 static MAZEEL * 00271 mazeelCreate(l_int32 x, 00272 l_int32 y, 00273 l_int32 dir) 00274 { 00275 MAZEEL *el; 00276 00277 el = (MAZEEL *)CALLOC(1, sizeof(MAZEEL)); 00278 el->x = x; 00279 el->y = y; 00280 el->dir = dir; 00281 return el; 00282 } 00283 00284 00285 /*---------------------------------------------------------------------* 00286 * Binary maze search * 00287 *---------------------------------------------------------------------*/ 00288 /*! 00289 * pixSearchBinaryMaze() 00290 * 00291 * Input: pixs (1 bpp, maze) 00292 * xi, yi (beginning point; use same initial point 00293 * that was used to generate the maze) 00294 * xf, yf (end point, or close to it) 00295 * &ppixd (<optional return> maze with path illustrated, or 00296 * if no path possible, the part of the maze 00297 * that was searched) 00298 * Return: pta (shortest path), or null if either no path 00299 * exists or on error 00300 * 00301 * Notes: 00302 * (1) Because of the overhead in calling pixGetPixel() and 00303 * pixSetPixel(), we have used raster line pointers and the 00304 * GET_DATA* and SET_DATA* macros for many of the pix accesses. 00305 * (2) Commentary: 00306 * The goal is to find the shortest path between beginning and 00307 * end points, without going through walls, and there are many 00308 * ways to solve this problem. 00309 * We use a queue to implement a breadth-first search. Two auxiliary 00310 * "image" data structures can be used: one to mark the visited 00311 * pixels and one to give the direction to the parent for each 00312 * visited pixels. The first structure is used to avoid putting 00313 * pixels on the queue more than once, and the second is used 00314 * for retracing back to the origin, like the breadcrumbs in 00315 * Hansel and Gretel. Each pixel taken off the queue is destroyed 00316 * after it is used to locate the allowed neighbors. In fact, 00317 * only one distance image is required, if you initialize it 00318 * to some value that signifies "not yet visited." (We use 00319 * a binary image for marking visited pixels because it is clearer.) 00320 * This method for a simple search of a binary maze is implemented in 00321 * searchBinaryMaze(). 00322 * An alternative method would store the (manhattan) distance 00323 * from the start point with each pixel on the queue. The children 00324 * of each pixel get a distance one larger than the parent. These 00325 * values can be stored in an auxiliary distance map image 00326 * that is constructed simultaneously with the search. Once the 00327 * end point is reached, the distance map is used to backtrack 00328 * along a minimum path. There may be several equal length 00329 * minimum paths, any one of which can be chosen this way. 00330 */ 00331 PTA * 00332 pixSearchBinaryMaze(PIX *pixs, 00333 l_int32 xi, 00334 l_int32 yi, 00335 l_int32 xf, 00336 l_int32 yf, 00337 PIX **ppixd) 00338 { 00339 l_int32 i, j, x, y, w, h, d, found; 00340 l_uint32 val, rpixel, gpixel, bpixel; 00341 void **lines1, **linem1, **linep8, **lined32; 00342 MAZEEL *el, *elp; 00343 PIX *pixd; /* the shortest path written on the maze image */ 00344 PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ 00345 PIX *pixp; /* for bookkeeping, to indicate direction to parent */ 00346 L_QUEUE *lq; 00347 PTA *pta; 00348 00349 PROCNAME("pixSearchBinaryMaze"); 00350 00351 if (ppixd) *ppixd = NULL; 00352 if (!pixs) 00353 return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); 00354 pixGetDimensions(pixs, &w, &h, &d); 00355 if (d != 1) 00356 return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00357 if (xi <= 0 || xi >= w) 00358 return (PTA *)ERROR_PTR("xi not valid", procName, NULL); 00359 if (yi <= 0 || yi >= h) 00360 return (PTA *)ERROR_PTR("yi not valid", procName, NULL); 00361 pixGetPixel(pixs, xi, yi, &val); 00362 if (val != 0) 00363 return (PTA *)ERROR_PTR("(xi,yi) not bg pixel", procName, NULL); 00364 pixd = NULL; 00365 pta = NULL; 00366 00367 /* Find a bg pixel near input point (xf, yf) */ 00368 localSearchForBackground(pixs, &xf, &yf, 5); 00369 00370 #if DEBUG_MAZE 00371 fprintf(stderr, "(xi, yi) = (%d, %d), (xf, yf) = (%d, %d)\n", 00372 xi, yi, xf, yf); 00373 #endif /* DEBUG_MAZE */ 00374 00375 pixm = pixCreate(w, h, 1); /* initialized to OFF */ 00376 pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ 00377 lines1 = pixGetLinePtrs(pixs, NULL); 00378 linem1 = pixGetLinePtrs(pixm, NULL); 00379 linep8 = pixGetLinePtrs(pixp, NULL); 00380 00381 lq = lqueueCreate(0); 00382 00383 /* Prime the queue with the first pixel; it is OFF */ 00384 el = mazeelCreate(xi, yi, 0); /* don't need direction here */ 00385 pixSetPixel(pixm, xi, yi, 1); /* mark visited */ 00386 lqueueAdd(lq, el); 00387 00388 /* Fill up the pix storing directions to parents, 00389 * stopping when we hit the point (xf, yf) */ 00390 found = FALSE; 00391 while (lqueueGetCount(lq) > 0) { 00392 elp = (MAZEEL *)lqueueRemove(lq); 00393 x = elp->x; 00394 y = elp->y; 00395 if (x == xf && y == yf) { 00396 found = TRUE; 00397 FREE(elp); 00398 break; 00399 } 00400 00401 if (x > 0) { /* check to west */ 00402 val = GET_DATA_BIT(linem1[y], x - 1); 00403 if (val == 0) { /* not yet visited */ 00404 SET_DATA_BIT(linem1[y], x - 1); /* mark visited */ 00405 val = GET_DATA_BIT(lines1[y], x - 1); 00406 if (val == 0) { /* bg, not a wall */ 00407 SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent E */ 00408 el = mazeelCreate(x - 1, y, 0); 00409 lqueueAdd(lq, el); 00410 } 00411 } 00412 } 00413 if (y > 0) { /* check north */ 00414 val = GET_DATA_BIT(linem1[y - 1], x); 00415 if (val == 0) { /* not yet visited */ 00416 SET_DATA_BIT(linem1[y - 1], x); /* mark visited */ 00417 val = GET_DATA_BIT(lines1[y - 1], x); 00418 if (val == 0) { /* bg, not a wall */ 00419 SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent S */ 00420 el = mazeelCreate(x, y - 1, 0); 00421 lqueueAdd(lq, el); 00422 } 00423 } 00424 } 00425 if (x < w - 1) { /* check east */ 00426 val = GET_DATA_BIT(linem1[y], x + 1); 00427 if (val == 0) { /* not yet visited */ 00428 SET_DATA_BIT(linem1[y], x + 1); /* mark visited */ 00429 val = GET_DATA_BIT(lines1[y], x + 1); 00430 if (val == 0) { /* bg, not a wall */ 00431 SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent W */ 00432 el = mazeelCreate(x + 1, y, 0); 00433 lqueueAdd(lq, el); 00434 } 00435 } 00436 } 00437 if (y < h - 1) { /* check south */ 00438 val = GET_DATA_BIT(linem1[y + 1], x); 00439 if (val == 0) { /* not yet visited */ 00440 SET_DATA_BIT(linem1[y + 1], x); /* mark visited */ 00441 val = GET_DATA_BIT(lines1[y + 1], x); 00442 if (val == 0) { /* bg, not a wall */ 00443 SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent N */ 00444 el = mazeelCreate(x, y + 1, 0); 00445 lqueueAdd(lq, el); 00446 } 00447 } 00448 } 00449 FREE(elp); 00450 } 00451 00452 lqueueDestroy(&lq, TRUE); 00453 pixDestroy(&pixm); 00454 FREE(linem1); 00455 00456 if (ppixd) { 00457 pixd = pixUnpackBinary(pixs, 32, 1); 00458 *ppixd = pixd; 00459 } 00460 composeRGBPixel(255, 0, 0, &rpixel); /* start point */ 00461 composeRGBPixel(0, 255, 0, &gpixel); 00462 composeRGBPixel(0, 0, 255, &bpixel); /* end point */ 00463 00464 00465 if (!found) { 00466 L_INFO(" No path found", procName); 00467 if (pixd) { /* paint all visited locations */ 00468 lined32 = pixGetLinePtrs(pixd, NULL); 00469 for (i = 0; i < h; i++) { 00470 for (j = 0; j < w; j++) { 00471 val = GET_DATA_BYTE(linep8[i], j); 00472 if (val != 0 && pixd) 00473 SET_DATA_FOUR_BYTES(lined32[i], j, gpixel); 00474 } 00475 } 00476 FREE(lined32); 00477 } 00478 } 00479 else { /* write path onto pixd */ 00480 L_INFO(" Path found", procName); 00481 pta = ptaCreate(0); 00482 x = xf; 00483 y = yf; 00484 while (1) { 00485 ptaAddPt(pta, x, y); 00486 if (x == xi && y == yi) 00487 break; 00488 if (pixd) 00489 pixSetPixel(pixd, x, y, gpixel); 00490 pixGetPixel(pixp, x, y, &val); 00491 if (val == DIR_NORTH) 00492 y--; 00493 else if (val == DIR_SOUTH) 00494 y++; 00495 else if (val == DIR_EAST) 00496 x++; 00497 else if (val == DIR_WEST) 00498 x--; 00499 } 00500 } 00501 if (pixd) { 00502 pixSetPixel(pixd, xi, yi, rpixel); 00503 pixSetPixel(pixd, xf, yf, bpixel); 00504 } 00505 00506 pixDestroy(&pixp); 00507 FREE(lines1); 00508 FREE(linep8); 00509 return pta; 00510 } 00511 00512 00513 /*! 00514 * localSearchForBackground() 00515 * 00516 * Input: &x, &y (starting position for search; return found position) 00517 * maxrad (max distance to search from starting location) 00518 * Return: 0 if bg pixel found; 1 if not found 00519 */ 00520 static l_int32 00521 localSearchForBackground(PIX *pix, 00522 l_int32 *px, 00523 l_int32 *py, 00524 l_int32 maxrad) 00525 { 00526 l_int32 x, y, w, h, r, i, j; 00527 l_uint32 val; 00528 00529 x = *px; 00530 y = *py; 00531 pixGetPixel(pix, x, y, &val); 00532 if (val == 0) return 0; 00533 00534 /* For each value of r, restrict the search to the boundary 00535 * pixels in a square centered on (x,y), clipping to the 00536 * image boundaries if necessary. */ 00537 pixGetDimensions(pix, &w, &h, NULL); 00538 for (r = 1; r < maxrad; r++) { 00539 for (i = -r; i <= r; i++) { 00540 if (y + i < 0 || y + i >= h) 00541 continue; 00542 for (j = -r; j <= r; j++) { 00543 if (x + j < 0 || x + j >= w) 00544 continue; 00545 if (L_ABS(i) != r && L_ABS(j) != r) /* not on "r ring" */ 00546 continue; 00547 pixGetPixel(pix, x + j, y + i, &val); 00548 if (val == 0) { 00549 *px = x + j; 00550 *py = y + i; 00551 return 0; 00552 } 00553 } 00554 } 00555 } 00556 return 1; 00557 } 00558 00559 00560 00561 /*---------------------------------------------------------------------* 00562 * Gray maze search * 00563 *---------------------------------------------------------------------*/ 00564 /*! 00565 * pixSearchGrayMaze() 00566 * 00567 * Input: pixs (1 bpp, maze) 00568 * xi, yi (beginning point; use same initial point 00569 * that was used to generate the maze) 00570 * xf, yf (end point, or close to it) 00571 * &ppixd (<optional return> maze with path illustrated, or 00572 * if no path possible, the part of the maze 00573 * that was searched) 00574 * Return: pta (shortest path), or null if either no path 00575 * exists or on error 00576 * 00577 * Commentary: 00578 * Consider first a slight generalization of the binary maze 00579 * search problem. Suppose that you can go through walls, 00580 * but the cost is higher (say, an increment of 3 to go into 00581 * a wall pixel rather than 1)? You're still trying to find 00582 * the shortest path. One way to do this is with an ordered 00583 * queue, and a simple way to visualize an ordered queue is as 00584 * a set of stacks, each stack being marked with the distance 00585 * of each pixel in the stack from the start. We place the 00586 * start pixel in stack 0, pop it, and process its 4 children. 00587 * Each pixel is given a distance that is incremented from that 00588 * of its parent (0 in this case), depending on if it is a wall 00589 * pixel or not. That value may be recorded on a distance map, 00590 * according to the algorithm below. For children of the first 00591 * pixel, those not on a wall go in stack 1, and wall 00592 * children go in stack 3. Stack 0 being emptied, the process 00593 * then continues with pixels being popped from stack 1. 00594 * Here is the algorithm for each child pixel. The pixel's 00595 * distance value, were it to be placed on a stack, is compared 00596 * with the value for it that is on the distance map. There 00597 * are three possible cases: 00598 * (1) If the pixel has not yet been registered, it is pushed 00599 * on its stack and the distance is written to the map. 00600 * (2) If it has previously been registered with a higher distance, 00601 * the distance on the map is relaxed to that of the 00602 * current pixel, which is then placed on its stack. 00603 * (3) If it has previously been registered with an equal 00604 * or lower value, the pixel is discarded. 00605 * The pixels are popped and processed successively from 00606 * stack 1, and when stack 1 is empty, popping starts on stack 2. 00607 * This continues until the destination pixel is popped off 00608 * a stack. The minimum path is then derived from the distance map, 00609 * going back from the end point as before. This is just Dijkstra's 00610 * algorithm for a directed graph; here, the underlying graph 00611 * (consisting of the pixels and four edges connecting each pixel 00612 * to its 4-neighbor) is a special case of a directed graph, where 00613 * each edge is bi-directional. The implementation of this generalized 00614 * maze search is left as an exercise to the reader. 00615 * 00616 * Let's generalize a bit further. Suppose the "maze" is just 00617 * a grayscale image -- think of it as an elevation map. The cost 00618 * of moving on this surface depends on the height, or the gradient, 00619 * or whatever you want. All that is required is that the cost 00620 * is specified and non-negative on each link between adjacent 00621 * pixels. Now the problem becomes: find the least cost path 00622 * moving on this surface between two specified end points. 00623 * For example, if the cost across an edge between two pixels 00624 * depends on the "gradient", you can use: 00625 * cost = 1 + L_ABS(deltaV) 00626 * where deltaV is the difference in value between two adjacent 00627 * pixels. If the costs are all integers, we can still use an array 00628 * of stacks to avoid ordering the queue (e.g., by using a heap sort.) 00629 * This is a neat problem, because you don't even have to build a 00630 * maze -- you can can use it on any grayscale image! 00631 * 00632 * Rather than using an array of stacks, a more practical 00633 * approach is to implement with a priority queue, which is 00634 * a queue that is sorted so that the elements with the largest 00635 * (or smallest) key values always come off first. The 00636 * priority queue is efficiently implemented as a heap, and 00637 * this is how we do it. Suppose you run the algorithm 00638 * using a priority queue, doing the bookkeeping with an 00639 * auxiliary image data structure that saves the distance of 00640 * each pixel put on the queue as before, according to the method 00641 * described above. We implement it as a 2-way choice by 00642 * initializing the distance array to a large value and putting 00643 * a pixel on the queue if its distance is less than the value 00644 * found on the array. When you finally pop the end pixel from 00645 * the queue, you're done, and you can trace the path backward, 00646 * either always going downhill or using an auxiliary image to 00647 * give you the direction to go at each step. This is implemented 00648 * here in searchGrayMaze(). 00649 * 00650 * Do we really have to use a sorted queue? Can we solve this 00651 * generalized maze with an unsorted queue of pixels? (Or even 00652 * an unsorted stack, doing a depth-first search (DFS)?) 00653 * Consider a different algorithm for this generalized maze, where 00654 * we travel again breadth first, but this time use a single, 00655 * unsorted queue. An auxiliary image is used as before to 00656 * store the distances and to determine if pixels get pushed 00657 * on the stack or dropped. As before, we must allow pixels 00658 * to be revisited, with relaxation of the distance if a shorter 00659 * path arrives later. As a result, we will in general have 00660 * multiple instances of the same pixel on the stack with different 00661 * distances. However, because the queue is not ordered, some of 00662 * these pixels will be popped when another instance with a lower 00663 * distance is still on the stack. Here, we're just popping them 00664 * in the order they go on, rather than setting up a priority 00665 * based on minimum distance. Thus, unlike the priority queue, 00666 * when a pixel is popped we have to check the distance map to 00667 * see if a pixel with a lower distance has been put on the queue, 00668 * and, if so, we discard the pixel we just popped. So the 00669 * "while" loop looks like this: 00670 * - pop a pixel from the queue 00671 * - check its distance against the distance stored in the 00672 * distance map; if larger, discard 00673 * - otherwise, for each of its neighbors: 00674 * - compute its distance from the start pixel 00675 * - compare this distance with that on the distance map: 00676 * - if the distance map value higher, relax the distance 00677 * and push the pixel on the queue 00678 * - if the distance map value is lower, discard the pixel 00679 * 00680 * How does this loop terminate? Before, with an ordered queue, 00681 * it terminates when you pop the end pixel. But with an unordered 00682 * queue (or stack), the first time you hit the end pixel, the 00683 * distance is not guaranteed to be correct, because the pixels 00684 * along the shortest path may not have yet been visited and relaxed. 00685 * Because the shortest path can theoretically go anywhere, 00686 * we must keep going. How do we know when to stop? Dijkstra 00687 * uses an ordered queue to systematically remove nodes from 00688 * further consideration. (Each time a pixel is popped, we're 00689 * done with it; it's "finalized" in the Dijkstra sense because 00690 * we know the shortest path to it.) However, with an unordered 00691 * queue, the brute force answer is: stop when the queue 00692 * (or stack) is empty, because then every pixel in the image 00693 * has been assigned its minimum "distance" from the start pixel. 00694 * 00695 * This is similar to the situation when you use a stack for the 00696 * simpler uniform-step problem: with breadth-first search (BFS) 00697 * the pixels on the queue are automatically ordered, so you are 00698 * done when you locate the end pixel as a neighbor of a popped pixel; 00699 * whereas depth-first search (DFS), using a stack, requires, 00700 * in general, a search of every accessible pixel. Further, if 00701 * a pixel is revisited with a smaller distance, that distance is 00702 * recorded and the pixel is put on the stack again. 00703 * 00704 * But surely, you ask, can't we stop sooner? What if the 00705 * start and end pixels are very close to each other? 00706 * OK, suppose they are, and you have very high walls and a 00707 * long snaking level path that is actually the minimum cost. 00708 * That long path can wind back and forth across the entire 00709 * maze many times before ending up at the end point, which 00710 * could be just over a wall from the start. With the unordered 00711 * queue, you very quickly get a high distance for the end 00712 * pixel, which will be relaxed to the minimum distance only 00713 * after all the pixels of the path have been visited and placed 00714 * on the queue, multiple times for many of them. So that's the 00715 * price for not ordering the queue! 00716 */ 00717 PTA * 00718 pixSearchGrayMaze(PIX *pixs, 00719 l_int32 xi, 00720 l_int32 yi, 00721 l_int32 xf, 00722 l_int32 yf, 00723 PIX **ppixd) 00724 { 00725 l_int32 x, y, w, h, d; 00726 l_uint32 val, valr, vals, rpixel, gpixel, bpixel; 00727 void **lines8, **liner32, **linep8; 00728 l_int32 cost, dist, distparent, sival, sivals; 00729 MAZEEL *el, *elp; 00730 PIX *pixd; /* optionally plot the path on this RGB version of pixs */ 00731 PIX *pixr; /* for bookkeeping, to indicate the minimum distance */ 00732 /* to pixels already visited */ 00733 PIX *pixp; /* for bookkeeping, to indicate direction to parent */ 00734 L_HEAP *lh; 00735 PTA *pta; 00736 00737 PROCNAME("pixSearchGrayMaze"); 00738 00739 if (ppixd) *ppixd = NULL; 00740 if (!pixs) 00741 return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); 00742 pixGetDimensions(pixs, &w, &h, &d); 00743 if (d != 8) 00744 return (PTA *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00745 if (xi <= 0 || xi >= w) 00746 return (PTA *)ERROR_PTR("xi not valid", procName, NULL); 00747 if (yi <= 0 || yi >= h) 00748 return (PTA *)ERROR_PTR("yi not valid", procName, NULL); 00749 pixd = NULL; 00750 pta = NULL; 00751 00752 pixr = pixCreate(w, h, 32); 00753 pixSetAll(pixr); /* initialize to max value */ 00754 pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ 00755 lines8 = pixGetLinePtrs(pixs, NULL); 00756 linep8 = pixGetLinePtrs(pixp, NULL); 00757 liner32 = pixGetLinePtrs(pixr, NULL); 00758 00759 lh = lheapCreate(0, L_SORT_INCREASING); /* always remove closest pixels */ 00760 00761 /* Prime the heap with the first pixel */ 00762 pixGetPixel(pixs, xi, yi, &val); 00763 el = mazeelCreate(xi, yi, 0); /* don't need direction here */ 00764 el->distance = 0; 00765 pixGetPixel(pixs, xi, yi, &val); 00766 el->val = val; 00767 pixSetPixel(pixr, xi, yi, 0); /* distance is 0 */ 00768 lheapAdd(lh, el); 00769 00770 /* Breadth-first search with priority queue (implemented by 00771 a heap), labeling direction to parents in pixp and minimum 00772 distance to visited pixels in pixr. Stop when we pull 00773 the destination point (xf, yf) off the queue. */ 00774 while (lheapGetCount(lh) > 0) { 00775 elp = (MAZEEL *)lheapRemove(lh); 00776 if (!elp) 00777 return (PTA *)ERROR_PTR("heap broken!!", procName, NULL); 00778 x = elp->x; 00779 y = elp->y; 00780 if (x == xf && y == yf) { /* exit condition */ 00781 FREE(elp); 00782 break; 00783 } 00784 distparent = (l_int32)elp->distance; 00785 val = elp->val; 00786 sival = val; 00787 00788 if (x > 0) { /* check to west */ 00789 vals = GET_DATA_BYTE(lines8[y], x - 1); 00790 valr = GET_DATA_FOUR_BYTES(liner32[y], x - 1); 00791 sivals = (l_int32)vals; 00792 cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ 00793 dist = distparent + cost; 00794 if (dist < valr) { /* shortest path so far to this pixel */ 00795 SET_DATA_FOUR_BYTES(liner32[y], x - 1, dist); /* new dist */ 00796 SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent to E */ 00797 el = mazeelCreate(x - 1, y, 0); 00798 el->val = vals; 00799 el->distance = dist; 00800 lheapAdd(lh, el); 00801 } 00802 } 00803 if (y > 0) { /* check north */ 00804 vals = GET_DATA_BYTE(lines8[y - 1], x); 00805 valr = GET_DATA_FOUR_BYTES(liner32[y - 1], x); 00806 sivals = (l_int32)vals; 00807 cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ 00808 dist = distparent + cost; 00809 if (dist < valr) { /* shortest path so far to this pixel */ 00810 SET_DATA_FOUR_BYTES(liner32[y - 1], x, dist); /* new dist */ 00811 SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent to S */ 00812 el = mazeelCreate(x, y - 1, 0); 00813 el->val = vals; 00814 el->distance = dist; 00815 lheapAdd(lh, el); 00816 } 00817 } 00818 if (x < w - 1) { /* check east */ 00819 vals = GET_DATA_BYTE(lines8[y], x + 1); 00820 valr = GET_DATA_FOUR_BYTES(liner32[y], x + 1); 00821 sivals = (l_int32)vals; 00822 cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ 00823 dist = distparent + cost; 00824 if (dist < valr) { /* shortest path so far to this pixel */ 00825 SET_DATA_FOUR_BYTES(liner32[y], x + 1, dist); /* new dist */ 00826 SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent to W */ 00827 el = mazeelCreate(x + 1, y, 0); 00828 el->val = vals; 00829 el->distance = dist; 00830 lheapAdd(lh, el); 00831 } 00832 } 00833 if (y < h - 1) { /* check south */ 00834 vals = GET_DATA_BYTE(lines8[y + 1], x); 00835 valr = GET_DATA_FOUR_BYTES(liner32[y + 1], x); 00836 sivals = (l_int32)vals; 00837 cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ 00838 dist = distparent + cost; 00839 if (dist < valr) { /* shortest path so far to this pixel */ 00840 SET_DATA_FOUR_BYTES(liner32[y + 1], x, dist); /* new dist */ 00841 SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent to N */ 00842 el = mazeelCreate(x, y + 1, 0); 00843 el->val = vals; 00844 el->distance = dist; 00845 lheapAdd(lh, el); 00846 } 00847 } 00848 FREE(elp); 00849 } 00850 00851 lheapDestroy(&lh, TRUE); 00852 00853 if (ppixd) { 00854 pixd = pixConvert8To32(pixs); 00855 *ppixd = pixd; 00856 } 00857 composeRGBPixel(255, 0, 0, &rpixel); /* start point */ 00858 composeRGBPixel(0, 255, 0, &gpixel); 00859 composeRGBPixel(0, 0, 255, &bpixel); /* end point */ 00860 00861 x = xf; 00862 y = yf; 00863 pta = ptaCreate(0); 00864 while (1) { /* write path onto pixd */ 00865 ptaAddPt(pta, x, y); 00866 if (x == xi && y == yi) 00867 break; 00868 if (pixd) 00869 pixSetPixel(pixd, x, y, gpixel); 00870 pixGetPixel(pixp, x, y, &val); 00871 if (val == DIR_NORTH) 00872 y--; 00873 else if (val == DIR_SOUTH) 00874 y++; 00875 else if (val == DIR_EAST) 00876 x++; 00877 else if (val == DIR_WEST) 00878 x--; 00879 pixGetPixel(pixr, x, y, &val); 00880 00881 #if DEBUG_PATH 00882 fprintf(stderr, "(x,y) = (%d, %d); dist = %d\n", x, y, val); 00883 #endif /* DEBUG_PATH */ 00884 00885 } 00886 if (pixd) { 00887 pixSetPixel(pixd, xi, yi, rpixel); 00888 pixSetPixel(pixd, xf, yf, bpixel); 00889 } 00890 00891 pixDestroy(&pixp); 00892 pixDestroy(&pixr); 00893 FREE(lines8); 00894 FREE(linep8); 00895 FREE(liner32); 00896 return pta; 00897 } 00898 00899 00900 /*---------------------------------------------------------------------* 00901 * Largest rectangle in an image * 00902 *---------------------------------------------------------------------*/ 00903 /*! 00904 * pixFindLargestRectangle() 00905 * 00906 * Input: pixs (1 bpp) 00907 * polarity (0 within background, 1 within foreground) 00908 * &box (<return> largest rectangle, either by area or 00909 * by perimeter) 00910 * debugflag (1 to output image with rectangle drawn on it) 00911 * Return: 0 if OK, 1 on error 00912 * 00913 * Notes: 00914 * (1) Why is this here? This is a simple and elegant solution to 00915 * a problem in computational geometry that at first appears 00916 * quite difficult: what is the largest rectangle that can 00917 * be placed in the image, covering only pixels of one polarity 00918 * (bg or fg)? The solution is O(n), where n is the number 00919 * of pixels in the image, and it requires nothing more than 00920 * using a simple recursion relation in a single sweep of the image. 00921 * (2) In a sweep from UL to LR with left-to-right being the fast 00922 * direction, calculate the largest white rectangle at (x, y), 00923 * using previously calculated values at pixels #1 and #2: 00924 * #1: (x, y - 1) 00925 * #2: (x - 1, y) 00926 * We also need the most recent "black" pixels that were seen 00927 * in the current row and column. 00928 * Consider the largest area. There are only two possibilities: 00929 * (a) Min(w(1), horizdist) * (h(1) + 1) 00930 * (b) Min(h(2), vertdist) * (w(2) + 1) 00931 * where 00932 * horizdist: the distance from the rightmost "black" pixel seen 00933 * in the current row across to the current pixel 00934 * vertdist: the distance from the lowest "black" pixel seen 00935 * in the current column down to the current pixel 00936 * and we choose the Max of (a) and (b). 00937 * (3) To convince yourself that these recursion relations are correct, 00938 * it helps to draw the maximum rectangles at #1 and #2. 00939 * Then for #1, you try to extend the rectangle down one line, 00940 * so that the height is h(1) + 1. Do you get the full 00941 * width of #1, w(1)? It depends on where the black pixels are 00942 * in the current row. You know the final width is bounded by w(1) 00943 * and w(2) + 1, but the actual value depends on the distribution 00944 * of black pixels in the current row that are at a distance 00945 * from the current pixel that is between these limits. 00946 * We call that value "horizdist", and the area is then given 00947 * by the expression (a) above. Using similar reasoning for #2, 00948 * where you attempt to extend the rectangle to the right 00949 * by 1 pixel, you arrive at (b). The largest rectangle is 00950 * then found by taking the Max. 00951 */ 00952 l_int32 00953 pixFindLargestRectangle(PIX *pixs, 00954 l_int32 polarity, 00955 BOX **pbox, 00956 const char *debugfile) 00957 { 00958 l_int32 i, j, w, h, d, wpls, val; 00959 l_int32 wp, hp, w1, w2, h1, h2, wmin, hmin, area1, area2; 00960 l_int32 xmax, ymax; /* LR corner of the largest rectangle */ 00961 l_int32 maxarea, wmax, hmax, vertdist, horizdist, prevfg; 00962 l_int32 *lowestfg; 00963 l_uint32 *datas, *lines; 00964 l_uint32 **linew, **lineh; 00965 BOX *box; 00966 PIX *pixw, *pixh; /* keeps the width and height for the largest */ 00967 /* rectangles whose LR corner is located there. */ 00968 00969 PROCNAME("pixFindLargestRectangle"); 00970 00971 if (!pbox) 00972 return ERROR_INT("&box not defined", procName, 1); 00973 *pbox = NULL; 00974 if (!pixs) 00975 return ERROR_INT("pixs not defined", procName, 1); 00976 pixGetDimensions(pixs, &w, &h, &d); 00977 if (d != 1) 00978 return ERROR_INT("pixs not 1 bpp", procName, 1); 00979 if (polarity != 0 && polarity != 1) 00980 return ERROR_INT("invalid polarity", procName, 1); 00981 00982 /* Initialize lowest "fg" seen so far for each column */ 00983 lowestfg = (l_int32 *)CALLOC(w, sizeof(l_int32)); 00984 for (i = 0; i < w; i++) 00985 lowestfg[i] = -1; 00986 00987 /* The combination (val ^ polarity) is the color for which we 00988 * are searching for the maximum rectangle. For polarity == 0, 00989 * we search in the bg (white). */ 00990 pixw = pixCreate(w, h, 32); /* stores width */ 00991 pixh = pixCreate(w, h, 32); /* stores height */ 00992 linew = (l_uint32 **)pixGetLinePtrs(pixw, NULL); 00993 lineh = (l_uint32 **)pixGetLinePtrs(pixh, NULL); 00994 datas = pixGetData(pixs); 00995 wpls = pixGetWpl(pixs); 00996 maxarea = xmax = ymax = wmax = hmax = 0; 00997 for (i = 0; i < h; i++) { 00998 lines = datas + i * wpls; 00999 prevfg = -1; 01000 for (j = 0; j < w; j++) { 01001 val = GET_DATA_BIT(lines, j); 01002 if ((val ^ polarity) == 0) { /* bg (0) if polarity == 0, etc. */ 01003 if (i == 0 && j == 0) { 01004 wp = hp = 1; 01005 } 01006 else if (i == 0) { 01007 wp = linew[i][j - 1] + 1; 01008 hp = 1; 01009 } 01010 else if (j == 0) { 01011 wp = 1; 01012 hp = lineh[i - 1][j] + 1; 01013 } 01014 else { 01015 /* Expand #1 prev rectangle down */ 01016 w1 = linew[i - 1][j]; 01017 h1 = lineh[i - 1][j]; 01018 horizdist = j - prevfg; 01019 wmin = L_MIN(w1, horizdist); /* width of new rectangle */ 01020 area1 = wmin * (h1 + 1); 01021 01022 /* Expand #2 prev rectangle to right */ 01023 w2 = linew[i][j - 1]; 01024 h2 = lineh[i][j - 1]; 01025 vertdist = i - lowestfg[j]; 01026 hmin = L_MIN(h2, vertdist); /* height of new rectangle */ 01027 area2 = hmin * (w2 + 1); 01028 01029 if (area1 > area2) { 01030 wp = wmin; 01031 hp = h1 + 1; 01032 } 01033 else { 01034 wp = w2 + 1; 01035 hp = hmin; 01036 } 01037 } 01038 } 01039 else { /* fg (1) if polarity == 0; bg (0) if polarity == 1 */ 01040 prevfg = j; 01041 lowestfg[j] = i; 01042 wp = hp = 0; 01043 } 01044 linew[i][j] = wp; 01045 lineh[i][j] = hp; 01046 if (wp * hp > maxarea) { 01047 maxarea = wp * hp; 01048 xmax = j; 01049 ymax = i; 01050 wmax = wp; 01051 hmax = hp; 01052 } 01053 } 01054 } 01055 01056 /* Translate from LR corner to Box coords (UL corner, w, h) */ 01057 box = boxCreate(xmax - wmax + 1, ymax - hmax + 1, wmax, hmax); 01058 *pbox = box; 01059 01060 if (debugfile) { 01061 PIX *pixdb; 01062 pixdb = pixConvertTo8(pixs, TRUE); 01063 pixRenderHashBoxArb(pixdb, box, 6, 2, L_NEG_SLOPE_LINE, 1, 255, 0, 0); 01064 pixWrite(debugfile, pixdb, IFF_PNG); 01065 pixDestroy(&pixdb); 01066 } 01067 01068 FREE(linew); 01069 FREE(lineh); 01070 FREE(lowestfg); 01071 pixDestroy(&pixw); 01072 pixDestroy(&pixh); 01073 return 0; 01074 } 01075 01076