/*! * recogAddAllSamples() * * Input: recog * paa (pixaa from previously trained recog) * debug * Return: 0 if OK, 1 on error * * Notes: * (1) This is used with the serialization routine recogRead(), * where each pixa in the pixaa represents a set of characters * in a different class. Two different pixa may represent * characters with the same label. Before calling this * function, we verify that the number of character classes, * given by the setsize field in recog, equals the number of * pixa in the paa. The character labels for each set are * in the sa_text field. */ static l_int32 recogAddAllSamples(L_RECOG *recog, PIXAA *paa, l_int32 debug) { char *text; l_int32 i, j, nc, ns; PIX *pix; PIXA *pixa; PROCNAME("recogAddAllSamples"); if (!recog) return ERROR_INT("recog not defined", procName, 1); if (!paa) return ERROR_INT("paa not defined", procName, 1); nc = pixaaGetCount(paa, NULL); for (i = 0; i < nc; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); ns = pixaGetCount(pixa); text = sarrayGetString(recog->sa_text, i, L_NOCOPY); for (j = 0; j < ns; j++) { pix = pixaGetPix(pixa, j, L_CLONE); if (debug) { fprintf(stderr, "pix[%d,%d]: text = %s\n", i, j, text); } pixaaAddPix(recog->pixaa_u, i, pix, NULL, L_INSERT); } pixaDestroy(&pixa); } recogTrainingFinished(recog, debug); return 0; }
/*! * \brief recogAddCharstrLabels() * * \param[in] recog * \return 0 if OK, 1 on error */ static l_int32 recogAddCharstrLabels(L_RECOG *recog) { char *text; l_int32 i, j, n1, n2; PIX *pix; PIXA *pixa; PIXAA *paa; PROCNAME("recogAddCharstrLabels"); if (!recog) return ERROR_INT("recog not defined", procName, 1); /* Add the labels to each unscaled pix */ paa = recog->pixaa_u; n1 = pixaaGetCount(paa, NULL); for (i = 0; i < n1; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); text = sarrayGetString(recog->sa_text, i, L_NOCOPY); n2 = pixaGetCount(pixa); for (j = 0; j < n2; j++) { pix = pixaGetPix(pixa, j, L_CLONE); pixSetText(pix, text); pixDestroy(&pix); } pixaDestroy(&pixa); } return 0; }
/*! * pixaaAddPixa() * * Input: pixaa * pixa (to be added) * copyflag: * L_INSERT inserts the pixa directly * L_COPY makes a new pixa and copies each pix and each box * L_CLONE gives a new handle to the input pixa * L_COPY_CLONE makes a new pixa and inserts clones of * all pix and boxes * Return: 0 if OK; 1 on error */ l_int32 pixaaAddPixa(PIXAA *pixaa, PIXA *pixa, l_int32 copyflag) { l_int32 n; PIXA *pixac; PROCNAME("pixaaAddPixa"); if (!pixaa) return ERROR_INT("pixaa not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE && copyflag != L_COPY_CLONE) return ERROR_INT("invalid copyflag", procName, 1); if (copyflag == L_INSERT) pixac = pixa; else { if ((pixac = pixaCopy(pixa, copyflag)) == NULL) return ERROR_INT("pixac not made", procName, 1); } n = pixaaGetCount(pixaa); if (n >= pixaa->nalloc) pixaaExtendArray(pixaa); pixaa->pixa[n] = pixac; pixaa->n++; return 0; }
/*! * pixaaWriteStream() * * Input: stream * pixaa * Return: 0 if OK, 1 on error */ l_int32 pixaaWriteStream(FILE *fp, PIXAA *pixaa) { l_int32 n, i; PIXA *pixa; PROCNAME("pixaaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!pixaa) return ERROR_INT("pixaa not defined", procName, 1); n = pixaaGetCount(pixaa); fprintf(fp, "\nPixaa Version %d\n", PIXAA_VERSION_NUMBER); fprintf(fp, "Number of pixa = %d\n", n); boxaWriteStream(fp, pixaa->boxa); for (i = 0; i < n; i++) { if ((pixa = pixaaGetPixa(pixaa, i, L_CLONE)) == NULL) return ERROR_INT("pixa not found", procName, 1); fprintf(fp, "\n\n --------------- pixa[%d] ---------------\n", i); pixaWriteStream(fp, pixa); pixaDestroy(&pixa); } return 0; }
/*! * recogaCreateFromPixaa() * * Input: paa (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) * Return: recoga, or null on error * * Notes: * (1) This is a convenience function for training from labelled data. * (2) Each pixa in the paa is a set of labelled data that is used * to train a recognizer (e.g., for a set of characters in a font). * Each image DC in the pixa is put into a class in its * recognizer, defined by its character label. All examples in * the same class should be similar. * (3) The pixaa can be written by recogaWritePixaa(), and must contain * the unscaled bitmaps used for training. */ L_RECOGA * recogaCreateFromPixaa(PIXAA *paa, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift) { l_int32 n, i, full; L_RECOG *recog; L_RECOGA *recoga; PIXA *pixa; PROCNAME("recogaCreateFromPixaa"); if (!paa) return (L_RECOGA *)ERROR_PTR("paa not defined", procName, NULL); if (pixaaVerifyDepth(paa, NULL) != 1) return (L_RECOGA *)ERROR_PTR("all pix not 1 bpp", procName, NULL); pixaaIsFull(paa, &full); if (!full) return (L_RECOGA *)ERROR_PTR("all pix not present", procName, NULL); n = pixaaGetCount(paa, NULL); recoga = recogaCreate(n); for (i = 0; i < n; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); recog = recogCreateFromPixa(pixa, scalew, scaleh, templ_type, threshold, maxyshift); recogaAddRecog(recoga, recog); pixaDestroy(&pixa); } return recoga; }
/*! * \brief recogAddAllSamples() * * \param[in] precog addr of recog * \param[in] paa pixaa from previously trained recog * \param[in] debug * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) On error, the input recog is destroyed. * (2) This is used with the serialization routine recogRead(), * where each pixa in the pixaa represents a set of characters * in a different class. Before calling this function, we have * verified that the number of character classes, given by the * setsize field in %recog, equals the number of pixa in the paa. * The character labels for each set are in the sa_text field. * </pre> */ static l_int32 recogAddAllSamples(L_RECOG **precog, PIXAA *paa, l_int32 debug) { char *text; l_int32 i, j, nc, ns; PIX *pix; PIXA *pixa, *pixa1; L_RECOG *recog; PROCNAME("recogAddAllSamples"); if (!precog) return ERROR_INT("&recog not defined", procName, 1); if ((recog = *precog) == NULL) return ERROR_INT("recog not defined", procName, 1); if (!paa) { recogDestroy(&recog); return ERROR_INT("paa not defined", procName, 1); } nc = pixaaGetCount(paa, NULL); for (i = 0; i < nc; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); ns = pixaGetCount(pixa); text = sarrayGetString(recog->sa_text, i, L_NOCOPY); pixa1 = pixaCreate(ns); pixaaAddPixa(recog->pixaa_u, pixa1, L_INSERT); for (j = 0; j < ns; j++) { pix = pixaGetPix(pixa, j, L_CLONE); if (debug) fprintf(stderr, "pix[%d,%d]: text = %s\n", i, j, text); pixaaAddPix(recog->pixaa_u, i, pix, NULL, L_INSERT); } pixaDestroy(&pixa); } recogTrainingFinished(&recog, 0, -1, -1.0); /* For second parameter, see comment in recogRead() */ if (!recog) return ERROR_INT("bad templates; recog destroyed", procName, 1); return 0; }
/*! * pixaaFlattenToPixa() * * Input: pixaa * &naindex (<optional return> the pixa index in the pixaa) * copyflag (L_COPY or L_CLONE) * Return: pixa, or null on error * * Notes: * (1) This 'flattens' the pixaa to a pixa, taking the pix in * order in the first pixa, then the second, etc. * (2) If &naindex is defined, we generate a Numa that gives, for * each pix in the pixaa, the index of the pixa to which it belongs. */ PIXA * pixaaFlattenToPixa(PIXAA *pixaa, NUMA **pnaindex, l_int32 copyflag) { l_int32 i, j, m, n; BOX *box; NUMA *naindex; PIX *pix; PIXA *pixa, *pixat; PROCNAME("pixaaFlattenToPixa"); if (pnaindex) *pnaindex = NULL; if (!pixaa) return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL); if (copyflag != L_COPY && copyflag != L_CLONE) return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); if (pnaindex) { naindex = numaCreate(0); *pnaindex = naindex; } n = pixaaGetCount(pixaa); pixa = pixaCreate(n); for (i = 0; i < n; i++) { pixat = pixaaGetPixa(pixaa, i, L_CLONE); m = pixaGetCount(pixat); for (j = 0; j < m; j++) { pix = pixaGetPix(pixat, j, copyflag); box = pixaGetBox(pixat, j, copyflag); pixaAddPix(pixa, pix, L_INSERT); pixaAddBox(pixa, box, L_INSERT); if (pnaindex) numaAddNumber(naindex, i); /* save 'row' number */ } pixaDestroy(&pixat); } return pixa; }
/*! * pixaaDisplayTiledAndScaled() * * Input: pixaa * outdepth (output depth: 1, 8 or 32 bpp) * tilewidth (each pix is scaled to this width) * ncols (number of tiles in each row) * 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 additional black border on each image; * use 0 for no border) * Return: pixa (of tiled images, one image for each pixa in * the pixaa), or null on error * * Notes: * (1) For each pixa, this generates from all the pix a * tiled/scaled output pix, and puts it in the output pixa. * (2) See comments in pixaDisplayTiledAndScaled(). */ PIXA * pixaaDisplayTiledAndScaled(PIXAA *pixaa, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border) { l_int32 i, n; PIX *pix; PIXA *pixa, *pixad; PROCNAME("pixaaDisplayTiledAndScaled"); if (!pixaa) return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL); if (outdepth != 1 && outdepth != 8 && outdepth != 32) return (PIXA *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); if (border < 0 || border > tilewidth / 5) border = 0; if ((n = pixaaGetCount(pixaa)) == 0) return (PIXA *)ERROR_PTR("no components", procName, NULL); pixad = pixaCreate(n); for (i = 0; i < n; i++) { pixa = pixaaGetPixa(pixaa, i, L_CLONE); pix = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols, background, spacing, border); pixaAddPix(pixad, pix, L_INSERT); pixaDestroy(&pixa); } return pixad; }
/*! * \brief recogReadStream() * * \param[in] fp file stream * \return recog, or NULL on error */ L_RECOG * recogReadStream(FILE *fp) { l_int32 version, setsize, threshold, scalew, scaleh, linew; l_int32 maxyshift, nc; L_DNA *dna_tochar; PIXAA *paa; L_RECOG *recog; SARRAY *sa_text; PROCNAME("recogReadStream"); if (!fp) return (L_RECOG *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\nRecog Version %d\n", &version) != 1) return (L_RECOG *)ERROR_PTR("not a recog file", procName, NULL); if (version != RECOG_VERSION_NUMBER) return (L_RECOG *)ERROR_PTR("invalid recog version", procName, NULL); if (fscanf(fp, "Size of character set = %d\n", &setsize) != 1) return (L_RECOG *)ERROR_PTR("setsize not read", procName, NULL); if (fscanf(fp, "Binarization threshold = %d\n", &threshold) != 1) return (L_RECOG *)ERROR_PTR("binary thresh not read", procName, NULL); if (fscanf(fp, "Maxyshift = %d\n", &maxyshift) != 1) return (L_RECOG *)ERROR_PTR("maxyshift not read", procName, NULL); if (fscanf(fp, "Scale to width = %d\n", &scalew) != 1) return (L_RECOG *)ERROR_PTR("width not read", procName, NULL); if (fscanf(fp, "Scale to height = %d\n", &scaleh) != 1) return (L_RECOG *)ERROR_PTR("height not read", procName, NULL); if (fscanf(fp, "Normalized line width = %d\n", &linew) != 1) return (L_RECOG *)ERROR_PTR("line width not read", procName, NULL); if ((recog = recogCreate(scalew, scaleh, linew, threshold, maxyshift)) == NULL) return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL); if (fscanf(fp, "\nLabels for character set:\n") != 0) { recogDestroy(&recog); return (L_RECOG *)ERROR_PTR("label intro not read", procName, NULL); } l_dnaDestroy(&recog->dna_tochar); if ((dna_tochar = l_dnaReadStream(fp)) == NULL) { recogDestroy(&recog); return (L_RECOG *)ERROR_PTR("dna_tochar not read", procName, NULL); } recog->dna_tochar = dna_tochar; sarrayDestroy(&recog->sa_text); if ((sa_text = sarrayReadStream(fp)) == NULL) { recogDestroy(&recog); return (L_RECOG *)ERROR_PTR("sa_text not read", procName, NULL); } recog->sa_text = sa_text; if (fscanf(fp, "\nPixaa of all samples in the training set:\n") != 0) { recogDestroy(&recog); return (L_RECOG *)ERROR_PTR("pixaa intro not read", procName, NULL); } if ((paa = pixaaReadStream(fp)) == NULL) { recogDestroy(&recog); return (L_RECOG *)ERROR_PTR("pixaa not read", procName, NULL); } recog->setsize = setsize; nc = pixaaGetCount(paa, NULL); if (nc != setsize) { recogDestroy(&recog); pixaaDestroy(&paa); L_ERROR("(setsize = %d) != (paa count = %d)\n", procName, setsize, nc); return NULL; } recogAddAllSamples(&recog, paa, 0); /* this finishes */ pixaaDestroy(&paa); if (!recog) return (L_RECOG *)ERROR_PTR("bad templates", procName, NULL); return recog; }
/*! * pixaaDisplay() * * Input: pixaa * w, h (if set to 0, determines the size from the * b.b. of the components in pixaa) * Return: pix, or null on error * * Notes: * (1) Each pix of the pixaa is displayed at the location given by * its box, translated by the box of the containing pixa * if it exists. */ PIX * pixaaDisplay(PIXAA *pixaa, l_int32 w, l_int32 h) { l_int32 i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb; BOXA *boxa1; /* top-level boxa */ BOXA *boxa; PIX *pixt, *pixd; PIXA *pixa; PROCNAME("pixaaDisplay"); if (!pixaa) return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL); n = pixaaGetCount(pixaa); if (n == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* If w and h not input, determine the minimum size required * to contain the origin and all c.c. */ boxa1 = pixaaGetBoxa(pixaa, L_CLONE); nbox = boxaGetCount(boxa1); if (w == 0 || h == 0) { if (nbox == n) boxaGetExtent(boxa1, &w, &h, NULL); else { /* have to use the lower-level boxa for each pixa */ wmax = hmax = 0; for (i = 0; i < n; i++) { pixa = pixaaGetPixa(pixaa, i, L_CLONE); boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); wmax = L_MAX(wmax, w); hmax = L_MAX(hmax, h); pixaDestroy(&pixa); boxaDestroy(&boxa); } w = wmax; h = hmax; } } /* Get depth from first pix */ pixa = pixaaGetPixa(pixaa, 0, L_CLONE); pixt = pixaGetPix(pixa, 0, L_CLONE); d = pixGetDepth(pixt); pixaDestroy(&pixa); pixDestroy(&pixt); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); x = y = 0; for (i = 0; i < n; i++) { pixa = pixaaGetPixa(pixaa, i, L_CLONE); if (nbox == n) boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL); na = pixaGetCount(pixa); for (j = 0; j < na; j++) { pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb); pixt = pixaGetPix(pixa, j, L_CLONE); pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pixt, 0, 0); pixDestroy(&pixt); } pixaDestroy(&pixa); } boxaDestroy(&boxa1); return pixd; }
/*! * pixaaDisplayByPixa() * * Input: pixaa * xspace between pix in pixa * yspace between pixa * max width of output pix * Return: pix, or null on error * * Notes: * (1) Displays each pixa on a line (or set of lines), * in order from top to bottom. Within each pixa, * the pix are displayed in order from left to right. * (2) The size of each pix in each pixa is assumed to be * approximately equal to the size of the first pix in * the pixa. If this assumption is not correct, this * function will not work properly. * (3) This ignores the boxa of the pixaa. */ PIX * pixaaDisplayByPixa(PIXAA *pixaa, l_int32 xspace, l_int32 yspace, l_int32 maxw) { l_int32 i, j, npixa, npix; l_int32 width, height, depth, nlines, lwidth; l_int32 x, y, w, h, w0, h0; PIX *pixt, *pixd; PIXA *pixa; PROCNAME("pixaaDisplayByPixa"); if (!pixaa) return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL); if ((npixa = pixaaGetCount(pixaa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Get size of output pix. The width is the minimum of the * maxw and the largest pixa line width. The height is whatever * it needs to be to accommodate all pixa. */ height = 2 * yspace; width = 0; for (i = 0; i < npixa; i++) { pixa = pixaaGetPixa(pixaa, i, L_CLONE); npix = pixaGetCount(pixa); pixt = pixaGetPix(pixa, 0, L_CLONE); if (i == 0) depth = pixGetDepth(pixt); w = pixGetWidth(pixt); lwidth = npix * (w + xspace); nlines = (lwidth + maxw - 1) / maxw; if (nlines > 1) width = maxw; else width = L_MAX(lwidth, width); height += nlines * (pixGetHeight(pixt) + yspace); pixDestroy(&pixt); pixaDestroy(&pixa); } if ((pixd = pixCreate(width, height, depth)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); /* Now layout the pix by pixa */ y = yspace; for (i = 0; i < npixa; i++) { x = 0; pixa = pixaaGetPixa(pixaa, i, L_CLONE); npix = pixaGetCount(pixa); for (j = 0; j < npix; j++) { pixt = pixaGetPix(pixa, j, L_CLONE); if (j == 0) { w0 = pixGetWidth(pixt); h0 = pixGetHeight(pixt); } w = pixGetWidth(pixt); if (width == maxw && x + w >= maxw) { x = 0; y += h0 + yspace; } h = pixGetHeight(pixt); pixRasterop(pixd, x, y, w, h, PIX_PAINT, pixt, 0, 0); pixDestroy(&pixt); x += w0 + xspace; } y += h0 + yspace; pixaDestroy(&pixa); } return pixd; }