Leptonica 1.68
C Image Processing Library

maze.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines