/*! * pixColorSegmentCluster() * * Input: pixs (32 bpp; 24-bit color) * maxdist (max euclidean dist to existing cluster) * maxcolors (max number of colors allowed in first pass) * Return: pixd (8 bit with colormap), or null on error * * Notes: * (1) This is phase 1. See description in pixColorSegment(). * (2) Greedy unsupervised classification. If the limit 'maxcolors' * is exceeded, the computation is repeated with a larger * allowed cluster size. * (3) On each successive iteration, 'maxdist' is increased by a * constant factor. See comments in pixColorSegment() for * a guideline on parameter selection. * Note that the diagonal of the 8-bit rgb color cube is about * 440, so for 'maxdist' = 440, you are guaranteed to get 1 color! */ PIX * pixColorSegmentCluster(PIX *pixs, l_int32 maxdist, l_int32 maxcolors) { l_int32 w, h, newmaxdist, ret, niters, ncolors, success; PIX *pixd; PIXCMAP *cmap; PROCNAME("pixColorSegmentCluster"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); w = pixGetWidth(pixs); h = pixGetHeight(pixs); if ((pixd = pixCreate(w, h, 8)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); cmap = pixcmapCreate(8); pixSetColormap(pixd, cmap); pixCopyResolution(pixd, pixs); newmaxdist = maxdist; niters = 0; success = TRUE; while (1) { ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist, maxcolors); niters++; if (!ret) { ncolors = pixcmapGetCount(cmap); L_INFO_INT2("Success with %d colors after %d iters", procName, ncolors, niters); break; } if (niters == MAX_ALLOWED_ITERATIONS) { L_WARNING_INT("too many iters; newmaxdist = %d", procName, newmaxdist); success = FALSE; break; } newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist); } if (!success) { pixDestroy(&pixd); return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL); } return pixd; }
/*! * pixTilingCreate() * * Input: pixs (pix to be tiled; any depth; colormap OK) * nx (number of tiles across image) * ny (number of tiles down image) * w (desired width of each tile) * h (desired height of each tile) * overlap (amount of overlap into neighboring tile on each side) * Return: pixtiling, or null on error * * Notes: * (1) We put a clone of pixs in the PixTiling. * (2) The input to pixTilingCreate() for horizontal tiling can be * either the number of tiles across the image or the approximate * width of the tiles. If the latter, the actual width will be * determined by making all tiles but the last of equal width, and * making the last as close to the others as possible. The same * consideration is applied independently to the vertical tiling. * To specify tile width, set nx = 0; to specify the number of * tiles horizontally across the image, set w = 0. * (3) If pixs is to be tiled in one-dimensional strips, use ny = 1 for * vertical strips and nx = 1 for horizontal strips. * (4) The overlap must not be larger than the width or height of * the leftmost or topmost tile(s). */ PIXTILING * pixTilingCreate(PIX *pixs, l_int32 nx, l_int32 ny, l_int32 w, l_int32 h, l_int32 xoverlap, l_int32 yoverlap) { l_int32 width, height; PIXTILING *pt; PROCNAME("pixTilingCreate"); if (!pixs) return (PIXTILING *)ERROR_PTR("pixs not defined", procName, NULL); if (nx < 1 && w < 1) return (PIXTILING *)ERROR_PTR("invalid width spec", procName, NULL); if (ny < 1 && h < 1) return (PIXTILING *)ERROR_PTR("invalid height spec", procName, NULL); /* Find the tile width and number of tiles. All tiles except the * rightmost ones have the same width. The width of the * rightmost ones are at least the width of the others and * less than twice that width. Ditto for tile height. */ pixGetDimensions(pixs, &width, &height, NULL); if (nx == 0) nx = L_MAX(1, width / w); w = width / nx; /* possibly reset */ if (ny == 0) ny = L_MAX(1, height / h); h = height / ny; /* possibly reset */ if (xoverlap > w || yoverlap > h) { L_INFO_INT2("tile width = %d, tile height = %d", procName, w, h); return (PIXTILING *)ERROR_PTR("overlap too large", procName, NULL); } if ((pt = (PIXTILING *)CALLOC(1, sizeof(PIXTILING))) == NULL) return (PIXTILING *)ERROR_PTR("pt not made", procName, NULL); pt->pix = pixClone(pixs); pt->xoverlap = xoverlap; pt->yoverlap = yoverlap; pt->nx = nx; pt->ny = ny; pt->w = w; pt->h = h; pt->strip = TRUE; return pt; }
/*! * 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; }