l_int32 renderTransformedBoxa(PIX *pixt, BOXA *boxa, l_int32 i) { l_int32 j, n, rval, gval, bval; BOX *box; n = boxaGetCount(boxa); rval = (1413 * i) % 256; gval = (4917 * i) % 256; bval = (7341 * i) % 256; for (j = 0; j < n; j++) { box = boxaGetBox(boxa, j, L_CLONE); pixRenderHashBoxArb(pixt, box, 10, 3, i % 4, 1, rval, gval, bval); boxDestroy(&box); } return 0; }
/*! * pixFindLargestRectangle() * * Input: pixs (1 bpp) * polarity (0 within background, 1 within foreground) * &box (<return> largest rectangle, either by area or * by perimeter) * debugflag (1 to output image with rectangle drawn on it) * Return: 0 if OK, 1 on error * * Notes: * (1) Why is this here? This is a simple and elegant solution to * a problem in computational geometry that at first appears * quite difficult: what is the largest rectangle that can * be placed in the image, covering only pixels of one polarity * (bg or fg)? The solution is O(n), where n is the number * of pixels in the image, and it requires nothing more than * using a simple recursion relation in a single sweep of the image. * (2) In a sweep from UL to LR with left-to-right being the fast * direction, calculate the largest white rectangle at (x, y), * using previously calculated values at pixels #1 and #2: * #1: (x, y - 1) * #2: (x - 1, y) * We also need the most recent "black" pixels that were seen * in the current row and column. * Consider the largest area. There are only two possibilities: * (a) Min(w(1), horizdist) * (h(1) + 1) * (b) Min(h(2), vertdist) * (w(2) + 1) * where * horizdist: the distance from the rightmost "black" pixel seen * in the current row across to the current pixel * vertdist: the distance from the lowest "black" pixel seen * in the current column down to the current pixel * and we choose the Max of (a) and (b). * (3) To convince yourself that these recursion relations are correct, * it helps to draw the maximum rectangles at #1 and #2. * Then for #1, you try to extend the rectangle down one line, * so that the height is h(1) + 1. Do you get the full * width of #1, w(1)? It depends on where the black pixels are * in the current row. You know the final width is bounded by w(1) * and w(2) + 1, but the actual value depends on the distribution * of black pixels in the current row that are at a distance * from the current pixel that is between these limits. * We call that value "horizdist", and the area is then given * by the expression (a) above. Using similar reasoning for #2, * where you attempt to extend the rectangle to the right * by 1 pixel, you arrive at (b). The largest rectangle is * then found by taking the Max. */ l_int32 pixFindLargestRectangle(PIX *pixs, l_int32 polarity, BOX **pbox, const char *debugfile) { l_int32 i, j, w, h, d, wpls, val; l_int32 wp, hp, w1, w2, h1, h2, wmin, hmin, area1, area2; l_int32 xmax, ymax; /* LR corner of the largest rectangle */ l_int32 maxarea, wmax, hmax, vertdist, horizdist, prevfg; l_int32 *lowestfg; l_uint32 *datas, *lines; l_uint32 **linew, **lineh; BOX *box; PIX *pixw, *pixh; /* keeps the width and height for the largest */ /* rectangles whose LR corner is located there. */ PROCNAME("pixFindLargestRectangle"); if (!pbox) return ERROR_INT("&box not defined", procName, 1); *pbox = NULL; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return ERROR_INT("pixs not 1 bpp", procName, 1); if (polarity != 0 && polarity != 1) return ERROR_INT("invalid polarity", procName, 1); /* Initialize lowest "fg" seen so far for each column */ lowestfg = (l_int32 *)CALLOC(w, sizeof(l_int32)); for (i = 0; i < w; i++) lowestfg[i] = -1; /* The combination (val ^ polarity) is the color for which we * are searching for the maximum rectangle. For polarity == 0, * we search in the bg (white). */ pixw = pixCreate(w, h, 32); /* stores width */ pixh = pixCreate(w, h, 32); /* stores height */ linew = (l_uint32 **)pixGetLinePtrs(pixw, NULL); lineh = (l_uint32 **)pixGetLinePtrs(pixh, NULL); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); maxarea = xmax = ymax = wmax = hmax = 0; for (i = 0; i < h; i++) { lines = datas + i * wpls; prevfg = -1; for (j = 0; j < w; j++) { val = GET_DATA_BIT(lines, j); if ((val ^ polarity) == 0) { /* bg (0) if polarity == 0, etc. */ if (i == 0 && j == 0) { wp = hp = 1; } else if (i == 0) { wp = linew[i][j - 1] + 1; hp = 1; } else if (j == 0) { wp = 1; hp = lineh[i - 1][j] + 1; } else { /* Expand #1 prev rectangle down */ w1 = linew[i - 1][j]; h1 = lineh[i - 1][j]; horizdist = j - prevfg; wmin = L_MIN(w1, horizdist); /* width of new rectangle */ area1 = wmin * (h1 + 1); /* Expand #2 prev rectangle to right */ w2 = linew[i][j - 1]; h2 = lineh[i][j - 1]; vertdist = i - lowestfg[j]; hmin = L_MIN(h2, vertdist); /* height of new rectangle */ area2 = hmin * (w2 + 1); if (area1 > area2) { wp = wmin; hp = h1 + 1; } else { wp = w2 + 1; hp = hmin; } } } else { /* fg (1) if polarity == 0; bg (0) if polarity == 1 */ prevfg = j; lowestfg[j] = i; wp = hp = 0; } linew[i][j] = wp; lineh[i][j] = hp; if (wp * hp > maxarea) { maxarea = wp * hp; xmax = j; ymax = i; wmax = wp; hmax = hp; } } } /* Translate from LR corner to Box coords (UL corner, w, h) */ box = boxCreate(xmax - wmax + 1, ymax - hmax + 1, wmax, hmax); *pbox = box; if (debugfile) { PIX *pixdb; pixdb = pixConvertTo8(pixs, TRUE); pixRenderHashBoxArb(pixdb, box, 6, 2, L_NEG_SLOPE_LINE, 1, 255, 0, 0); pixWrite(debugfile, pixdb, IFF_PNG); pixDestroy(&pixdb); } FREE(linew); FREE(lineh); FREE(lowestfg); pixDestroy(&pixw); pixDestroy(&pixh); return 0; }
main(int argc, char **argv) { l_int32 i, n, ws, hs, w, h, rval, gval, bval, order; l_float32 *mat1, *mat2, *mat3; l_float32 matd[9]; BOX *box, *boxt; BOXA *boxa, *boxat, *boxa1, *boxa2, *boxa3, *boxa4, *boxa5; PIX *pix, *pixs, *pixb, *pixc, *pixt, *pixt1, *pixt2, *pixt3; PIXA *pixa; static char mainName[] = "xformbox_reg"; /* ----------------------------------------------------------- * * Test hash rendering in 3 modes * * ----------------------------------------------------------- */ pixs = pixRead("feyn.tif"); box = boxCreate(461, 429, 1393, 342); pixt1 = pixClipRectangle(pixs, box, NULL); boxa = pixConnComp(pixt1, NULL, 8); n = boxaGetCount(boxa); pixt2 = pixConvertTo8(pixt1, 1); pixt3 = pixConvertTo32(pixt1); for (i = 0; i < n; i++) { boxt = boxaGetBox(boxa, i, L_CLONE); rval = (1413 * i) % 256; gval = (4917 * i) % 256; bval = (7341 * i) % 256; pixRenderHashBox(pixt1, boxt, 8, 2, i % 4, 1, L_SET_PIXELS); pixRenderHashBoxArb(pixt2, boxt, 7, 2, i % 4, 1, rval, gval, bval); pixRenderHashBoxBlend(pixt3, boxt, 7, 2, i % 4, 1, rval, gval, bval, 0.5); boxDestroy(&boxt); } pixDisplay(pixt1, 0, 0); pixDisplay(pixt2, 0, 300); pixDisplay(pixt3, 0, 570); pixWrite("/tmp/junkpixt1.png", pixt1, IFF_PNG); pixWrite("/tmp/junkpixt2.png", pixt2, IFF_PNG); pixWrite("/tmp/junkpixt3.png", pixt3, IFF_PNG); boxaDestroy(&boxa); boxDestroy(&box); pixDestroy(&pixs); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* ----------------------------------------------------------- * * Test box transforms with either translation or scaling * * combined with rotation, using the simple 'ordered' * * function. Show that the order of the operations does * * not matter; different hashing schemes end up in the * * identical boxes. * * ----------------------------------------------------------- */ pix = pixRead("feyn.tif"); box = boxCreate(420, 360, 1500, 465); pixt = pixClipRectangle(pix, box, NULL); pixs = pixAddBorderGeneral(pixt, 0, 200, 0, 0, 0); boxDestroy(&box); pixDestroy(&pix); pixDestroy(&pixt); boxa = pixConnComp(pixs, NULL, 8); n = boxaGetCount(boxa); pixa = pixaCreate(0); pixt = pixConvertTo32(pixs); for (i = 0; i < 3; i++) { if (i == 0) order = L_TR_SC_RO; else if (i == 1) order = L_TR_RO_SC; else order = L_SC_TR_RO; boxat = boxaTransformOrdered(boxa, SHIFTX_2, SHIFTY_2, 1.0, 1.0, 450, 250, ROTATION_2, order); RenderTransformedBoxa(pixt, boxat, i); boxaDestroy(&boxat); } pixSaveTiled(pixt, pixa, 1, 1, 30, 32); pixDestroy(&pixt); pixt = pixConvertTo32(pixs); for (i = 0; i < 3; i++) { if (i == 0) order = L_RO_TR_SC; else if (i == 1) order = L_RO_SC_TR; else order = L_SC_RO_TR; boxat = boxaTransformOrdered(boxa, SHIFTX_2, SHIFTY_2, 1.0, 1.0, 450, 250, ROTATION_2, order); RenderTransformedBoxa(pixt, boxat, i + 4); boxaDestroy(&boxat); } pixSaveTiled(pixt, pixa, 1, 1, 30, 0); pixDestroy(&pixt); pixt = pixConvertTo32(pixs); for (i = 0; i < 3; i++) { if (i == 0) order = L_TR_SC_RO; else if (i == 1) order = L_SC_RO_TR; else order = L_SC_TR_RO; boxat = boxaTransformOrdered(boxa, 0, 0, SCALEX_2, SCALEY_2, 450, 250, ROTATION_2, order); RenderTransformedBoxa(pixt, boxat, i + 8); boxaDestroy(&boxat); } pixSaveTiled(pixt, pixa, 1, 1, 30, 0); pixDestroy(&pixt); pixt = pixConvertTo32(pixs); for (i = 0; i < 3; i++) { if (i == 0) order = L_RO_TR_SC; else if (i == 1) order = L_RO_SC_TR; else order = L_TR_RO_SC; boxat = boxaTransformOrdered(boxa, 0, 0, SCALEX_2, SCALEY_2, 450, 250, ROTATION_2, order); RenderTransformedBoxa(pixt, boxat, i + 16); boxaDestroy(&boxat); } pixSaveTiled(pixt, pixa, 1, 1, 30, 0); pixDestroy(&pixt); pixt = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkxform1.png", pixt, IFF_PNG); pixDisplay(pixt, 1000, 0); pixDestroy(&pixt); pixDestroy(&pixs); boxaDestroy(&boxa); pixaDestroy(&pixa); /* ----------------------------------------------------------- * * Do more testing of box and pta transforms. Show that * * resulting boxes are identical by three methods. * * ----------------------------------------------------------- */ /* Set up pix and boxa */ pixa = pixaCreate(0); pix = pixRead("lucasta.1.300.tif"); pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE); pixt = pixCloseBrick(NULL, pix, 14, 5); pixOpenBrick(pixt, pixt, 1, 2); boxa = pixConnComp(pixt, NULL, 8); pixs = pixConvertTo32(pix); pixc = pixCopy(NULL, pixs); RenderTransformedBoxa(pixc, boxa, 113); pixSaveTiled(pixc, pixa, 2, 1, 30, 32); pixDestroy(&pix); pixDestroy(&pixc); pixDestroy(&pixt); /* (a) Do successive discrete operations: shift, scale, rotate */ pixt1 = pixTranslate(NULL, pixs, SHIFTX_3, SHIFTY_3, L_BRING_IN_WHITE); boxa1 = boxaTranslate(boxa, SHIFTX_3, SHIFTY_3); pixc = pixCopy(NULL, pixt1); RenderTransformedBoxa(pixc, boxa1, 213); pixSaveTiled(pixc, pixa, 2, 0, 30, 32); pixDestroy(&pixc); pixt2 = pixScale(pixt1, SCALEX_3, SCALEY_3); boxa2 = boxaScale(boxa1, SCALEX_3, SCALEY_3); pixc = pixCopy(NULL, pixt2); RenderTransformedBoxa(pixc, boxa2, 313); pixSaveTiled(pixc, pixa, 2, 1, 30, 32); pixDestroy(&pixc); pixGetDimensions(pixt2, &w, &h, NULL); pixt3 = pixRotateAM(pixt2, ROTATION_3, L_BRING_IN_WHITE); boxa3 = boxaRotate(boxa2, w / 2, h / 2, ROTATION_3); pixc = pixCopy(NULL, pixt3); RenderTransformedBoxa(pixc, boxa3, 413); pixSaveTiled(pixc, pixa, 2, 0, 30, 32); pixDestroy(&pixc); /* (b) Set up and use the composite transform */ mat1 = createMatrix2dTranslate(SHIFTX_3, SHIFTY_3); mat2 = createMatrix2dScale(SCALEX_3, SCALEY_3); mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION_3); l_productMat3(mat3, mat2, mat1, matd, 3); boxa4 = boxaAffineTransform(boxa, matd); pixc = pixCopy(NULL, pixt3); RenderTransformedBoxa(pixc, boxa4, 513); pixSaveTiled(pixc, pixa, 2, 1, 30, 32); pixDestroy(&pixc); /* (c) Use the special 'ordered' function */ pixGetDimensions(pixs, &ws, &hs, NULL); boxa5 = boxaTransformOrdered(boxa, SHIFTX_3, SHIFTY_3, SCALEX_3, SCALEY_3, ws / 2, hs / 2, ROTATION_3, L_TR_SC_RO); pixc = pixCopy(NULL, pixt3); RenderTransformedBoxa(pixc, boxa5, 613); pixSaveTiled(pixc, pixa, 2, 0, 30, 32); pixDestroy(&pixc); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); boxaDestroy(&boxa1); boxaDestroy(&boxa2); boxaDestroy(&boxa3); boxaDestroy(&boxa4); boxaDestroy(&boxa5); lept_free(mat1); lept_free(mat2); lept_free(mat3); pixt = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkxform2.png", pixt, IFF_PNG); pixDisplay(pixt, 1000, 300); pixDestroy(&pixt); pixDestroy(&pixs); boxaDestroy(&boxa); pixaDestroy(&pixa); return 0; }
main(int argc, char **argv) { l_int32 i, w, h, bx, by, bw, bh, index, rval, gval, bval; BOX *box; BOXA *boxa; PIX *pixm, *pixs, *pixg, *pixt, *pixd; PIXA *pixa; PIXCMAP *cmap; PTA *pta; PTAA *ptaa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixa = pixaCreate(0); /* ---------------- Shortest path in binary maze ---------------- */ /* Generate the maze */ pixm = generateBinaryMaze(200, 200, 20, 20, 0.65, 0.25); pixd = pixExpandBinaryReplicate(pixm, 3); pixSaveTiledOutline(pixd, pixa, 1, 1, 20, 2, 32); pixDestroy(&pixd); /* Find the shortest path between two points */ pta = pixSearchBinaryMaze(pixm, 20, 20, 170, 170, NULL); pixt = pixDisplayPta(NULL, pixm, pta); pixd = pixScaleBySampling(pixt, 3., 3.); pixSaveTiledOutline(pixd, pixa, 1, 0, 20, 2, 32); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 0 */ ptaDestroy(&pta); pixDestroy(&pixt); pixDestroy(&pixd); pixDestroy(&pixm); /* ---------------- Shortest path in gray maze ---------------- */ pixg = pixRead("test8.jpg"); pixGetDimensions(pixg, &w, &h, NULL); ptaa = ptaaCreate(NPATHS); for (i = 0; i < NPATHS; i++) { if (x0[i] >= w || x1[i] >= w || y0[i] >= h || y1[i] >= h) { fprintf(stderr, "path %d extends beyond image; skipping\n", i); continue; } pta = pixSearchGrayMaze(pixg, x0[i], y0[i], x1[i], y1[i], NULL); ptaaAddPta(ptaa, pta, L_INSERT); } pixt = pixDisplayPtaa(pixg, ptaa); pixd = pixScaleBySampling(pixt, 2., 2.); pixSaveTiledOutline(pixd, pixa, 1, 1, 20, 2, 32); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 1 */ ptaaDestroy(&ptaa); pixDestroy(&pixg); pixDestroy(&pixt); pixDestroy(&pixd); /* ---------------- Largest rectangles in image ---------------- */ pixs = pixRead("test1.png"); pixd = pixConvertTo8(pixs, FALSE); cmap = pixcmapCreateRandom(8, 1, 1); pixSetColormap(pixd, cmap); boxa = boxaCreate(0); for (i = 0; i < NBOXES; i++) { pixFindLargestRectangle(pixs, POLARITY, &box, NULL); boxGetGeometry(box, &bx, &by, &bw, &bh); pixSetInRect(pixs, box); fprintf(stderr, "bx = %5d, by = %5d, bw = %5d, bh = %5d, area = %d\n", bx, by, bw, bh, bw * bh); boxaAddBox(boxa, box, L_INSERT); } for (i = 0; i < NBOXES; i++) { index = 32 + (i & 254); pixcmapGetColor(cmap, index, &rval, &gval, &bval); box = boxaGetBox(boxa, i, L_CLONE); pixRenderHashBoxArb(pixd, box, 6, 2, L_NEG_SLOPE_LINE, 1, rval, gval, bval); boxDestroy(&box); } pixSaveTiledOutline(pixd, pixa, 1, 1, 20, 2, 32); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 2 */ pixDestroy(&pixs); pixDestroy(&pixd); boxaDestroy(&boxa); pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 3 */ pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); return regTestCleanup(rp); }