/*! * pixRotate180() * * Input: pixd (<optional>; can be null, equal to pixs, * or different from pixs) * pixs (all depths) * Return: pixd, or null on error * * Notes: * (1) This does a 180 rotation of the image about the center, * which is equivalent to a left-right flip about a vertical * line through the image center, followed by a top-bottom * flip about a horizontal line through the image center. * (2) There are 3 cases for input: * (a) pixd == null (creates a new pixd) * (b) pixd == pixs (in-place operation) * (c) pixd != pixs (existing pixd) * (3) For clarity, use these three patterns, respectively: * (a) pixd = pixRotate180(NULL, pixs); * (b) pixRotate180(pixs, pixs); * (c) pixRotate180(pixd, pixs); */ PIX * pixRotate180(PIX *pixd, PIX *pixs) { l_int32 d; PROCNAME("pixRotate180"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); d = pixGetDepth(pixs); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", procName, NULL); /* Prepare pixd for in-place operation */ if ((pixd = pixCopy(pixd, pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixFlipLR(pixd, pixd); pixFlipTB(pixd, pixd); return pixd; }
void RotateOrthTest(PIX *pixs, L_REGPARAMS *rp) { l_int32 zero, count; PIX *pixt, *pixd; PIXCMAP *cmap; cmap = pixGetColormap(pixs); /* Test 4 successive 90 degree rotations */ pixt = pixRotate90(pixs, 1); pixd = pixRotate90(pixt, 1); pixDestroy(&pixt); pixt = pixRotate90(pixd, 1); pixDestroy(&pixd); pixd = pixRotate90(pixt, 1); pixDestroy(&pixt); regTestComparePix(rp, pixs, pixd); if (!cmap) { pixXor(pixd, pixd, pixs); pixZero(pixd, &zero); if (zero) fprintf(stderr, "OK. Four 90-degree rotations gives I\n"); else { pixCountPixels(pixd, &count, NULL); fprintf(stderr, "Failure for four 90-degree rots; count = %d\n", count); } } pixDestroy(&pixd); /* Test 2 successive 180 degree rotations */ pixt = pixRotate180(NULL, pixs); pixRotate180(pixt, pixt); regTestComparePix(rp, pixs, pixt); if (!cmap) { pixXor(pixt, pixt, pixs); pixZero(pixt, &zero); if (zero) fprintf(stderr, "OK. Two 180-degree rotations gives I\n"); else { pixCountPixels(pixt, &count, NULL); fprintf(stderr, "Failure for two 180-degree rots; count = %d\n", count); } } pixDestroy(&pixt); /* Test 2 successive LR flips */ pixt = pixFlipLR(NULL, pixs); pixFlipLR(pixt, pixt); regTestComparePix(rp, pixs, pixt); if (!cmap) { pixXor(pixt, pixt, pixs); pixZero(pixt, &zero); if (zero) fprintf(stderr, "OK. Two LR flips gives I\n"); else { pixCountPixels(pixt, &count, NULL); fprintf(stderr, "Failure for two LR flips; count = %d\n", count); } } pixDestroy(&pixt); /* Test 2 successive TB flips */ pixt = pixFlipTB(NULL, pixs); pixFlipTB(pixt, pixt); regTestComparePix(rp, pixs, pixt); if (!cmap) { pixXor(pixt, pixt, pixs); pixZero(pixt, &zero); if (zero) fprintf(stderr, "OK. Two TB flips gives I\n"); else { pixCountPixels(pixt, &count, NULL); fprintf(stderr, "Failure for two TB flips; count = %d\n", count); } } pixDestroy(&pixt); return; }
l_int32 main(int argc, char **argv) { l_int32 bx, by, bw, bh; l_uint32 pixval; BOX *box1, *box2; BOXA *boxa; PIX *pixs, *pixm, *pixd; PIX *pix0, *pix1, *pix2, *pix3, *pix4, *pix5, *pix6; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; /* Find a mask for repainting pixels */ pixs = pixRead("amoris.2.150.jpg"); pix1 = MakeReplacementMask(pixs); boxa = pixConnCompBB(pix1, 8); box1 = boxaGetBox(boxa, 0, L_COPY); boxaDestroy(&boxa); /*--------------------------------------------------------* * Show the individual steps * *--------------------------------------------------------*/ /* Locate a good tile to use */ pixFindRepCloseTile(pixs, box1, L_VERT, 20, 30, 7, &box2, 1); pix0 = pixCopy(NULL, pix1); pixRenderBox(pix0, box2, 2, L_SET_PIXELS); /* Make a patch using this tile */ boxGetGeometry(box1, &bx, &by, &bw, &bh); pix2 = pixClipRectangle(pixs, box2, NULL); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 0 */ pixDisplayWithTitle(pix2, 400, 100, NULL, rp->display); pix3 = pixMirroredTiling(pix2, bw, bh); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 1 */ pixDisplayWithTitle(pix3, 1000, 0, NULL, rp->display); /* Paint the patch through the mask */ pixd = pixCopy(NULL, pixs); pixm = pixClipRectangle(pix1, box1, NULL); pixCombineMaskedGeneral(pixd, pix3, pixm, bx, by); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 2 */ pixDisplayWithTitle(pixd, 0, 0, NULL, rp->display); boxDestroy(&box2); pixDestroy(&pixm); pixDestroy(&pixd); pixDestroy(&pix2); /* Blend two patches and then overlay. Use the previous * tile found vertically and a new one found horizontally. */ pixFindRepCloseTile(pixs, box1, L_HORIZ, 20, 30, 7, &box2, 1); pixRenderBox(pix0, box2, 2, L_SET_PIXELS); regTestWritePixAndCheck(rp, pix0, IFF_TIFF_G4); /* 3 */ pixDisplayWithTitle(pix0, 100, 100, NULL, rp->display); pix2 = pixClipRectangle(pixs, box2, NULL); pix4 = pixMirroredTiling(pix2, bw, bh); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 4 */ pixDisplayWithTitle(pix4, 1100, 0, NULL, rp->display); pix5 = pixBlend(pix3, pix4, 0, 0, 0.5); regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 5 */ pixDisplayWithTitle(pix5, 1200, 0, NULL, rp->display); pix6 = pixClipRectangle(pix1, box1, NULL); pixd = pixCopy(NULL, pixs); pixCombineMaskedGeneral(pixd, pix5, pix6, bx, by); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 6 */ pixDisplayWithTitle(pixd, 700, 200, NULL, rp->display); boxDestroy(&box2); pixDestroy(&pixd); pixDestroy(&pix0); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); /*--------------------------------------------------------* * Show painting from a color near region * *--------------------------------------------------------*/ pix2 = pixCopy(NULL, pixs); pixGetColorNearMaskBoundary(pix2, pix1, box1, 20, &pixval, 0); pix3 = pixClipRectangle(pix1, box1, NULL); boxGetGeometry(box1, &bx, &by, NULL, NULL); pixSetMaskedGeneral(pix2, pix3, pixval, bx, by); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 7 */ pixDisplayWithTitle(pix2, 0, 0, NULL, rp->display); boxDestroy(&box1); pixDestroy(&pix2); pixDestroy(&pix3); /*--------------------------------------------------------* * Use the higher-level function * *--------------------------------------------------------*/ /* Use various tile selections and tile blending with one component */ pix2 = pixCopy(NULL, pixs); pix3 = pixCopy(NULL, pixs); pix4 = pixCopy(NULL, pixs); pixPaintSelfThroughMask(pix2, pix1, 0, 0, L_HORIZ, 30, 50, 5, 10); pixPaintSelfThroughMask(pix3, pix1, 0, 0, L_VERT, 30, 50, 5, 0); pixPaintSelfThroughMask(pixs, pix1, 0, 0, L_BOTH_DIRECTIONS, 30, 50, 5, 20); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 8 */ regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 9 */ regTestWritePixAndCheck(rp, pixs, IFF_PNG); /* 10 */ pixDisplayWithTitle(pix2, 300, 0, NULL, rp->display); pixDisplayWithTitle(pix3, 500, 0, NULL, rp->display); pixDisplayWithTitle(pixs, 700, 0, NULL, rp->display); /* Test with two components; */ pix5 = pixFlipLR(NULL, pix1); pixOr(pix5, pix5, pix1); pixPaintSelfThroughMask(pix4, pix5, 0, 0, L_BOTH_DIRECTIONS, 50, 100, 5, 9); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 11 */ pixDisplayWithTitle(pix4, 900, 0, NULL, rp->display); pixDestroy(&pixs); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); return regTestCleanup(rp); }
/*! * pixaGenerateFont() * * Input: pix (of 95 characters in 3 rows) * fontsize (4, 6, 8, ... , 20, in pts at 300 ppi) * &bl1 (<return> baseline of row 1) * &bl2 (<return> baseline of row 2) * &bl3 (<return> baseline of row 3) * Return: pixa of font bitmaps for 95 characters, or null on error * * Notes: * (1) This does all the work. See pixaGenerateFontFromFile() * for an overview. * (2) The pix is for one of the 9 fonts. @fontsize is only * used here for debugging. */ PIXA * pixaGenerateFont(PIX *pixs, l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2) { l_int32 i, j, nrows, nrowchars, nchars, h, yval; l_int32 width, height; l_int32 baseline[3]; l_int32 *tab = NULL; BOX *box, *box1, *box2; BOXA *boxar, *boxac, *boxacs; PIX *pix1, *pix2, *pixr, *pixrc, *pixc; PIXA *pixa; l_int32 n, w, inrow, top; l_int32 *ia; NUMA *na; PROCNAME("pixaGenerateFont"); if (!pbl0 || !pbl1 || !pbl2) return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); *pbl0 = *pbl1 = *pbl2 = 0; if (!pixs) return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); /* Locate the 3 rows of characters */ w = pixGetWidth(pixs); na = pixCountPixelsByRow(pixs, NULL); boxar = boxaCreate(0); n = numaGetCount(na); ia = numaGetIArray(na); inrow = 0; for (i = 0; i < n; i++) { if (!inrow && ia[i] > 0) { inrow = 1; top = i; } else if (inrow && ia[i] == 0) { inrow = 0; box = boxCreate(0, top, w, i - top); boxaAddBox(boxar, box, L_INSERT); } } FREE(ia); numaDestroy(&na); nrows = boxaGetCount(boxar); #if DEBUG_FONT_GEN L_INFO("For fontsize %s, have %d rows\n", procName, fontsize, nrows); #endif /* DEBUG_FONT_GEN */ if (nrows != 3) { L_INFO("nrows = %d; skipping fontsize %d\n", procName, nrows, fontsize); return (PIXA *)ERROR_PTR("3 rows not generated", procName, NULL); } /* Grab the character images and baseline data */ #if DEBUG_BASELINE lept_rmdir("baseline"); lept_mkdir("baseline"); #endif /* DEBUG_BASELINE */ tab = makePixelSumTab8(); pixa = pixaCreate(95); for (i = 0; i < nrows; i++) { box = boxaGetBox(boxar, i, L_CLONE); pixr = pixClipRectangle(pixs, box, NULL); /* row of chars */ pixGetTextBaseline(pixr, tab, &yval); baseline[i] = yval; #if DEBUG_BASELINE L_INFO("Baseline info: row %d, yval = %d, h = %d\n", procName, i, yval, pixGetHeight(pixr)); pix1 = pixCopy(NULL, pixr); pixRenderLine(pix1, 0, yval, pixGetWidth(pix1), yval, 1, L_FLIP_PIXELS); if (i == 0 ) pixWrite("/tmp/baseline/row0.png", pix1, IFF_PNG); else if (i == 1) pixWrite("/tmp/baseline/row1.png", pix1, IFF_PNG); else pixWrite("/tmp/baseline/row2.png", pix1, IFF_PNG); pixDestroy(&pix1); #endif /* DEBUG_BASELINE */ boxDestroy(&box); pixrc = pixCloseSafeBrick(NULL, pixr, 1, 35); boxac = pixConnComp(pixrc, NULL, 8); boxacs = boxaSort(boxac, L_SORT_BY_X, L_SORT_INCREASING, NULL); if (i == 0) { /* consolidate the two components of '"' */ box1 = boxaGetBox(boxacs, 1, L_CLONE); box2 = boxaGetBox(boxacs, 2, L_CLONE); box1->w = box2->x + box2->w - box1->x; /* increase width */ boxDestroy(&box1); boxDestroy(&box2); boxaRemoveBox(boxacs, 2); } h = pixGetHeight(pixr); nrowchars = boxaGetCount(boxacs); for (j = 0; j < nrowchars; j++) { box = boxaGetBox(boxacs, j, L_COPY); if (box->w <= 2 && box->h == 1) { /* skip 1x1, 2x1 components */ boxDestroy(&box); continue; } box->y = 0; box->h = h - 1; pixc = pixClipRectangle(pixr, box, NULL); boxDestroy(&box); if (i == 0 && j == 0) /* add a pix for the space; change later */ pixaAddPix(pixa, pixc, L_COPY); if (i == 2 && j == 0) /* add a pix for the '\'; change later */ pixaAddPix(pixa, pixc, L_COPY); pixaAddPix(pixa, pixc, L_INSERT); } pixDestroy(&pixr); pixDestroy(&pixrc); boxaDestroy(&boxac); boxaDestroy(&boxacs); } FREE(tab); nchars = pixaGetCount(pixa); if (nchars != 95) return (PIXA *)ERROR_PTR("95 chars not generated", procName, NULL); *pbl0 = baseline[0]; *pbl1 = baseline[1]; *pbl2 = baseline[2]; /* Fix the space character up; it should have no ON pixels, * and be about twice as wide as the '!' character. */ pix1 = pixaGetPix(pixa, 0, L_CLONE); width = 2 * pixGetWidth(pix1); height = pixGetHeight(pix1); pixDestroy(&pix1); pix1 = pixCreate(width, height, 1); pixaReplacePix(pixa, 0, pix1, NULL); /* Fix up the '\' character; use a LR flip of the '/' char */ pix1 = pixaGetPix(pixa, 15, L_CLONE); pix2 = pixFlipLR(NULL, pix1); pixDestroy(&pix1); pixaReplacePix(pixa, 60, pix2, NULL); #if DEBUG_CHARS pix1 = pixaDisplayTiled(pixa, 1500, 0, 10); pixDisplay(pix1, 100 * i, 200); pixDestroy(&pix1); #endif /* DEBUG_CHARS */ boxaDestroy(&boxar); return pixa; }
/*! * pixaGenerateFont() * * Input: dir (directory holding image of character set) * size (4, 6, 8, ... , 20, in pts at 300 ppi) * &bl1 (<return> baseline of row 1) * &bl2 (<return> baseline of row 2) * &bl3 (<return> baseline of row 3) * Return: pixa of font bitmaps for 95 characters, or null on error * * These font generation functions use 9 sets, each with bitmaps * of 94 ascii characters, all in Palatino-Roman font. * Each input bitmap has 3 rows of characters. The range of * ascii values in each row is as follows: * row 0: 32-57 (32 is a space) * row 1: 58-91 (92, '\', is not represented in this font) * row 2: 93-126 * We LR flip the '/' char to generate a bitmap for the missing * '\' character, so that we have representations of all 95 * printable chars. * * Computation of the bitmaps and baselines for a single * font takes from 40 to 200 msec on a 2 GHz processor, * depending on the size. Use pixaGetFont() to read the * generated character set directly from files that were * produced in prog/genfonts.c using this function. */ PIXA * pixaGenerateFont(const char *dir, l_int32 size, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2) { char *pathname; l_int32 fileno; l_int32 i, j, nrows, nrowchars, nchars, h, yval; l_int32 width, height; l_int32 baseline[3]; l_int32 *tab; BOX *box, *box1, *box2; BOXA *boxar, *boxac, *boxacs; PIX *pixs, *pixt1, *pixt2, *pixt3; PIX *pixr, *pixrc, *pixc; PIXA *pixa; PROCNAME("pixaGenerateFont"); if (!pbl0 || !pbl1 || !pbl2) return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); *pbl0 = *pbl1 = *pbl2 = 0; fileno = (size / 2) - 2; if (fileno < 0 || fileno > NFONTS) return (PIXA *)ERROR_PTR("font size invalid", procName, NULL); tab = makePixelSumTab8(); pathname = genPathname(dir, inputfonts[fileno]); if ((pixs = pixRead(pathname)) == NULL) return (PIXA *)ERROR_PTR("pixs not all defined", procName, NULL); FREE(pathname); pixa = pixaCreate(95); pixt1 = pixMorphSequence(pixs, "c1.35 + c101.1", 0); boxar = pixConnComp(pixt1, NULL, 8); /* one box for each row */ pixDestroy(&pixt1); nrows = boxaGetCount(boxar); #if DEBUG_FONT_GEN fprintf(stderr, "For font %s, number of rows is %d\n", inputfonts[fileno], nrows); #endif /* DEBUG_FONT_GEN */ if (nrows != 3) { L_INFO_INT2("nrows = %d; skipping font %d", procName, nrows, fileno); return (PIXA *)ERROR_PTR("3 rows not generated", procName, NULL); } for (i = 0; i < nrows; i++) { box = boxaGetBox(boxar, i, L_CLONE); pixr = pixClipRectangle(pixs, box, NULL); /* row of chars */ pixGetTextBaseline(pixr, tab, &yval); baseline[i] = yval; #if DEBUG_BASELINE { PIX *pixbl; fprintf(stderr, "row %d, yval = %d, h = %d\n", i, yval, pixGetHeight(pixr)); pixbl = pixCopy(NULL, pixr); pixRenderLine(pixbl, 0, yval, pixGetWidth(pixbl), yval, 1, L_FLIP_PIXELS); if (i == 0 ) pixWrite("junktl0", pixbl, IFF_PNG); else if (i == 1) pixWrite("junktl1", pixbl, IFF_PNG); else pixWrite("junktl2", pixbl, IFF_PNG); pixDestroy(&pixbl); } #endif /* DEBUG_BASELINE */ boxDestroy(&box); pixrc = pixCloseSafeBrick(NULL, pixr, 1, 35); boxac = pixConnComp(pixrc, NULL, 8); boxacs = boxaSort(boxac, L_SORT_BY_X, L_SORT_INCREASING, NULL); if (i == 0) { /* consolidate the two components of '"' */ box1 = boxaGetBox(boxacs, 1, L_CLONE); box2 = boxaGetBox(boxacs, 2, L_CLONE); box1->w = box2->x + box2->w - box1->x; /* increase width */ boxDestroy(&box1); boxDestroy(&box2); boxaRemoveBox(boxacs, 2); } h = pixGetHeight(pixr); nrowchars = boxaGetCount(boxacs); for (j = 0; j < nrowchars; j++) { box = boxaGetBox(boxacs, j, L_COPY); if (box->w <= 2 && box->h == 1) { /* skip 1x1, 2x1 components */ boxDestroy(&box); continue; } box->y = 0; box->h = h - 1; pixc = pixClipRectangle(pixr, box, NULL); boxDestroy(&box); if (i == 0 && j == 0) /* add a pix for the space; change later */ pixaAddPix(pixa, pixc, L_COPY); if (i == 2 && j == 0) /* add a pix for the '\'; change later */ pixaAddPix(pixa, pixc, L_COPY); pixaAddPix(pixa, pixc, L_INSERT); } pixDestroy(&pixr); pixDestroy(&pixrc); boxaDestroy(&boxac); boxaDestroy(&boxacs); } nchars = pixaGetCount(pixa); if (nchars != 95) return (PIXA *)ERROR_PTR("95 chars not generated", procName, NULL); *pbl0 = baseline[0]; *pbl1 = baseline[1]; *pbl2 = baseline[2]; /* Fix the space character up; it should have no ON pixels, * and be about twice as wide as the '!' character. */ pixt2 = pixaGetPix(pixa, 0, L_CLONE); width = 2 * pixGetWidth(pixt2); height = pixGetHeight(pixt2); pixDestroy(&pixt2); pixt2 = pixCreate(width, height, 1); pixaReplacePix(pixa, 0, pixt2, NULL); /* Fix up the '\' character; use a LR flip of the '/' char */ pixt2 = pixaGetPix(pixa, 15, L_CLONE); pixt3 = pixFlipLR(NULL, pixt2); pixDestroy(&pixt2); pixaReplacePix(pixa, 60, pixt3, NULL); #if DEBUG_CHARS { PIX *pixd; pixd = pixaDisplayTiled(pixa, 1500, 0, 10); pixDisplay(pixd, 100 * i, 200); pixDestroy(&pixd); } #endif /* DEBUG_CHARS */ pixDestroy(&pixs); boxaDestroy(&boxar); FREE(tab); return pixa; }