/*! * \brief pixaSetStrokeWidth() * * \param[in] pixas of 1 bpp pix * \param[in] width set stroke width to this value, in [1 ... 100]. * \param[in] thinfirst 1 to thin all pix to a skeleton first; 0 to skip * \param[in] connectivity 4 or 8, to be used if %thinfirst == 1 * \return pixa with all stroke widths being %width, or NULL on error * * <pre> * Notes: * (1) If %thinfirst == 1, thin to a skeleton using the specified * %connectivity. Use %thinfirst == 0 if all pix in pixas * have already been thinned as far as possible. * (2) The image is dilated to the required %width. This dilation * is not connectivity preserving, so this is typically * used in a situation where merging of c.c. in the individual * pix is not a problem; e.g., where each pix is a single c.c. * </pre> */ PIXA * pixaSetStrokeWidth(PIXA *pixas, l_int32 width, l_int32 thinfirst, l_int32 connectivity) { l_int32 i, n, maxd, same; PIX *pix1, *pix2; PIXA *pixad; PROCNAME("pixaSetStrokeWidth"); if (!pixas) return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); if (width < 1 || width > 100) return (PIXA *)ERROR_PTR("width not in [1 ... 100]", procName, NULL); if (connectivity != 4 && connectivity != 8) return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); pixaVerifyDepth(pixas, &same, &maxd); if (maxd > 1) return (PIXA *)ERROR_PTR("pix are not all 1 bpp", procName, NULL); n = pixaGetCount(pixas); pixad = pixaCreate(n); for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixas, i, L_CLONE); pix2 = pixSetStrokeWidth(pix1, width, thinfirst, connectivity); pixaAddPix(pixad, pix2, L_INSERT); pixDestroy(&pix1); } return pixad; }
/*! * \brief pixaModifyStrokeWidth() * * \param[in] pixas of 1 bpp pix * \param[out] targetw desired width for strokes in each pix * \return pixa with modified stroke widths, or NULL on error */ PIXA * pixaModifyStrokeWidth(PIXA *pixas, l_float32 targetw) { l_int32 i, n, same, maxd; l_float32 width; NUMA *na; PIX *pix1, *pix2; PIXA *pixad; PROCNAME("pixaModifyStrokeWidth"); if (!pixas) return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); if (targetw < 1) return (PIXA *)ERROR_PTR("target width < 1", procName, NULL); pixaVerifyDepth(pixas, &same, &maxd); if (maxd > 1) return (PIXA *)ERROR_PTR("pix not all 1 bpp", procName, NULL); na = pixaFindStrokeWidth(pixas, 0.1, NULL, 0); n = pixaGetCount(pixas); pixad = pixaCreate(n); for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixas, i, L_CLONE); numaGetFValue(na, i, &width); pix2 = pixModifyStrokeWidth(pix1, width, targetw); pixaAddPix(pixad, pix2, L_INSERT); pixDestroy(&pix1); } numaDestroy(&na); return pixad; }
/*! * \brief pixaFindStrokeWidth() * * \param[in] pixa of 1 bpp images * \param[in] thresh fractional count threshold relative to distance 1 * \param[in] tab8 [optional] table for counting fg pixels; can be NULL * \param[in] debug 1 for debug output; 0 to skip * \return na array of stroke widths for each pix in %pixa; NULL on error * * <pre> * Notes: * (1) See pixFindStrokeWidth() for details. * </pre> */ NUMA * pixaFindStrokeWidth(PIXA *pixa, l_float32 thresh, l_int32 *tab8, l_int32 debug) { l_int32 i, n, same, maxd; l_int32 *tab; l_float32 width; NUMA *na; PIX *pix; PROCNAME("pixaFindStrokeWidth"); if (!pixa) return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); pixaVerifyDepth(pixa, &same, &maxd); if (maxd > 1) return (NUMA *)ERROR_PTR("pix not all 1 bpp", procName, NULL); tab = (tab8) ? tab8 : makePixelSumTab8(); n = pixaGetCount(pixa); na = numaCreate(n); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, L_CLONE); pixFindStrokeWidth(pix, thresh, tab8, &width, NULL); numaAddNumber(na, width); pixDestroy(&pix); } if (!tab8) LEPT_FREE(tab); return na; }
/*! * recogCreateFromPixa() * * Input: pixa (of labelled, 1 bpp images) * scalew (scale all widths to this; use 0 for no scaling) * scaleh (scale all heights to this; use 0 for no scaling) * templ_type (L_USE_AVERAGE or L_USE_ALL) * threshold (for binarization; typically ~128) * maxyshift (from nominal centroid alignment; typically 0 or 1) * fontdir (<optional> directory for bitmap fonts for debugging) * Return: recog, or null on error * * Notes: * (1) This is a convenience function for training from labelled data. * The pixa can be read from file. * (2) The pixa should contain the unscaled bitmaps used for training. * (3) The characters here should work as a single "font", because * each image example is put into a class defined by its * character label. All examples in the same class should be * similar. */ L_RECOG * recogCreateFromPixa(PIXA *pixa, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift, const char *fontdir) { char *text; l_int32 full, n, i, ntext; L_RECOG *recog; PIX *pix; PROCNAME("recogCreateFromPixa"); if (!pixa) return (L_RECOG *)ERROR_PTR("pixa not defined", procName, NULL); if (pixaVerifyDepth(pixa, NULL) != 1) return (L_RECOG *)ERROR_PTR("not all pix are 1 bpp", procName, NULL); pixaIsFull(pixa, &full, NULL); if (!full) return (L_RECOG *)ERROR_PTR("not all pix are present", procName, NULL); n = pixaGetCount(pixa); pixaCountText(pixa, &ntext); if (ntext == 0) return (L_RECOG *)ERROR_PTR("no pix have text strings", procName, NULL); if (ntext < n) L_ERROR("%d text strings < %d pix\n", procName, ntext, n); recog = recogCreate(scalew, scaleh, templ_type, threshold, maxyshift, fontdir); if (!recog) return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, L_CLONE); text = pixGetText(pix); if (!text || strlen(text) == 0) { L_ERROR("pix[%d] has no text\n", procName, i); pixDestroy(&pix); continue; } recogTrainLabelled(recog, pix, NULL, text, 0, 0); pixDestroy(&pix); } recogTrainingFinished(recog, 0); return recog; }
int main(int argc, char **argv) { char buf[32]; char *filein, *fileout, *fontdir, *textstr; l_int32 n, i, maxdepth, ntext, border, lossless, display, showtext; l_float32 scalefact; L_BMF *bmf; PIX *pix1, *pix2, *pix3, *pix4, *pixd; PIXA *pixa, *pixad; static char mainName[] = "displaypixa"; if (argc != 3 && argc != 4 && argc != 7 && argc != 8) { fprintf(stderr, "Syntax error in displaypixa:\n" " displaypixa filein fileout [showtext]\n" " displaypixa filein scalefact border" " lossless disp fileout [showtext]\n"); return 1; } filein = argv[1]; if ((pixa = pixaRead(filein)) == NULL) return ERROR_INT("pixa not made", mainName, 1); pixaCountText(pixa, &ntext); if (argc == 3 || argc == 4) fileout = argv[2]; if (argc == 4) showtext = atoi(argv[3]); /* Simple specification; no output text */ if (argc == 3 || (argc == 4 && (ntext == 0 || showtext == 0))) { /* no text output */ pixaVerifyDepth(pixa, &maxdepth); pixd = pixaDisplayTiledInRows(pixa, maxdepth, 1400, 1.0, 0, 10, 0); pixDisplay(pixd, 100, 100); if (pixGetDepth(pixd) == 1) pixWrite(fileout, pixd, IFF_PNG); else pixWrite(fileout, pixd, IFF_JFIF_JPEG); pixDestroy(&pixd); pixaDestroy(&pixa); return 0; } /* Simple specification with output text */ if (argc == 4) { /* showtext == 1 && ntext > 0 */ n = pixaGetCount(pixa); bmf = bmfCreate(NULL, 6); pixad = pixaCreate(n); for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixa, i, L_CLONE); pix2 = pixConvertTo32(pix1); pix3 = pixAddBorderGeneral(pix2, 10, 10, 5, 5, 0xffffff00); textstr = pixGetText(pix1); if (textstr && strlen(textstr) > 0) { snprintf(buf, sizeof(buf), "%s", textstr); pix4 = pixAddSingleTextblock(pix3, bmf, buf, 0xff000000, L_ADD_BELOW, NULL); } else { pix4 = pixClone(pix3); } pixaAddPix(pixad, pix4, L_INSERT); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); } bmfDestroy(&bmf); pixaVerifyDepth(pixad, &maxdepth); pixd = pixaDisplayTiledInRows(pixad, maxdepth, 1400, 1.0, 0, 10, 0); pixDisplay(pixd, 100, 100); if (pixGetDepth(pixd) == 1) pixWrite(fileout, pixd, IFF_PNG); else pixWrite(fileout, pixd, IFF_JFIF_JPEG); pixDestroy(&pixd); pixaDestroy(&pixa); pixaDestroy(&pixad); return 0; } /* Full specification */ scalefact = atof(argv[2]); border = atoi(argv[3]); lossless = atoi(argv[4]); display = atoi(argv[5]); fileout = argv[6]; showtext = (argc == 8) ? atoi(argv[7]) : 0; if (showtext && ntext == 0) L_INFO("No text found in any of the pix\n", mainName); bmf = (showtext && ntext > 0) ? bmfCreate(NULL, 6) : NULL; n = pixaGetCount(pixa); pixad = pixaCreate(n); for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixa, i, L_CLONE); pix2 = pixConvertTo32(pix1); pix3 = pixAddBorderGeneral(pix2, 10, 10, 5, 5, 0xffffff00); textstr = pixGetText(pix1); if (bmf && textstr && strlen(textstr) > 0) { snprintf(buf, sizeof(buf), "%s", textstr); pix4 = pixAddSingleTextblock(pix3, bmf, buf, 0xff000000, L_ADD_BELOW, NULL); } else { pix4 = pixClone(pix3); } pixaAddPix(pixad, pix4, L_INSERT); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); } bmfDestroy(&bmf); pixaVerifyDepth(pixad, &maxdepth); pixd = pixaDisplayTiledInRows(pixad, maxdepth, 1400, scalefact, 0, 10, border); if (display) pixDisplay(pixd, 20, 20); if (pixGetDepth(pixd) == 1 || lossless) pixWrite(fileout, pixd, IFF_PNG); else pixWrite(fileout, pixd, IFF_JFIF_JPEG); pixDestroy(&pixd); pixaDestroy(&pixa); pixaDestroy(&pixad); return 0; }