/*! * ptaaSortByIndex() * * Input: ptaas * naindex (na that maps from the new ptaa to the input ptaa) * Return: ptaad (sorted), or null on error */ PTAA * ptaaSortByIndex(PTAA *ptaas, NUMA *naindex) { l_int32 i, n, index; PTA *pta; PTAA *ptaad; PROCNAME("ptaaSortByIndex"); if (!ptaas) return (PTAA *)ERROR_PTR("ptaas not defined", procName, NULL); if (!naindex) return (PTAA *)ERROR_PTR("naindex not defined", procName, NULL); n = ptaaGetCount(ptaas); if (numaGetCount(naindex) != n) return (PTAA *)ERROR_PTR("numa and ptaa sizes differ", procName, NULL); ptaad = ptaaCreate(n); for (i = 0; i < n; i++) { numaGetIValue(naindex, i, &index); pta = ptaaGetPta(ptaas, index, L_COPY); ptaaAddPta(ptaad, pta, L_INSERT); } return ptaad; }
/*! * ptaaRemoveShortLines() * * Input: pixs (1 bpp) * ptaas (input lines) * fract (minimum fraction of longest line to keep) * debugflag * Return: ptaad (containing only lines of sufficient length), * or null on error */ PTAA * ptaaRemoveShortLines(PIX *pixs, PTAA *ptaas, l_float32 fract, l_int32 debugflag) { l_int32 w, n, i, index, maxlen, len; l_float32 minx, maxx; NUMA *na, *naindex; PIX *pixt1, *pixt2; PTA *pta; PTAA *ptaad; PROCNAME("ptaaRemoveShortLines"); if (!pixs || pixGetDepth(pixs) != 1) return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (!ptaas) return (PTAA *)ERROR_PTR("ptaas undefined", procName, NULL); pixGetDimensions(pixs, &w, NULL, NULL); n = ptaaGetCount(ptaas); ptaad = ptaaCreate(n); na = numaCreate(n); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaas, i, L_CLONE); ptaGetRange(pta, &minx, &maxx, NULL, NULL); numaAddNumber(na, maxx - minx + 1); ptaDestroy(&pta); } /* Sort by length and find all that are long enough */ naindex = numaGetSortIndex(na, L_SORT_DECREASING); numaGetIValue(naindex, 0, &index); numaGetIValue(na, index, &maxlen); if (maxlen < 0.5 * w) L_WARNING("lines are relatively short", procName); pta = ptaaGetPta(ptaas, index, L_CLONE); ptaaAddPta(ptaad, pta, L_INSERT); for (i = 1; i < n; i++) { numaGetIValue(naindex, i, &index); numaGetIValue(na, index, &len); if (len < fract * maxlen) break; pta = ptaaGetPta(ptaas, index, L_CLONE); ptaaAddPta(ptaad, pta, L_INSERT); } if (debugflag) { pixt1 = pixCopy(NULL, pixs); pixt2 = pixDisplayPtaa(pixt1, ptaad); pixDisplayWithTitle(pixt2, 0, 200, "pix4", 1); pixDestroy(&pixt1); pixDestroy(&pixt2); } numaDestroy(&na); numaDestroy(&naindex); return ptaad; }
l_int32 main(int argc, char **argv) { l_int32 i, n; l_float32 a, b, c, d, e; NUMA *nax, *nafit; PIX *pixs, *pixn, *pixg, *pixb, *pixt1, *pixt2; PIXA *pixa; PTA *pta, *ptad; PTAA *ptaa1, *ptaa2; pixs = pixRead("cat-35.jpg"); /* pixs = pixRead("zanotti-78.jpg"); */ /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); pixDestroy(&pixn); pixDestroy(&pixg); /* Get the textline centers */ pixa = pixaCreate(6); ptaa1 = dewarpGetTextlineCenters(pixb, 0); pixt1 = pixCreateTemplate(pixs); pixSetAll(pixt1); pixt2 = pixDisplayPtaa(pixt1, ptaa1); pixWrite("/tmp/textline1.png", pixt2, IFF_PNG); pixDisplayWithTitle(pixt2, 0, 100, "textline centers 1", 1); pixaAddPix(pixa, pixt2, L_INSERT); pixDestroy(&pixt1); /* Remove short lines */ fprintf(stderr, "Num all lines = %d\n", ptaaGetCount(ptaa1)); ptaa2 = dewarpRemoveShortLines(pixb, ptaa1, 0.8, 0); pixt1 = pixCreateTemplate(pixs); pixSetAll(pixt1); pixt2 = pixDisplayPtaa(pixt1, ptaa2); pixWrite("/tmp/textline2.png", pixt2, IFF_PNG); pixDisplayWithTitle(pixt2, 300, 100, "textline centers 2", 1); pixaAddPix(pixa, pixt2, L_INSERT); pixDestroy(&pixt1); n = ptaaGetCount(ptaa2); fprintf(stderr, "Num long lines = %d\n", n); ptaaDestroy(&ptaa1); pixDestroy(&pixb); /* Long lines over input image */ pixt1 = pixCopy(NULL, pixs); pixt2 = pixDisplayPtaa(pixt1, ptaa2); pixWrite("/tmp/textline3.png", pixt2, IFF_PNG); pixDisplayWithTitle(pixt2, 600, 100, "textline centers 3", 1); pixaAddPix(pixa, pixt2, L_INSERT); pixDestroy(&pixt1); /* Quadratic fit to curve */ pixt1 = pixCopy(NULL, pixs); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetQuadraticLSF(pta, &a, &b, &c, &nafit); fprintf(stderr, "Quadratic: a = %10.6f, b = %7.3f, c = %7.3f\n", a, b, c); ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt1, pixt1, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } pixWrite("/tmp/textline4.png", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 900, 100, "textline centers 4", 1); pixaAddPix(pixa, pixt1, L_INSERT); /* Cubic fit to curve */ pixt1 = pixCopy(NULL, pixs); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetCubicLSF(pta, &a, &b, &c, &d, &nafit); fprintf(stderr, "Cubic: a = %10.6f, b = %10.6f, c = %7.3f, d = %7.3f\n", a, b, c, d); ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt1, pixt1, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } pixWrite("/tmp/textline5.png", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 1200, 100, "textline centers 5", 1); pixaAddPix(pixa, pixt1, L_INSERT); /* Quartic fit to curve */ pixt1 = pixCopy(NULL, pixs); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetQuarticLSF(pta, &a, &b, &c, &d, &e, &nafit); fprintf(stderr, "Quartic: a = %7.3f, b = %7.3f, c = %9.5f, d = %7.3f, e = %7.3f\n", a, b, c, d, e); ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt1, pixt1, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } pixWrite("/tmp/textline6.png", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 1500, 100, "textline centers 6", 1); pixaAddPix(pixa, pixt1, L_INSERT); pixaConvertToPdf(pixa, 300, 0.5, L_JPEG_ENCODE, 75, "LS fittings to textlines", "/tmp/dewarp_fittings.pdf"); pixaDestroy(&pixa); pixDestroy(&pixs); ptaaDestroy(&ptaa2); return 0; }
int main(int argc, char **argv) { l_int32 i, w, h, nbox, npta, fgcount, bgcount, count; BOXA *boxa; PIX *pixs, *pixfg, *pixbg, *pixc, *pixb, *pixd; PIX *pix1, *pix2, *pix3, *pix4; PIXA *pixa; PTA *pta; PTAA *ptaafg, *ptaabg; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixs = pixRead("feyn-fract.tif"); boxa = pixConnComp(pixs, NULL, 8); nbox = boxaGetCount(boxa); regTestCompareValues(rp, nbox, 464, 0); /* 0 */ /* Get fg and bg boundary pixels */ pixfg = pixMorphSequence(pixs, "e3.3", 0); pixXor(pixfg, pixfg, pixs); pixCountPixels(pixfg, &fgcount, NULL); regTestCompareValues(rp, fgcount, 58764, 0); /* 1 */ pixbg = pixMorphSequence(pixs, "d3.3", 0); pixXor(pixbg, pixbg, pixs); pixCountPixels(pixbg, &bgcount, NULL); regTestCompareValues(rp, bgcount, 60335, 0); /* 2 */ /* Get ptaa of fg pixels */ ptaafg = ptaaGetBoundaryPixels(pixs, L_BOUNDARY_FG, 8, NULL, NULL); npta = ptaaGetCount(ptaafg); regTestCompareValues(rp, npta, nbox, 0); /* 3 */ count = 0; for (i = 0; i < npta; i++) { pta = ptaaGetPta(ptaafg, i, L_CLONE); count += ptaGetCount(pta); ptaDestroy(&pta); } regTestCompareValues(rp, fgcount, count, 0); /* 4 */ /* Get ptaa of bg pixels. Note that the number of bg pts * is, in general, larger than the number of bg boundary pixels, * because bg boundary pixels are shared by two c.c. that * are 1 pixel apart. */ ptaabg = ptaaGetBoundaryPixels(pixs, L_BOUNDARY_BG, 8, NULL, NULL); npta = ptaaGetCount(ptaabg); regTestCompareValues(rp, npta, nbox, 0); /* 5 */ count = 0; for (i = 0; i < npta; i++) { pta = ptaaGetPta(ptaabg, i, L_CLONE); count += ptaGetCount(pta); ptaDestroy(&pta); } regTestCompareValues(rp, count, 60602, 0); /* 6 */ /* Render the fg boundary pixels on top of pixs. */ pixa = pixaCreate(4); pixc = pixRenderRandomCmapPtaa(pixs, ptaafg, 0, 0, 0); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 7 */ pixSaveTiledOutline(pixc, pixa, 1.0, 1, 30, 2, 32); pixDestroy(&pixc); /* Render the bg boundary pixels on top of pixs. */ pixc = pixRenderRandomCmapPtaa(pixs, ptaabg, 0, 0, 0); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 8 */ pixSaveTiledOutline(pixc, pixa, 1.0, 0, 30, 2, 32); pixDestroy(&pixc); pixClearAll(pixs); /* Render the fg boundary pixels alone. */ pixc = pixRenderRandomCmapPtaa(pixs, ptaafg, 0, 0, 0); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 9 */ pixSaveTiledOutline(pixc, pixa, 1.0, 1, 30, 2, 32); /* Verify that the fg pixels are the same set as we * originally started with. */ pixb = pixConvertTo1(pixc, 255); regTestComparePix(rp, pixb, pixfg); /* 10 */ pixDestroy(&pixc); pixDestroy(&pixb); /* Render the bg boundary pixels alone. */ pixc = pixRenderRandomCmapPtaa(pixs, ptaabg, 0, 0, 0); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 11 */ pixSaveTiledOutline(pixc, pixa, 1.0, 0, 30, 2, 32); /* Verify that the bg pixels are the same set as we * originally started with. */ pixb = pixConvertTo1(pixc, 255); regTestComparePix(rp, pixb, pixbg); /* 12 */ pixDestroy(&pixc); pixDestroy(&pixb); pixd = pixaDisplay(pixa, 0, 0); pixDisplayWithTitle(pixd, 0, 0, NULL, rp->display); ptaaDestroy(&ptaafg); ptaaDestroy(&ptaabg); pixDestroy(&pixs); pixDestroy(&pixfg); pixDestroy(&pixbg); pixDestroy(&pixd); pixaDestroy(&pixa); boxaDestroy(&boxa); /* Test rotation */ pix1 = pixRead("feyn-word.tif"); pix2 = pixAddBorderGeneral(pix1, 200, 200, 200, 200, 0); pixa = pixaCreate(0); pix3 = PtaDisplayRotate(pix2, 0, 0); pixaAddPix(pixa, pix3, L_INSERT); pix3 = PtaDisplayRotate(pix2, 500, 100); pixaAddPix(pixa, pix3, L_INSERT); pix3 = PtaDisplayRotate(pix2, 100, 410); pixaAddPix(pixa, pix3, L_INSERT); pix3 = PtaDisplayRotate(pix2, 500, 410); pixaAddPix(pixa, pix3, L_INSERT); pix4 = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 30, 2); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 13 */ pixDisplayWithTitle(pix4, 800, 0, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix4); pixaDestroy(&pixa); return regTestCleanup(rp); }
/*! * dewarpBuildModel() * * Input: dew * debugflag (1 for debugging output) * Return: 0 if OK, 1 on error * * Notes: * (1) This is the basic function that builds the vertical * disparity array, which allows determination of the * src pixel in the input image corresponding to each * dest pixel in the dewarped image. * (2) The method is as follows: * * Estimate the centers of all the long textlines and * fit a LS quadratic to each one. This smooths the curves. * * Sample each curve at a regular interval, find the y-value * of the flat point on each curve, and subtract the sampled * curve value from this value. This is the vertical * disparity. * * Fit a LS quadratic to each set of vertically aligned * disparity samples. This smooths the disparity values * in the vertical direction. Then resample at the same * regular interval, We now have a regular grid of smoothed * vertical disparity valuels. * * Interpolate this grid to get a full resolution disparity * map. This can be applied directly to the src image * pixels to dewarp the image in the vertical direction, * making all textlines horizontal. */ l_int32 dewarpBuildModel(L_DEWARP *dew, l_int32 debugflag) { char *tempname; l_int32 i, j, nlines, nx, ny, sampling; l_float32 c0, c1, c2, x, y, flaty, val; l_float32 *faflats; NUMA *nax, *nafit, *nacurve, *nacurves, *naflat, *naflats, *naflatsi; PIX *pixs, *pixt1, *pixt2; PTA *pta, *ptad; PTAA *ptaa1, *ptaa2, *ptaa3, *ptaa4, *ptaa5, *ptaa6, *ptaa7; FPIX *fpix1, *fpix2, *fpix3; PROCNAME("dewarpBuildModel"); if (!dew) return ERROR_INT("dew not defined", procName, 1); pixs = dew->pixs; if (debugflag) { pixDisplayWithTitle(pixs, 0, 0, "pixs", 1); pixWriteTempfile("/tmp", "pixs.png", pixs, IFF_PNG, NULL); } /* Make initial estimate of centers of textlines */ ptaa1 = pixGetTextlineCenters(pixs, DEBUG_TEXTLINE_CENTERS); if (debugflag) { pixt1 = pixConvertTo32(pixs); pixt2 = pixDisplayPtaa(pixt1, ptaa1); pixWriteTempfile("/tmp", "lines1.png", pixt2, IFF_PNG, NULL); pixDestroy(&pixt1); pixDestroy(&pixt2); } /* Remove all lines that are not near the length * of the longest line. */ ptaa2 = ptaaRemoveShortLines(pixs, ptaa1, 0.8, DEBUG_SHORT_LINES); if (debugflag) { pixt1 = pixConvertTo32(pixs); pixt2 = pixDisplayPtaa(pixt1, ptaa2); pixWriteTempfile("/tmp", "lines2.png", pixt2, IFF_PNG, NULL); pixDestroy(&pixt1); pixDestroy(&pixt2); } nlines = ptaaGetCount(ptaa2); if (nlines < dew->minlines) return ERROR_INT("insufficient lines to build model", procName, 1); /* Do quadratic fit to smooth each line. A single quadratic * over the entire width of the line appears to be sufficient. * Quartics tend to overfit to noise. Each line is thus * represented by three coefficients: c2 * x^2 + c1 * x + c0. * Using the coefficients, sample each fitted curve uniformly * across the full width of the image. */ sampling = dew->sampling; nx = dew->nx; ny = dew->ny; ptaa3 = ptaaCreate(nlines); nacurve = numaCreate(nlines); /* stores curvature coeff c2 */ for (i = 0; i < nlines; i++) { /* for each line */ pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL); numaAddNumber(nacurve, c2); ptad = ptaCreate(nx); for (j = 0; j < nx; j++) { /* uniformly sampled in x */ x = j * sampling; applyQuadraticFit(c2, c1, c0, x, &y); ptaAddPt(ptad, x, y); } ptaaAddPta(ptaa3, ptad, L_INSERT); ptaDestroy(&pta); } if (debugflag) { ptaa4 = ptaaCreate(nlines); for (i = 0; i < nlines; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit); ptad = ptaCreateFromNuma(nax, nafit); ptaaAddPta(ptaa4, ptad, L_INSERT); ptaDestroy(&pta); numaDestroy(&nax); numaDestroy(&nafit); } pixt1 = pixConvertTo32(pixs); pixt2 = pixDisplayPtaa(pixt1, ptaa4); pixWriteTempfile("/tmp", "lines3.png", pixt2, IFF_PNG, NULL); pixDestroy(&pixt1); pixDestroy(&pixt2); ptaaDestroy(&ptaa4); } /* Find and save the flat points in each curve. */ naflat = numaCreate(nlines); for (i = 0; i < nlines; i++) { pta = ptaaGetPta(ptaa3, i, L_CLONE); numaGetFValue(nacurve, i, &c2); if (c2 <= 0) /* flat point at bottom; max value of y in curve */ ptaGetRange(pta, NULL, NULL, NULL, &flaty); else /* flat point at top; min value of y in curve */ ptaGetRange(pta, NULL, NULL, &flaty, NULL); numaAddNumber(naflat, flaty); ptaDestroy(&pta); } /* Sort the lines in ptaa3 by their position */ naflatsi = numaGetSortIndex(naflat, L_SORT_INCREASING); naflats = numaSortByIndex(naflat, naflatsi); nacurves = numaSortByIndex(nacurve, naflatsi); dew->naflats = naflats; dew->nacurves = nacurves; ptaa4 = ptaaSortByIndex(ptaa3, naflatsi); numaDestroy(&naflat); numaDestroy(&nacurve); numaDestroy(&naflatsi); if (debugflag) { tempname = genTempFilename("/tmp", "naflats.na", 0); numaWrite(tempname, naflats); FREE(tempname); } /* Convert the sampled points in ptaa3 to a sampled disparity with * with respect to the flat point in the curve. */ ptaa5 = ptaaCreate(nlines); for (i = 0; i < nlines; i++) { pta = ptaaGetPta(ptaa4, i, L_CLONE); numaGetFValue(naflats, i, &flaty); ptad = ptaCreate(nx); for (j = 0; j < nx; j++) { ptaGetPt(pta, j, &x, &y); ptaAddPt(ptad, x, flaty - y); } ptaaAddPta(ptaa5, ptad, L_INSERT); ptaDestroy(&pta); } if (debugflag) { tempname = genTempFilename("/tmp", "ptaa5.ptaa", 0); ptaaWrite(tempname, ptaa5, 0); FREE(tempname); } /* Generate a ptaa taking vertical 'columns' from ptaa5. * We want to fit the vertical disparity on the column to the * vertical position of the line, which we call 'y' here and * obtain from naflats. */ ptaa6 = ptaaCreate(nx); faflats = numaGetFArray(naflats, L_NOCOPY); for (j = 0; j < nx; j++) { pta = ptaCreate(nlines); for (i = 0; i < nlines; i++) { y = faflats[i]; ptaaGetPt(ptaa5, i, j, NULL, &val); /* disparity value */ ptaAddPt(pta, y, val); } ptaaAddPta(ptaa6, pta, L_INSERT); } if (debugflag) { tempname = genTempFilename("/tmp", "ptaa6.ptaa", 0); ptaaWrite(tempname, ptaa6, 0); FREE(tempname); } /* Do quadratic fit vertically on a subset of pixel columns * for the vertical displacement, which identifies the * src pixel(s) for each dest pixel. Sample the displacement * on a regular grid in the vertical direction. */ ptaa7 = ptaaCreate(nx); /* uniformly sampled across full height of image */ for (j = 0; j < nx; j++) { /* for each column */ pta = ptaaGetPta(ptaa6, j, L_CLONE); ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL); ptad = ptaCreate(ny); for (i = 0; i < ny; i++) { /* uniformly sampled in y */ y = i * sampling; applyQuadraticFit(c2, c1, c0, y, &val); ptaAddPt(ptad, y, val); } ptaaAddPta(ptaa7, ptad, L_INSERT); ptaDestroy(&pta); } if (debugflag) { tempname = genTempFilename("/tmp", "ptaa7.ptaa", 0); ptaaWrite(tempname, ptaa7, 0); FREE(tempname); } /* Save the result in a fpix at the specified subsampling */ fpix1 = fpixCreate(nx, ny); for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { ptaaGetPt(ptaa7, j, i, NULL, &val); fpixSetPixel(fpix1, j, i, val); } } dew->sampvdispar = fpix1; /* Generate a full res fpix for vertical dewarping. We require that * the size of this fpix is at least as big as the input image. */ fpix2 = fpixScaleByInteger(fpix1, sampling); dew->fullvdispar = fpix2; if (debugflag) { pixt1 = fpixRenderContours(fpix2, -2., 2.0, 0.2); pixWriteTempfile("/tmp", "vert-contours.png", pixt1, IFF_PNG, NULL); pixDisplay(pixt1, 1000, 0); pixDestroy(&pixt1); } /* Generate full res and sampled fpix for horizontal dewarping. This * works to the extent that the line curvature is due to bending * out of the plane normal to the camera, and not wide-angle * "fishbowl" distortion. Also generate the sampled horizontal * disparity array. */ if (dew->applyhoriz) { fpix3 = fpixBuildHorizontalDisparity(fpix2, 0, &dew->extraw); dew->fullhdispar = fpix3; dew->samphdispar = fpixSampledDisparity(fpix3, dew->sampling); if (debugflag) { pixt1 = fpixRenderContours(fpix3, -2., 2.0, 0.2); pixWriteTempfile("/tmp", "horiz-contours.png", pixt1, IFF_PNG, NULL); pixDisplay(pixt1, 1000, 0); pixDestroy(&pixt1); } } dew->success = 1; ptaaDestroy(&ptaa1); ptaaDestroy(&ptaa2); ptaaDestroy(&ptaa3); ptaaDestroy(&ptaa4); ptaaDestroy(&ptaa5); ptaaDestroy(&ptaa6); ptaaDestroy(&ptaa7); return 0; }
l_int32 main(int argc, char **argv) { l_int32 i, n, ignore; l_float32 a, b, c, d, e; L_DEWARP *dew; FILE *fp; FPIX *fpix; NUMA *nax, *nay, *nafit; PIX *pixs, *pixn, *pixg, *pixb, *pixt1, *pixt2, *pixt3; PIX *pixs2, *pixn2, *pixg2, *pixb2, *pixv, *pixd; PTA *pta, *ptad; PTAA *ptaa1, *ptaa2; pixs = pixRead("1555-7.jpg"); /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); /* Run the basic functions */ dew = dewarpCreate(pixb, 7, 30, 15, 1); dewarpBuildModel(dew, 1); dewarpApplyDisparity(dew, pixg, 1); /* Save the intermediate dewarped images */ pixv = pixRead("/tmp/pixv.png"); pixd = pixRead("/tmp/pixd.png"); /* Normalize another image, that doesn't have enough textlines * to build an accurate model */ pixs2 = pixRead("1555-3.jpg"); pixn2 = pixBackgroundNormSimple(pixs2, NULL, NULL); pixg2 = pixConvertRGBToGray(pixn2, 0.5, 0.3, 0.2); pixb2 = pixThresholdToBinary(pixg2, 130); /* Apply the previous disparity model to this image */ dewarpApplyDisparity(dew, pixg2, 1); dewarpDestroy(&dew); /* Get the textline centers */ const char* const morph2 = "c15.1 + o15.1 + c50.1"; ptaa1 = pixGetTextlineCenters(pixb,morph2, 0); pixt1 = pixCreateTemplate(pixs); pixt2 = pixDisplayPtaa(pixt1, ptaa1); pixWrite("/tmp/textline1.png", pixt2, IFF_PNG); pixDisplayWithTitle(pixt2, 500, 100, "textline centers", 1); pixDestroy(&pixt1); /* Remove short lines */ fprintf(stderr, "Num all lines = %d\n", ptaaGetCount(ptaa1)); ptaa2 = ptaaRemoveShortLines(pixb, ptaa1, 0.8, 0); /* Fit to curve */ n = ptaaGetCount(ptaa2); fprintf(stderr, "Num long lines = %d\n", n); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); #if DO_QUAD ptaGetQuadraticLSF(pta, &a, &b, &c, &nafit); /* fprintf(stderr, "a = %7.3f, b = %7.3f, c = %7.3f\n", a, b, c); */ #elif DO_CUBIC ptaGetCubicLSF(pta, &a, &b, &c, &d, &nafit); /* fprintf(stderr, "a = %7.3f, b = %7.3f, c = %7.3f, d = %7.3f\n", a, b, c, d); */ #elif DO_QUARTIC ptaGetQuarticLSF(pta, &a, &b, &c, &d, &e, &nafit); /* fprintf(stderr, "a = %7.3f, b = %7.3f, c = %7.3f, d = %7.3f, e = %7.3f\n", a, b, c, d, e); */ #endif ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt2, pixt2, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } pixDisplayWithTitle(pixt2, 700, 100, "fitted lines superimposed", 1); pixWrite("/tmp/textline2.png", pixt2, IFF_PNG); ptaaDestroy(&ptaa1); ptaaDestroy(&ptaa2); pixDestroy(&pixt2); /* Write out the files to be imaged */ lept_mkdir("junkdir"); pixWrite("/tmp/junkdir/001.jpg", pixs, IFF_JFIF_JPEG); pixWrite("/tmp/junkdir/002.jpg", pixn, IFF_JFIF_JPEG); pixWrite("/tmp/junkdir/003.jpg", pixg, IFF_JFIF_JPEG); pixWrite("/tmp/junkdir/004.png", pixb, IFF_TIFF_G4); pixt1 = pixRead("/tmp/textline1.png"); pixWrite("/tmp/junkdir/005.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixt1 = pixRead("/tmp/textline2.png"); pixWrite("/tmp/junkdir/006.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixt1 = pixRead("/tmp/lines1.png"); pixWrite("/tmp/junkdir/007.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixt1 = pixRead("/tmp/lines2.png"); pixWrite("/tmp/junkdir/008.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixt1 = pixRead("/tmp/vert-contours.png"); pixWrite("/tmp/junkdir/009.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixWrite("/tmp/junkdir/010.png", pixv, IFF_PNG); pixt1 = pixThresholdToBinary(pixv, 130); pixWrite("/tmp/junkdir/011.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixt1 = pixRead("/tmp/horiz-contours.png"); pixWrite("/tmp/junkdir/012.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixWrite("/tmp/junkdir/013.png", pixd, IFF_PNG); pixt1 = pixThresholdToBinary(pixd, 130); pixWrite("/tmp/junkdir/014.png", pixt1, IFF_PNG); pixDestroy(&pixt1); pixWrite("/tmp/junkdir/015.png", pixb, IFF_TIFF_G4); /* (these are for the second image) */ pixWrite("/tmp/junkdir/016.jpg", pixs2, IFF_JFIF_JPEG); pixWrite("/tmp/junkdir/017.png", pixb2, IFF_TIFF_G4); pixt1 = pixRead("/tmp/pixv.png"); pixt2 = pixThresholdToBinary(pixt1, 130); pixWrite("/tmp/junkdir/018.png", pixt2, IFF_PNG); pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixRead("/tmp/pixd.png"); pixt2 = pixThresholdToBinary(pixt1, 130); pixWrite("/tmp/junkdir/019.png", pixt2, IFF_PNG); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Generate the 19 page ps and pdf files */ convertFilesToPS("/tmp/junkdir", NULL, 135, "/tmp/dewarp.ps"); fprintf(stderr, "ps file made: /tmp/dewarp.ps\n"); ignore = system("ps2pdf /tmp/dewarp.ps /tmp/dewarp.pdf"); fprintf(stderr, "pdf file made: /tmp/dewarp.pdf\n"); pixDestroy(&pixs); pixDestroy(&pixn); pixDestroy(&pixg); pixDestroy(&pixb); pixDestroy(&pixs2); pixDestroy(&pixn2); pixDestroy(&pixg2); pixDestroy(&pixb2); pixDestroy(&pixv); pixDestroy(&pixd); return 0; }
/*! * \brief pixConnCompIncrAdd() * * \param[in] pixs 32 bpp, with pixels labeled by c.c. * \param[in] ptaa with each pta of pixel locations indexed by c.c. * \param[out] pncc number of c.c * \param[in] x,y location of added pixel * \param[in] debug 0 for no output; otherwise output whenever * debug <= nvals, up to debug == 3 * \return -1 if nothing happens; 0 if a pixel is added; 1 on error * * <pre> * Notes: * (1) This adds a pixel and updates the labeled connected components. * Before calling this function, initialize the process using * pixConnCompIncrInit(). * (2) As a result of adding a pixel, one of the following can happen, * depending on the number of neighbors with non-zero value: * (a) nothing: the pixel is already a member of a c.c. * (b) no neighbors: a new component is added, increasing the * number of c.c. * (c) one neighbor: the pixel is added to an existing c.c. * (d) more than one neighbor: the added pixel causes joining of * two or more c.c., reducing the number of c.c. A maximum * of 4 c.c. can be joined. * (3) When two c.c. are joined, the pixels in the larger index are * relabeled to those of the smaller in pixs, and their locations * are transferred to the pta with the smaller index in the ptaa. * The pta corresponding to the larger index is then deleted. * (4) This is an efficient implementation of a "union-find" operation, * which supports the generation and merging of disjoint sets * of pixels. This function can be called about 1.3 million times * per second. * </pre> */ l_int32 pixConnCompIncrAdd(PIX *pixs, PTAA *ptaa, l_int32 *pncc, l_float32 x, l_float32 y, l_int32 debug) { l_int32 conn, i, j, w, h, count, nvals, ns, firstindex; l_uint32 val; l_int32 *neigh; PTA *ptas, *ptad; PROCNAME("pixConnCompIncrAdd"); if (!pixs || pixGetDepth(pixs) != 32) return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); if (!ptaa) return ERROR_INT("ptaa not defined", procName, 1); if (!pncc) return ERROR_INT("&ncc not defined", procName, 1); conn = pixs->special; if (conn != 4 && conn != 8) return ERROR_INT("connectivity must be 4 or 8", procName, 1); pixGetDimensions(pixs, &w, &h, NULL); if (x < 0 || x >= w) return ERROR_INT("invalid x pixel location", procName, 1); if (y < 0 || y >= h) return ERROR_INT("invalid y pixel location", procName, 1); pixGetPixel(pixs, x, y, &val); if (val > 0) /* already belongs to a set */ return -1; /* Find unique neighbor pixel values in increasing order of value. * If %nvals > 0, these are returned in the %neigh array, which * is of size %nvals. Note that the pixel values in each * connected component are used as the index into the pta * array of the ptaa, giving the pixel locations. */ pixGetSortedNeighborValues(pixs, x, y, conn, &neigh, &nvals); /* If there are no neighbors, just add a new component */ if (nvals == 0) { count = ptaaGetCount(ptaa); pixSetPixel(pixs, x, y, count); ptas = ptaCreate(1); ptaAddPt(ptas, x, y); ptaaAddPta(ptaa, ptas, L_INSERT); *pncc += 1; LEPT_FREE(neigh); return 0; } /* Otherwise, there is at least one neighbor. Add the pixel * to the first neighbor c.c. */ firstindex = neigh[0]; pixSetPixel(pixs, x, y, firstindex); ptaaAddPt(ptaa, neigh[0], x, y); if (nvals == 1) { if (debug == 1) fprintf(stderr, "nvals = %d: neigh = (%d)\n", nvals, neigh[0]); LEPT_FREE(neigh); return 0; } /* If nvals > 1, there are at least 2 neighbors, so this pixel * joins at least one pair of existing c.c. Join each component * to the first component in the list, which is the one with * the smallest integer label. This is done in two steps: * (a) re-label the pixels in the component to the label of the * first component, and * (b) save the pixel locations in the pta for the first component. */ if (nvals == 2) { if (debug >= 1 && debug <= 2) { fprintf(stderr, "nvals = %d: neigh = (%d,%d)\n", nvals, neigh[0], neigh[1]); } } else if (nvals == 3) { if (debug >= 1 && debug <= 3) { fprintf(stderr, "nvals = %d: neigh = (%d,%d,%d)\n", nvals, neigh[0], neigh[1], neigh[2]); } } else { /* nvals == 4 */ if (debug >= 1 && debug <= 4) { fprintf(stderr, "nvals = %d: neigh = (%d,%d,%d,%d)\n", nvals, neigh[0], neigh[1], neigh[2], neigh[3]); } } ptad = ptaaGetPta(ptaa, firstindex, L_CLONE); for (i = 1; i < nvals; i++) { ptas = ptaaGetPta(ptaa, neigh[i], L_CLONE); ns = ptaGetCount(ptas); for (j = 0; j < ns; j++) { /* relabel pixels */ ptaGetPt(ptas, j, &x, &y); pixSetPixel(pixs, x, y, firstindex); } ptaJoin(ptad, ptas, 0, -1); /* add relabeled pixel locations */ *pncc -= 1; ptaDestroy(&ptaa->pta[neigh[i]]); ptaDestroy(&ptas); /* the clone */ } ptaDestroy(&ptad); /* the clone */ LEPT_FREE(neigh); return 0; }
l_int32 main(int argc, char **argv) { l_int32 i, n; l_float32 a, b, c; L_DEWARP *dew, *dew2; DPIX *dpix1, *dpix2, *dpix3; FPIX *fpix1, *fpix2, *fpix3; NUMA *nax, *nafit; PIX *pixs, *pixn, *pixg, *pixb, *pixt1, *pixt2; PIX *pixs2, *pixn2, *pixg2, *pixb2; PTA *pta, *ptad; PTAA *ptaa1, *ptaa2; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixs = pixRead("1555-7.jpg"); /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); pixDestroy(&pixn); pixDestroy(&pixg); regTestWritePixAndCheck(rp, pixb, IFF_PNG); /* 0 */ pixDisplayWithTitle(pixb, 0, 0, "binarized input", rp->display); /* Get the textline centers */ ptaa1 = pixGetTextlineCenters(pixb, 0); pixt1 = pixCreateTemplate(pixs); pixt2 = pixDisplayPtaa(pixt1, ptaa1); regTestWritePixAndCheck(rp, pixt2, IFF_PNG); /* 1 */ pixDisplayWithTitle(pixt2, 0, 500, "textline centers", rp->display); pixDestroy(&pixt1); /* Remove short lines */ ptaa2 = ptaaRemoveShortLines(pixb, ptaa1, 0.8, 0); /* Fit to quadratic */ n = ptaaGetCount(ptaa2); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetQuadraticLSF(pta, &a, &b, &c, &nafit); ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt2, pixt2, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } regTestWritePixAndCheck(rp, pixt2, IFF_PNG); /* 2 */ pixDisplayWithTitle(pixt2, 300, 500, "fitted lines superimposed", rp->display); ptaaDestroy(&ptaa1); ptaaDestroy(&ptaa2); pixDestroy(&pixt2); /* Run with only vertical disparity correction */ if ((dew = dewarpCreate(pixb, 7, 30, 15, 0)) == NULL) return ERROR_INT("\n\n\n FAILURE !!! \n\n\n", rp->testname, 1); dewarpBuildModel(dew, 0); dewarpApplyDisparity(dew, pixb, 0); regTestWritePixAndCheck(rp, dew->pixd, IFF_PNG); /* 3 */ pixDisplayWithTitle(dew->pixd, 400, 0, "fixed for vert disparity", rp->display); dewarpDestroy(&dew); /* Run with both vertical and horizontal disparity correction */ if ((dew = dewarpCreate(pixb, 7, 30, 15, 1)) == NULL) return ERROR_INT("\n\n\n FAILURE !!! \n\n\n", rp->testname, 1); dewarpBuildModel(dew, 0); dewarpApplyDisparity(dew, pixb, 0); regTestWritePixAndCheck(rp, dew->pixd, IFF_PNG); /* 4 */ pixDisplayWithTitle(dew->pixd, 800, 0, "fixed for both disparities", rp->display); /* Read another image, normalize background and binarize */ pixs2 = pixRead("1555-3.jpg"); pixn2 = pixBackgroundNormSimple(pixs2, NULL, NULL); pixg2 = pixConvertRGBToGray(pixn2, 0.5, 0.3, 0.2); pixb2 = pixThresholdToBinary(pixg2, 130); pixDestroy(&pixn2); pixDestroy(&pixg2); regTestWritePixAndCheck(rp, pixb, IFF_PNG); /* 5 */ pixDisplayWithTitle(pixb, 0, 400, "binarized input (2)", rp->display); /* Minimize and re-apply previous disparity to this image */ dewarpMinimize(dew); dewarpApplyDisparity(dew, pixb2, 0); regTestWritePixAndCheck(rp, dew->pixd, IFF_PNG); /* 6 */ pixDisplayWithTitle(dew->pixd, 400, 400, "fixed (2) for both disparities", rp->display); /* Write and read back minimized dewarp struct */ dewarpWrite("/tmp/dewarp.7.dew", dew); regTestCheckFile(rp, "/tmp/dewarp.7.dew"); /* 7 */ dew2 = dewarpRead("/tmp/dewarp.7.dew"); dewarpWrite("/tmp/dewarp.8.dew", dew2); regTestCheckFile(rp, "/tmp/dewarp.8.dew"); /* 8 */ regTestCompareFiles(rp, 7, 8); /* 9 */ /* Apply dew2 to pixb2 */ dewarpApplyDisparity(dew2, pixb2, 0); regTestWritePixAndCheck(rp, dew2->pixd, IFF_PNG); /* 10 */ pixDisplayWithTitle(dew->pixd, 800, 400, "fixed (3) for both disparities", rp->display); /* Minimize, repopulate disparity arrays, and apply again */ dewarpMinimize(dew2); dewarpApplyDisparity(dew2, pixb2, 0); regTestWritePixAndCheck(rp, dew2->pixd, IFF_PNG); /* 11 */ regTestCompareFiles(rp, 10, 11); /* 12 */ pixDisplayWithTitle(dew->pixd, 900, 400, "fixed (4) for both disparities", rp->display); /* Test a few of the fpix functions */ fpix1 = fpixClone(dew->sampvdispar); fpixWrite("/tmp/sampv.13.fpix", fpix1); regTestCheckFile(rp, "/tmp/sampv.13.fpix"); /* 13 */ fpix2 = fpixRead("/tmp/sampv.13.fpix"); fpixWrite("/tmp/sampv.14.fpix", fpix2); regTestCheckFile(rp, "/tmp/sampv.14.fpix"); /* 14 */ regTestCompareFiles(rp, 13, 14); /* 15 */ fpix3 = fpixScaleByInteger(fpix2, 30); pixt1 = fpixRenderContours(fpix3, -2., 2.0, 0.2); regTestWritePixAndCheck(rp, pixt1, IFF_PNG); /* 16 */ pixDisplayWithTitle(pixt1, 0, 800, "v. disparity contours", rp->display); fpixDestroy(&fpix1); fpixDestroy(&fpix2); fpixDestroy(&fpix3); pixDestroy(&pixt1); /* Test a few of the dpix functions */ dpix1 = fpixConvertToDPix(dew->sampvdispar); dpixWrite("/tmp/sampv.17.dpix", dpix1); regTestCheckFile(rp, "/tmp/sampv.17.dpix"); /* 17 */ dpix2 = dpixRead("/tmp/sampv.17.dpix"); dpixWrite("/tmp/sampv.18.dpix", dpix2); regTestCheckFile(rp, "/tmp/sampv.18.dpix"); /* 18 */ regTestCompareFiles(rp, 17, 18); /* 19 */ dpix3 = dpixScaleByInteger(dpix2, 30); fpix3 = dpixConvertToFPix(dpix3); pixt1 = fpixRenderContours(fpix3, -2., 2.0, 0.2); regTestWritePixAndCheck(rp, pixt1, IFF_PNG); /* 20 */ pixDisplayWithTitle(pixt1, 400, 800, "v. disparity contours", rp->display); regTestCompareFiles(rp, 16, 20); /* 21 */ dpixDestroy(&dpix1); dpixDestroy(&dpix2); dpixDestroy(&dpix3); fpixDestroy(&fpix3); pixDestroy(&pixt1); dewarpDestroy(&dew); dewarpDestroy(&dew2); pixDestroy(&pixs); pixDestroy(&pixb); pixDestroy(&pixs2); pixDestroy(&pixb2); regTestCleanup(rp); return 0; }