/*! * bmfGetWidth() * * Input: bmf * chr (should be one of the 95 supported bitmaps) * &w (<return> character width; -1 if not printable) * Return: 0 if OK, 1 on error */ l_int32 bmfGetWidth(L_BMF *bmf, char chr, l_int32 *pw) { l_int32 i, index; PIXA *pixa; PROCNAME("bmfGetWidth"); if (!pw) return ERROR_INT("&w not defined", procName, 1); *pw = -1; if (!bmf) return ERROR_INT("bmf not defined", procName, 1); if ((index = (l_int32)chr) == 10) /* NL */ return 0; i = bmf->fonttab[index]; if (i == UNDEF) { L_ERROR("no bitmap representation for %d\n", procName, index); return 1; } if ((pixa = bmf->pixa) == NULL) return ERROR_INT("pixa not found", procName, 1); return pixaGetPixDimensions(pixa, i, pw, NULL, NULL); }
/* ----------------------------------------------------- */ void PixaDisplayNumbered(PIXA *pixa, const char *basename) { char buf[64]; l_int32 fill, color, d; L_BMF *bmf; PIX *pix1; PIXA *pixa1, *pixa2; bmf = bmfCreate(NULL, 4); pixaGetPixDimensions(pixa, 0, NULL, NULL, &d); fill = (d == 8) ? 0xff : 0; color = (d == 8) ? 0x00000000 : 0xffffff00; pixa1 = pixaAddBorderGeneral(NULL, pixa, 10, 10, 0, 0, fill); pixa2 = pixaAddTextNumber(pixa1, bmf, NULL, color, L_ADD_BELOW); snprintf(buf, sizeof(buf), "%s.pa", basename); pixaWrite(buf, pixa2); pix1 = pixaDisplayTiledInColumns(pixa2, 20, 2.5, 15, 2); snprintf(buf, sizeof(buf), "%s.png", basename); pixWrite(buf, pix1, IFF_PNG); pixDisplay(pix1, 500, 500); pixDestroy(&pix1); pixaDestroy(&pixa1); pixaDestroy(&pixa2); bmfDestroy(&bmf); }
/*! * \brief recogRescoreDidResult() * * \param[in] recog with LUT's pre-computed * \param[out] ppixdb [optional] debug result; can be null * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This does correlation matching with all templates using the * viterbi path segmentation. * </pre> */ static l_int32 recogRescoreDidResult(L_RECOG *recog, PIX **ppixdb) { l_int32 i, n, w2, h1, templ, x, xloc, dely, index; char *text; l_float32 score; BOX *box1; PIX *pixs, *pix1; L_RDID *did; PROCNAME("recogRescoreDidResult"); if (ppixdb) *ppixdb = NULL; if (!recog) return ERROR_INT("recog not defined", procName, 1); if ((did = recogGetDid(recog)) == NULL) return ERROR_INT("did not defined", procName, 1); if (did->fullarrays == 0) return ERROR_INT("did full arrays not made", procName, 1); if ((n = numaGetCount(did->naxloc)) == 0) return ERROR_INT("no elements in path", procName, 1); pixs = did->pixs; h1 = pixGetHeight(pixs); for (i = 0; i < n; i++) { numaGetIValue(did->natempl, i, &templ); numaGetIValue(did->naxloc, i, &xloc); numaGetIValue(did->nadely, i, &dely); pixaGetPixDimensions(recog->pixa_u, templ, &w2, NULL, NULL); /* TODO: try to fix xloc - 4, etc. */ x = L_MAX(xloc, 0); box1 = boxCreate(x, dely, w2, h1); pix1 = pixClipRectangle(pixs, box1, NULL); recogIdentifyPix(recog, pix1, NULL); recogTransferRchToDid(recog, x, dely); if (ppixdb) { rchExtract(recog->rch, &index, &score, &text, NULL, NULL, NULL, NULL); fprintf(stderr, "text = %s, index = %d, score = %5.3f\n", text, index, score); } pixDestroy(&pix1); boxDestroy(&box1); LEPT_FREE(text); } /* numaWriteStream(stderr, recog->did->nadely_r); */ if (ppixdb) *ppixdb = recogShowPath(recog, 1); return 0; }
/*! * pixaDisplayUnsplit() * * Input: pixa * nx (number of mosaic cells horizontally) * ny (number of mosaic cells vertically) * borderwidth (of added border on all sides) * bordercolor (in our RGBA format: 0xrrggbbaa) * Return: pix of tiled images, or null on error * * Notes: * (1) This is a logical inverse of pixaSplitPix(). It * constructs a pix from a mosaic of tiles, all of equal size. * (2) For added generality, a border of arbitrary color can * be added to each of the tiles. * (3) In use, pixa will typically have either been generated * from pixaSplitPix() or will derived from a pixa that * was so generated. * (4) All pix in the pixa must be of equal depth, and, if * colormapped, have the same colormap. */ PIX * pixaDisplayUnsplit(PIXA *pixa, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor) { l_int32 w, h, d, wt, ht; l_int32 i, j, k, x, y, n; PIX *pixt, *pixd; PROCNAME("pixaDisplayUnsplit"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if (nx <= 0 || ny <= 0) return (PIX *)ERROR_PTR("nx and ny must be > 0", procName, NULL); if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); if (n != nx * ny) return (PIX *)ERROR_PTR("n != nx * ny", procName, NULL); borderwidth = L_MAX(0, borderwidth); pixaGetPixDimensions(pixa, 0, &wt, &ht, &d); w = nx * (wt + 2 * borderwidth); h = ny * (ht + 2 * borderwidth); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixt = pixaGetPix(pixa, 0, L_CLONE); pixCopyColormap(pixd, pixt); pixDestroy(&pixt); if (borderwidth > 0) pixSetAllArbitrary(pixd, bordercolor); y = borderwidth; for (i = 0, k = 0; i < ny; i++) { x = borderwidth; for (j = 0; j < nx; j++, k++) { pixt = pixaGetPix(pixa, k, L_CLONE); pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pixt, 0, 0); pixDestroy(&pixt); x += wt + 2 * borderwidth; } y += ht + 2 * borderwidth; } return pixd; }
/*! * selaAddTJunctions() * * Input: sela (<optional>) * hlsize (length of each line of hits from origin) * mdist (distance of misses from the origin) * norient (number of orientations; max of 8) * debugflag (1 for debug output) * Return: sela with additional sels, or null on error * * Notes: * (1) Adds hitmiss Sels for the T-junction of two lines. * If the lines are very thin, they must be nearly orthogonal * to register. * (2) The number of Sels generated is 4 * @norient. * (3) It is suggested that @hlsize be chosen at least 1 greater * than @mdist. Try values of (@hlsize, @mdist) such as * (6,5), (7,6), (8,7), (9,7), etc. */ SELA * selaAddTJunctions(SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag) { char name[L_BUF_SIZE]; l_int32 i, j, k, w, xc, yc; l_float64 pi, halfpi, radincr, jang, radang; l_float64 angle[3], dist[3]; PIX *pixc, *pixm, *pixt; PIXA *pixa; PTA *pta1, *pta2, *pta3; SEL *sel; PROCNAME("selaAddTJunctions"); if (hlsize <= 2) return (SELA *)ERROR_PTR("hlsizel not > 1", procName, NULL); if (norient < 1 || norient > 8) return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL); if (!sela) { if ((sela = selaCreate(0)) == NULL) return (SELA *)ERROR_PTR("sela not made", procName, NULL); } pi = 3.1415926535; halfpi = 3.1415926535 / 2.0; radincr = halfpi / (l_float32)norient; w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5)); if (w % 2 == 0) w++; xc = w / 2; yc = w / 2; pixa = pixaCreate(4 * norient); for (i = 0; i < norient; i++) { for (j = 0; j < 4; j++) { /* 4 orthogonal orientations */ jang = (l_float32)j * halfpi; /* Set the don't cares */ pixc = pixCreate(w, w, 32); pixSetAll(pixc); /* Add the green lines of hits */ pixm = pixCreate(w, w, 1); radang = (l_float32)i * radincr; pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang); pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang + halfpi); pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang + pi); ptaJoin(pta1, pta2, 0, -1); ptaJoin(pta1, pta3, 0, -1); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); /* Add red misses between the lines */ angle[0] = radang + jang - halfpi; angle[1] = radang + jang + 0.5 * halfpi; angle[2] = radang + jang + 1.5 * halfpi; dist[0] = 0.8 * mdist; dist[1] = dist[2] = mdist; for (k = 0; k < 3; k++) { pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])), yc + (l_int32)(dist[k] * sin(angle[k])), 0xff000000); } /* Add dark green for origin */ pixSetPixel(pixc, xc, yc, 0x00550000); /* Generate the sel */ sel = selCreateFromColorPix(pixc, NULL); sprintf(name, "sel_cross_%d", 4 * i + j); selaAddSel(sela, sel, name, 0); if (debugflag) { pixt = pixScaleBySampling(pixc, 10.0, 10.0); pixaAddPix(pixa, pixt, L_INSERT); } pixDestroy(&pixm); pixDestroy(&pixc); } } if (debugflag) { l_int32 w; pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2); pixWriteTempfile("/tmp", "tsel1.png", pixt, IFF_PNG, 0); pixDisplay(pixt, 0, 100); pixDestroy(&pixt); pixt = selaDisplayInPix(sela, 15, 2, 20, 4); pixWriteTempfile("/tmp", "tsel2.png", pixt, IFF_PNG, 0); pixDisplay(pixt, 500, 100); pixDestroy(&pixt); selaWriteStream(stderr, sela); } pixaDestroy(&pixa); return sela; }
/*! * pixaDisplayTiledInRows() * * Input: pixa * outdepth (output depth: 1, 8 or 32 bpp) * maxwidth (of output image) * scalefactor (applied to every pix; use 1.0 for no scaling) * background (0 for white, 1 for black; this is the color * of the spacing between the images) * spacing (between images, and on outside) * border (width of black border added to each image; * use 0 for no border) * Return: pixd (of tiled images), or null on error * * Notes: * (1) This saves a pixa to a single image file of width not to * exceed maxwidth, with background color either white or black, * and with each row tiled such that the top of each pix is * aligned and separated by 'spacing' from the next one. * A black border can be added to each pix. * (2) All pix are converted to outdepth; existing colormaps are removed. * (3) This does a reasonably spacewise-efficient job of laying * out the individual pix images into a tiled composite. */ PIX * pixaDisplayTiledInRows(PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border) { l_int32 h; /* cumulative height over all the rows */ l_int32 w; /* cumulative height in the current row */ l_int32 bordval, wtry, wt, ht; l_int32 irow; /* index of current pix in current row */ l_int32 wmaxrow; /* width of the largest row */ l_int32 maxh; /* max height in row */ l_int32 i, j, index, n, x, y, nrows, ninrow; NUMA *nainrow; /* number of pix in the row */ NUMA *namaxh; /* height of max pix in the row */ PIX *pix, *pixn, *pixt, *pixd; PIXA *pixan; PROCNAME("pixaDisplayTiledInRows"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if (outdepth != 1 && outdepth != 8 && outdepth != 32) return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); if (border < 0) border = 0; if (scalefactor <= 0.0) scalefactor = 1.0; if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Normalize depths, scale, remove colormaps; optionally add border */ pixan = pixaCreate(n); bordval = (outdepth == 1) ? 1 : 0; for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) continue; if (outdepth == 1) pixn = pixConvertTo1(pix, 128); else if (outdepth == 8) pixn = pixConvertTo8(pix, FALSE); else /* outdepth == 32 */ pixn = pixConvertTo32(pix); pixDestroy(&pix); if (scalefactor != 1.0) pixt = pixScale(pixn, scalefactor, scalefactor); else pixt = pixClone(pixn); if (border) pixd = pixAddBorder(pixt, border, bordval); else pixd = pixClone(pixt); pixDestroy(&pixn); pixDestroy(&pixt); pixaAddPix(pixan, pixd, L_INSERT); } if (pixaGetCount(pixan) != n) { n = pixaGetCount(pixan); L_WARNING_INT("only got %d components", procName, n); if (n == 0) { pixaDestroy(&pixan); return (PIX *)ERROR_PTR("no components", procName, NULL); } } /* Compute parameters for layout */ nainrow = numaCreate(0); namaxh = numaCreate(0); wmaxrow = 0; w = h = spacing; maxh = 0; /* max height in row */ for (i = 0, irow = 0; i < n; i++, irow++) { pixaGetPixDimensions(pixan, i, &wt, &ht, NULL); wtry = w + wt + spacing; if (wtry > maxwidth) { /* end the current row and start next one */ numaAddNumber(nainrow, irow); numaAddNumber(namaxh, maxh); wmaxrow = L_MAX(wmaxrow, w); h += maxh + spacing; irow = 0; w = wt + 2 * spacing; maxh = ht; } else { w = wtry; maxh = L_MAX(maxh, ht); } } /* Enter the parameters for the last row */ numaAddNumber(nainrow, irow); numaAddNumber(namaxh, maxh); wmaxrow = L_MAX(wmaxrow, w); h += maxh + spacing; if ((pixd = pixCreate(wmaxrow, h, outdepth)) == NULL) { numaDestroy(&nainrow); numaDestroy(&namaxh); pixaDestroy(&pixan); return (PIX *)ERROR_PTR("pixd not made", procName, NULL); } /* Reset the background color if necessary */ if ((background == 1 && outdepth == 1) || (background == 0 && outdepth != 1)) pixSetAll(pixd); /* Blit the images to the dest */ nrows = numaGetCount(nainrow); y = spacing; for (i = 0, index = 0; i < nrows; i++) { /* over rows */ numaGetIValue(nainrow, i, &ninrow); numaGetIValue(namaxh, i, &maxh); x = spacing; for (j = 0; j < ninrow; j++, index++) { /* over pix in row */ pix = pixaGetPix(pixan, index, L_CLONE); pixGetDimensions(pix, &wt, &ht, NULL); pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0); pixDestroy(&pix); x += wt + spacing; } y += maxh + spacing; } numaDestroy(&nainrow); numaDestroy(&namaxh); pixaDestroy(&pixan); return pixd; }
/*! * pixaDisplayOnLattice() * * Input: pixa * xspace * yspace * Return: pix of composite images, or null on error * * Notes: * (1) This places each pix on sequentially on a regular lattice * in the rendered composite. If a pix is too large to fit in the * allocated lattice space, it is not rendered. * (2) If any pix has a colormap, all pix are rendered in rgb. * (3) This is useful when putting bitmaps of components, * such as characters, into a single image. */ PIX * pixaDisplayOnLattice(PIXA *pixa, l_int32 xspace, l_int32 yspace) { l_int32 n, nw, nh, w, h, d, wt, ht; l_int32 index, i, j, hascmap; PIX *pix, *pixt, *pixd; PIXA *pixat; PROCNAME("pixaDisplayOnLattice"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); /* If any pix have colormaps, generate rgb */ if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); pixaAnyColormaps(pixa, &hascmap); if (hascmap) { pixat = pixaCreate(n); for (i = 0; i < n; i++) { pixt = pixaGetPix(pixa, i, L_CLONE); pix = pixConvertTo32(pixt); pixaAddPix(pixat, pix, L_INSERT); pixDestroy(&pixt); } } else pixat = pixaCopy(pixa, L_CLONE); nw = (l_int32)sqrt((l_float64)n); nh = (n + nw - 1) / nw; w = xspace * nw; h = yspace * nh; /* Use the first pix in pixa to determine the depth. */ pixaGetPixDimensions(pixat, 0, NULL, NULL, &d); if ((pixd = pixCreate(w, h, d)) == NULL) { pixaDestroy(&pixat); return (PIX *)ERROR_PTR("pixd not made", procName, NULL); } index = 0; for (i = 0; i < nh; i++) { for (j = 0; j < nw && index < n; j++, index++) { pixt = pixaGetPix(pixat, index, L_CLONE); pixGetDimensions(pixt, &wt, &ht, NULL); if (wt > xspace || ht > yspace) { fprintf(stderr, "pix(%d) omitted; size %dx%d\n", index, wt, ht); pixDestroy(&pixt); continue; } pixRasterop(pixd, j * xspace, i * yspace, wt, ht, PIX_PAINT, pixt, 0, 0); pixDestroy(&pixt); } } pixaDestroy(&pixat); return pixd; }
/*! * \brief selaAddCrossJunctions() * * \param[in] sela [optional] * \param[in] hlsize length of each line of hits from origin * \param[in] mdist distance of misses from the origin * \param[in] norient number of orientations; max of 8 * \param[in] debugflag 1 for debug output * \return sela with additional sels, or NULL on error * * <pre> * Notes: * (1) Adds hitmiss Sels for the intersection of two lines. * If the lines are very thin, they must be nearly orthogonal * to register. * (2) The number of Sels generated is equal to %norient. * (3) If %norient == 2, this generates 2 Sels of crosses, each with * two perpendicular lines of hits. One Sel has horizontal and * vertical hits; the other has hits along lines at +-45 degrees. * Likewise, if %norient == 3, this generates 3 Sels of crosses * oriented at 30 degrees with each other. * (4) It is suggested that %hlsize be chosen at least 1 greater * than %mdist. Try values of (%hlsize, %mdist) such as * (6,5), (7,6), (8,7), (9,7), etc. * </pre> */ SELA * selaAddCrossJunctions(SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag) { char name[L_BUF_SIZE]; l_int32 i, j, w, xc, yc; l_float64 pi, halfpi, radincr, radang; l_float64 angle; PIX *pixc, *pixm, *pixt; PIXA *pixa; PTA *pta1, *pta2, *pta3, *pta4; SEL *sel; PROCNAME("selaAddCrossJunctions"); if (hlsize <= 0) return (SELA *)ERROR_PTR("hlsize not > 0", procName, NULL); if (norient < 1 || norient > 8) return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL); if (!sela) { if ((sela = selaCreate(0)) == NULL) return (SELA *)ERROR_PTR("sela not made", procName, NULL); } pi = 3.1415926535; halfpi = 3.1415926535 / 2.0; radincr = halfpi / (l_float64)norient; w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5)); if (w % 2 == 0) w++; xc = w / 2; yc = w / 2; pixa = pixaCreate(norient); for (i = 0; i < norient; i++) { /* Set the don't cares */ pixc = pixCreate(w, w, 32); pixSetAll(pixc); /* Add the green lines of hits */ pixm = pixCreate(w, w, 1); radang = (l_float32)i * radincr; pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang); pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi); pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi); pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi); ptaJoin(pta1, pta2, 0, -1); ptaJoin(pta1, pta3, 0, -1); ptaJoin(pta1, pta4, 0, -1); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); ptaDestroy(&pta4); /* Add red misses between the lines */ for (j = 0; j < 4; j++) { angle = radang + (j - 0.5) * halfpi; pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)), yc + (l_int32)(mdist * sin(angle)), 0xff000000); } /* Add dark green for origin */ pixSetPixel(pixc, xc, yc, 0x00550000); /* Generate the sel */ sel = selCreateFromColorPix(pixc, NULL); sprintf(name, "sel_cross_%d", i); selaAddSel(sela, sel, name, 0); if (debugflag) { pixt = pixScaleBySampling(pixc, 10.0, 10.0); pixaAddPix(pixa, pixt, L_INSERT); } pixDestroy(&pixm); pixDestroy(&pixc); } if (debugflag) { l_int32 w; lept_mkdir("lept/sel"); pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2); pixWrite("/tmp/lept/sel/xsel1.png", pixt, IFF_PNG); pixDisplay(pixt, 0, 100); pixDestroy(&pixt); pixt = selaDisplayInPix(sela, 15, 2, 20, 1); pixWrite("/tmp/lept/sel/xsel2.png", pixt, IFF_PNG); pixDisplay(pixt, 500, 100); pixDestroy(&pixt); selaWriteStream(stderr, sela); } pixaDestroy(&pixa); return sela; }