Пример #1
0
/*!
 *  generateBinaryMaze()
 *
 *      Input:  w, h  (size of maze)
 *              xi, yi  (initial location)
 *              wallps (probability that a pixel to the side is ON)
 *              ranis (ratio of prob that pixel in forward direction
 *                     is a wall to the probability that pixel in
 *                     side directions is a wall)
 *      Return: pix, or null on error
 *
 *  Notes:
 *      (1) We have two input probability factors that determine the
 *          density of walls and average length of straight passages.
 *          When ranis < 1.0, you are more likely to generate a wall
 *          to the side than going forward.  Enter 0.0 for either if
 *          you want to use the default values.
 *      (2) This is a type of percolation problem, and exhibits
 *          different phases for different parameters wallps and ranis.
 *          For larger values of these parameters, regions in the maze
 *          are not explored because the maze generator walls them
 *          off and cannot get through.  The boundary between the
 *          two phases in this two-dimensional parameter space goes
 *          near these values:
 *                wallps       ranis
 *                0.35         1.00
 *                0.40         0.85
 *                0.45         0.70
 *                0.50         0.50
 *                0.55         0.40
 *                0.60         0.30
 *                0.65         0.25
 *                0.70         0.19
 *                0.75         0.15
 *                0.80         0.11
 *      (3) Because here is a considerable amount of overhead in calling
 *          pixGetPixel() and pixSetPixel(), this function can be sped
 *          up with little effort using raster line pointers and the
 *          GET_DATA* and SET_DATA* macros.
 */
PIX *
generateBinaryMaze(l_int32  w,
                   l_int32  h,
                   l_int32  xi,
                   l_int32  yi,
                   l_float32  wallps,
                   l_float32  ranis)
{
l_int32    x, y, dir;
l_uint32   val;
l_float32  frand, wallpf, testp;
MAZEEL    *el, *elp;
PIX       *pixd;  /* the destination maze */
PIX       *pixm;  /* for bookkeeping, to indicate pixels already visited */
L_QUEUE   *lq;

    /* On Windows, seeding is apparently necessary to get decent mazes.
     * Windows rand() returns a value up to 2^15 - 1, whereas unix
     * rand() returns a value up to 2^31 - 1.  Therefore the generated
     * mazes will differ on the two platforms. */
#ifdef _WIN32
    srand(28*333);
#endif /* _WIN32 */

    if (w < MIN_MAZE_WIDTH)
        w = MIN_MAZE_WIDTH;
    if (h < MIN_MAZE_HEIGHT)
        h = MIN_MAZE_HEIGHT;
    if (xi <= 0 || xi >= w)
        xi = w / 6;
    if (yi <= 0 || yi >= h)
        yi = h / 5;
    if (wallps < 0.05 || wallps > 0.95)
        wallps = DEFAULT_WALL_PROBABILITY;
    if (ranis < 0.05 || ranis > 1.0)
        ranis = DEFAULT_ANISOTROPY_RATIO;
    wallpf = wallps * ranis;

#if  DEBUG_MAZE
    fprintf(stderr, "(w, h) = (%d, %d), (xi, yi) = (%d, %d)\n", w, h, xi, yi);
    fprintf(stderr, "Using: prob(wall) = %7.4f, anisotropy factor = %7.4f\n",
            wallps, ranis);
#endif  /* DEBUG_MAZE */

        /* These are initialized to OFF */
    pixd = pixCreate(w, h, 1);
    pixm = pixCreate(w, h, 1);

    lq = lqueueCreate(0);

        /* Prime the queue with the first pixel; it is OFF */
    el = mazeelCreate(xi, yi, START_LOC);
    pixSetPixel(pixm, xi, yi, 1);  /* mark visited */
    lqueueAdd(lq, el);

        /* While we're at it ... */
    while (lqueueGetCount(lq) > 0) {
        elp = (MAZEEL *)lqueueRemove(lq);
        x = elp->x;
        y = elp->y;
        dir = elp->dir;
        if (x > 0) {  /* check west */
            pixGetPixel(pixm, x - 1, y, &val);
            if (val == 0) {  /* not yet visited */
                pixSetPixel(pixm, x - 1, y, 1);  /* mark visited */
                frand = (l_float32)rand() / (l_float32)RAND_MAX;
                testp = wallps;
                if (dir == DIR_WEST)
                    testp = wallpf;
                if (frand <= testp) {  /* make it a wall */
                    pixSetPixel(pixd, x - 1, y, 1);
                }
                else {  /* not a wall */
                    el = mazeelCreate(x - 1, y, DIR_WEST);
                    lqueueAdd(lq, el);
                }
            }
        }
        if (y > 0) {  /* check north */
            pixGetPixel(pixm, x, y - 1, &val);
            if (val == 0) {  /* not yet visited */
                pixSetPixel(pixm, x, y - 1, 1);  /* mark visited */
                frand = (l_float32)rand() / (l_float32)RAND_MAX;
                testp = wallps;
                if (dir == DIR_NORTH)
                    testp = wallpf;
                if (frand <= testp) {  /* make it a wall */
                    pixSetPixel(pixd, x, y - 1, 1);
                }
                else {  /* not a wall */
                    el = mazeelCreate(x, y - 1, DIR_NORTH);
                    lqueueAdd(lq, el);
                }
            }
        }
        if (x < w - 1) {  /* check east */
            pixGetPixel(pixm, x + 1, y, &val);
            if (val == 0) {  /* not yet visited */
                pixSetPixel(pixm, x + 1, y, 1);  /* mark visited */
                frand = (l_float32)rand() / (l_float32)RAND_MAX;
                testp = wallps;
                if (dir == DIR_EAST)
                    testp = wallpf;
                if (frand <= testp) {  /* make it a wall */
                    pixSetPixel(pixd, x + 1, y, 1);
                }
                else {  /* not a wall */
                    el = mazeelCreate(x + 1, y, DIR_EAST);
                    lqueueAdd(lq, el);
                }
            }
        }
        if (y < h - 1) {  /* check south */
            pixGetPixel(pixm, x, y + 1, &val);
            if (val == 0) {  /* not yet visited */
                pixSetPixel(pixm, x, y + 1, 1);  /* mark visited */
                frand = (l_float32)rand() / (l_float32)RAND_MAX;
                testp = wallps;
                if (dir == DIR_SOUTH)
                    testp = wallpf;
                if (frand <= testp) {  /* make it a wall */
                    pixSetPixel(pixd, x, y + 1, 1);
                }
                else {  /* not a wall */
                    el = mazeelCreate(x, y + 1, DIR_SOUTH);
                    lqueueAdd(lq, el);
                }
            }
        }
        FREE(elp);
    }

    lqueueDestroy(&lq, TRUE);
    pixDestroy(&pixm);
    return pixd;
}
Пример #2
0
/*!
 *  pixSearchBinaryMaze()
 *
 *      Input:  pixs (1 bpp, maze)
 *              xi, yi  (beginning point; use same initial point
 *                       that was used to generate the maze)
 *              xf, yf  (end point, or close to it)
 *              &ppixd (<optional return> maze with path illustrated, or
 *                     if no path possible, the part of the maze
 *                     that was searched)
 *      Return: pta (shortest path), or null if either no path
 *              exists or on error
 *
 *  Notes:
 *      (1) Because of the overhead in calling pixGetPixel() and
 *          pixSetPixel(), we have used raster line pointers and the
 *          GET_DATA* and SET_DATA* macros for many of the pix accesses.
 *      (2) Commentary:
 *            The goal is to find the shortest path between beginning and
 *          end points, without going through walls, and there are many
 *          ways to solve this problem.
 *            We use a queue to implement a breadth-first search.  Two auxiliary
 *          "image" data structures can be used: one to mark the visited
 *          pixels and one to give the direction to the parent for each
 *          visited pixels.  The first structure is used to avoid putting
 *          pixels on the queue more than once, and the second is used
 *          for retracing back to the origin, like the breadcrumbs in
 *          Hansel and Gretel.  Each pixel taken off the queue is destroyed
 *          after it is used to locate the allowed neighbors.  In fact,
 *          only one distance image is required, if you initialize it
 *          to some value that signifies "not yet visited."  (We use
 *          a binary image for marking visited pixels because it is clearer.)
 *          This method for a simple search of a binary maze is implemented in
 *          searchBinaryMaze().
 *            An alternative method would store the (manhattan) distance
 *          from the start point with each pixel on the queue.  The children
 *          of each pixel get a distance one larger than the parent.  These
 *          values can be stored in an auxiliary distance map image
 *          that is constructed simultaneously with the search.  Once the
 *          end point is reached, the distance map is used to backtrack
 *          along a minimum path.  There may be several equal length
 *          minimum paths, any one of which can be chosen this way.
 */
PTA *
pixSearchBinaryMaze(PIX     *pixs,
                    l_int32  xi,
                    l_int32  yi, 
                    l_int32  xf,
                    l_int32  yf,
                    PIX    **ppixd)
{
l_int32    i, j, x, y, w, h, d, found;
l_uint32   val, rpixel, gpixel, bpixel;
void     **lines1, **linem1, **linep8, **lined32;
MAZEEL    *el, *elp;
PIX       *pixd;  /* the shortest path written on the maze image */
PIX       *pixm;  /* for bookkeeping, to indicate pixels already visited */
PIX       *pixp;  /* for bookkeeping, to indicate direction to parent */
L_QUEUE   *lq;
PTA       *pta;

    PROCNAME("pixSearchBinaryMaze");

    if (ppixd) *ppixd = NULL;
    if (!pixs)
        return (PTA *)ERROR_PTR("pixs not defined", procName, NULL);
    pixGetDimensions(pixs, &w, &h, &d);
    if (d != 1)
        return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (xi <= 0 || xi >= w)
        return (PTA *)ERROR_PTR("xi not valid", procName, NULL);
    if (yi <= 0 || yi >= h)
        return (PTA *)ERROR_PTR("yi not valid", procName, NULL);
    pixGetPixel(pixs, xi, yi, &val);
    if (val != 0)
        return (PTA *)ERROR_PTR("(xi,yi) not bg pixel", procName, NULL);
    pixd = NULL;
    pta = NULL;

        /* Find a bg pixel near input point (xf, yf) */
    localSearchForBackground(pixs, &xf, &yf, 5);

#if  DEBUG_MAZE
    fprintf(stderr, "(xi, yi) = (%d, %d), (xf, yf) = (%d, %d)\n",
            xi, yi, xf, yf);
#endif  /* DEBUG_MAZE */

    pixm = pixCreate(w, h, 1);  /* initialized to OFF */
    pixp = pixCreate(w, h, 8);  /* direction to parent stored as enum val */
    lines1 = pixGetLinePtrs(pixs, NULL);
    linem1 = pixGetLinePtrs(pixm, NULL);
    linep8 = pixGetLinePtrs(pixp, NULL);

    lq = lqueueCreate(0);

        /* Prime the queue with the first pixel; it is OFF */
    el = mazeelCreate(xi, yi, 0);  /* don't need direction here */
    pixSetPixel(pixm, xi, yi, 1);  /* mark visited */
    lqueueAdd(lq, el);

        /* Fill up the pix storing directions to parents,
         * stopping when we hit the point (xf, yf)  */
    found = FALSE;
    while (lqueueGetCount(lq) > 0) {
        elp = (MAZEEL *)lqueueRemove(lq);
        x = elp->x;
        y = elp->y;
        if (x == xf && y == yf) {
            found = TRUE;
            FREE(elp);
            break;
        }
            
        if (x > 0) {  /* check to west */
            val = GET_DATA_BIT(linem1[y], x - 1);
            if (val == 0) {  /* not yet visited */
                SET_DATA_BIT(linem1[y], x - 1);  /* mark visited */
                val = GET_DATA_BIT(lines1[y], x - 1);
                if (val == 0) {  /* bg, not a wall */
                    SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST);  /* parent E */
                    el = mazeelCreate(x - 1, y, 0);
                    lqueueAdd(lq, el);
                }
            }
        }
        if (y > 0) {  /* check north */
            val = GET_DATA_BIT(linem1[y - 1], x);
            if (val == 0) {  /* not yet visited */
                SET_DATA_BIT(linem1[y - 1], x);  /* mark visited */
                val = GET_DATA_BIT(lines1[y - 1], x);
                if (val == 0) {  /* bg, not a wall */
                    SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH);  /* parent S */
                    el = mazeelCreate(x, y - 1, 0);
                    lqueueAdd(lq, el);
                }
            }
        }
        if (x < w - 1) {  /* check east */
            val = GET_DATA_BIT(linem1[y], x + 1);
            if (val == 0) {  /* not yet visited */
                SET_DATA_BIT(linem1[y], x + 1);  /* mark visited */
                val = GET_DATA_BIT(lines1[y], x + 1);
                if (val == 0) {  /* bg, not a wall */
                    SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST);  /* parent W */
                    el = mazeelCreate(x + 1, y, 0);
                    lqueueAdd(lq, el);
                }
            }
        }
        if (y < h - 1) {  /* check south */
            val = GET_DATA_BIT(linem1[y + 1], x);
            if (val == 0) {  /* not yet visited */
                SET_DATA_BIT(linem1[y + 1], x);  /* mark visited */
                val = GET_DATA_BIT(lines1[y + 1], x);
                if (val == 0) {  /* bg, not a wall */
                    SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH);  /* parent N */
                    el = mazeelCreate(x, y + 1, 0);
                    lqueueAdd(lq, el);
                }
            }
        }
        FREE(elp);
    }

    lqueueDestroy(&lq, TRUE);
    pixDestroy(&pixm);
    FREE(linem1);

    if (ppixd) {
        pixd = pixUnpackBinary(pixs, 32, 1);
        *ppixd = pixd;
    }
    composeRGBPixel(255, 0, 0, &rpixel);  /* start point */
    composeRGBPixel(0, 255, 0, &gpixel);
    composeRGBPixel(0, 0, 255, &bpixel);  /* end point */


    if (!found) {
        L_INFO(" No path found", procName);
        if (pixd) {  /* paint all visited locations */
            lined32 = pixGetLinePtrs(pixd, NULL);
            for (i = 0; i < h; i++) {
                for (j = 0; j < w; j++) {
                    val = GET_DATA_BYTE(linep8[i], j);
                    if (val != 0 && pixd)
                        SET_DATA_FOUR_BYTES(lined32[i], j, gpixel);
                }
            }
            FREE(lined32);
        }
    }
    else {   /* write path onto pixd */
        L_INFO(" Path found", procName);
        pta = ptaCreate(0);
        x = xf;
        y = yf;
        while (1) {
            ptaAddPt(pta, x, y);
            if (x == xi && y == yi)
                break;
            if (pixd)
                pixSetPixel(pixd, x, y, gpixel);
            pixGetPixel(pixp, x, y, &val);
            if (val == DIR_NORTH)
                y--;
            else if (val == DIR_SOUTH)
                y++;
            else if (val == DIR_EAST)
                x++;
            else if (val == DIR_WEST)
                x--;
        }
    }
    if (pixd) {
        pixSetPixel(pixd, xi, yi, rpixel);
        pixSetPixel(pixd, xf, yf, bpixel);
    }

    pixDestroy(&pixp);
    FREE(lines1);
    FREE(linep8);
    return pta;
}
Пример #3
0
/*!
 *  identifyWatershedBasin()
 *
 *      Input:  wshed
 *              index (index of basin to be located)
 *              level (of basin at point at which the two basins met)
 *              &box (<return> bounding box of basin)
 *              &pixd (<return> pix of basin, cropped to its bounding box)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This is a static function, so we assume pixlab, pixs and pixt
 *          exist and are the same size.
 *      (2) It selects all pixels that have the label @index in pixlab
 *          and that have a value in pixs that is less than @level.
 *      (3) It is used whenever two seeded basins meet (typically at a saddle),
 *          or when one seeded basin meets a 'filler'.  All identified
 *          basins are saved as a watershed.
 */
static l_int32
identifyWatershedBasin(L_WSHED *wshed,
                       l_int32 index,
                       l_int32 level,
                       BOX **pbox,
                       PIX **ppixd) {
    l_int32 imin, imax, jmin, jmax, minx, miny, maxx, maxy;
    l_int32 bw, bh, i, j, w, h, x, y;
    l_int32 *lut;
    l_uint32 label, bval, lval;
    void **lines8, **linelab32, **linet1;
    BOX *box;
    PIX *pixs, *pixt, *pixd;
    L_QUEUE *lq;

    PROCNAME("identifyWatershedBasin");

    if (!pbox)
        return ERROR_INT("&box not defined", procName, 1);
    *pbox = NULL;
    if (!ppixd)
        return ERROR_INT("&pixd not defined", procName, 1);
    *ppixd = NULL;
    if (!wshed)
        return ERROR_INT("wshed not defined", procName, 1);

    /* Make a queue and an auxiliary stack */
    lq = lqueueCreate(0);
    lq->stack = lstackCreate(0);

    pixs = wshed->pixs;
    pixt = wshed->pixt;
    lines8 = wshed->lines8;
    linelab32 = wshed->linelab32;
    linet1 = wshed->linet1;
    lut = wshed->lut;
    pixGetDimensions(pixs, &w, &h, NULL);

    /* Prime the queue with the seed pixel for this watershed. */
    minx = miny = 1000000;
    maxx = maxy = 0;
    ptaGetIPt(wshed->ptas, index, &x, &y);
    pixSetPixel(pixt, x, y, 1);
    pushNewPixel(lq, x, y, &minx, &maxx, &miny, &maxy);
    if (wshed->debug) fprintf(stderr, "prime: (x,y) = (%d, %d)\n", x, y);

    /* Each pixel in a spreading breadth-first search is inspected.
     * It is accepted as part of this watershed, and pushed on
     * the search queue, if:
     *     (1) It has a label value equal to @index
     *     (2) The pixel value is less than @level, the overflow
     *         height at which the two basins join.
     *     (3) It has not yet been seen in this search.  */
    while (lqueueGetCount(lq) > 0) {
        popNewPixel(lq, &x, &y);
        imin = L_MAX(0, y - 1);
        imax = L_MIN(h - 1, y + 1);
        jmin = L_MAX(0, x - 1);
        jmax = L_MIN(w - 1, x + 1);
        for (i = imin; i <= imax; i++) {
            for (j = jmin; j <= jmax; j++) {
                if (j == x && i == y) continue;  /* parent */
                label = GET_DATA_FOUR_BYTES(linelab32[i], j);
                if (label == MAX_LABEL_VALUE || lut[label] != index) continue;
                bval = GET_DATA_BIT(linet1[i], j);
                if (bval == 1) continue;  /* already seen */
                lval = GET_DATA_BYTE(lines8[i], j);
                if (lval >= level) continue;  /* too high */
                SET_DATA_BIT(linet1[i], j);
                pushNewPixel(lq, j, i, &minx, &maxx, &miny, &maxy);
            }
        }
    }

    /* Extract the box and pix, and clear pixt */
    bw = maxx - minx + 1;
    bh = maxy - miny + 1;
    box = boxCreate(minx, miny, bw, bh);
    pixd = pixClipRectangle(pixt, box, NULL);
    pixRasterop(pixt, minx, miny, bw, bh, PIX_SRC ^ PIX_DST, pixd, 0, 0);
    *pbox = box;
    *ppixd = pixd;

    lqueueDestroy(&lq, 1);
    return 0;
}