/*! * pixaaCreateFromPixa() * * Input: pixa * n (number specifying subdivision of pixa) * type (L_CHOOSE_CONSECUTIVE, L_CHOOSE_SKIP_BY) * copyflag (L_CLONE, L_COPY) * Return: pixaa, or null on error * * Notes: * (1) This subdivides a pixa into a set of smaller pixa that * are accumulated into a pixaa. * (2) If type == L_CHOOSE_CONSECUTIVE, the first 'n' pix are * put in a pixa and added to pixaa, then the next 'n', etc. * If type == L_CHOOSE_SKIP_BY, the first pixa is made by * aggregating pix[0], pix[n], pix[2*n], etc. * (3) The copyflag specifies if each new pix is a copy or a clone. */ PIXAA * pixaaCreateFromPixa(PIXA *pixa, l_int32 n, l_int32 type, l_int32 copyflag) { l_int32 count, i, j, npixa; PIX *pix; PIXA *pixat; PIXAA *pixaa; PROCNAME("pixaaCreateFromPixa"); if (!pixa) return (PIXAA *)ERROR_PTR("pixa not defined", procName, NULL); count = pixaGetCount(pixa); if (count == 0) return (PIXAA *)ERROR_PTR("no pix in pixa", procName, NULL); if (n <= 0) return (PIXAA *)ERROR_PTR("n must be > 0", procName, NULL); if (type != L_CHOOSE_CONSECUTIVE && type != L_CHOOSE_SKIP_BY) return (PIXAA *)ERROR_PTR("invalid type", procName, NULL); if (copyflag != L_CLONE && copyflag != L_COPY) return (PIXAA *)ERROR_PTR("invalid copyflag", procName, NULL); if (type == L_CHOOSE_CONSECUTIVE) npixa = (count + n - 1) / n; else /* L_CHOOSE_SKIP_BY */ npixa = L_MIN(n, count); pixaa = pixaaCreate(npixa); if (type == L_CHOOSE_CONSECUTIVE) { for (i = 0; i < count; i++) { if (i % n == 0) pixat = pixaCreate(n); pix = pixaGetPix(pixa, i, copyflag); pixaAddPix(pixat, pix, L_INSERT); if (i % n == n - 1) pixaaAddPixa(pixaa, pixat, L_INSERT); } if (i % n != 0) pixaaAddPixa(pixaa, pixat, L_INSERT); } else { /* L_CHOOSE_SKIP_BY */ for (i = 0; i < npixa; i++) { pixat = pixaCreate(count / npixa + 1); for (j = i; j < count; j += n) { pix = pixaGetPix(pixa, j, copyflag); pixaAddPix(pixat, pix, L_INSERT); } pixaaAddPixa(pixaa, pixat, L_INSERT); } } return pixaa; }
/*! * recogaWritePixaa() * * Input: filename * recoga * Return: 0 if OK, 1 on error * * Notes: * (1) For each recognizer, this generates a pixa of all the * unscaled images. They are combined into a pixaa for * the set of recognizers. Each pix has has its character * string in the pix text field. * (2) As a side-effect, the character class label is written * into each pix in recog. */ l_int32 recogaWritePixaa(const char *filename, L_RECOGA *recoga) { l_int32 i; PIXA *pixa; PIXAA *paa; L_RECOG *recog; PROCNAME("recogaWritePixaa"); if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!recoga) return ERROR_INT("recoga not defined", procName, 1); paa = pixaaCreate(recoga->n); for (i = 0; i < recoga->n; i++) { recog = recogaGetRecog(recoga, i); recogAddCharstrLabels(recog); pixa = pixaaFlattenToPixa(recog->pixaa_u, NULL, L_CLONE); pixaaAddPixa(paa, pixa, L_INSERT); } pixaaWrite(filename, paa); pixaaDestroy(&paa); return 0; }
void CopyStoreClean(PIXA *pixas, l_int32 nlevels, l_int32 ncopies) { l_int32 i, j; PIX *pix, *pixt; PIXA *pixa; PIXAA *paa; paa = pixaaCreate(0); for (i = 0; i < nlevels ; i++) { pixa = pixaCreate(0); pixaaAddPixa(paa, pixa, L_INSERT); pix = pixaGetPix(pixas, i, L_CLONE); for (j = 0; j < ncopies; j++) { pixt = pixCopy(NULL, pix); pixaAddPix(pixa, pixt, L_INSERT); } pixDestroy(&pix); } pixaaDestroy(&paa); return; }
/*! * pixaSort2dByIndex() * * Input: pixas * naa (numaa that maps from the new pixaa to the input pixas) * copyflag (L_CLONE or L_COPY) * Return: pixaa (sorted), or null on error */ PIXAA * pixaSort2dByIndex(PIXA *pixas, NUMAA *naa, l_int32 copyflag) { l_int32 pixtot, ntot, i, j, n, nn, index; BOX *box; NUMA *na; PIX *pix; PIXA *pixa; PIXAA *pixaa; PROCNAME("pixaSort2dByIndex"); if (!pixas) return (PIXAA *)ERROR_PTR("pixas not defined", procName, NULL); if (!naa) return (PIXAA *)ERROR_PTR("naindex not defined", procName, NULL); /* Check counts */ ntot = numaaGetNumberCount(naa); pixtot = pixaGetCount(pixas); if (ntot != pixtot) return (PIXAA *)ERROR_PTR("element count mismatch", procName, NULL); n = numaaGetCount(naa); pixaa = pixaaCreate(n); for (i = 0; i < n; i++) { na = numaaGetNuma(naa, i, L_CLONE); nn = numaGetCount(na); pixa = pixaCreate(nn); for (j = 0; j < nn; j++) { numaGetIValue(na, j, &index); pix = pixaGetPix(pixas, index, copyflag); box = pixaGetBox(pixas, index, copyflag); pixaAddPix(pixa, pix, L_INSERT); pixaAddBox(pixa, box, L_INSERT); } pixaaAddPixa(pixaa, pixa, L_INSERT); numaDestroy(&na); } return pixaa; }
/*! * \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; }
/*! * pixaaReadStream() * * Input: stream * Return: pixaa, or null on error */ PIXAA * pixaaReadStream(FILE *fp) { l_int32 n, i, version; l_int32 ignore; BOXA *boxa; PIXA *pixa; PIXAA *pixaa; PROCNAME("pixaaReadStream"); if (!fp) return (PIXAA *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\nPixaa Version %d\n", &version) != 1) return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); if (version != PIXAA_VERSION_NUMBER) return (PIXAA *)ERROR_PTR("invalid pixaa version", procName, NULL); if (fscanf(fp, "Number of pixa = %d\n", &n) != 1) return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); if ((pixaa = pixaaCreate(n)) == NULL) return (PIXAA *)ERROR_PTR("pixaa not made", procName, NULL); if ((boxa = boxaReadStream(fp)) == NULL) return (PIXAA *)ERROR_PTR("boxa not made", procName, NULL); boxaDestroy(&pixaa->boxa); pixaa->boxa = boxa; for (i = 0; i < n; i++) { if ((fscanf(fp, "\n\n --------------- pixa[%d] ---------------\n", &ignore)) != 1) { return (PIXAA *)ERROR_PTR("text reading", procName, NULL); } if ((pixa = pixaReadStream(fp)) == NULL) return (PIXAA *)ERROR_PTR("pixa not read", procName, NULL); pixaaAddPixa(pixaa, pixa, L_INSERT); } return pixaa; }
int main(int argc, char **argv) { l_int32 w, h, d, w2, h2, i, ncols, ret; l_float32 angle, conf; BOX *box; BOXA *boxa, *boxa2; PIX *pix, *pixs, *pixb, *pixb2, *pixd; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6; PIXA *pixam; /* mask with a single component over each column */ PIXA *pixac, *pixad, *pixat; PIXAA *pixaa, *pixaa2; SEL *selsplit; static char mainName[] = "arabic_lines"; if (argc != 1) return ERROR_INT(" Syntax: arabic_lines", mainName, 1); pixDisplayWrite(NULL, -1); /* init debug output */ /* Binarize input */ pixs = pixRead("arabic.png"); pixGetDimensions(pixs, &w, &h, &d); pix = pixConvertTo1(pixs, 128); /* Deskew */ pixb = pixFindSkewAndDeskew(pix, 1, &angle, &conf); pixDestroy(&pix); fprintf(stderr, "Skew angle: %7.2f degrees; %6.2f conf\n", angle, conf); pixDisplayWrite(pixb, 1); /* Use full image morphology to find columns, at 2x reduction. This only works for very simple layouts where each column of text extends the full height of the input image. */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pix1 = pixMorphCompSequence(pixb2, "c5.500", 0); boxa = pixConnComp(pix1, &pixam, 8); ncols = boxaGetCount(boxa); fprintf(stderr, "Num columns: %d\n", ncols); pixDisplayWrite(pix1, 1); /* Use selective region-based morphology to get the textline mask. */ pixad = pixaMorphSequenceByRegion(pixb2, pixam, "c100.3", 0, 0); pixGetDimensions(pixb2, &w2, &h2, NULL); pix2 = pixaDisplay(pixad, w2, h2); pixDisplayWrite(pix2, 1); pixDestroy(&pix2); /* Some of the lines may be touching, so use a HMT to split the lines in each column, and use a pixaa to save the results. */ selsplit = selCreateFromString(seltext, 17, 7, "selsplit"); pixaa = pixaaCreate(ncols); for (i = 0; i < ncols; i++) { pix3 = pixaGetPix(pixad, i, L_CLONE); box = pixaGetBox(pixad, i, L_COPY); pix4 = pixHMT(NULL, pix3, selsplit); pixXor(pix4, pix4, pix3); boxa2 = pixConnComp(pix4, &pixac, 8); pixaaAddPixa(pixaa, pixac, L_INSERT); pixaaAddBox(pixaa, box, L_INSERT); pix5 = pixaDisplayRandomCmap(pixac, 0, 0); pixDisplayWrite(pix5, 1); fprintf(stderr, "Num textlines in col %d: %d\n", i, boxaGetCount(boxa2)); pixDestroy(&pix5); pixDestroy(&pix3); pixDestroy(&pix4); boxaDestroy(&boxa2); } /* Visual output */ ret = system("gthumb /tmp/display/file* &"); pixat = pixaReadFiles("/tmp/display", "file"); pix5 = selDisplayInPix(selsplit, 31, 2); pixaAddPix(pixat, pix5, L_INSERT); pix6 = pixaDisplayTiledAndScaled(pixat, 32, 400, 3, 0, 35, 3); pixWrite("/tmp/result.png", pix6, IFF_PNG); pixaDestroy(&pixat); pixDestroy(&pix6); /* Test pixaa I/O */ pixaaWrite("/tmp/pixaa", pixaa); pixaa2 = pixaaRead("/tmp/pixaa"); pixaaWrite("/tmp/pixaa2", pixaa2); /* Test pixaa display */ pixd = pixaaDisplay(pixaa, w2, h2); pixWrite("/tmp/textlines.png", pixd, IFF_PNG); pixDestroy(&pixd); /* Cleanup */ pixDestroy(&pixb2); pixDestroy(&pix1); pixaDestroy(&pixam); pixaDestroy(&pixad); pixaaDestroy(&pixaa); pixaaDestroy(&pixaa2); boxaDestroy(&boxa); selDestroy(&selsplit); pixDestroy(&pixs); pixDestroy(&pixb); return 0; }
main(int argc, char **argv) { char *filein, *fileout; l_int32 w, h, d, w2, h2, i, ncols; l_float32 angle, conf; BOX *box; BOXA *boxa, *boxas, *boxad, *boxa2; NUMA *numa; PIX *pixs, *pixt, *pixb, *pixb2, *pixd; PIX *pixtlm, *pixvws; PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; PIXA *pixam, *pixac, *pixad, *pixat; PIXAA *pixaa, *pixaa2; PTA *pta; SEL *selsplit; static char mainName[] = "textlinemask"; if (argc != 3) exit(ERROR_INT(" Syntax: textlinemask filein fileout", mainName, 1)); filein = argv[1]; fileout = argv[2]; pixDisplayWrite(NULL, -1); /* init debug output */ if ((pixs = pixRead(filein)) == NULL) return ERROR_INT("pixs not made", mainName, 1); pixGetDimensions(pixs, &w, &h, &d); /* Binarize input */ if (d == 8) pixt = pixThresholdToBinary(pixs, 128); else if (d == 1) pixt = pixClone(pixs); else { fprintf(stderr, "depth is %d\n", d); exit(1); } /* Deskew */ pixb = pixFindSkewAndDeskew(pixt, 1, &angle, &conf); pixDestroy(&pixt); fprintf(stderr, "Skew angle: %7.2f degrees; %6.2f conf\n", angle, conf); pixDisplayWrite(pixb, DEBUG_OUTPUT); #if 1 /* Use full image morphology to find columns, at 2x reduction. * This only works for very simple layouts where each column * of text extends the full height of the input image. * pixam has a pix component over each column. */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pixt1 = pixMorphCompSequence(pixb2, "c5.500", 0); boxa = pixConnComp(pixt1, &pixam, 8); ncols = boxaGetCount(boxa); fprintf(stderr, "Num columns: %d\n", ncols); pixDisplayWrite(pixt1, DEBUG_OUTPUT); /* Use selective region-based morphology to get the textline mask. */ pixad = pixaMorphSequenceByRegion(pixb2, pixam, "c100.3", 0, 0); pixGetDimensions(pixb2, &w2, &h2, NULL); if (DEBUG_OUTPUT) { pixt2 = pixaDisplay(pixad, w2, h2); pixDisplayWrite(pixt2, DEBUG_OUTPUT); pixDestroy(&pixt2); } /* Some of the lines may be touching, so use a HMT to split the * lines in each column, and use a pixaa to save the results. */ selsplit = selCreateFromString(seltext, 17, 7, "selsplit"); pixaa = pixaaCreate(ncols); for (i = 0; i < ncols; i++) { pixt3 = pixaGetPix(pixad, i, L_CLONE); box = pixaGetBox(pixad, i, L_COPY); pixt4 = pixHMT(NULL, pixt3, selsplit); pixXor(pixt4, pixt4, pixt3); boxa2 = pixConnComp(pixt4, &pixac, 8); pixaaAddPixa(pixaa, pixac, L_INSERT); pixaaAddBox(pixaa, box, L_INSERT); if (DEBUG_OUTPUT) { pixt5 = pixaDisplayRandomCmap(pixac, 0, 0); pixDisplayWrite(pixt5, DEBUG_OUTPUT); fprintf(stderr, "Num textlines in col %d: %d\n", i, boxaGetCount(boxa2)); pixDestroy(&pixt5); } pixDestroy(&pixt3); pixDestroy(&pixt4); boxaDestroy(&boxa2); } /* Visual output */ if (DEBUG_OUTPUT) { pixDisplayMultiple("/tmp/junk_write_display*"); pixat = pixaReadFiles("/tmp", "junk_write_display"); pixt5 = selDisplayInPix(selsplit, 31, 2); pixaAddPix(pixat, pixt5, L_INSERT); pixt6 = pixaDisplayTiledAndScaled(pixat, 32, 400, 3, 0, 35, 3); pixWrite(fileout, pixt6, IFF_PNG); pixaDestroy(&pixat); pixDestroy(&pixt6); } /* Test pixaa I/O */ pixaaWrite("/tmp/junkpixaa", pixaa); pixaa2 = pixaaRead("/tmp/junkpixaa"); pixaaWrite("/tmp/junkpixaa2", pixaa2); /* Test pixaa display */ pixd = pixaaDisplay(pixaa, w2, h2); pixWrite("/tmp/junkdisplay", pixd, IFF_PNG); pixDestroy(&pixd); /* Cleanup */ pixDestroy(&pixb2); pixDestroy(&pixt1); pixaDestroy(&pixam); pixaDestroy(&pixad); pixaaDestroy(&pixaa); pixaaDestroy(&pixaa2); boxaDestroy(&boxa); selDestroy(&selsplit); #endif #if 0 /* Use the baseline finder; not really what is needed */ numa = pixFindBaselines(pixb, &pta, 1); #endif #if 0 /* Use the textline mask function; parameters are not quite right */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pixtlm = pixGenTextlineMask(pixb2, &pixvws, NULL, 1); pixDisplay(pixtlm, 0, 100); pixDisplay(pixvws, 500, 100); pixDestroy(&pixb2); pixDestroy(&pixtlm); pixDestroy(&pixvws); #endif #if 0 /* Use the Breuel whitespace partition method; slow and we would * still need to work to extract the fg regions. */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); boxas = pixConnComp(pixb2, NULL, 8); boxad = boxaGetWhiteblocks(boxas, NULL, L_SORT_BY_HEIGHT, 3, 0.1, 200, 0.2, 0); pixd = pixDrawBoxa(pixb2, boxad, 7, 0xe0708000); pixDisplay(pixd, 100, 500); pixDestroy(&pixb2); pixDestroy(&pixd); boxaDestroy(&boxas); boxaDestroy(&boxad); #endif #if 0 /* Use morphology to find columns and then selective * region-based morphology to get the textline mask. * This is for display; we really want to get a pixa of the * specific textline masks. */ startTimer(); pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pixt1 = pixMorphCompSequence(pixb2, "c5.500", 0); /* column mask */ pixt2 = pixMorphSequenceByRegion(pixb2, pixt1, "c100.3", 8, 0, 0, &boxa); fprintf(stderr, "time = %7.3f sec\n", stopTimer()); pixDisplay(pixt1, 100, 500); pixDisplay(pixt2, 800, 500); pixDestroy(&pixb2); pixDestroy(&pixt1); pixDestroy(&pixt2); boxaDestroy(&boxa); #endif pixDestroy(&pixs); pixDestroy(&pixb); exit(0); }
// Creates new set of lines from the computed columns bool CubeLineSegmenter::AddLines(Pixa *lines) { // create an array that will hold the bounding boxes // of the concomps belonging to each line Boxaa *lines_con_comps = boxaaCreate(lines->n); if (lines_con_comps == NULL) { return false; } for (int line = 0; line < lines->n; line++) { // if the line is not valid if (ValidLine(lines->pix[line], lines->boxa->box[line]) == false) { // split it Pixa *split_lines = SplitLine(lines->pix[line], lines->boxa->box[line]); // remove the old line if (pixaRemovePix(lines, line) != 0) { return false; } line--; if (split_lines == NULL) { continue; } // add the split lines instead and move the pointer for (int s_line = 0; s_line < split_lines->n; s_line++) { Pix *sp_line = pixaGetPix(split_lines, s_line, L_CLONE); Box *sp_box = boxaGetBox(split_lines->boxa, s_line, L_CLONE); if (sp_line == NULL || sp_box == NULL) { return false; } // insert the new line if (pixaInsertPix(lines, ++line, sp_line, sp_box) != 0) { return false; } } // remove the split lines pixaDestroy(&split_lines); } } // compute the concomps bboxes of each line for (int line = 0; line < lines->n; line++) { Boxa *line_con_comps = ComputeLineConComps(lines->pix[line], lines->boxa->box[line], NULL); if (line_con_comps == NULL) { return false; } // insert it into the boxaa array if (boxaaAddBoxa(lines_con_comps, line_con_comps, L_INSERT) != 0) { return false; } } // post process the lines: // merge the contents of "small" lines info legitimate lines for (int line = 0; line < lines->n; line++) { // a small line detected if (SmallLine(lines->boxa->box[line]) == true) { // merge its components to one of the valid lines if (MergeLine(lines->pix[line], lines->boxa->box[line], lines, lines_con_comps) == true) { // remove the small line if (pixaRemovePix(lines, line) != 0) { return false; } if (boxaaRemoveBoxa(lines_con_comps, line) != 0) { return false; } line--; } } } boxaaDestroy(&lines_con_comps); // add the pix masks if (pixaaAddPixa(columns_, lines, L_INSERT) != 0) { return false; } return true; }