void MakeWordBoxes2(PIX *pixs, l_int32 reduction, L_REGPARAMS *rp) { l_int32 default_minwidth = 10; l_int32 default_minheight = 10; l_int32 default_maxwidth = 400; l_int32 default_maxheight = 70; l_int32 minwidth, minheight, maxwidth, maxheight; BOXA *boxa1, *boxa2; NUMA *na; PIX *pixd1, *pixd2; PIXA *pixa; minwidth = default_minwidth / reduction; minheight = default_minheight / reduction; maxwidth = default_maxwidth / reduction; maxheight = default_maxheight / reduction; /* Get the word boxes */ pixGetWordsInTextlines(pixs, reduction, minwidth, minheight, maxwidth, maxheight, &boxa1, &pixa, &na); pixaDestroy(&pixa); numaDestroy(&na); if (reduction == 1) boxa2 = boxaCopy(boxa1, L_CLONE); else boxa2 = boxaTransform(boxa1, 0, 0, 2.0, 2.0); pixd1 = pixConvertTo8(pixs, 1); pixRenderBoxaArb(pixd1, boxa2, 2, 255, 0, 0); regTestWritePixAndCheck(rp, pixd1, IFF_PNG); pixDisplayWithTitle(pixd1, 800, 100, NULL, rp->display); boxaDestroy(&boxa1); boxaDestroy(&boxa2); /* Do it again with this interface. The result should be the same. */ pixGetWordBoxesInTextlines(pixs, reduction, minwidth, minheight, maxwidth, maxheight, &boxa1, NULL); if (reduction == 1) boxa2 = boxaCopy(boxa1, L_CLONE); else boxa2 = boxaTransform(boxa1, 0, 0, 2.0, 2.0); pixd2 = pixConvertTo8(pixs, 1); pixRenderBoxaArb(pixd2, boxa2, 2, 255, 0, 0); if (regTestComparePix(rp, pixd1, pixd2)) { L_ERROR("pix not the same", "MakeWordBoxes2"); pixDisplayWithTitle(pixd2, 800, 100, NULL, rp->display); } pixDestroy(&pixd1); pixDestroy(&pixd2); boxaDestroy(&boxa1); boxaDestroy(&boxa2); return; }
void MakeWordBoxes1(PIX *pixs, l_int32 maxdil, L_REGPARAMS *rp) { BOXA *boxa; PIX *pix1, *pixd; pixWordMaskByDilation(pixs, maxdil, &pix1, NULL); pixd = NULL; if (pix1) { boxa = pixConnComp(pix1, NULL, 8); pixd = pixConvertTo8(pixs, 1); pixRenderBoxaArb(pixd, boxa, 2, 255, 0, 0); boxaDestroy(&boxa); } regTestWritePixAndCheck(rp, pixd, IFF_PNG); pixDisplayWithTitle(pixd, 0, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pixd); return; }
static PIX * DisplayBoxa(BOXA *boxa) { l_int32 w, h; BOX *box; PIX *pix1, *pix2, *pix3; PIXA *pixa; pixa = pixaCreate(2); boxaGetExtent(boxa, &w, &h, &box); pix1 = pixCreate(w, h, 1); pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS); pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixCreate(w, h, 32); pixSetAll(pix2); pixRenderBoxaArb(pix2, boxa, 2, 0, 255, 0); pixRenderBoxArb(pix2, box, 3, 255, 0, 0); pixaAddPix(pixa, pix2, L_INSERT); pix3 = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2); boxDestroy(&box); pixaDestroy(&pixa); return pix3; }
l_int32 main(int argc, char **argv) { char buf[64]; BOXA *boxa1, *boxa2, *boxa3, *boxa4; L_DEWARP *dew; L_DEWARPA *dewa; PIX *pixs, *pixn, *pixg, *pixb, *pix2, *pix3, *pix4, *pix5, *pix6; lept_mkdir("lept"); snprintf(buf, sizeof(buf), "cat.%03d.jpg", pageno); pixs = pixRead(buf); dewa = dewarpaCreate(40, 30, 1, 15, 10); dewarpaUseBothArrays(dewa, 1); /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); pixDisplay(pixb, 0, 100); /* Build the model */ dew = dewarpCreate(pixb, pageno); dewarpaInsertDewarp(dewa, dew); if (build_output) { snprintf(buf, sizeof(buf), "/tmp/lept/dewarp_build_%d.pdf", pageno); dewarpBuildPageModel(dew, buf); } else { dewarpBuildPageModel(dew, NULL); } /* Apply the model */ dewarpPopulateFullRes(dew, pixg, 0, 0); if (apply_output) { snprintf(buf, sizeof(buf), "/tmp/lept/dewarp_apply_%d.pdf", pageno); dewarpaApplyDisparity(dewa, pageno, pixb, 200, 0, 0, &pix2, buf); } else { dewarpaApplyDisparity(dewa, pageno, pixb, 200, 0, 0, &pix2, NULL); } pixDisplay(pix2, 200, 100); /* Reverse direction: get the word boxes for the dewarped pix ... */ pixGetWordBoxesInTextlines(pix2, 5, 5, 500, 100, &boxa1, NULL); pix3 = pixConvertTo32(pix2); pixRenderBoxaArb(pix3, boxa1, 2, 255, 0, 0); pixDisplay(pix3, 400, 100); /* ... and map to the word boxes for the input image */ if (map_output) { snprintf(buf, sizeof(buf), "/tmp/lept/dewarp_map1_%d.pdf", pageno); dewarpaApplyDisparityBoxa(dewa, pageno, pix2, boxa1, 0, 0, 0, &boxa2, buf); } else { dewarpaApplyDisparityBoxa(dewa, pageno, pix2, boxa1, 0, 0, 0, &boxa2, NULL); } pix4 = pixConvertTo32(pixb); pixRenderBoxaArb(pix4, boxa2, 2, 0, 255, 0); pixDisplay(pix4, 600, 100); /* Forward direction: get the word boxes for the input pix ... */ pixGetWordBoxesInTextlines(pixb, 5, 5, 500, 100, &boxa3, NULL); pix5 = pixConvertTo32(pixb); pixRenderBoxaArb(pix5, boxa3, 2, 255, 0, 0); pixDisplay(pix5, 800, 100); /* ... and map to the word boxes for the dewarped image */ if (map_output) { snprintf(buf, sizeof(buf), "/tmp/lept/dewarp_map2_%d.pdf", pageno); dewarpaApplyDisparityBoxa(dewa, pageno, pixb, boxa3, 1, 0, 0, &boxa4, buf); } else { dewarpaApplyDisparityBoxa(dewa, pageno, pixb, boxa3, 1, 0, 0, &boxa4, NULL); } pix6 = pixConvertTo32(pix2); pixRenderBoxaArb(pix6, boxa4, 2, 0, 255, 0); pixDisplay(pix6, 1000, 100); dewarpaDestroy(&dewa); pixDestroy(&pixs); pixDestroy(&pixn); pixDestroy(&pixg); pixDestroy(&pixb); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); boxaDestroy(&boxa1); boxaDestroy(&boxa2); boxaDestroy(&boxa3); boxaDestroy(&boxa4); return 0; }
/*! * \brief pixItalicWords() * * \param[in] pixs 1 bpp * \param[in] boxaw [optional] word bounding boxes; can be NULL * \param[in] pixw [optional] word box mask; can be NULL * \param[out] pboxa boxa of italic words * \param[in] debugflag 1 for debug output; 0 otherwise * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) You can input the bounding boxes for the words in one of * two forms: as bounding boxes (%boxaw) or as a word mask with * the word bounding boxes filled (%pixw). For example, * to compute %pixw, you can use pixWordMaskByDilation(). * (2) Alternatively, you can set both of these inputs to NULL, * in which case the word mask is generated here. This is * done by dilating and closing the input image to connect * letters within a word, while leaving the words separated. * The parameters are chosen under the assumption that the * input is 10 to 12 pt text, scanned at about 300 ppi. * (3) sel_ital1 and sel_ital2 detect the right edges that are * nearly vertical, at approximately the angle of italic * strokes. We use the right edge to avoid getting seeds * from lower-case 'y'. The typical italic slant has a smaller * angle with the vertical than the 'W', so in most cases we * will not trigger on the slanted lines in the 'W'. * (4) Note that sel_ital2 is shorter than sel_ital1. It is * more appropriate for a typical font scanned at 200 ppi. * </pre> */ l_int32 pixItalicWords(PIX *pixs, BOXA *boxaw, PIX *pixw, BOXA **pboxa, l_int32 debugflag) { char opstring[32]; l_int32 size; BOXA *boxa; PIX *pixsd, *pixm, *pixd; SEL *sel_ital1, *sel_ital2, *sel_ital3; PROCNAME("pixItalicWords"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pboxa) return ERROR_INT("&boxa not defined", procName, 1); if (boxaw && pixw) return ERROR_INT("both boxaw and pixw are defined", procName, 1); sel_ital1 = selCreateFromString(str_ital1, 13, 6, NULL); sel_ital2 = selCreateFromString(str_ital2, 10, 6, NULL); sel_ital3 = selCreateFromString(str_ital3, 4, 2, NULL); /* Make the italic seed: extract with HMT; remove noise. * The noise removal close/open is important to exclude * situations where a small slanted line accidentally * matches sel_ital1. */ pixsd = pixHMT(NULL, pixs, sel_ital1); pixClose(pixsd, pixsd, sel_ital3); pixOpen(pixsd, pixsd, sel_ital3); /* Make the word mask. Use input boxes or mask if given. */ size = 0; /* init */ if (boxaw) { pixm = pixCreateTemplate(pixs); pixMaskBoxa(pixm, pixm, boxaw, L_SET_PIXELS); } else if (pixw) { pixm = pixClone(pixw); } else { pixWordMaskByDilation(pixs, NULL, &size, NULL); L_INFO("dilation size = %d\n", procName, size); snprintf(opstring, sizeof(opstring), "d1.5 + c%d.1", size); pixm = pixMorphSequence(pixs, opstring, 0); } /* Binary reconstruction to fill in those word mask * components for which there is at least one seed pixel. */ pixd = pixSeedfillBinary(NULL, pixsd, pixm, 8); boxa = pixConnComp(pixd, NULL, 8); *pboxa = boxa; if (debugflag) { /* Save results at at 2x reduction */ lept_mkdir("lept/ital"); l_int32 res, upper; BOXA *boxat; GPLOT *gplot; NUMA *na; PIXA *pad; PIX *pix1, *pix2, *pix3; pad = pixaCreate(0); boxat = pixConnComp(pixm, NULL, 8); boxaWrite("/tmp/lept/ital/ital.ba", boxat); pixSaveTiledOutline(pixs, pad, 0.5, 1, 20, 2, 32); /* orig */ pixSaveTiledOutline(pixsd, pad, 0.5, 1, 20, 2, 0); /* seed */ pix1 = pixConvertTo32(pixm); pixRenderBoxaArb(pix1, boxat, 3, 255, 0, 0); pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0); /* mask + outline */ pixDestroy(&pix1); pixSaveTiledOutline(pixd, pad, 0.5, 1, 20, 2, 0); /* ital mask */ pix1 = pixConvertTo32(pixs); pixRenderBoxaArb(pix1, boxa, 3, 255, 0, 0); pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0); /* orig + outline */ pixDestroy(&pix1); pix1 = pixCreateTemplate(pixs); pix2 = pixSetBlackOrWhiteBoxa(pix1, boxa, L_SET_BLACK); pixCopy(pix1, pixs); pix3 = pixDilateBrick(NULL, pixs, 3, 3); pixCombineMasked(pix1, pix3, pix2); pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0); /* ital bolded */ pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pix2 = pixaDisplay(pad, 0, 0); pixWrite("/tmp/lept/ital/ital.png", pix2, IFF_PNG); pixDestroy(&pix2); /* Assuming the image represents 6 inches of actual page width, * the pixs resolution is approximately * (width of pixs in pixels) / 6 * and the images have been saved at half this resolution. */ res = pixGetWidth(pixs) / 12; L_INFO("resolution = %d\n", procName, res); l_pdfSetDateAndVersion(0); pixaConvertToPdf(pad, res, 1.0, L_FLATE_ENCODE, 75, "Italic Finder", "/tmp/lept/ital/ital.pdf"); l_pdfSetDateAndVersion(1); pixaDestroy(&pad); boxaDestroy(&boxat); /* Plot histogram of horizontal white run sizes. A small * initial vertical dilation removes most runs that are neither * inter-character nor inter-word. The larger first peak is * from inter-character runs, and the smaller second peak is * from inter-word runs. */ pix1 = pixDilateBrick(NULL, pixs, 1, 15); upper = L_MAX(30, 3 * size); na = pixRunHistogramMorph(pix1, L_RUN_OFF, L_HORIZ, upper); pixDestroy(&pix1); gplot = gplotCreate("/tmp/lept/ital/runhisto", GPLOT_PNG, "Histogram of horizontal runs of white pixels, vs length", "run length", "number of runs"); gplotAddPlot(gplot, NULL, na, GPLOT_LINES, "plot1"); gplotMakeOutput(gplot); gplotDestroy(&gplot); numaDestroy(&na); } selDestroy(&sel_ital1); selDestroy(&sel_ital2); selDestroy(&sel_ital3); pixDestroy(&pixsd); pixDestroy(&pixm); pixDestroy(&pixd); return 0; }
main(int argc, char **argv) { l_int32 i, j, w, h; l_int32 minsum[5] = { 2, 40, 50, 50, 70}; l_int32 skipdist[5] = { 5, 5, 10, 10, 30}; l_int32 delta[5] = { 2, 10, 10, 25, 40}; l_int32 maxbg[5] = {10, 15, 10, 20, 40}; BOX *box1, *box2, *box3, *box4; BOXA *boxa; PIX *pixs, *pixc, *pixt, *pixd, *pix32; PIXA *pixas, *pixad; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; /* Generate and save 1 bpp masks */ pixas = pixaCreate(0); pixs = pixCreate(300, 250, 1); pixSetAll(pixs); box1 = boxCreate(50, 0, 140, 25); box2 = boxCreate(120, 100, 100, 25); box3 = boxCreate(75, 170, 80, 20); box4 = boxCreate(150, 80, 25, 70); pixClearInRect(pixs, box1); pixaAddPix(pixas, pixs, L_COPY); pixt = pixRotateOrth(pixs, 1); pixaAddPix(pixas, pixt, L_INSERT); pixClearInRect(pixs, box2); pixaAddPix(pixas, pixs, L_COPY); pixt = pixRotateOrth(pixs, 1); pixaAddPix(pixas, pixt, L_INSERT); pixClearInRect(pixs, box3); pixaAddPix(pixas, pixs, L_COPY); pixt = pixRotateOrth(pixs, 1); pixaAddPix(pixas, pixt, L_INSERT); pixClearInRect(pixs, box4); pixaAddPix(pixas, pixs, L_COPY); pixt = pixRotateOrth(pixs, 1); pixaAddPix(pixas, pixt, L_INSERT); boxDestroy(&box1); boxDestroy(&box2); boxDestroy(&box3); boxDestroy(&box4); pixDestroy(&pixs); /* Do 5 splittings on each of the 8 masks */ pixad = pixaCreate(0); for (j = 0; j < 8; j++) { pixt = pixaGetPix(pixas, j, L_CLONE); pixGetDimensions(pixt, &w, &h, NULL); pix32 = pixCreate(w, h, 32); pixSetAll(pix32); pixPaintThroughMask(pix32, pixt, 0, 0, 0xc0c0c000); pixSaveTiled(pix32, pixad, 1, 1, 30, 32); for (i = 0; i < 5; i++) { pixc = pixCopy(NULL, pix32); boxa = pixSplitComponentIntoBoxa(pixt, NULL, minsum[i], skipdist[i], delta[i], maxbg[i], 0, 1); /* boxaWriteStream(stderr, boxa); */ pixd = pixBlendBoxaRandom(pixc, boxa, 0.4); pixRenderBoxaArb(pixd, boxa, 2, 255, 0, 0); pixSaveTiled(pixd, pixad, 1, 0, 30, 32); pixDestroy(&pixd); pixDestroy(&pixc); boxaDestroy(&boxa); } pixDestroy(&pixt); pixDestroy(&pix32); } /* Display results */ pixd = pixaDisplay(pixad, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 0 */ pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display); pixDestroy(&pixd); pixaDestroy(&pixad); /* Put the 8 masks all together, and split 5 ways */ pixad = pixaCreate(0); pixs = pixaDisplayOnLattice(pixas, 325, 325); pixGetDimensions(pixs, &w, &h, NULL); pix32 = pixCreate(w, h, 32); pixSetAll(pix32); pixPaintThroughMask(pix32, pixs, 0, 0, 0xc0c0c000); pixSaveTiled(pix32, pixad, 1, 1, 30, 32); for (i = 0; i < 5; i++) { pixc = pixCopy(NULL, pix32); boxa = pixSplitIntoBoxa(pixs, minsum[i], skipdist[i], delta[i], maxbg[i], 0, 1); /* boxaWriteStream(stderr, boxa); */ pixd = pixBlendBoxaRandom(pixc, boxa, 0.4); pixRenderBoxaArb(pixd, boxa, 2, 255, 0, 0); pixSaveTiled(pixd, pixad, 1, 0, 30, 32); pixDestroy(&pixd); pixDestroy(&pixc); boxaDestroy(&boxa); } pixDestroy(&pix32); pixDestroy(&pixs); /* Display results */ pixd = pixaDisplay(pixad, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 1 */ pixDisplayWithTitle(pixd, 600, 100, NULL, rp->display); pixDestroy(&pixd); pixaDestroy(&pixad); pixaDestroy(&pixas); regTestCleanup(rp); return 0; }
/*! * dewarpaApplyDisparityBoxa() * * Input: dewa * pageno (of page model to be used; may be a ref model) * pixs (initial pix reference; for alignment and debugging) * boxas (boxa to be mapped) * mapdir (1 if mapping forward from original to dewarped; * 0 if backward) * x, y (origin for generation of disparity arrays with * respect to the source region) * &boxad (<return> disparity corrected boxa) * debugfile (use null to skip writing this) * Return: 0 if OK, 1 on error (no models or ref models available) * * Notes: * (1) This applies the disparity arrays in one of two mapping directions * to the specified boxa. It can be used in the backward direction * to locate a box in the original coordinates that would have * been dewarped to to the specified image. * (2) If there is no model for @pageno, this will use the model for * 'refpage' and put the result in the dew for @pageno. * (3) This works with both stripped and full resolution page models. * If the full res disparity array(s) are missing, they are remade. * (4) If an error occurs, a copy of the input boxa is returned. */ l_int32 dewarpaApplyDisparityBoxa(L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, BOXA *boxas, l_int32 mapdir, l_int32 x, l_int32 y, BOXA **pboxad, const char *debugfile) { l_int32 debug_out; L_DEWARP *dew1, *dew; BOXA *boxav, *boxah; PIX *pixv, *pixh; PROCNAME("dewarpaApplyDisparityBoxa"); /* Initialize the output with the input, so we'll have that * in case we can't apply the page model. */ if (!pboxad) return ERROR_INT("&boxad not defined", procName, 1); *pboxad = boxaCopy(boxas, L_CLONE); /* Find the appropriate dew to use and fully populate its array(s) */ if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile)) return ERROR_INT("no model available", procName, 1); /* Correct for vertical disparity and save the result */ if ((boxav = boxaApplyDisparity(dew, boxas, L_VERT, mapdir)) == NULL) { dewarpMinimize(dew); return ERROR_INT("boxa1 not made", procName, 1); } boxaDestroy(pboxad); *pboxad = boxav; pixv = NULL; pixh = NULL; if (debugfile && mapdir != 1) L_INFO("Reverse map direction; no debug output\n", procName); debug_out = debugfile && (mapdir == 1); if (debug_out) { PIX *pix1; lept_rmdir("lept/dewboxa"); /* remove previous images */ lept_mkdir("lept/dewboxa"); pix1 = pixConvertTo32(pixs); pixRenderBoxaArb(pix1, boxas, 2, 255, 0, 0); pixWrite("/tmp/lept/dewboxa/01.png", pix1, IFF_PNG); pixDestroy(&pix1); pixv = pixApplyVertDisparity(dew, pixs, 255); pix1 = pixConvertTo32(pixv); pixRenderBoxaArb(pix1, boxav, 2, 0, 255, 0); pixWrite("/tmp/lept/dewboxa/02.png", pix1, IFF_PNG); pixDestroy(&pix1); } /* Optionally, correct for horizontal disparity */ if (dewa->useboth && dew->hsuccess) { if (dew->hvalid == FALSE) { L_INFO("invalid horiz model for page %d\n", procName, pageno); } else { boxah = boxaApplyDisparity(dew, boxav, L_HORIZ, mapdir); if (!boxah) { L_ERROR("horiz disparity fails on page %d\n", procName, pageno); } else { boxaDestroy(pboxad); *pboxad = boxah; if (debug_out) { PIX *pix1; pixh = pixApplyHorizDisparity(dew, pixv, 255); pix1 = pixConvertTo32(pixh); pixRenderBoxaArb(pix1, boxah, 2, 0, 0, 255); pixWrite("/tmp/lept/dewboxa/03.png", pix1, IFF_PNG); pixDestroy(&pixh); pixDestroy(&pix1); } } } } if (debug_out) { pixDestroy(&pixv); dew1 = dewarpaGetDewarp(dewa, pageno); dewarpDebug(dew1, "lept/dewapply", 0); convertFilesToPdf("/tmp/lept/dewboxa", NULL, 135, 1.0, 0, 0, "Dewarp Apply Disparity Boxa", debugfile); fprintf(stderr, "Dewarp Apply Disparity Boxa pdf file: %s\n", debugfile); } /* Get rid of the large full res disparity arrays */ dewarpMinimize(dew); return 0; }