/*! * boxaApplyDisparity() * * Input: dew * boxa * direction (L_HORIZ or L_VERT) * mapdir (1 if mapping forward from original to dewarped; * 0 if backward) * Return: boxad (modified by the disparity), or null on error */ static BOXA * boxaApplyDisparity(L_DEWARP *dew, BOXA *boxa, l_int32 direction, l_int32 mapdir) { l_int32 x, y, w, h, ib, ip, nbox, wpl; l_float32 xn, yn; l_float32 *data, *line; BOX *boxs, *boxd; BOXA *boxad; FPIX *fpix; PTA *ptas, *ptad; PROCNAME("boxaApplyDisparity"); if (!dew) return (BOXA *)ERROR_PTR("dew not defined", procName, NULL); if (!boxa) return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); if (direction == L_VERT) fpix = dew->fullvdispar; else if (direction == L_HORIZ) fpix = dew->fullhdispar; else return (BOXA *)ERROR_PTR("invalid direction", procName, NULL); if (!fpix) return (BOXA *)ERROR_PTR("full disparity not defined", procName, NULL); fpixGetDimensions(fpix, &w, &h); /* Clip the output to the positive quadrant because all box * coordinates must be non-negative. */ data = fpixGetData(fpix); wpl = fpixGetWpl(fpix); nbox = boxaGetCount(boxa); boxad = boxaCreate(nbox); for (ib = 0; ib < nbox; ib++) { boxs = boxaGetBox(boxa, ib, L_COPY); ptas = boxConvertToPta(boxs, 4); ptad = ptaCreate(4); for (ip = 0; ip < 4; ip++) { ptaGetIPt(ptas, ip, &x, &y); line = data + y * wpl; if (direction == L_VERT) { if (mapdir == 0) yn = y - line[x]; else yn = y + line[x]; yn = L_MAX(0, yn); ptaAddPt(ptad, x, yn); } else { /* direction == L_HORIZ */ if (mapdir == 0) xn = x - line[x]; else xn = x + line[x]; xn = L_MAX(0, xn); ptaAddPt(ptad, xn, y); } } boxd = ptaConvertToBox(ptad); boxaAddBox(boxad, boxd, L_INSERT); boxDestroy(&boxs); ptaDestroy(&ptas); ptaDestroy(&ptad); } return boxad; }
/*! * 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; }
main(int argc, char **argv) { l_int32 i, d, h; l_float32 rat; PIX *pixs, *pixgb, *pixt1, *pixt2, *pixt3, *pixt4, *pixg, *pixd; PIXA *pixa; PTA *ptas, *ptad; static char mainName[] = "bilinear_reg"; if (argc != 1) exit(ERROR_INT(" Syntax: bilinear_reg", mainName, 1)); pixs = pixRead("feyn.tif"); pixg = pixScaleToGray3(pixs); #if ALL /* Test non-invertability of sampling */ pixa = pixaCreate(0); for (i = 1; i < 3; i++) { pixgb = pixAddBorder(pixg, ADDED_BORDER_PIXELS, 255); MakePtas(i, &ptas, &ptad); pixt1 = pixBilinearSampledPta(pixgb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 2, 1, 20, 8); pixt2 = pixBilinearSampledPta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 2, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixInvert(pixd, pixd); pixXor(pixd, pixd, pixg); pixSaveTiled(pixd, pixa, 2, 0, 20, 0); if (i == 0) pixWrite("/tmp/junksamp.png", pixt1, IFF_PNG); pixDestroy(&pixgb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkbilin1.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 300); pixDestroy(&pixt1); pixaDestroy(&pixa); #endif #if ALL /* Test non-invertability of interpolation */ pixa = pixaCreate(0); for (i = 1; i < 3; i++) { pixgb = pixAddBorder(pixg, ADDED_BORDER_PIXELS, 255); MakePtas(i, &ptas, &ptad); pixt1 = pixBilinearPta(pixgb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 2, 1, 20, 8); pixt2 = pixBilinearPta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 2, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixInvert(pixd, pixd); pixXor(pixd, pixd, pixg); pixSaveTiled(pixd, pixa, 2, 0, 20, 0); if (i == 0) pixWrite("/tmp/junkinterp.png", pixt1, IFF_PNG); pixDestroy(&pixgb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkbilin2.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 300); pixDestroy(&pixt1); pixaDestroy(&pixa); #endif #if ALL /* test with large distortion and inversion */ MakePtas(0, &ptas, &ptad); pixa = pixaCreate(0); startTimer(); pixt1 = pixBilinearSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixBilinearSampled(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt1, pixa, 2, 1, 20, 8); startTimer(); pixt2 = pixBilinearPta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixBilinearInterpolated(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt2, pixa, 2, 0, 20, 8); pixt3 = pixBilinearSampledPta(pixt1, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt3, pixa, 2, 0, 20, 8); pixt4 = pixBilinearPta(pixt2, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt4, pixa, 2, 0, 20, 8); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkbilin3.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 300); pixDestroy(&pixt1); pixaDestroy(&pixa); pixDestroy(&pixs); pixDestroy(&pixg); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif return 0; }
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"); /* 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; }
main(int argc, char **argv) { l_int32 i, j, x, y, rval, gval, bval; l_uint32 pixel; l_float32 frval, fgval, fbval; NUMA *nahue, *nasat, *napk; PIX *pixs, *pixhsv, *pixh, *pixg, *pixf, *pixd; PIX *pixr, *pixt1, *pixt2, *pixt3; PIXA *pixa, *pixapk; PTA *ptapk; L_REGPARAMS *rp; l_chooseDisplayProg(L_DISPLAY_WITH_XV); if (regTestSetup(argc, argv, &rp)) return 1; /* Make a graded frame color */ pixs = pixCreate(650, 900, 32); for (i = 0; i < 900; i++) { rval = 40 + i / 30; for (j = 0; j < 650; j++) { gval = 255 - j / 30; bval = 70 + j / 30; composeRGBPixel(rval, gval, bval, &pixel); pixSetPixel(pixs, j, i, pixel); } } /* Place an image inside the frame and convert to HSV */ pixt1 = pixRead("1555-3.jpg"); pixt2 = pixScale(pixt1, 0.5, 0.5); pixRasterop(pixs, 100, 100, 2000, 2000, PIX_SRC, pixt2, 0, 0); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDisplayWithTitle(pixs, 400, 0, "Input image", rp->display); pixa = pixaCreate(0); pixhsv = pixConvertRGBToHSV(NULL, pixs); /* Work in the HS projection of HSV */ pixh = pixMakeHistoHS(pixhsv, 5, &nahue, &nasat); pixg = pixMaxDynamicRange(pixh, L_LOG_SCALE); pixf = pixConvertGrayToFalseColor(pixg, 1.0); regTestWritePixAndCheck(rp, pixf, IFF_PNG); /* 0 */ pixDisplayWithTitle(pixf, 100, 0, "False color HS histo", rp->display); pixaAddPix(pixa, pixs, L_COPY); pixaAddPix(pixa, pixhsv, L_INSERT); pixaAddPix(pixa, pixg, L_INSERT); pixaAddPix(pixa, pixf, L_INSERT); gplotSimple1(nahue, GPLOT_PNG, "/tmp/junkhue", "Histogram of hue values"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixt3 = pixRead("/tmp/junkhue.png"); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 1 */ pixDisplayWithTitle(pixt3, 100, 300, "Histo of hue", rp->display); pixaAddPix(pixa, pixt3, L_INSERT); gplotSimple1(nasat, GPLOT_PNG, "/tmp/junksat", "Histogram of saturation values"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixt3 = pixRead("/tmp/junksat.png"); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 2 */ pixDisplayWithTitle(pixt3, 100, 800, "Histo of saturation", rp->display); pixaAddPix(pixa, pixt3, L_INSERT); pixd = pixaDisplayTiledAndScaled(pixa, 32, 270, 7, 0, 30, 3); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 3 */ pixDisplayWithTitle(pixd, 0, 400, "Hue and Saturation Mosaic", rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); numaDestroy(&nahue); numaDestroy(&nasat); /* Find all the peaks */ pixFindHistoPeaksHSV(pixh, L_HS_HISTO, 20, 20, 6, 2.0, &ptapk, &napk, &pixapk); numaWriteStream(stderr, napk); ptaWriteStream(stderr, ptapk, 1); pixd = pixaDisplayTiledInRows(pixapk, 32, 1400, 1.0, 0, 30, 2); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 4 */ pixDisplayWithTitle(pixd, 0, 550, "Peaks in HS", rp->display); pixDestroy(&pixh); pixDestroy(&pixd); pixaDestroy(&pixapk); /* Make masks for each of the peaks */ pixa = pixaCreate(0); pixr = pixScaleBySampling(pixs, 0.4, 0.4); for (i = 0; i < 6; i++) { ptaGetIPt(ptapk, i, &x, &y); pixt1 = pixMakeRangeMaskHS(pixr, y, 20, x, 20, L_INCLUDE_REGION); pixaAddPix(pixa, pixt1, L_INSERT); pixGetAverageMaskedRGB(pixr, pixt1, 0, 0, 1, L_MEAN_ABSVAL, &frval, &fgval, &fbval); composeRGBPixel((l_int32)frval, (l_int32)fgval, (l_int32)fbval, &pixel); pixt2 = pixCreateTemplate(pixr); pixSetAll(pixt2); pixPaintThroughMask(pixt2, pixt1, 0, 0, pixel); pixaAddPix(pixa, pixt2, L_INSERT); pixt3 = pixCreateTemplate(pixr); pixSetAllArbitrary(pixt3, pixel); pixaAddPix(pixa, pixt3, L_INSERT); } pixd = pixaDisplayTiledAndScaled(pixa, 32, 225, 3, 0, 30, 3); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 5 */ pixDisplayWithTitle(pixd, 600, 0, "Masks over peaks", rp->display); pixDestroy(&pixs); pixDestroy(&pixr); pixDestroy(&pixd); pixaDestroy(&pixa); ptaDestroy(&ptapk); numaDestroy(&napk); regTestCleanup(rp); return 0; }
/*! * wshedApply() * * Input: wshed (generated from wshedCreate()) * Return: 0 if OK, 1 on error * * Iportant note: * (1) This is buggy. It seems to locate watersheds that are * duplicates. The watershed extraction after complete fill * grabs some regions belonging to existing watersheds. * See prog/watershedtest.c for testing. */ l_int32 wshedApply(L_WSHED *wshed) { char two_new_watersheds[] = "Two new watersheds"; char seed_absorbed_into_seeded_basin[] = "Seed absorbed into seeded basin"; char one_new_watershed_label[] = "One new watershed (label)"; char one_new_watershed_index[] = "One new watershed (index)"; char minima_absorbed_into_seeded_basin[] = "Minima absorbed into seeded basin"; char minima_absorbed_by_filler_or_another[] = "Minima absorbed by filler or another"; l_int32 nseeds, nother, nboth, arraysize; l_int32 i, j, val, x, y, w, h, index, mindepth; l_int32 imin, imax, jmin, jmax, cindex, clabel, nindex; l_int32 hindex, hlabel, hmin, hmax, minhindex, maxhindex; l_int32 *lut; l_uint32 ulabel, uval; void **lines8, **linelab32; NUMA *nalut, *nalevels, *nash, *namh, *nasi; NUMA **links; L_HEAP *lh; PIX *pixmin, *pixsd; PIXA *pixad; L_STACK *rstack; PTA *ptas, *ptao; PROCNAME("wshedApply"); if (!wshed) return ERROR_INT("wshed not defined", procName, 1); /* ------------------------------------------------------------ * * Initialize priority queue and pixlab with seeds and minima * * ------------------------------------------------------------ */ lh = lheapCreate(0, L_SORT_INCREASING); /* remove lowest values first */ rstack = lstackCreate(0); /* for reusing the WSPixels */ pixGetDimensions(wshed->pixs, &w, &h, NULL); lines8 = wshed->lines8; /* wshed owns this */ linelab32 = wshed->linelab32; /* ditto */ /* Identify seed (marker) pixels, 1 for each c.c. in pixm */ pixSelectMinInConnComp(wshed->pixs, wshed->pixm, &ptas, &nash); pixsd = pixGenerateFromPta(ptas, w, h); nseeds = ptaGetCount(ptas); for (i = 0; i < nseeds; i++) { ptaGetIPt(ptas, i, &x, &y); uval = GET_DATA_BYTE(lines8[y], x); pushWSPixel(lh, rstack, (l_int32) uval, x, y, i); } wshed->ptas = ptas; nasi = numaMakeConstant(1, nseeds); /* indicator array */ wshed->nasi = nasi; wshed->nash = nash; wshed->nseeds = nseeds; /* Identify minima that are not seeds. Use these 4 steps: * (1) Get the local minima, which can have components * of arbitrary size. This will be a clipping mask. * (2) Get the image of the actual seeds (pixsd) * (3) Remove all elements of the clipping mask that have a seed. * (4) Shrink each of the remaining elements of the minima mask * to a single pixel. */ pixLocalExtrema(wshed->pixs, 200, 0, &pixmin, NULL); pixRemoveSeededComponents(pixmin, pixsd, pixmin, 8, 2); pixSelectMinInConnComp(wshed->pixs, pixmin, &ptao, &namh); nother = ptaGetCount(ptao); for (i = 0; i < nother; i++) { ptaGetIPt(ptao, i, &x, &y); uval = GET_DATA_BYTE(lines8[y], x); pushWSPixel(lh, rstack, (l_int32) uval, x, y, nseeds + i); } wshed->namh = namh; /* ------------------------------------------------------------ * * Initialize merging lookup tables * * ------------------------------------------------------------ */ /* nalut should always give the current after-merging index. * links are effectively backpointers: they are numas associated with * a dest index of all indices in nalut that point to that index. */ mindepth = wshed->mindepth; nboth = nseeds + nother; arraysize = 2 * nboth; wshed->arraysize = arraysize; nalut = numaMakeSequence(0, 1, arraysize); lut = numaGetIArray(nalut); wshed->lut = lut; /* wshed owns this */ links = (NUMA **) CALLOC(arraysize, sizeof(NUMA * )); wshed->links = links; /* wshed owns this */ nindex = nseeds + nother; /* the next unused index value */ /* ------------------------------------------------------------ * * Fill the basins, using the priority queue * * ------------------------------------------------------------ */ pixad = pixaCreate(nseeds); wshed->pixad = pixad; /* wshed owns this */ nalevels = numaCreate(nseeds); wshed->nalevels = nalevels; /* wshed owns this */ L_INFO("nseeds = %d, nother = %d\n", procName, nseeds, nother); while (lheapGetCount(lh) > 0) { popWSPixel(lh, rstack, &val, &x, &y, &index); /* fprintf(stderr, "x = %d, y = %d, index = %d\n", x, y, index); */ ulabel = GET_DATA_FOUR_BYTES(linelab32[y], x); if (ulabel == MAX_LABEL_VALUE) clabel = ulabel; else clabel = lut[ulabel]; cindex = lut[index]; if (clabel == cindex) continue; /* have already seen this one */ if (clabel == MAX_LABEL_VALUE) { /* new one; assign index and try to * propagate to all neighbors */ SET_DATA_FOUR_BYTES(linelab32[y], x, cindex); imin = L_MAX(0, y - 1); imax = L_MIN(h - 1, y + 1); jmin = L_MAX(0, x - 1); jmax = L_MIN(w - 1, x + 1); for (i = imin; i <= imax; i++) { for (j = jmin; j <= jmax; j++) { if (i == y && j == x) continue; uval = GET_DATA_BYTE(lines8[i], j); pushWSPixel(lh, rstack, (l_int32) uval, j, i, cindex); } } } else { /* pixel is already labeled (differently); must resolve */ /* If both indices are seeds, check if the min height is * greater than mindepth. If so, we have two new watersheds; * locate them and assign to both regions a new index * for further waterfill. If not, absorb the shallower * watershed into the deeper one and continue filling it. */ pixGetPixel(pixsd, x, y, &uval); if (clabel < nseeds && cindex < nseeds) { wshedGetHeight(wshed, val, clabel, &hlabel); wshedGetHeight(wshed, val, cindex, &hindex); hmin = L_MIN(hlabel, hindex); hmax = L_MAX(hlabel, hindex); if (hmin == hmax) { hmin = hlabel; hmax = hindex; } if (wshed->debug) { fprintf(stderr, "clabel,hlabel = %d,%d\n", clabel, hlabel); fprintf(stderr, "hmin = %d, hmax = %d\n", hmin, hmax); fprintf(stderr, "cindex,hindex = %d,%d\n", cindex, hindex); if (hmin < mindepth) fprintf(stderr, "Too shallow!\n"); } if (hmin >= mindepth) { debugWshedMerge(wshed, two_new_watersheds, x, y, clabel, cindex); wshedSaveBasin(wshed, cindex, val - 1); wshedSaveBasin(wshed, clabel, val - 1); numaSetValue(nasi, cindex, 0); numaSetValue(nasi, clabel, 0); if (wshed->debug) fprintf(stderr, "nindex = %d\n", nindex); debugPrintLUT(lut, nindex, wshed->debug); mergeLookup(wshed, clabel, nindex); debugPrintLUT(lut, nindex, wshed->debug); mergeLookup(wshed, cindex, nindex); debugPrintLUT(lut, nindex, wshed->debug); nindex++; } else /* extraneous seed within seeded basin; absorb */ { debugWshedMerge(wshed, seed_absorbed_into_seeded_basin, x, y, clabel, cindex); } maxhindex = clabel; /* TODO: is this part of above 'else'? */ minhindex = cindex; if (hindex > hlabel) { maxhindex = cindex; minhindex = clabel; } mergeLookup(wshed, minhindex, maxhindex); } else if (clabel < nseeds && cindex >= nboth) { /* If one index is a seed and the other is a merge of * 2 watersheds, generate a single watershed. */ debugWshedMerge(wshed, one_new_watershed_label, x, y, clabel, cindex); wshedSaveBasin(wshed, clabel, val - 1); numaSetValue(nasi, clabel, 0); mergeLookup(wshed, clabel, cindex); } else if (cindex < nseeds && clabel >= nboth) { debugWshedMerge(wshed, one_new_watershed_index, x, y, clabel, cindex); wshedSaveBasin(wshed, cindex, val - 1); numaSetValue(nasi, cindex, 0); mergeLookup(wshed, cindex, clabel); } else if (clabel < nseeds) { /* cindex from minima; absorb */ /* If one index is a seed and the other is from a minimum, * merge the minimum wshed into the seed wshed. */ debugWshedMerge(wshed, minima_absorbed_into_seeded_basin, x, y, clabel, cindex); mergeLookup(wshed, cindex, clabel); } else if (cindex < nseeds) { /* clabel from minima; absorb */ debugWshedMerge(wshed, minima_absorbed_into_seeded_basin, x, y, clabel, cindex); mergeLookup(wshed, clabel, cindex); } else { /* If neither index is a seed, just merge */ debugWshedMerge(wshed, minima_absorbed_by_filler_or_another, x, y, clabel, cindex); mergeLookup(wshed, clabel, cindex); } } } #if 0 /* Use the indicator array to save any watersheds that fill * to the maximum value. This seems to screw things up! */ for (i = 0; i < nseeds; i++) { numaGetIValue(nasi, i, &ival); if (ival == 1) { wshedSaveBasin(wshed, lut[i], val - 1); numaSetValue(nasi, i, 0); } } #endif numaDestroy(&nalut); pixDestroy(&pixmin); pixDestroy(&pixsd); ptaDestroy(&ptao); lheapDestroy(&lh, TRUE); lstackDestroy(&rstack, TRUE); return 0; }
/*! * pixProjectivePtaWithAlpha() * * Input: pixs (32 bpp rgb) * ptad (4 pts of final coordinate space) * ptas (4 pts of initial coordinate space) * pixg (<optional> 8 bpp, for alpha channel, can be null) * fract (between 0.0 and 1.0, with 0.0 fully transparent * and 1.0 fully opaque) * border (of pixels added to capture transformed source pixels) * Return: pixd, or null on error * * Notes: * (1) The alpha channel is transformed separately from pixs, * and aligns with it, being fully transparent outside the * boundary of the transformed pixs. For pixels that are fully * transparent, a blending function like pixBlendWithGrayMask() * will give zero weight to corresponding pixels in pixs. * (2) If pixg is NULL, it is generated as an alpha layer that is * partially opaque, using @fract. Otherwise, it is cropped * to pixs if required and @fract is ignored. The alpha channel * in pixs is never used. * (3) Colormaps are removed. * (4) When pixs is transformed, it doesn't matter what color is brought * in because the alpha channel will be transparent (0) there. * (5) To avoid losing source pixels in the destination, it may be * necessary to add a border to the source pix before doing * the projective transformation. This can be any non-negative * number. * (6) The input @ptad and @ptas are in a coordinate space before * the border is added. Internally, we compensate for this * before doing the projective transform on the image after * the border is added. * (7) The default setting for the border values in the alpha channel * is 0 (transparent) for the outermost ring of pixels and * (0.5 * fract * 255) for the second ring. When blended over * a second image, this * (a) shrinks the visible image to make a clean overlap edge * with an image below, and * (b) softens the edges by weakening the aliasing there. * Use l_setAlphaMaskBorder() to change these values. */ PIX * pixProjectivePtaWithAlpha(PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border) { l_int32 ws, hs, d; PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; PTA *ptad2, *ptas2; PROCNAME("pixProjectivePtaWithAlpha"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &ws, &hs, &d); if (d != 32 && pixGetColormap(pixs) == NULL) return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); if (pixg && pixGetDepth(pixg) != 8) { L_WARNING("pixg not 8 bpp; using @fract transparent alpha", procName); pixg = NULL; } if (!pixg && (fract < 0.0 || fract > 1.0)) { L_WARNING("invalid fract; using 1.0 (fully transparent)", procName); fract = 1.0; } if (!pixg && fract == 0.0) L_WARNING("fully opaque alpha; image will not be blended", procName); if (!ptad) return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); if (!ptas) return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); /* Add border; the color doesn't matter */ pixb1 = pixAddBorder(pixs, border, 0); /* Transform the ptr arrays to work on the bordered image */ ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); /* Do separate projective transform of rgb channels of pixs * and of pixg */ pixd = pixProjectivePtaColor(pixb1, ptad2, ptas2, 0); if (!pixg) { pixg2 = pixCreate(ws, hs, 8); if (fract == 1.0) pixSetAll(pixg2); else pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); } else pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); if (ws > 10 && hs > 10) { /* see note 7 */ pixSetBorderRingVal(pixg2, 1, (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); pixSetBorderRingVal(pixg2, 2, (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); } pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ pixga = pixProjectivePtaGray(pixb2, ptad2, ptas2, 0); pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); pixDestroy(&pixg2); pixDestroy(&pixb1); pixDestroy(&pixb2); pixDestroy(&pixga); ptaDestroy(&ptad2); ptaDestroy(&ptas2); return pixd; }
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; }
int main(int argc, char* argv[]) { PIX *pixs, *pixb, *pixt; int minb; if (argc < 2) { USAGE: fprintf(stderr, "Usage: %s </path/to/text-image>\n" "\t\t[binarize-threshold] [minw:minh:maxw:maxh]\n", strrchr(argv[0], '/') + 1); return EINVAL; } if (2 < argc) { errno = 0; minb = strtol(argv[2], NULL, 10); if (errno < 0) { fprintf(stderr, "strtol: %s\n", strerror(errno)); goto USAGE; } } else minb = 180; if (!(pixs = pixRead(argv[1]))) ; if (1 && (pixt = pixBackgroundNormMorph(pixs, NULL, 4, 5, 248))) { pixDestroy(&pixs); pixs = pixt; } else if (0 && (pixt = pixBackgroundNorm(pixs, NULL, NULL, 10, 15, 60, 40, 248, 2, 1))) { pixDestroy(&pixs); pixs = pixt; } if (1 && (pixt = pixFindSkewAndDeskew(pixs, 1, NULL, NULL))) { pixDestroy(&pixs); pixs = pixt; } if (0 && pixDisplay(pixs, 0, 0)) ; if (1) { PTA *ptas, *ptad; if (!(pixb = pixConvertTo1(pixs, minb))) ; // pixt = pixDeskewLocal(pixs, 10, 0, 0, 0.0, 0.0, 0.0)) if (!pixGetLocalSkewTransform(pixb, 10, 0, 0, 0.0, 0.0, 0.0, &ptas, &ptad)) { if ((pixt = pixProjectiveSampledPta(pixs, ptad, ptas, L_BRING_IN_WHITE))) { pixDestroy(&pixs); pixs = pixt; } ptaDestroy(&ptas); ptaDestroy(&ptad); } pixDestroy(&pixb); } if (0 && (pixt = pixGammaTRC(NULL, pixs, 1.0, 30, 180))) { pixDestroy(&pixs); pixs = pixt; } if (!(pixb = pixConvertTo1(pixs, minb))) ; if (0) { pixDestroy(&pixs); pixs = pixCopy(pixs, pixb); } // XXX: if (1) { BOX* box; int i, n, j, m; PIX *pixi, *pixl; BOXA *boxi, *boxl; int x, y, w, h, wid; int X = INT_MAX, Y = INT_MAX, W = 0, H; // XXX: do smaller(or no) pixOpenBrick if (pixGetRegionsBinary(pixb, &pixi, &pixl, NULL, 0)) ; boxl = pixConnComp(pixl, NULL, 4); n = boxaGetCount(boxl); for (i = 0; i < n; ++i) { BOXA* boxa; box = boxaGetBox(boxl, i, L_CLONE); boxGetGeometry(box, &x, &y, &w, &h); if (w < 30 || h < 30 || w < h || h < (w / 40)) { boxDestroy(&box); continue; boxaRemoveBox(boxl, i); } if (x < X) X = x; if (y < Y) Y = y; if (W < w) W = w; pixt = pixClipRectangle(pixb, box, NULL); boxDestroy(&box); // XXX: for English if (0) pixt = pixDilateBrick(pixt, pixt, h >> 1, h >> 1); else pixt = pixDilateBrick(pixt, pixt, 16 < h ? h >> 4 : 1, h << 1); if (0 && pixDisplay(pixt, 0, 0)) ; boxa = pixConnComp(pixt, NULL, 8); pixDestroy(&pixt); wid = (h * 3) >> 2; //boxaShift(boxa, x, y); m = boxaGetCount(boxa); for (j = 0; j < m; ++j) { int x0, y0, w0; box = boxaGetBox(boxa, j, L_CLONE); boxGetGeometry(box, &x0, &y0, &w0, NULL); // merge adjacent 2 or 3 small boxes if (1 && w0 < wid && (j + 1) < m) { BOX* boxn; int xn, wn; boxn = boxaGetBox(boxa, j + 1, L_CLONE); boxGetGeometry(boxn, &xn, NULL, &wn, NULL); if ((w0 = xn + wn - x0) < h) { boxaSparseClearBox(boxa, ++j); if (w0 < wid && (j + 1) < m) { boxDestroy(&boxn); boxn = boxaGetBox(boxa, j + 1, L_CLONE); boxGetGeometry(boxn, &xn, NULL, &wn, NULL); if ((wn = xn + wn - x0) < h) { boxaSparseClearBox(boxa, ++j); w0 = wn; } } boxSetGeometry(box, -1, -1, w0, -1); } boxDestroy(&boxn); } boxSetGeometry(box, x + x0, y + y0, -1, -1); boxDestroy(&box); } boxaSparseCompact(boxa); if (1 && (pixt = pixDrawBoxa(pixs, boxa, 1, 0xff000000))) { pixDestroy(&pixs); pixs = pixt; } boxaDestroy(&boxa); } H = y + h;
main(int argc, char **argv) { char bufname[256]; l_int32 i, j, w, h, d, x, y, wpls; l_uint32 *datas, *lines; l_float32 *vc; PIX *pixs, *pixsc, *pixb, *pixg, *pixc, *pixcs, *pixd; PIX *pixt1, *pixt2, *pixt3; PIXA *pixa; PTA *ptas, *ptad; static char mainName[] = "projective_reg"; if (argc != 1) exit(ERROR_INT(" Syntax: projective_reg", mainName, 1)); if ((pixs = pixRead("feyn.tif")) == NULL) exit(ERROR_INT("pixs not made", mainName, 1)); pixsc = pixScale(pixs, 0.5, 0.5); #if ALL /* Test invertability of sampling */ pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixsc, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pixt1 = pixProjectiveSampledPta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixProjectiveSampledPta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 1, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixsc); pixSaveTiled(pixd, pixa, 1, 0, 20, 0); if (i == 0) pixWrite("/tmp/junksamp.png", pixt1, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkproj1.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 300); pixDestroy(&pixt1); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of interpolation on grayscale */ pixa = pixaCreate(0); pixg = pixScaleToGray3(pixs); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixg, ADDED_BORDER_PIXELS / 2, 255); MakePtas(i, &ptas, &ptad); pixt1 = pixProjectivePta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixProjectivePta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 1, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS / 2); pixXor(pixd, pixd, pixg); pixSaveTiled(pixd, pixa, 1, 0, 20, 0); if (i == 0) pixWrite("/tmp/junkinterp.png", pixt1, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkproj2.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 500); pixDestroy(&pixt1); pixaDestroy(&pixa); pixDestroy(&pixg); #endif #if ALL /* Test invertability of interpolation on color */ pixa = pixaCreate(0); pixc = pixRead("test24.jpg"); pixcs = pixScale(pixc, 0.3, 0.3); for (i = 0; i < 5; i++) { pixb = pixAddBorder(pixcs, ADDED_BORDER_PIXELS, 0xffffff00); MakePtas(i, &ptas, &ptad); pixt1 = pixProjectivePta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 1, 1, 20, 32); pixt2 = pixProjectivePta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 1, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixcs); pixSaveTiled(pixd, pixa, 1, 0, 20, 0); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkproj3.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 500); pixDestroy(&pixt1); pixaDestroy(&pixa); pixDestroy(&pixc); pixDestroy(&pixcs); #endif #if ALL /* Comparison between sampling and interpolated */ MakePtas(3, &ptas, &ptad); pixa = pixaCreate(0); /* Use sampled transform */ pixt1 = pixProjectiveSampledPta(pixs, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 2, 1, 20, 8); /* Use interpolated transforms */ pixt2 = pixProjectivePta(pixs, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 2, 0, 20, 8); /* Compare the results */ pixXor(pixt2, pixt2, pixt1); pixSaveTiled(pixt2, pixa, 2, 0, 20, 8); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkproj4.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 700); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif #if ALL /* Get timings */ MakePtas(4, &ptas, &ptad); pixa = pixaCreate(0); pixg = pixScaleToGray3(pixs); startTimer(); pixt1 = pixProjectiveSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixProjectiveSampledPta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); startTimer(); pixt2 = pixProjectivePta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixProjectivePta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); pixXor(pixt1, pixt1, pixt2); pixSaveTiled(pixt1, pixa, 1, 0, 20, 8); pixDestroy(&pixt1); pixDestroy(&pixt2); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkproj5.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 900); pixDestroy(&pixd); pixDestroy(&pixg); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif pixDestroy(&pixs); pixDestroy(&pixsc); return 0; }
void DoWatershed(L_REGPARAMS *rp, PIX *pixs) { l_uint8 *data; size_t size; l_int32 w, h, empty; l_uint32 redval, greenval; L_WSHED *wshed; PIX *pixc, *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; PIXA *pixa; PTA *pta; /* Find local extrema */ pixa = pixaCreate(0); pixGetDimensions(pixs, &w, &h, NULL); regTestWritePixAndCheck(rp, pixs, IFF_PNG); /* 0 */ pixSaveTiled(pixs, pixa, 1.0, 1, 10, 32); startTimer(); pixLocalExtrema(pixs, 0, 0, &pix1, &pix2); fprintf(stderr, "Time for extrema: %7.3f\n", stopTimer()); pixSetOrClearBorder(pix1, 2, 2, 2, 2, PIX_CLR); composeRGBPixel(255, 0, 0, &redval); composeRGBPixel(0, 255, 0, &greenval); pixc = pixConvertTo32(pixs); pixPaintThroughMask(pixc, pix2, 0, 0, greenval); pixPaintThroughMask(pixc, pix1, 0, 0, redval); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 1 */ pixSaveTiled(pixc, pixa, 1.0, 0, 10, 32); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 2 */ pixSaveTiled(pix1, pixa, 1.0, 0, 10, 32); /* Generate seeds for watershed */ pixSelectMinInConnComp(pixs, pix1, &pta, NULL); pix3 = pixGenerateFromPta(pta, w, h); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 3 */ pixSaveTiled(pix3, pixa, 1.0, 1, 10, 32); pix4 = pixConvertTo32(pixs); pixPaintThroughMask(pix4, pix3, 0, 0, greenval); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 4 */ pixSaveTiled(pix4, pixa, 1.0, 0, 10, 32); pix5 = pixRemoveSeededComponents(NULL, pix3, pix1, 8, 2); regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 5 */ pixSaveTiled(pix5, pixa, 1.0, 0, 10, 32); pixZero(pix5, &empty); regTestCompareValues(rp, 1, empty, 0.0); /* 6 */ /* Make and display watershed */ wshed = wshedCreate(pixs, pix3, 10, 0); startTimer(); wshedApply(wshed); fprintf(stderr, "Time for wshed: %7.3f\n", stopTimer()); pix6 = pixaDisplayRandomCmap(wshed->pixad, w, h); regTestWritePixAndCheck(rp, pix6, IFF_PNG); /* 7 */ pixSaveTiled(pix6, pixa, 1.0, 1, 10, 32); numaWriteMem(&data, &size, wshed->nalevels); regTestWriteDataAndCheck(rp, data, size, "na"); /* 8 */ pix7 = wshedRenderFill(wshed); regTestWritePixAndCheck(rp, pix7, IFF_PNG); /* 9 */ pixSaveTiled(pix7, pixa, 1.0, 0, 10, 32); pix8 = wshedRenderColors(wshed); regTestWritePixAndCheck(rp, pix8, IFF_PNG); /* 10 */ pixSaveTiled(pix8, pixa, 1.0, 0, 10, 32); wshedDestroy(&wshed); pix9 = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pix9, IFF_PNG); /* 11 */ pixDisplayWithTitle(pix9, 100, 100, NULL, rp->display); lept_free(data); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); pixDestroy(&pix7); pixDestroy(&pix8); pixDestroy(&pix9); pixDestroy(&pixc); pixaDestroy(&pixa); ptaDestroy(&pta); }
/*! * \brief pixGetSortedNeighborValues() * * \param[in] pixs 8, 16 or 32 bpp, with pixels labeled by c.c. * \param[in] x, y location of pixel * \param[in] conn 4 or 8 connected neighbors * \param[out] pneigh array of integers, to be filled with * the values of the neighbors, if any * \param[out] pnvals the number of unique neighbor values found * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) The returned %neigh array is the unique set of neighboring * pixel values, of size nvals, sorted from smallest to largest. * The value 0, which represents background pixels that do * not belong to any set of connected components, is discarded. * (2) If there are no neighbors, this returns %neigh = NULL; otherwise, * the caller must free the array. * (3) For either 4 or 8 connectivity, the maximum number of unique * neighbor values is 4. * </pre> */ l_int32 pixGetSortedNeighborValues(PIX *pixs, l_int32 x, l_int32 y, l_int32 conn, l_int32 **pneigh, l_int32 *pnvals) { l_int32 i, npt, index; l_int32 neigh[4]; l_uint32 val; l_float32 fx, fy; L_ASET *aset; L_ASET_NODE *node; PTA *pta; RB_TYPE key; PROCNAME("pixGetSortedNeighborValues"); if (pneigh) *pneigh = NULL; if (pnvals) *pnvals = 0; if (!pneigh || !pnvals) return ERROR_INT("&neigh and &nvals not both defined", procName, 1); if (!pixs || pixGetDepth(pixs) < 8) return ERROR_INT("pixs not defined or depth < 8", procName, 1); /* Identify the locations of nearest neighbor pixels */ if ((pta = ptaGetNeighborPixLocs(pixs, x, y, conn)) == NULL) return ERROR_INT("pta of neighbors not made", procName, 1); /* Find the pixel values and insert into a set as keys */ aset = l_asetCreate(L_UINT_TYPE); npt = ptaGetCount(pta); for (i = 0; i < npt; i++) { ptaGetPt(pta, i, &fx, &fy); pixGetPixel(pixs, (l_int32)fx, (l_int32)fy, &val); key.utype = val; l_asetInsert(aset, key); } /* Extract the set keys and put them into the %neigh array. * Omit the value 0, which indicates the pixel doesn't * belong to one of the sets of connected components. */ node = l_asetGetFirst(aset); index = 0; while (node) { val = node->key.utype; if (val > 0) neigh[index++] = (l_int32)val; node = l_asetGetNext(node); } *pnvals = index; if (index > 0) { *pneigh = (l_int32 *)LEPT_CALLOC(index, sizeof(l_int32)); for (i = 0; i < index; i++) (*pneigh)[i] = neigh[i]; } ptaDestroy(&pta); l_asetDestroy(&aset); 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; }
main(int argc, char **argv) { l_int32 i, w, h, bx, by, bw, bh, index, rval, gval, bval; BOX *box; BOXA *boxa; PIX *pixm, *pixs, *pixg, *pixt, *pixd; PIXA *pixa; PIXCMAP *cmap; PTA *pta; PTAA *ptaa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixa = pixaCreate(0); /* ---------------- Shortest path in binary maze ---------------- */ /* Generate the maze */ pixm = generateBinaryMaze(200, 200, 20, 20, 0.65, 0.25); pixd = pixExpandBinaryReplicate(pixm, 3); pixSaveTiledOutline(pixd, pixa, 1, 1, 20, 2, 32); pixDestroy(&pixd); /* Find the shortest path between two points */ pta = pixSearchBinaryMaze(pixm, 20, 20, 170, 170, NULL); pixt = pixDisplayPta(NULL, pixm, pta); pixd = pixScaleBySampling(pixt, 3., 3.); pixSaveTiledOutline(pixd, pixa, 1, 0, 20, 2, 32); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 0 */ ptaDestroy(&pta); pixDestroy(&pixt); pixDestroy(&pixd); pixDestroy(&pixm); /* ---------------- Shortest path in gray maze ---------------- */ pixg = pixRead("test8.jpg"); pixGetDimensions(pixg, &w, &h, NULL); ptaa = ptaaCreate(NPATHS); for (i = 0; i < NPATHS; i++) { if (x0[i] >= w || x1[i] >= w || y0[i] >= h || y1[i] >= h) { fprintf(stderr, "path %d extends beyond image; skipping\n", i); continue; } pta = pixSearchGrayMaze(pixg, x0[i], y0[i], x1[i], y1[i], NULL); ptaaAddPta(ptaa, pta, L_INSERT); } pixt = pixDisplayPtaa(pixg, ptaa); pixd = pixScaleBySampling(pixt, 2., 2.); pixSaveTiledOutline(pixd, pixa, 1, 1, 20, 2, 32); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 1 */ ptaaDestroy(&ptaa); pixDestroy(&pixg); pixDestroy(&pixt); pixDestroy(&pixd); /* ---------------- Largest rectangles in image ---------------- */ pixs = pixRead("test1.png"); pixd = pixConvertTo8(pixs, FALSE); cmap = pixcmapCreateRandom(8, 1, 1); pixSetColormap(pixd, cmap); boxa = boxaCreate(0); for (i = 0; i < NBOXES; i++) { pixFindLargestRectangle(pixs, POLARITY, &box, NULL); boxGetGeometry(box, &bx, &by, &bw, &bh); pixSetInRect(pixs, box); fprintf(stderr, "bx = %5d, by = %5d, bw = %5d, bh = %5d, area = %d\n", bx, by, bw, bh, bw * bh); boxaAddBox(boxa, box, L_INSERT); } for (i = 0; i < NBOXES; i++) { index = 32 + (i & 254); pixcmapGetColor(cmap, index, &rval, &gval, &bval); box = boxaGetBox(boxa, i, L_CLONE); pixRenderHashBoxArb(pixd, box, 6, 2, L_NEG_SLOPE_LINE, 1, rval, gval, bval); boxDestroy(&box); } pixSaveTiledOutline(pixd, pixa, 1, 1, 20, 2, 32); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 2 */ pixDestroy(&pixs); pixDestroy(&pixd); boxaDestroy(&boxa); pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 3 */ pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); return regTestCleanup(rp); }
main(int argc, char **argv) { l_int32 i; l_float32 pi, scale, angle; PIX *pixc, *pixm, *pix1, *pix2, *pix3; PIXA *pixa; PTA *pta1, *pta2, *pta3, *pta4; static char mainName[] = "smallpix_reg"; /* Make a small test image, the hard way! */ pi = 3.1415926535; pixc = pixCreate(9, 9, 32); pixm = pixCreate(9, 9, 1); pta1 = generatePtaLineFromPt(4, 4, 3.1, 0.0); pta2 = generatePtaLineFromPt(4, 4, 3.1, 0.5 * pi); pta3 = generatePtaLineFromPt(4, 4, 3.1, pi); pta4 = generatePtaLineFromPt(4, 4, 3.1, 1.5 * pi); ptaJoin(pta1, pta2, 0, 0); ptaJoin(pta1, pta3, 0, 0); ptaJoin(pta1, pta4, 0, 0); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); ptaDestroy(&pta4); pixDestroy(&pixm); /* Results differ for scaleSmoothLow() w/ and w/out + 0.5. * Neither is properly symmetric (with symm pattern on odd-sized * pix, because the smoothing is destroying the symmetry. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleSmooth(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 100, NULL); /* Results same for pixScaleAreaMap w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleAreaMap(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 200, NULL); /* Results better for pixScaleBySampling with + 0.5, for small, * odd-dimension pix. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleBySampling(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 300, NULL); /* Results same for pixRotateAM w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAM(pix1, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 400, NULL); /* If the size is odd, we express the center exactly, and the * results are better for pixRotateBySampling() w/out 0.5 * However, if the size is even, the center value is not * exact, and if we choose it 0.5 smaller than the actual * center, we get symmetrical results with +0.5. * So we choose not to include + 0.5. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateBySampling(pix1, 4, 4, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 500, NULL); /* Results same for pixRotateAMCorner w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAMCorner(pix1, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 600, NULL); /* Results better for pixRotateAMColorFast without + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAMColorFast(pix1, angle, 0); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 700, NULL); /* Results slightly better for pixScaleColorLI() w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { scale = 1.0 + 0.2 * (l_float32)i; pix2 = pixScaleColorLI(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 4); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 800, NULL); /* Results slightly better for pixScaleColorLI() w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { scale = 1.0 + 0.2 * (l_float32)i; pix2 = pixScaleLI(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 4); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 940, NULL); pixDestroy(&pixc); return 0; }
/*! * \brief pixGenerateSelWithRuns() * * \param[in] pixs 1 bpp, typically small, to be used as a pattern * \param[in] nhlines number of hor lines along which elements are found * \param[in] nvlines number of vert lines along which elements are found * \param[in] distance min distance from boundary pixel; use 0 for default * \param[in] minlength min runlength to set hit or miss; use 0 for default * \param[in] toppix number of extra pixels of bg added above * \param[in] botpix number of extra pixels of bg added below * \param[in] leftpix number of extra pixels of bg added to left * \param[in] rightpix number of extra pixels of bg added to right * \param[out] ppixe [optional] input pix expanded by extra pixels * \return sel hit-miss for input pattern, or NULL on error * * <pre> * Notes: * (1) The horizontal and vertical lines along which elements are * selected are roughly equally spaced. The actual locations of * the hits and misses are the centers of respective run-lengths. * (2) No elements are selected that are less than 'distance' pixels away * from a boundary pixel of the same color. This makes the * match much more robust to edge noise. Valid inputs of * 'distance' are 0, 1, 2, 3 and 4. If distance is either 0 or * greater than 4, we reset it to the default value. * (3) The 4 numbers for adding rectangles of pixels outside the fg * can be use if the pattern is expected to be surrounded by bg * (white) pixels. On the other hand, if the pattern may be near * other fg (black) components on some sides, use 0 for those sides. * (4) The pixels added to a side allow you to have miss elements there. * There is a constraint between distance, minlength, and * the added pixels for this to work. We illustrate using the * default values. If you add 5 pixels to the top, and use a * distance of 1, then you end up with a vertical run of at least * 4 bg pixels along the top edge of the image. If you use a * minimum runlength of 3, each vertical line will always find * a miss near the center of its run. However, if you use a * minimum runlength of 5, you will not get a miss on every vertical * line. As another example, if you have 7 added pixels and a * distance of 2, you can use a runlength up to 5 to guarantee * that the miss element is recorded. We give a warning if the * contraint does not guarantee a miss element outside the * image proper. * (5) The input pix, as extended by the extra pixels on selected sides, * can optionally be returned. For debugging, call * pixDisplayHitMissSel() to visualize the hit-miss sel superimposed * on the generating bitmap. * </pre> */ SEL * pixGenerateSelWithRuns(PIX *pixs, l_int32 nhlines, l_int32 nvlines, l_int32 distance, l_int32 minlength, l_int32 toppix, l_int32 botpix, l_int32 leftpix, l_int32 rightpix, PIX **ppixe) { l_int32 ws, hs, w, h, x, y, xval, yval, i, j, nh, nm; l_float32 delh, delw; NUMA *nah, *nam; PIX *pixt1, *pixt2, *pixfg, *pixbg; PTA *ptah, *ptam; SEL *seld, *sel; PROCNAME("pixGenerateSelWithRuns"); if (ppixe) *ppixe = NULL; if (!pixs) return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 1) return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); if (nhlines < 1 && nvlines < 1) return (SEL *)ERROR_PTR("nvlines and nhlines both < 1", procName, NULL); if (distance <= 0) distance = DEFAULT_DISTANCE_TO_BOUNDARY; if (minlength <= 0) minlength = DEFAULT_MIN_RUNLENGTH; if (distance > MAX_DISTANCE_TO_BOUNDARY) { L_WARNING("distance too large; setting to max value\n", procName); distance = MAX_DISTANCE_TO_BOUNDARY; } /* Locate the foreground */ pixClipToForeground(pixs, &pixt1, NULL); if (!pixt1) return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); ws = pixGetWidth(pixt1); hs = pixGetHeight(pixt1); w = ws; h = hs; /* Crop out a region including the foreground, and add pixels * on sides depending on the side flags */ if (toppix || botpix || leftpix || rightpix) { x = y = 0; if (toppix) { h += toppix; y = toppix; if (toppix < distance + minlength) L_WARNING("no miss elements in added top pixels\n", procName); } if (botpix) { h += botpix; if (botpix < distance + minlength) L_WARNING("no miss elements in added bot pixels\n", procName); } if (leftpix) { w += leftpix; x = leftpix; if (leftpix < distance + minlength) L_WARNING("no miss elements in added left pixels\n", procName); } if (rightpix) { w += rightpix; if (rightpix < distance + minlength) L_WARNING("no miss elements in added right pixels\n", procName); } pixt2 = pixCreate(w, h, 1); pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); } else { pixt2 = pixClone(pixt1); } if (ppixe) *ppixe = pixClone(pixt2); pixDestroy(&pixt1); /* Identify fg and bg pixels that are at least 'distance' pixels * away from the boundary pixels in their set */ seld = selCreateBrick(2 * distance + 1, 2 * distance + 1, distance, distance, SEL_HIT); pixfg = pixErode(NULL, pixt2, seld); pixbg = pixDilate(NULL, pixt2, seld); pixInvert(pixbg, pixbg); selDestroy(&seld); pixDestroy(&pixt2); /* Accumulate hit and miss points */ ptah = ptaCreate(0); ptam = ptaCreate(0); if (nhlines >= 1) { delh = (l_float32)h / (l_float32)(nhlines + 1); for (i = 0, y = 0; i < nhlines; i++) { y += (l_int32)(delh + 0.5); nah = pixGetRunCentersOnLine(pixfg, -1, y, minlength); nam = pixGetRunCentersOnLine(pixbg, -1, y, minlength); nh = numaGetCount(nah); nm = numaGetCount(nam); for (j = 0; j < nh; j++) { numaGetIValue(nah, j, &xval); ptaAddPt(ptah, xval, y); } for (j = 0; j < nm; j++) { numaGetIValue(nam, j, &xval); ptaAddPt(ptam, xval, y); } numaDestroy(&nah); numaDestroy(&nam); } } if (nvlines >= 1) { delw = (l_float32)w / (l_float32)(nvlines + 1); for (i = 0, x = 0; i < nvlines; i++) { x += (l_int32)(delw + 0.5); nah = pixGetRunCentersOnLine(pixfg, x, -1, minlength); nam = pixGetRunCentersOnLine(pixbg, x, -1, minlength); nh = numaGetCount(nah); nm = numaGetCount(nam); for (j = 0; j < nh; j++) { numaGetIValue(nah, j, &yval); ptaAddPt(ptah, x, yval); } for (j = 0; j < nm; j++) { numaGetIValue(nam, j, &yval); ptaAddPt(ptam, x, yval); } numaDestroy(&nah); numaDestroy(&nam); } } /* Make the Sel with those points */ sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); nh = ptaGetCount(ptah); for (i = 0; i < nh; i++) { ptaGetIPt(ptah, i, &x, &y); selSetElement(sel, y, x, SEL_HIT); } nm = ptaGetCount(ptam); for (i = 0; i < nm; i++) { ptaGetIPt(ptam, i, &x, &y); selSetElement(sel, y, x, SEL_MISS); } pixDestroy(&pixfg); pixDestroy(&pixbg); ptaDestroy(&ptah); ptaDestroy(&ptam); return sel; }
int main(int argc, char **argv) { l_int32 i, j, w, h, empty; l_uint32 redval, greenval; l_float32 f; L_WSHED *wshed; PIX *pixs, *pixc, *pixd; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8; PIXA *pixac; PTA *pta; static char mainName[] = "watershedtest"; if (argc != 1) return ERROR_INT(" Syntax: watershedtest", mainName, 1); pixac = pixaCreate(0); pixs = pixCreate(500, 500, 8); pixGetDimensions(pixs, &w, &h, NULL); for (i = 0; i < 500; i++) { for (j = 0; j < 500; j++) { #if 1 f = 128.0 + 26.3 * sin(0.0438 * (l_float32)i); f += 33.4 * cos(0.0712 * (l_float32)i); f += 18.6 * sin(0.0561 * (l_float32)j); f += 23.6 * cos(0.0327 * (l_float32)j); #else f = 128.0 + 26.3 * sin(0.0238 * (l_float32)i); f += 33.4 * cos(0.0312 * (l_float32)i); f += 18.6 * sin(0.0261 * (l_float32)j); f += 23.6 * cos(0.0207 * (l_float32)j); #endif pixSetPixel(pixs, j, i, (l_int32)f); } } pixSaveTiled(pixs, pixac, 1.0, 1, 10, 32); pixWrite("/tmp/pattern.png", pixs, IFF_PNG); startTimer(); pixLocalExtrema(pixs, 0, 0, &pix1, &pix2); fprintf(stderr, "Time for extrema: %7.3f\n", stopTimer()); pixSetOrClearBorder(pix1, 2, 2, 2, 2, PIX_CLR); composeRGBPixel(255, 0, 0, &redval); composeRGBPixel(0, 255, 0, &greenval); pixc = pixConvertTo32(pixs); pixPaintThroughMask(pixc, pix2, 0, 0, greenval); pixPaintThroughMask(pixc, pix1, 0, 0, redval); pixSaveTiled(pixc, pixac, 1.0, 0, 10, 32); pixWrite("/tmp/pixc.png", pixc, IFF_PNG); pixSaveTiled(pix1, pixac, 1.0, 0, 10, 32); pixSelectMinInConnComp(pixs, pix1, &pta, NULL); /* ptaWriteStream(stderr, pta, 1); */ pix3 = pixGenerateFromPta(pta, w, h); pixSaveTiled(pix3, pixac, 1.0, 1, 10, 32); pix4 = pixConvertTo32(pixs); pixPaintThroughMask(pix4, pix3, 0, 0, greenval); pixSaveTiled(pix4, pixac, 1.0, 0, 10, 32); pix5 = pixRemoveSeededComponents(NULL, pix3, pix1, 8, 2); pixSaveTiled(pix5, pixac, 1.0, 0, 10, 32); pixZero(pix5, &empty); fprintf(stderr, "Is empty? %d\n", empty); pixDestroy(&pix4); pixDestroy(&pix5); wshed = wshedCreate(pixs, pix3, 10, 0); startTimer(); wshedApply(wshed); fprintf(stderr, "Time for wshed: %7.3f\n", stopTimer()); pix6 = pixaDisplayRandomCmap(wshed->pixad, w, h); pixSaveTiled(pix6, pixac, 1.0, 1, 10, 32); numaWriteStream(stderr, wshed->nalevels); pix7 = wshedRenderFill(wshed); pixSaveTiled(pix7, pixac, 1.0, 0, 10, 32); pix8 = wshedRenderColors(wshed); pixSaveTiled(pix8, pixac, 1.0, 0, 10, 32); wshedDestroy(&wshed); pixd = pixaDisplay(pixac, 0, 0); pixDisplay(pixd, 100, 100); pixWrite("/tmp/wshed.png", pixd, IFF_PNG); pixDestroy(&pixd); pixaDestroy(&pixac); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix6); pixDestroy(&pix7); pixDestroy(&pix8); pixDestroy(&pixs); pixDestroy(&pixc); ptaDestroy(&pta); return 0; }
/*! * \brief pixGenerateSelBoundary() * * \param[in] pixs 1 bpp, typically small, to be used as a pattern * \param[in] hitdist min distance from fg boundary pixel * \param[in] missdist min distance from bg boundary pixel * \param[in] hitskip number of boundary pixels skipped between hits * \param[in] missskip number of boundary pixels skipped between misses * \param[in] topflag flag for extra pixels of bg added above * \param[in] botflag flag for extra pixels of bg added below * \param[in] leftflag flag for extra pixels of bg added to left * \param[in] rightflag flag for extra pixels of bg added to right * \param[out] ppixe [optional] input pix expanded by extra pixels * \return sel hit-miss for input pattern, or NULL on error * * <pre> * Notes: * (1) All fg elements selected are exactly hitdist pixels away from * the nearest fg boundary pixel, and ditto for bg elements. * Valid inputs of hitdist and missdist are 0, 1, 2, 3 and 4. * For example, a hitdist of 0 puts the hits at the fg boundary. * Usually, the distances should be > 0 avoid the effect of * noise at the boundary. * (2) Set hitskip < 0 if no hits are to be used. Ditto for missskip. * If both hitskip and missskip are < 0, the sel would be empty, * and NULL is returned. * (3) The 4 flags determine whether the sel is increased on that side * to allow bg misses to be placed all along that boundary. * The increase in sel size on that side is the minimum necessary * to allow the misses to be placed at mindist. For text characters, * the topflag and botflag are typically set to 1, and the leftflag * and rightflag to 0. * (4) The input pix, as extended by the extra pixels on selected sides, * can optionally be returned. For debugging, call * pixDisplayHitMissSel() to visualize the hit-miss sel superimposed * on the generating bitmap. * (5) This is probably the best of the three sel generators, in the * sense that you have the most flexibility with the smallest number * of hits and misses. * </pre> */ SEL * pixGenerateSelBoundary(PIX *pixs, l_int32 hitdist, l_int32 missdist, l_int32 hitskip, l_int32 missskip, l_int32 topflag, l_int32 botflag, l_int32 leftflag, l_int32 rightflag, PIX **ppixe) { l_int32 ws, hs, w, h, x, y, ix, iy, i, npt; PIX *pixt1, *pixt2, *pixt3, *pixfg, *pixbg; SEL *selh, *selm, *sel_3, *sel; PTA *ptah, *ptam; PROCNAME("pixGenerateSelBoundary"); if (ppixe) *ppixe = NULL; if (!pixs) return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 1) return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); if (hitdist < 0 || hitdist > 4 || missdist < 0 || missdist > 4) return (SEL *)ERROR_PTR("dist not in {0 .. 4}", procName, NULL); if (hitskip < 0 && missskip < 0) return (SEL *)ERROR_PTR("no hits or misses", procName, NULL); /* Locate the foreground */ pixClipToForeground(pixs, &pixt1, NULL); if (!pixt1) return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); ws = pixGetWidth(pixt1); hs = pixGetHeight(pixt1); w = ws; h = hs; /* Crop out a region including the foreground, and add pixels * on sides depending on the side flags */ if (topflag || botflag || leftflag || rightflag) { x = y = 0; if (topflag) { h += missdist + 1; y = missdist + 1; } if (botflag) h += missdist + 1; if (leftflag) { w += missdist + 1; x = missdist + 1; } if (rightflag) w += missdist + 1; pixt2 = pixCreate(w, h, 1); pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); } else { pixt2 = pixClone(pixt1); } if (ppixe) *ppixe = pixClone(pixt2); pixDestroy(&pixt1); /* Identify fg and bg pixels that are exactly hitdist and * missdist (rsp) away from the boundary pixels in their set. * Then get a subsampled set of these points. */ sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT); if (hitskip >= 0) { selh = selCreateBrick(2 * hitdist + 1, 2 * hitdist + 1, hitdist, hitdist, SEL_HIT); pixt3 = pixErode(NULL, pixt2, selh); pixfg = pixErode(NULL, pixt3, sel_3); pixXor(pixfg, pixfg, pixt3); ptah = pixSubsampleBoundaryPixels(pixfg, hitskip); pixDestroy(&pixt3); pixDestroy(&pixfg); selDestroy(&selh); } if (missskip >= 0) { selm = selCreateBrick(2 * missdist + 1, 2 * missdist + 1, missdist, missdist, SEL_HIT); pixt3 = pixDilate(NULL, pixt2, selm); pixbg = pixDilate(NULL, pixt3, sel_3); pixXor(pixbg, pixbg, pixt3); ptam = pixSubsampleBoundaryPixels(pixbg, missskip); pixDestroy(&pixt3); pixDestroy(&pixbg); selDestroy(&selm); } selDestroy(&sel_3); pixDestroy(&pixt2); /* Generate the hit-miss sel from these point */ sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); if (hitskip >= 0) { npt = ptaGetCount(ptah); for (i = 0; i < npt; i++) { ptaGetIPt(ptah, i, &ix, &iy); selSetElement(sel, iy, ix, SEL_HIT); } } if (missskip >= 0) { npt = ptaGetCount(ptam); for (i = 0; i < npt; i++) { ptaGetIPt(ptam, i, &ix, &iy); selSetElement(sel, iy, ix, SEL_MISS); } } ptaDestroy(&ptah); ptaDestroy(&ptam); return sel; }
int main(int argc, char **argv) { PIX *pixs2, *pixs3, *pixb1, *pixb2, *pixb3; PIX *pixr2, *pixr3; PIX *pixc1, *pixc2, *pixc3, *pixcs1, *pixcs2, *pixcs3; PIX *pixd, *pixt1, *pixt2, *pixt3; PTA *ptas1, *ptas2, *ptas3, *ptad1, *ptad2, *ptad3; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixc1 = pixRead("test24.jpg"); pixc2 = pixRead("wyom.jpg"); pixc3 = pixRead("marge.jpg"); /* Test alpha blend scaling */ pixd = pixCreate(900, 400, 32); pixSetAll(pixd); pixs2 = pixScaleWithAlpha(pixc2, 0.5, 0.5, NULL, 0.3); pixs3 = pixScaleWithAlpha(pixc3, 0.4, 0.4, NULL, 0.7); pixb1 = pixBlendWithGrayMask(pixd, pixs3, NULL, 100, 100); pixb2 = pixBlendWithGrayMask(pixb1, pixs2, NULL, 300, 130); pixb3 = pixBlendWithGrayMask(pixb2, pixs3, NULL, 600, 160); regTestWritePixAndCheck(rp, pixb3, IFF_PNG); /* 0 */ pixDisplayWithTitle(pixb3, 900, 100, NULL, rp->display); pixDestroy(&pixd); pixDestroy(&pixs2); pixDestroy(&pixs3); pixDestroy(&pixb1); pixDestroy(&pixb2); pixDestroy(&pixb3); /* Test alpha blend rotation */ pixd = pixCreate(1200, 800, 32); pixSetAll(pixd); pixr3 = pixRotateWithAlpha(pixc3, -0.3, NULL, 1.0); pixr2 = pixRotateWithAlpha(pixc2, +0.3, NULL, 1.0); pixb3 = pixBlendWithGrayMask(pixd, pixr3, NULL, 100, 100); pixb2 = pixBlendWithGrayMask(pixb3, pixr2, NULL, 400, 100); regTestWritePixAndCheck(rp, pixb2, IFF_PNG); /* 1 */ pixDisplayWithTitle(pixb2, 500, 100, NULL, rp->display); pixDestroy(&pixd); pixDestroy(&pixr3); pixDestroy(&pixr2); pixDestroy(&pixb3); pixDestroy(&pixb2); pixcs1 = pixScale(pixc1, 0.35, 0.35); pixcs2 = pixScale(pixc2, 0.55, 0.55); pixcs3 = pixScale(pixc3, 0.65, 0.65); /* Test alpha blend affine */ pixd = pixCreate(800, 900, 32); pixSetAll(pixd); MakePtas(2, 3, &ptas1, &ptad1); MakePtas(4, 3, &ptas2, &ptad2); MakePtas(3, 3, &ptas3, &ptad3); pixt1 = pixAffinePtaWithAlpha(pixcs1, ptad1, ptas1, NULL, 1.0, 300); pixt2 = pixAffinePtaWithAlpha(pixcs2, ptad2, ptas2, NULL, 0.8, 400); pixt3 = pixAffinePtaWithAlpha(pixcs3, ptad3, ptas3, NULL, 0.7, 300); pixb1 = pixBlendWithGrayMask(pixd, pixt1, NULL, -250, 20); pixb2 = pixBlendWithGrayMask(pixb1, pixt2, NULL, -150, -250); pixb3 = pixBlendWithGrayMask(pixb2, pixt3, NULL, -100, 220); regTestWritePixAndCheck(rp, pixb3, IFF_PNG); /* 2 */ pixDisplayWithTitle(pixb3, 100, 100, NULL, rp->display); pixDestroy(&pixd); pixDestroy(&pixb1); pixDestroy(&pixb2); pixDestroy(&pixb3); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); ptaDestroy(&ptas1); ptaDestroy(&ptas2); ptaDestroy(&ptas3); ptaDestroy(&ptad1); ptaDestroy(&ptad2); ptaDestroy(&ptad3); /* Test alpha blend projective */ pixd = pixCreate(900, 900, 32); pixSetAll(pixd); MakePtas(2, 4, &ptas1, &ptad1); MakePtas(4, 4, &ptas2, &ptad2); MakePtas(3, 4, &ptas3, &ptad3); pixt1 = pixProjectivePtaWithAlpha(pixcs1, ptad1, ptas1, NULL, 1.0, 300); pixt2 = pixProjectivePtaWithAlpha(pixcs2, ptad2, ptas2, NULL, 0.8, 400); pixt3 = pixProjectivePtaWithAlpha(pixcs3, ptad3, ptas3, NULL, 0.7, 400); pixb1 = pixBlendWithGrayMask(pixd, pixt1, NULL, -150, 20); pixb2 = pixBlendWithGrayMask(pixb1, pixt2, NULL, -50, -250); pixb3 = pixBlendWithGrayMask(pixb2, pixt3, NULL, -100, 220); regTestWritePixAndCheck(rp, pixb3, IFF_PNG); /* 3 */ pixDisplayWithTitle(pixb3, 300, 100, NULL, rp->display); pixDestroy(&pixd); pixDestroy(&pixb1); pixDestroy(&pixb2); pixDestroy(&pixb3); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); ptaDestroy(&ptas1); ptaDestroy(&ptas2); ptaDestroy(&ptas3); ptaDestroy(&ptad1); ptaDestroy(&ptad2); ptaDestroy(&ptad3); /* Test alpha blend bilinear */ pixd = pixCreate(900, 900, 32); pixSetAll(pixd); MakePtas(2, 4, &ptas1, &ptad1); MakePtas(4, 4, &ptas2, &ptad2); MakePtas(3, 4, &ptas3, &ptad3); pixt1 = pixBilinearPtaWithAlpha(pixcs1, ptad1, ptas1, NULL, 1.0, 300); pixt2 = pixBilinearPtaWithAlpha(pixcs2, ptad2, ptas2, NULL, 0.8, 400); pixt3 = pixBilinearPtaWithAlpha(pixcs3, ptad3, ptas3, NULL, 0.7, 400); pixb1 = pixBlendWithGrayMask(pixd, pixt1, NULL, -150, 20); pixb2 = pixBlendWithGrayMask(pixb1, pixt2, NULL, -50, -250); pixb3 = pixBlendWithGrayMask(pixb2, pixt3, NULL, -100, 220); regTestWritePixAndCheck(rp, pixb3, IFF_PNG); /* 4 */ pixDisplayWithTitle(pixb3, 500, 100, NULL, rp->display); pixDestroy(&pixd); pixDestroy(&pixb1); pixDestroy(&pixb2); pixDestroy(&pixb3); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); ptaDestroy(&ptas1); ptaDestroy(&ptas2); ptaDestroy(&ptas3); ptaDestroy(&ptad1); ptaDestroy(&ptad2); ptaDestroy(&ptad3); pixDestroy(&pixc1); pixDestroy(&pixc2); pixDestroy(&pixc3); pixDestroy(&pixcs1); pixDestroy(&pixcs2); pixDestroy(&pixcs3); return regTestCleanup(rp); }
/*! * \brief pixGetRunsOnLine() * * \param[in] pixs 1 bpp * \param[in] x1, y1, x2, y2 * \return numa, or NULL on error * * <pre> * Notes: * (1) Action: this function uses the bresenham algorithm to compute * the pixels along the specified line. It returns a Numa of the * runlengths of the fg (black) and bg (white) runs, always * starting with a white run. * (2) If the first pixel on the line is black, the length of the * first returned run (which is white) is 0. * </pre> */ NUMA * pixGetRunsOnLine(PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2) { l_int32 w, h, x, y, npts; l_int32 i, runlen, preval; l_uint32 val; NUMA *numa; PTA *pta; PROCNAME("pixGetRunsOnLine"); if (!pixs) return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 1) return (NUMA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); w = pixGetWidth(pixs); h = pixGetHeight(pixs); if (x1 < 0 || x1 >= w) return (NUMA *)ERROR_PTR("x1 not valid", procName, NULL); if (x2 < 0 || x2 >= w) return (NUMA *)ERROR_PTR("x2 not valid", procName, NULL); if (y1 < 0 || y1 >= h) return (NUMA *)ERROR_PTR("y1 not valid", procName, NULL); if (y2 < 0 || y2 >= h) return (NUMA *)ERROR_PTR("y2 not valid", procName, NULL); if ((pta = generatePtaLine(x1, y1, x2, y2)) == NULL) return (NUMA *)ERROR_PTR("pta not made", procName, NULL); if ((npts = ptaGetCount(pta)) == 0) { ptaDestroy(&pta); return (NUMA *)ERROR_PTR("pta has no pts", procName, NULL); } if ((numa = numaCreate(0)) == NULL) { ptaDestroy(&pta); return (NUMA *)ERROR_PTR("numa not made", procName, NULL); } for (i = 0; i < npts; i++) { ptaGetIPt(pta, i, &x, &y); pixGetPixel(pixs, x, y, &val); if (i == 0) { if (val == 1) { /* black pixel; append white run of size 0 */ numaAddNumber(numa, 0); } preval = val; runlen = 1; continue; } if (val == preval) { /* extend current run */ preval = val; runlen++; } else { /* end previous run */ numaAddNumber(numa, runlen); preval = val; runlen = 1; } } numaAddNumber(numa, runlen); /* append last run */ ptaDestroy(&pta); return numa; }
/*! * selaAddTJunctions() * * Input: sela (<optional>) * hlsize (length of each line of hits from origin) * mdist (distance of misses from the origin) * norient (number of orientations; max of 8) * debugflag (1 for debug output) * Return: sela with additional sels, or null on error * * Notes: * (1) Adds hitmiss Sels for the T-junction of two lines. * If the lines are very thin, they must be nearly orthogonal * to register. * (2) The number of Sels generated is 4 * @norient. * (3) It is suggested that @hlsize be chosen at least 1 greater * than @mdist. Try values of (@hlsize, @mdist) such as * (6,5), (7,6), (8,7), (9,7), etc. */ SELA * selaAddTJunctions(SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag) { char name[L_BUF_SIZE]; l_int32 i, j, k, w, xc, yc; l_float64 pi, halfpi, radincr, jang, radang; l_float64 angle[3], dist[3]; PIX *pixc, *pixm, *pixt; PIXA *pixa; PTA *pta1, *pta2, *pta3; SEL *sel; PROCNAME("selaAddTJunctions"); if (hlsize <= 2) return (SELA *) ERROR_PTR("hlsizel not > 1", procName, NULL); if (norient < 1 || norient > 8) return (SELA *) ERROR_PTR("norient not in [1, ... 8]", procName, NULL); if (!sela) { if ((sela = selaCreate(0)) == NULL) return (SELA *) ERROR_PTR("sela not made", procName, NULL); } pi = 3.1415926535; halfpi = 3.1415926535 / 2.0; radincr = halfpi / (l_float32) norient; w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5)); if (w % 2 == 0) w++; xc = w / 2; yc = w / 2; pixa = pixaCreate(4 * norient); for (i = 0; i < norient; i++) { for (j = 0; j < 4; j++) { /* 4 orthogonal orientations */ jang = (l_float32) j * halfpi; /* Set the don't cares */ pixc = pixCreate(w, w, 32); pixSetAll(pixc); /* Add the green lines of hits */ pixm = pixCreate(w, w, 1); radang = (l_float32) i * radincr; pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang); pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang + halfpi); pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang + pi); ptaJoin(pta1, pta2, 0, -1); ptaJoin(pta1, pta3, 0, -1); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); /* Add red misses between the lines */ angle[0] = radang + jang - halfpi; angle[1] = radang + jang + 0.5 * halfpi; angle[2] = radang + jang + 1.5 * halfpi; dist[0] = 0.8 * mdist; dist[1] = dist[2] = mdist; for (k = 0; k < 3; k++) { pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])), yc + (l_int32)(dist[k] * sin(angle[k])), 0xff000000); } /* Add dark green for origin */ pixSetPixel(pixc, xc, yc, 0x00550000); /* Generate the sel */ sel = selCreateFromColorPix(pixc, NULL); sprintf(name, "sel_cross_%d", 4 * i + j); selaAddSel(sela, sel, name, 0); if (debugflag) { pixt = pixScaleBySampling(pixc, 10.0, 10.0); pixaAddPix(pixa, pixt, L_INSERT); } pixDestroy(&pixm); pixDestroy(&pixc); } } if (debugflag) { l_int32 w; pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2); pixWriteTempfile("/tmp", "tsel1.png", pixt, IFF_PNG, 0); pixDisplay(pixt, 0, 100); pixDestroy(&pixt); pixt = selaDisplayInPix(sela, 15, 2, 20, 4); pixWriteTempfile("/tmp", "tsel2.png", pixt, IFF_PNG, 0); pixDisplay(pixt, 500, 100); pixDestroy(&pixt); selaWriteStream(stderr, sela); } pixaDestroy(&pixa); return sela; }
/*! * boxIntersectByLine() * * Input: box * x, y (point that line goes through) * slope (of line) * (&x1, &y1) (<return> 1st point of intersection with box) * (&x2, &y2) (<return> 2nd point of intersection with box) * &n (<return> number of points of intersection) * Return: 0 if OK, 1 on error * * Notes: * (1) If the intersection is at only one point (a corner), the * coordinates are returned in (x1, y1). * (2) Represent a vertical line by one with a large but finite slope. */ l_int32 boxIntersectByLine(BOX *box, l_int32 x, l_int32 y, l_float32 slope, l_int32 *px1, l_int32 *py1, l_int32 *px2, l_int32 *py2, l_int32 *pn) { l_int32 bx, by, bw, bh, xp, yp, xt, yt, i, n; l_float32 invslope; PTA *pta; PROCNAME("boxIntersectByLine"); if (!px1 || !py1 || !px2 || !py2) return ERROR_INT("&x1, &y1, &x2, &y2 not all defined", procName, 1); *px1 = *py1 = *px2 = *py2 = 0; if (!pn) return ERROR_INT("&n not defined", procName, 1); *pn = 0; if (!box) return ERROR_INT("box not defined", procName, 1); boxGetGeometry(box, &bx, &by, &bw, &bh); if (slope == 0.0) { if (y >= by && y < by + bh) { *py1 = *py2 = y; *px1 = bx; *px2 = bx + bw - 1; } return 0; } if (slope > 1000000.0) { if (x >= bx && x < bx + bw) { *px1 = *px2 = x; *py1 = by; *py2 = by + bh - 1; } return 0; } /* Intersection with top and bottom lines of box */ pta = ptaCreate(2); invslope = 1.0 / slope; xp = (l_int32)(x + invslope * (y - by)); if (xp >= bx && xp < bx + bw) ptaAddPt(pta, xp, by); xp = (l_int32)(x + invslope * (y - by - bh + 1)); if (xp >= bx && xp < bx + bw) ptaAddPt(pta, xp, by + bh - 1); /* Intersection with left and right lines of box */ yp = (l_int32)(y + slope * (x - bx)); if (yp >= by && yp < by + bh) ptaAddPt(pta, bx, yp); yp = (l_int32)(y + slope * (x - bx - bw + 1)); if (yp >= by && yp < by + bh) ptaAddPt(pta, bx + bw - 1, yp); /* There is a maximum of 2 unique points; remove duplicates. */ n = ptaGetCount(pta); if (n > 0) { ptaGetIPt(pta, 0, px1, py1); /* accept the first one */ *pn = 1; } for (i = 1; i < n; i++) { ptaGetIPt(pta, i, &xt, &yt); if ((*px1 != xt) || (*py1 != yt)) { *px2 = xt; *py2 = yt; *pn = 2; break; } } ptaDestroy(&pta); return 0; }
/*! * pixGetLocalSkewAngles() * * Input: pixs * nslices (the number of horizontal overlapping slices; must * be larger than 1 and not exceed 20; use 0 for default) * redsweep (sweep reduction factor: 1, 2, 4 or 8; * use 0 for default value) * redsearch (search reduction factor: 1, 2, 4 or 8, and * not larger than redsweep; use 0 for default value) * sweeprange (half the full range, assumed about 0; in degrees; * use 0.0 for default value) * sweepdelta (angle increment of sweep; in degrees; * use 0.0 for default value) * minbsdelta (min binary search increment angle; in degrees; * use 0.0 for default value) * &a (<optional return> slope of skew as fctn of y) * &b (<optional return> intercept at y=0 of skew as fctn of y) * Return: naskew, or null on error * * Notes: * (1) The local skew is measured in a set of overlapping strips. * We then do a least square linear fit parameters to get * the slope and intercept parameters a and b in * skew-angle = a * y + b (degrees) * for the local skew as a function of raster line y. * This is then used to make naskew, which can be interpreted * as the computed skew angle (in degrees) at the left edge * of each raster line. * (2) naskew can then be used to find the baselines of text, because * each text line has a baseline that should intersect * the left edge of the image with the angle given by this * array, evaluated at the raster line of intersection. */ NUMA * pixGetLocalSkewAngles(PIX *pixs, l_int32 nslices, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_float32 *pa, l_float32 *pb) { l_int32 w, h, hs, i, ystart, yend, ovlap, npts; l_float32 angle, conf, ycenter, a, b; BOX *box; NUMA *naskew; PIX *pix; PTA *pta; PROCNAME("pixGetLocalSkewAngles"); if (!pixs) return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); if (nslices < 2 || nslices > 20) nslices = DEFAULT_SLICES; if (redsweep < 1 || redsweep > 8) redsweep = DEFAULT_SWEEP_REDUCTION; if (redsearch < 1 || redsearch > redsweep) redsearch = DEFAULT_BS_REDUCTION; if (sweeprange == 0.0) sweeprange = DEFAULT_SWEEP_RANGE; if (sweepdelta == 0.0) sweepdelta = DEFAULT_SWEEP_DELTA; if (minbsdelta == 0.0) minbsdelta = DEFAULT_MINBS_DELTA; h = pixGetHeight(pixs); w = pixGetWidth(pixs); hs = h / nslices; ovlap = (l_int32)(OVERLAP_FRACTION * hs); pta = ptaCreate(nslices); for (i = 0; i < nslices; i++) { ystart = L_MAX(0, hs * i - ovlap); yend = L_MIN(h - 1, hs * (i + 1) + ovlap); ycenter = (ystart + yend) / 2; box = boxCreate(0, ystart, w, yend - ystart + 1); pix = pixClipRectangle(pixs, box, NULL); pixFindSkewSweepAndSearch(pix, &angle, &conf, redsweep, redsearch, sweeprange, sweepdelta, minbsdelta); if (conf > MIN_ALLOWED_CONFIDENCE) ptaAddPt(pta, ycenter, angle); pixDestroy(&pix); boxDestroy(&box); } /* ptaWriteStream(stderr, pta, 0); */ /* Do linear least squares fit */ if ((npts = ptaGetCount(pta)) < 2) { ptaDestroy(&pta); return (NUMA *)ERROR_PTR("can't fit skew", procName, NULL); } ptaGetLinearLSF(pta, &a, &b, NULL); if (pa) *pa = a; if (pb) *pb = b; /* Make skew angle array as function of raster line */ naskew = numaCreate(h); for (i = 0; i < h; i++) { angle = a * i + b; numaAddNumber(naskew, angle); } #if DEBUG_PLOT { NUMA *nax, *nay; GPLOT *gplot; ptaGetArrays(pta, &nax, &nay); gplot = gplotCreate("/tmp/lept/baseline/kew", GPLOT_PNG, "skew as fctn of y", "y (in raster lines from top)", "angle (in degrees)"); gplotAddPlot(gplot, NULL, naskew, GPLOT_POINTS, "linear lsf"); gplotAddPlot(gplot, nax, nay, GPLOT_POINTS, "actual data pts"); gplotMakeOutput(gplot); gplotDestroy(&gplot); numaDestroy(&nax); numaDestroy(&nay); } #endif /* DEBUG_PLOT */ ptaDestroy(&pta); return naskew; }
int main(int argc, char **argv) { l_int32 i, j, n; l_int32 w, h, bw, bh, wpls, rval, gval, bval, same; l_uint32 pixel; l_uint32 *lines, *datas; l_float32 sum1, sum2, sum3, ave1, ave2, ave3, ave4, diff1, diff2; l_float32 var1, var2, var3; BOX *box1, *box2; NUMA *na, *na1, *na2, *na3, *na4; PIX *pix, *pixs, *pix1, *pix2, *pix3, *pix4, *pix5, *pixg, *pixd; PIXA *pixa; PTA *pta; static char mainName[] = "numa2_reg"; if (argc != 1) return ERROR_INT(" Syntax: numa2_reg", mainName, 1); /* -------------------------------------------------------------------* * Numa-windowed stats * * -------------------------------------------------------------------*/ #if DO_ALL na = numaRead("lyra-5.numa"); numaWindowedStats(na, 5, &na1, &na2, &na3, &na4); gplotSimple1(na, GPLOT_PNG, "/tmp/lyraroot6", "Original"); gplotSimple1(na1, GPLOT_PNG, "/tmp/lyraroot7", "Mean"); gplotSimple1(na2, GPLOT_PNG, "/tmp/lyraroot8", "Mean Square"); gplotSimple1(na3, GPLOT_PNG, "/tmp/lyraroot9", "Variance"); gplotSimple1(na4, GPLOT_PNG, "/tmp/lyraroot10", "RMS Difference"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixa = pixaCreate(5); pix1 = pixRead("/tmp/lyraroot6.png"); pix2 = pixRead("/tmp/lyraroot7.png"); pix3 = pixRead("/tmp/lyraroot8.png"); pix4 = pixRead("/tmp/lyraroot9.png"); pix5 = pixRead("/tmp/lyraroot10.png"); pixSaveTiled(pix1, pixa, 1.0, 1, 25, 32); pixSaveTiled(pix2, pixa, 1.0, 1, 25, 32); pixSaveTiled(pix3, pixa, 1.0, 0, 25, 32); pixSaveTiled(pix4, pixa, 1.0, 1, 25, 32); pixSaveTiled(pix5, pixa, 1.0, 0, 25, 32); pixd = pixaDisplay(pixa, 0, 0); pixDisplay(pixd, 100, 100); pixWrite("/tmp/numawindow.png", pixd, IFF_PNG); numaDestroy(&na); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); numaDestroy(&na4); pixaDestroy(&pixa); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pixd); #endif /* -------------------------------------------------------------------* * Extraction on a line * * -------------------------------------------------------------------*/ #if DO_ALL /* First, make a pretty image */ w = h = 200; pixs = pixCreate(w, h, 32); wpls = pixGetWpl(pixs); datas = pixGetData(pixs); for (i = 0; i < 200; i++) { lines = datas + i * wpls; for (j = 0; j < 200; j++) { rval = (l_int32)((255. * j) / w + (255. * i) / h); gval = (l_int32)((255. * 2 * j) / w + (255. * 2 * i) / h) % 255; bval = (l_int32)((255. * 4 * j) / w + (255. * 4 * i) / h) % 255; composeRGBPixel(rval, gval, bval, &pixel); lines[j] = pixel; } } pixg = pixConvertTo8(pixs, 0); /* and a grayscale version */ pixWrite("/tmp/junkpixg", pixg, IFF_PNG); pixDisplay(pixg, 450, 100); na1 = pixExtractOnLine(pixg, 20, 20, 180, 20, 1); na2 = pixExtractOnLine(pixg, 40, 30, 40, 170, 1); na3 = pixExtractOnLine(pixg, 20, 170, 180, 30, 1); na4 = pixExtractOnLine(pixg, 20, 190, 180, 10, 1); gplotSimple1(na1, GPLOT_PNG, "/tmp/extroot1", "Horizontal"); gplotSimple1(na2, GPLOT_PNG, "/tmp/extroot2", "Vertical"); gplotSimple1(na3, GPLOT_PNG, "/tmp/extroot3", "Slightly more horizontal than vertical"); gplotSimple1(na4, GPLOT_PNG, "/tmp/extroot4", "Slightly more vertical than horizontal"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixa = pixaCreate(4); pix1 = pixRead("/tmp/extroot1.png"); pix2 = pixRead("/tmp/extroot2.png"); pix3 = pixRead("/tmp/extroot3.png"); pix4 = pixRead("/tmp/extroot4.png"); pixSaveTiled(pix1, pixa, 1.0, 1, 25, 32); pixSaveTiled(pix2, pixa, 1.0, 0, 25, 32); pixSaveTiled(pix3, pixa, 1.0, 1, 25, 32); pixSaveTiled(pix4, pixa, 1.0, 0, 25, 32); pixd = pixaDisplay(pixa, 0, 0); pixDisplay(pixd, 100, 500); pixWrite("/tmp/numaextract.png", pixd, IFF_PNG); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); numaDestroy(&na4); pixaDestroy(&pixa); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pixs); pixDestroy(&pixg); pixDestroy(&pixd); #endif /* -------------------------------------------------------------------* * Row and column pixel sums * * -------------------------------------------------------------------*/ #if DO_ALL /* Sum by columns in two halves (left and right) */ pixs = pixRead("test8.jpg"); pixGetDimensions(pixs, &w, &h, NULL); box1 = boxCreate(0, 0, w / 2, h); box2 = boxCreate(w / 2, 0, w - 2 / 2, h); na1 = pixAverageByColumn(pixs, box1, L_BLACK_IS_MAX); na2 = pixAverageByColumn(pixs, box2, L_BLACK_IS_MAX); numaJoin(na1, na2, 0, -1); na3 = pixAverageByColumn(pixs, NULL, L_BLACK_IS_MAX); numaSimilar(na1, na3, 0.0, &same); if (same) fprintf(stderr, "Same for columns\n"); else fprintf(stderr, "Error for columns\n"); pta = generatePlotPtaFromNuma(na3, L_HORIZONTAL_LINE, 3, h / 2, 80, 1); pix = pixConvertTo32(pixs); pixRenderPtaArb(pix, pta, 255, 0, 0); boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); ptaDestroy(&pta); /* Sum by rows in two halves (top and bottom) */ box1 = boxCreate(0, 0, w, h / 2); box2 = boxCreate(0, h / 2, w, h - h / 2); na1 = pixAverageByRow(pixs, box1, L_WHITE_IS_MAX); na2 = pixAverageByRow(pixs, box2, L_WHITE_IS_MAX); numaJoin(na1, na2, 0, -1); na3 = pixAverageByRow(pixs, NULL, L_WHITE_IS_MAX); numaSimilar(na1, na3, 0.0, &same); if (same) fprintf(stderr, "Same for rows\n"); else fprintf(stderr, "Error for rows\n"); pta = generatePlotPtaFromNuma(na3, L_VERTICAL_LINE, 3, w / 2, 80, 1); pixRenderPtaArb(pix, pta, 0, 255, 0); pixDisplay(pix, 500, 200); boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); pixDestroy(&pix); ptaDestroy(&pta); /* Average left by rows; right by columns; compare totals */ box1 = boxCreate(0, 0, w / 2, h); box2 = boxCreate(w / 2, 0, w - 2 / 2, h); na1 = pixAverageByRow(pixs, box1, L_WHITE_IS_MAX); na2 = pixAverageByColumn(pixs, box2, L_WHITE_IS_MAX); numaGetSum(na1, &sum1); /* sum of averages of left box */ numaGetSum(na2, &sum2); /* sum of averages of right box */ ave1 = sum1 / h; ave2 = 2.0 * sum2 / w; ave3 = 0.5 * (ave1 + ave2); /* average over both halves */ fprintf(stderr, "ave1 = %8.4f\n", sum1 / h); fprintf(stderr, "ave2 = %8.4f\n", 2.0 * sum2 / w); pixAverageInRect(pixs, NULL, &ave4); /* entire image */ diff1 = ave4 - ave3; diff2 = w * h * ave4 - (0.5 * w * sum1 + h * sum2); if (diff1 < 0.001) fprintf(stderr, "Average diffs are correct\n"); else fprintf(stderr, "Average diffs are wrong: diff1 = %7.5f\n", diff1); if (diff2 < 20) /* float-to-integer roundoff */ fprintf(stderr, "Pixel sums are correct\n"); else fprintf(stderr, "Pixel sums are in error: diff = %7.0f\n", diff2); /* Variance left and right halves. Variance doesn't average * in a simple way, unlike pixel sums. */ pixVarianceInRect(pixs, box1, &var1); /* entire image */ pixVarianceInRect(pixs, box2, &var2); /* entire image */ pixVarianceInRect(pixs, NULL, &var3); /* entire image */ fprintf(stderr, "0.5 * (var1 + var2) = %7.3f, var3 = %7.3f\n", 0.5 * (var1 + var2), var3); boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); #endif /* -------------------------------------------------------------------* * Row and column variances * * -------------------------------------------------------------------*/ #if DO_ALL /* Display variance by rows and columns */ box1 = boxCreate(415, 0, 130, 425); boxGetGeometry(box1, NULL, NULL, &bw, &bh); na1 = pixVarianceByRow(pixs, box1); na2 = pixVarianceByColumn(pixs, box1); pix = pixConvertTo32(pixs); pta = generatePlotPtaFromNuma(na1, L_VERTICAL_LINE, 3, 415, 100, 1); pixRenderPtaArb(pix, pta, 255, 0, 0); ptaDestroy(&pta); pta = generatePlotPtaFromNuma(na2, L_HORIZONTAL_LINE, 3, bh / 2, 100, 1); pixRenderPtaArb(pix, pta, 0, 255, 0); pixDisplay(pix, 500, 900); boxDestroy(&box1); numaDestroy(&na1); numaDestroy(&na2); ptaDestroy(&pta); pixDestroy(&pix); pixDestroy(&pixs); /* Again on a different image */ pix1 = pixRead("boxedpage.jpg"); pix2 = pixConvertTo8(pix1, 0); pixGetDimensions(pix2, &w, &h, NULL); na1 = pixVarianceByRow(pix2, NULL); pta = generatePlotPtaFromNuma(na1, L_VERTICAL_LINE, 3, 0, 70, 1); pix3 = pixConvertTo32(pix1); pixRenderPtaArb(pix3, pta, 255, 0, 0); ptaDestroy(&pta); na2 = pixVarianceByColumn(pix2, NULL); pta = generatePlotPtaFromNuma(na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1); pixRenderPtaArb(pix3, pta, 0, 255, 0); pixDisplay(pix3, 1000, 0); numaDestroy(&na1); numaDestroy(&na2); ptaDestroy(&pta); pixDestroy(&pix3); /* Again, with an erosion */ pix3 = pixErodeGray(pix2, 3, 21); pixDisplay(pix3, 1400, 0); na1 = pixVarianceByRow(pix3, NULL); pta = generatePlotPtaFromNuma(na1, L_VERTICAL_LINE, 3, 30, 70, 1); pix4 = pixConvertTo32(pix1); pixRenderPtaArb(pix4, pta, 255, 0, 0); ptaDestroy(&pta); na2 = pixVarianceByColumn(pix3, NULL); pta = generatePlotPtaFromNuma(na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1); pixRenderPtaArb(pix4, pta, 0, 255, 0); pixDisplay(pix4, 1000, 550); numaDestroy(&na1); numaDestroy(&na2); ptaDestroy(&pta); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); #endif /* -------------------------------------------------------------------* * Windowed variance along a line * * -------------------------------------------------------------------*/ #if DO_ALL pix1 = pixRead("boxedpage.jpg"); pix2 = pixConvertTo8(pix1, 0); pixGetDimensions(pix2, &w, &h, NULL); /* Plot along horizontal line */ pixWindowedVarianceOnLine(pix2, L_HORIZONTAL_LINE, h / 2 - 30, 0, w, 5, &na1); pta = generatePlotPtaFromNuma(na1, L_HORIZONTAL_LINE, 3, h / 2 - 30, 80, 1); pixRenderPtaArb(pix1, pta, 255, 0, 0); numaDestroy(&na1); ptaDestroy(&pta); /* Plot along vertical line */ pixWindowedVarianceOnLine(pix2, L_VERTICAL_LINE, 0.78 * w, 0, h, 5, &na1); pta = generatePlotPtaFromNuma(na1, L_VERTICAL_LINE, 3, 0.78 * w, 60, 1); pixRenderPtaArb(pix1, pta, 0, 255, 0); pixDisplay(pix1, 100, 100); pixDestroy(&pix1); pixDestroy(&pix2); numaDestroy(&na1); ptaDestroy(&pta); #endif return 0; }
int main(int argc, char **argv) { char bufname[256]; l_int32 i, w, h; l_float32 *mat1, *mat2, *mat3, *mat1i, *mat2i, *mat3i, *matdinv; l_float32 matd[9], matdi[9]; BOXA *boxa, *boxa2; PIX *pix, *pixs, *pixb, *pixg, *pixc, *pixcs; PIX *pixd, *pix1, *pix2, *pix3; PIXA *pixa; PTA *ptas, *ptad; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pix = pixRead("feyn.tif"); pixs = pixScale(pix, 0.22, 0.22); pixDestroy(&pix); #if ALL /* Test invertability of sequential. */ fprintf(stderr, "Test invertability of sequential\n"); pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pix1 = pixAffineSequential(pixb, ptad, ptas, 0, 0); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 0,3,6 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffineSequential(pix1, ptas, ptad, 0, 0); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 1,4,7 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 2,5,8 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScaleToGray(pix1, 0.2); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 9 */ pixDisplayWithTitle(pix2, 0, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of sampling */ fprintf(stderr, "Test invertability of sampling\n"); pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pix1 = pixAffineSampledPta(pixb, ptad, ptas, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 10,13,16 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffineSampledPta(pix1, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 11,14,17 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 12,15,18 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScaleToGray(pix1, 0.2); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 19 */ pixDisplayWithTitle(pix2, 200, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixs); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of interpolation on grayscale */ fprintf(stderr, "Test invertability of grayscale interpolation\n"); pix = pixRead("feyn.tif"); pixg = pixScaleToGray3(pix); pixDestroy(&pix); pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixg, ADDED_BORDER_PIXELS / 3, 255); MakePtas(i, &ptas, &ptad); pix1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG); /* 20,23,26 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffinePta(pix1, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 21,24,27 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS / 3); pixXor(pixd, pixd, pixg); pixInvert(pixd, pixd); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 22,25,28 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScale(pix1, 0.2, 0.2); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 29 */ pixDisplayWithTitle(pix2, 400, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixg); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of interpolation on color */ fprintf(stderr, "Test invertability of color interpolation\n"); pixa = pixaCreate(0); pixc = pixRead("test24.jpg"); pixcs = pixScale(pixc, 0.3, 0.3); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixcs, ADDED_BORDER_PIXELS / 4, 0xffffff00); MakePtas(i, &ptas, &ptad); pix1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG); /* 30,33,36 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffinePta(pix1, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 31,34,37 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS / 4); pixXor(pixd, pixd, pixcs); pixInvert(pixd, pixd); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 32,35,38 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScale(pix1, 0.25, 0.25); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 39 */ pixDisplayWithTitle(pix2, 600, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixc); pixaDestroy(&pixa); #endif #if ALL /* Comparison between sequential and sampling */ fprintf(stderr, "Compare sequential with sampling\n"); pix = pixRead("feyn.tif"); pixs = pixScale(pix, 0.22, 0.22); pixDestroy(&pix); MakePtas(3, &ptas, &ptad); pixa = pixaCreate(0); /* Use sequential transforms */ pix1 = pixAffineSequential(pixs, ptas, ptad, ADDED_BORDER_PIXELS, ADDED_BORDER_PIXELS); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 40 */ pixaAddPix(pixa, pix1, L_INSERT); /* Use sampled transform */ pix2 = pixAffineSampledPta(pixs, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 41 */ pixaAddPix(pixa, pix2, L_COPY); /* Compare the results */ pixXor(pix2, pix2, pix1); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 42 */ pixaAddPix(pixa, pix2, L_INSERT); pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScale(pix1, 0.5, 0.5); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 43 */ pixDisplayWithTitle(pix2, 800, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixs); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif #if ALL /* Test with large distortion */ fprintf(stderr, "Test with large distortion\n"); MakePtas(4, &ptas, &ptad); pixa = pixaCreate(0); pix = pixRead("feyn.tif"); pixg = pixScaleToGray6(pix); pixDestroy(&pix); pix1 = pixAffineSequential(pixg, ptas, ptad, 0, 0); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 44 */ pixaAddPix(pixa, pix1, L_COPY); pix2 = pixAffineSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 45 */ pixaAddPix(pixa, pix2, L_COPY); pix3 = pixAffinePta(pixg, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 46 */ pixaAddPix(pixa, pix3, L_INSERT); pixXor(pix1, pix1, pix2); pixInvert(pix1, pix1); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 47 */ pixaAddPix(pixa, pix1, L_INSERT); pixXor(pix2, pix2, pix3); pixInvert(pix2, pix2); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 48 */ pixaAddPix(pixa, pix2, L_INSERT); pix1 = pixaDisplayTiledInColumns(pixa, 5, 1.0, 20, 3); pix2 = pixScale(pix1, 0.8, 0.8); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 49 */ pixDisplayWithTitle(pix2, 1000, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixg); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif #if ALL /* Set up pix and boxa */ fprintf(stderr, "Test affine transforms and inverses on pix and boxa\n"); pixa = pixaCreate(0); pix = pixRead("lucasta.1.300.tif"); pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE); pix1 = pixCloseBrick(NULL, pix, 14, 5); pixOpenBrick(pix1, pix1, 1, 2); boxa = pixConnComp(pix1, NULL, 8); pixs = pixConvertTo32(pix); pixGetDimensions(pixs, &w, &h, NULL); pixc = pixCopy(NULL, pixs); RenderHashedBoxa(pixc, boxa, 113); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 50 */ pixaAddPix(pixa, pixc, L_INSERT); pixDestroy(&pix); pixDestroy(&pix1); /* Set up an affine transform in matd, and apply it to boxa */ mat1 = createMatrix2dTranslate(SHIFTX, SHIFTY); mat2 = createMatrix2dScale(SCALEX, SCALEY); mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION); l_productMat3(mat3, mat2, mat1, matd, 3); boxa2 = boxaAffineTransform(boxa, matd); /* Set up the inverse transform --> matdi */ mat1i = createMatrix2dTranslate(-SHIFTX, -SHIFTY); mat2i = createMatrix2dScale(1.0/ SCALEX, 1.0 / SCALEY); mat3i = createMatrix2dRotate(w / 2, h / 2, -ROTATION); l_productMat3(mat1i, mat2i, mat3i, matdi, 3); /* Invert the original affine transform --> matdinv */ affineInvertXform(matd, &matdinv); if (rp->display) { fprintf(stderr, " Affine transform, applied to boxa\n"); for (i = 0; i < 9; i++) { if (i && (i % 3 == 0)) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matd[i]); } fprintf(stderr, "\n Inverse transform, by composing inverse parts"); for (i = 0; i < 9; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdi[i]); } fprintf(stderr, "\n Inverse transform, by inverting affine xform"); for (i = 0; i < 6; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdinv[i]); } fprintf(stderr, "\n"); } /* Apply the inverted affine transform --> pixs */ pixd = pixAffine(pixs, matdinv, L_BRING_IN_WHITE); RenderHashedBoxa(pixd, boxa2, 513); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 51 */ pixaAddPix(pixa, pixd, L_INSERT); pix1 = pixaDisplayTiledInColumns(pixa, 2, 1.0, 30, 2); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 52 */ pixDisplayWithTitle(pix1, 1200, 100, NULL, rp->display); pixDestroy(&pix1); pixaDestroy(&pixa); pixDestroy(&pixs); boxaDestroy(&boxa); boxaDestroy(&boxa2); lept_free(mat1); lept_free(mat2); lept_free(mat3); lept_free(mat1i); lept_free(mat2i); lept_free(mat3i); lept_free(matdinv); #endif return regTestCleanup(rp); }
int main(int argc, char **argv) { char bufname[256]; l_int32 i, w, h; l_float32 *mat1, *mat2, *mat3, *mat1i, *mat2i, *mat3i, *matdinv; l_float32 matd[9], matdi[9]; BOXA *boxa, *boxa2; PIX *pix, *pixs, *pixb, *pixg, *pixc, *pixcs; PIX *pixd, *pixt1, *pixt2, *pixt3; PIXA *pixa; PTA *ptas, *ptad; static char mainName[] = "affine_reg"; if (argc != 1) return ERROR_INT(" Syntax: affine_reg", mainName, 1); if ((pixs = pixRead("feyn.tif")) == NULL) return ERROR_INT("pixs not made", mainName, 1); #if 1 /* Test invertability of sequential. */ pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pixt1 = pixAffineSequential(pixb, ptad, ptas, 0, 0); pixSaveTiled(pixt1, pixa, 0.3333, 1, 20, 8); pixt2 = pixAffineSequential(pixt1, ptas, ptad, 0, 0); pixSaveTiled(pixt2, pixa, 0.3333, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); pixSaveTiled(pixd, pixa, 0.3333, 0, 20, 0); sprintf(bufname, "/tmp/seq%d.png", i); pixWrite(bufname, pixd, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/affine1.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 100); pixDestroy(&pixt1); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of sampling */ pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pixt1 = pixAffineSampledPta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 0.3333, 1, 20, 8); pixt2 = pixAffineSampledPta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 0.3333, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); pixSaveTiled(pixd, pixa, 0.3333, 0, 20, 0); if (i == 0) pixWrite("/tmp/samp.png", pixt1, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/affine2.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 300); pixDestroy(&pixt1); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of interpolation on grayscale */ pixa = pixaCreate(0); pixg = pixScaleToGray3(pixs); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixg, ADDED_BORDER_PIXELS / 3, 255); MakePtas(i, &ptas, &ptad); pixt1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 1.0, 1, 20, 8); pixt2 = pixAffinePta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 1.0, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS / 3); pixXor(pixd, pixd, pixg); pixSaveTiled(pixd, pixa, 1.0, 0, 20, 0); if (i == 0) pixWrite("/tmp/interp.png", pixt1, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/affine3.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 500); pixDestroy(&pixt1); pixaDestroy(&pixa); pixDestroy(&pixg); #endif #if ALL /* Test invertability of interpolation on color */ pixa = pixaCreate(0); pixc = pixRead("test24.jpg"); pixcs = pixScale(pixc, 0.3, 0.3); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixcs, ADDED_BORDER_PIXELS / 4, 0xffffff00); MakePtas(i, &ptas, &ptad); pixt1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 1.0, 1, 20, 32); pixt2 = pixAffinePta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 1.0, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS / 4); pixXor(pixd, pixd, pixcs); pixSaveTiled(pixd, pixa, 1.0, 0, 20, 0); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/affine4.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 500); pixDestroy(&pixt1); pixaDestroy(&pixa); pixDestroy(&pixc); pixDestroy(&pixcs); #endif #if ALL /* Comparison between sequential and sampling */ MakePtas(3, &ptas, &ptad); pixa = pixaCreate(0); /* Use sequential transforms */ pixt1 = pixAffineSequential(pixs, ptas, ptad, ADDED_BORDER_PIXELS, ADDED_BORDER_PIXELS); pixSaveTiled(pixt1, pixa, 0.5, 0, 20, 8); /* Use sampled transform */ pixt2 = pixAffineSampledPta(pixs, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 0.5, 0, 20, 8); /* Compare the results */ pixXor(pixt2, pixt2, pixt1); pixSaveTiled(pixt2, pixa, 0.5, 0, 20, 8); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/affine5.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 700); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif #if ALL /* Get timings and test with large distortion */ MakePtas(4, &ptas, &ptad); pixa = pixaCreate(0); pixg = pixScaleToGray3(pixs); startTimer(); pixt1 = pixAffineSequential(pixg, ptas, ptad, 0, 0); fprintf(stderr, " Time for pixAffineSequentialPta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt1, pixa, 1.0, 1, 20, 8); startTimer(); pixt2 = pixAffineSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixAffineSampledPta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt2, pixa, 1.0, 0, 20, 8); startTimer(); pixt3 = pixAffinePta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixAffinePta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt3, pixa, 1.0, 0, 20, 8); pixXor(pixt1, pixt1, pixt2); pixSaveTiled(pixt1, pixa, 1.0, 1, 20, 8); pixXor(pixt2, pixt2, pixt3); pixSaveTiled(pixt2, pixa, 1.0, 0, 20, 8); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/affine6.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 900); pixDestroy(&pixd); pixDestroy(&pixg); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif pixDestroy(&pixs); #if ALL /* Set up pix and boxa */ pixa = pixaCreate(0); pix = pixRead("lucasta.1.300.tif"); pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE); pixt1 = pixCloseBrick(NULL, pix, 14, 5); pixOpenBrick(pixt1, pixt1, 1, 2); boxa = pixConnComp(pixt1, NULL, 8); pixs = pixConvertTo32(pix); pixGetDimensions(pixs, &w, &h, NULL); pixc = pixCopy(NULL, pixs); RenderHashedBoxa(pixc, boxa, 113); pixSaveTiled(pixc, pixa, 0.5, 1, 30, 32); pixDestroy(&pix); pixDestroy(&pixc); pixDestroy(&pixt1); /* Set up an affine transform in matd, and apply it to boxa */ mat1 = createMatrix2dTranslate(SHIFTX, SHIFTY); mat2 = createMatrix2dScale(SCALEX, SCALEY); mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION); l_productMat3(mat3, mat2, mat1, matd, 3); boxa2 = boxaAffineTransform(boxa, matd); /* Set up the inverse transform in matdi */ mat1i = createMatrix2dTranslate(-SHIFTX, -SHIFTY); mat2i = createMatrix2dScale(1.0 / SCALEX, 1.0 / SCALEY); mat3i = createMatrix2dRotate(w / 2, h / 2, -ROTATION); l_productMat3(mat1i, mat2i, mat3i, matdi, 3); /* Invert the original affine transform in matdinv */ affineInvertXform(matd, &matdinv); fprintf(stderr, "Affine transform, applied to boxa\n"); for (i = 0; i < 9; i++) { if (i && (i % 3 == 0)) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matd[i]); } fprintf(stderr, "\nInverse transform, made by composing inverse parts"); for (i = 0; i < 9; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdi[i]); } fprintf(stderr, "\nInverse transform, made by inverting the affine xform"); for (i = 0; i < 6; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdinv[i]); } fprintf(stderr, "\n"); /* Apply the inverted affine transform pixs */ pixd = pixAffine(pixs, matdinv, L_BRING_IN_WHITE); RenderHashedBoxa(pixd, boxa2, 513); pixSaveTiled(pixd, pixa, 0.5, 0, 30, 32); pixDestroy(&pixd); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/affine7.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 900); pixDestroy(&pixd); pixDestroy(&pixs); pixaDestroy(&pixa); boxaDestroy(&boxa); boxaDestroy(&boxa2); lept_free(mat1); lept_free(mat2); lept_free(mat3); lept_free(mat1i); lept_free(mat2i); lept_free(mat3i); #endif return 0; }
int main(int argc, char **argv) { l_float32 sum, sumx, sumy, diff; L_DEWARP *dew; L_DEWARPA *dewa; FPIX *fpixs, *fpixs2, *fpixs3, *fpixs4, *fpixg, *fpixd; FPIX *fpix1, *fpix2, *fpixt1, *fpixt2; DPIX *dpix, *dpix2; L_KERNEL *kel, *kelx, *kely; PIX *pixs, *pixs2, *pixs3, *pixt, *pixd, *pixg, *pixb, *pixn; PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; PIXA *pixa; PTA *ptas, *ptad; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixa = pixaCreate(0); /* Gaussian kernel */ kel = makeGaussianKernel(5, 5, 3.0, 4.0); kernelGetSum(kel, &sum); if (rp->display) fprintf(stderr, "Sum for 2d gaussian kernel = %f\n", sum); pixt = kernelDisplayInPix(kel, 41, 2); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 0 */ pixSaveTiled(pixt, pixa, 1.0, 1, 20, 8); pixDestroy(&pixt); /* Separable gaussian kernel */ makeGaussianKernelSep(5, 5, 3.0, 4.0, &kelx, &kely); kernelGetSum(kelx, &sumx); if (rp->display) fprintf(stderr, "Sum for x gaussian kernel = %f\n", sumx); kernelGetSum(kely, &sumy); if (rp->display) fprintf(stderr, "Sum for y gaussian kernel = %f\n", sumy); if (rp->display) fprintf(stderr, "Sum for x * y gaussian kernel = %f\n", sumx * sumy); pixt = kernelDisplayInPix(kelx, 41, 2); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 1 */ pixSaveTiled(pixt, pixa, 1.0, 0, 20, 8); pixDestroy(&pixt); pixt = kernelDisplayInPix(kely, 41, 2); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 2 */ pixSaveTiled(pixt, pixa, 1.0, 0, 20, 8); pixDestroy(&pixt); /* Use pixRasterop() to generate source image */ pixs = pixRead("test8.jpg"); pixs2 = pixRead("karen8.jpg"); pixRasterop(pixs, 150, 125, 150, 100, PIX_SRC, pixs2, 75, 100); regTestWritePixAndCheck(rp, pixs, IFF_JFIF_JPEG); /* 3 */ /* Convolution directly with pix */ pixt1 = pixConvolve(pixs, kel, 8, 1); regTestWritePixAndCheck(rp, pixt1, IFF_JFIF_JPEG); /* 4 */ pixSaveTiled(pixt1, pixa, 1.0, 1, 20, 8); pixt2 = pixConvolveSep(pixs, kelx, kely, 8, 1); regTestWritePixAndCheck(rp, pixt2, IFF_JFIF_JPEG); /* 5 */ pixSaveTiled(pixt2, pixa, 1.0, 0, 20, 8); /* Convolution indirectly with fpix, using fpixRasterop() * to generate the source image. */ fpixs = pixConvertToFPix(pixs, 3); fpixs2 = pixConvertToFPix(pixs2, 3); fpixRasterop(fpixs, 150, 125, 150, 100, fpixs2, 75, 100); fpixt1 = fpixConvolve(fpixs, kel, 1); pixt3 = fpixConvertToPix(fpixt1, 8, L_CLIP_TO_ZERO, 1); regTestWritePixAndCheck(rp, pixt3, IFF_JFIF_JPEG); /* 6 */ pixSaveTiled(pixt3, pixa, 1.0, 1, 20, 8); fpixt2 = fpixConvolveSep(fpixs, kelx, kely, 1); pixt4 = fpixConvertToPix(fpixt2, 8, L_CLIP_TO_ZERO, 1); regTestWritePixAndCheck(rp, pixt4, IFF_JFIF_JPEG); /* 7 */ pixSaveTiled(pixt4, pixa, 1.0, 0, 20, 8); pixDestroy(&pixs2); fpixDestroy(&fpixs2); fpixDestroy(&fpixt1); fpixDestroy(&fpixt2); /* Comparison of results */ pixCompareGray(pixt1, pixt2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of pixConvolve and pixConvolveSep: %f\n", diff); pixCompareGray(pixt3, pixt4, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of fpixConvolve and fpixConvolveSep: %f\n", diff); pixCompareGray(pixt1, pixt3, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of pixConvolve and fpixConvolve: %f\n", diff); pixCompareGray(pixt2, pixt4, L_COMPARE_ABS_DIFF, GPLOT_PNG, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of pixConvolveSep and fpixConvolveSep: %f\n", diff); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); /* Test arithmetic operations; add in a fraction rotated by 180 */ pixs3 = pixRotate180(NULL, pixs); regTestWritePixAndCheck(rp, pixs3, IFF_JFIF_JPEG); /* 8 */ pixSaveTiled(pixs3, pixa, 1.0, 1, 20, 8); fpixs3 = pixConvertToFPix(pixs3, 3); fpixd = fpixLinearCombination(NULL, fpixs, fpixs3, 20.0, 5.0); fpixAddMultConstant(fpixd, 0.0, 23.174); /* multiply up in magnitude */ pixd = fpixDisplayMaxDynamicRange(fpixd); /* bring back to 8 bpp */ regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 9 */ pixSaveTiled(pixd, pixa, 1.0, 0, 20, 8); pixDestroy(&pixs3); fpixDestroy(&fpixs3); fpixDestroy(&fpixd); pixDestroy(&pixd); pixDestroy(&pixs); fpixDestroy(&fpixs); /* Save the comparison graph; gnuplot should have made it by now! */ #ifndef _WIN32 sleep(2); #else Sleep(2000); #endif /* _WIN32 */ pixt5 = pixRead("/tmp/lept/compare_gray0.png"); regTestWritePixAndCheck(rp, pixt5, IFF_PNG); /* 10 */ pixSaveTiled(pixt5, pixa, 1.0, 1, 20, 8); pixDestroy(&pixt5); /* Display results */ pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 11 */ pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); /* Test some more convolutions, with sampled output. First on pix */ pixa = pixaCreate(0); pixs = pixRead("1555-7.jpg"); pixg = pixConvertTo8(pixs, 0); l_setConvolveSampling(5, 5); pixt1 = pixConvolve(pixg, kel, 8, 1); regTestWritePixAndCheck(rp, pixt1, IFF_JFIF_JPEG); /* 12 */ pixSaveTiled(pixt1, pixa, 1.0, 1, 20, 32); pixt2 = pixConvolveSep(pixg, kelx, kely, 8, 1); regTestWritePixAndCheck(rp, pixt2, IFF_JFIF_JPEG); /* 13 */ pixSaveTiled(pixt2, pixa, 1.0, 0, 20, 32); pixt3 = pixConvolveRGB(pixs, kel); regTestWritePixAndCheck(rp, pixt3, IFF_JFIF_JPEG); /* 14 */ pixSaveTiled(pixt3, pixa, 1.0, 0, 20, 32); pixt4 = pixConvolveRGBSep(pixs, kelx, kely); regTestWritePixAndCheck(rp, pixt4, IFF_JFIF_JPEG); /* 15 */ pixSaveTiled(pixt4, pixa, 1.0, 0, 20, 32); /* Then on fpix */ fpixg = pixConvertToFPix(pixg, 1); fpixt1 = fpixConvolve(fpixg, kel, 1); pixt5 = fpixConvertToPix(fpixt1, 8, L_CLIP_TO_ZERO, 0); regTestWritePixAndCheck(rp, pixt5, IFF_JFIF_JPEG); /* 16 */ pixSaveTiled(pixt5, pixa, 1.0, 1, 20, 32); fpixt2 = fpixConvolveSep(fpixg, kelx, kely, 1); pixt6 = fpixConvertToPix(fpixt2, 8, L_CLIP_TO_ZERO, 0); regTestWritePixAndCheck(rp, pixt6, IFF_JFIF_JPEG); /* 17 */ pixSaveTiled(pixt2, pixa, 1.0, 0, 20, 32); regTestCompareSimilarPix(rp, pixt1, pixt5, 2, 0.00, 0); /* 18 */ regTestCompareSimilarPix(rp, pixt2, pixt6, 2, 0.00, 0); /* 19 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); fpixDestroy(&fpixg); fpixDestroy(&fpixt1); fpixDestroy(&fpixt2); pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 20 */ pixDisplayWithTitle(pixd, 600, 100, NULL, rp->display); pixDestroy(&pixs); pixDestroy(&pixg); pixDestroy(&pixd); pixaDestroy(&pixa); /* Test extension (continued and slope). * First, build a smooth vertical disparity array; * then extend and show the contours. */ pixs = pixRead("cat-35.jpg"); pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); dewa = dewarpaCreate(1, 30, 1, 15, 0); if ((dew = dewarpCreate(pixb, 35)) == NULL) { rp->success = FALSE; L_ERROR("dew not made; tests 21-28 skipped (failed)\n", "fpix1_reg"); return regTestCleanup(rp); } dewarpaInsertDewarp(dewa, dew); dewarpBuildPageModel(dew, NULL); dewarpPopulateFullRes(dew, NULL, 0, 0); fpixs = dew->fullvdispar; fpixs2 = fpixAddContinuedBorder(fpixs, 200, 200, 100, 300); fpixs3 = fpixAddSlopeBorder(fpixs, 200, 200, 100, 300); dpix = fpixConvertToDPix(fpixs3); fpixs4 = dpixConvertToFPix(dpix); pixt1 = fpixRenderContours(fpixs, 2.0, 0.2); pixt2 = fpixRenderContours(fpixs2, 2.0, 0.2); pixt3 = fpixRenderContours(fpixs3, 2.0, 0.2); pixt4 = fpixRenderContours(fpixs4, 2.0, 0.2); pixt5 = pixRead("karen8.jpg"); dpix2 = pixConvertToDPix(pixt5, 1); pixt6 = dpixConvertToPix(dpix2, 8, L_CLIP_TO_ZERO, 0); regTestWritePixAndCheck(rp, pixt1, IFF_PNG); /* 21 */ pixDisplayWithTitle(pixt1, 0, 100, NULL, rp->display); regTestWritePixAndCheck(rp, pixt2, IFF_PNG); /* 22 */ pixDisplayWithTitle(pixt2, 470, 100, NULL, rp->display); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 23 */ pixDisplayWithTitle(pixt3, 1035, 100, NULL, rp->display); regTestComparePix(rp, pixt3, pixt4); /* 24 */ regTestComparePix(rp, pixt5, pixt6); /* 25 */ pixDestroy(&pixs); pixDestroy(&pixn); pixDestroy(&pixg); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); fpixDestroy(&fpixs2); fpixDestroy(&fpixs3); fpixDestroy(&fpixs4); dpixDestroy(&dpix); dpixDestroy(&dpix2); /* Test affine and projective transforms on fpix */ fpixWrite("/tmp/regout/fpix1.fp", dew->fullvdispar); fpix1 = fpixRead("/tmp/regout/fpix1.fp"); pixt1 = fpixAutoRenderContours(fpix1, 40); regTestWritePixAndCheck(rp, pixt1, IFF_PNG); /* 26 */ pixDisplayWithTitle(pixt1, 0, 500, NULL, rp->display); pixDestroy(&pixt1); MakePtasAffine(1, &ptas, &ptad); fpix2 = fpixAffinePta(fpix1, ptad, ptas, 200, 0.0); pixt2 = fpixAutoRenderContours(fpix2, 40); regTestWritePixAndCheck(rp, pixt2, IFF_PNG); /* 27 */ pixDisplayWithTitle(pixt2, 400, 500, NULL, rp->display); fpixDestroy(&fpix2); pixDestroy(&pixt2); ptaDestroy(&ptas); ptaDestroy(&ptad); MakePtas(1, &ptas, &ptad); fpix2 = fpixProjectivePta(fpix1, ptad, ptas, 200, 0.0); pixt3 = fpixAutoRenderContours(fpix2, 40); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 28 */ pixDisplayWithTitle(pixt3, 400, 500, NULL, rp->display); fpixDestroy(&fpix2); pixDestroy(&pixt3); ptaDestroy(&ptas); ptaDestroy(&ptad); fpixDestroy(&fpix1); dewarpaDestroy(&dewa); kernelDestroy(&kel); kernelDestroy(&kelx); kernelDestroy(&kely); return regTestCleanup(rp); }
main(int argc, char **argv) { char *filein, *fileout; l_int32 x, y, n, i; PIX *pixs; PTA *pta; PTAA *ptaa, *ptaa2, *ptaa3; static char mainName[] = "cornertest"; if (argc != 3) exit(ERROR_INT(" Syntax: cornertest filein fileout", mainName, 1)); filein = argv[1]; fileout = argv[2]; if ((pixs = pixRead(filein)) == NULL) exit(ERROR_INT("pixs not made", mainName, 1)); /* Clean noise in LR corner of witten.tif */ pixSetPixel(pixs, 2252, 3051, 0); pixSetPixel(pixs, 2252, 3050, 0); pixSetPixel(pixs, 2251, 3050, 0); pta = pixFindCornerPixels(pixs); ptaWriteStream(stderr, pta, 1); /* Test pta and ptaa I/O */ #if 1 ptaa = ptaaCreate(3); ptaaAddPta(ptaa, pta, L_COPY); ptaaAddPta(ptaa, pta, L_COPY); ptaaAddPta(ptaa, pta, L_COPY); ptaaWriteStream(stderr, ptaa, 1); ptaaWrite("/tmp/junkptaa", ptaa, 1); ptaa2 = ptaaRead("/tmp/junkptaa"); ptaaWrite("/tmp/junkptaa2", ptaa2, 1); ptaaWrite("/tmp/junkptaa3", ptaa, 0); ptaa3 = ptaaRead("/tmp/junkptaa3"); ptaaWrite("/tmp/junkptaa4", ptaa3, 0); ptaaDestroy(&ptaa); ptaaDestroy(&ptaa2); ptaaDestroy(&ptaa3); #endif /* mark corner pixels */ n = ptaGetCount(pta); for (i = 0; i < n; i++) { ptaGetIPt(pta, i, &x, &y); pixRenderLine(pixs, x - LINE_SIZE, y, x + LINE_SIZE, y, 3, L_FLIP_PIXELS); pixRenderLine(pixs, x, y - LINE_SIZE, x, y + LINE_SIZE, 3, L_FLIP_PIXELS); } pixWrite(fileout, pixs, IFF_PNG); pixDestroy(&pixs); ptaDestroy(&pta); ptaDestroy(&pta); 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); }
/*! * \brief selaAddCrossJunctions() * * \param[in] sela [optional] * \param[in] hlsize length of each line of hits from origin * \param[in] mdist distance of misses from the origin * \param[in] norient number of orientations; max of 8 * \param[in] debugflag 1 for debug output * \return sela with additional sels, or NULL on error * * <pre> * Notes: * (1) Adds hitmiss Sels for the intersection of two lines. * If the lines are very thin, they must be nearly orthogonal * to register. * (2) The number of Sels generated is equal to %norient. * (3) If %norient == 2, this generates 2 Sels of crosses, each with * two perpendicular lines of hits. One Sel has horizontal and * vertical hits; the other has hits along lines at +-45 degrees. * Likewise, if %norient == 3, this generates 3 Sels of crosses * oriented at 30 degrees with each other. * (4) It is suggested that %hlsize be chosen at least 1 greater * than %mdist. Try values of (%hlsize, %mdist) such as * (6,5), (7,6), (8,7), (9,7), etc. * </pre> */ SELA * selaAddCrossJunctions(SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag) { char name[L_BUF_SIZE]; l_int32 i, j, w, xc, yc; l_float64 pi, halfpi, radincr, radang; l_float64 angle; PIX *pixc, *pixm, *pixt; PIXA *pixa; PTA *pta1, *pta2, *pta3, *pta4; SEL *sel; PROCNAME("selaAddCrossJunctions"); if (hlsize <= 0) return (SELA *)ERROR_PTR("hlsize not > 0", procName, NULL); if (norient < 1 || norient > 8) return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL); if (!sela) { if ((sela = selaCreate(0)) == NULL) return (SELA *)ERROR_PTR("sela not made", procName, NULL); } pi = 3.1415926535; halfpi = 3.1415926535 / 2.0; radincr = halfpi / (l_float64)norient; w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5)); if (w % 2 == 0) w++; xc = w / 2; yc = w / 2; pixa = pixaCreate(norient); for (i = 0; i < norient; i++) { /* Set the don't cares */ pixc = pixCreate(w, w, 32); pixSetAll(pixc); /* Add the green lines of hits */ pixm = pixCreate(w, w, 1); radang = (l_float32)i * radincr; pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang); pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi); pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi); pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi); ptaJoin(pta1, pta2, 0, -1); ptaJoin(pta1, pta3, 0, -1); ptaJoin(pta1, pta4, 0, -1); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); ptaDestroy(&pta4); /* Add red misses between the lines */ for (j = 0; j < 4; j++) { angle = radang + (j - 0.5) * halfpi; pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)), yc + (l_int32)(mdist * sin(angle)), 0xff000000); } /* Add dark green for origin */ pixSetPixel(pixc, xc, yc, 0x00550000); /* Generate the sel */ sel = selCreateFromColorPix(pixc, NULL); sprintf(name, "sel_cross_%d", i); selaAddSel(sela, sel, name, 0); if (debugflag) { pixt = pixScaleBySampling(pixc, 10.0, 10.0); pixaAddPix(pixa, pixt, L_INSERT); } pixDestroy(&pixm); pixDestroy(&pixc); } if (debugflag) { l_int32 w; lept_mkdir("lept/sel"); pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2); pixWrite("/tmp/lept/sel/xsel1.png", pixt, IFF_PNG); pixDisplay(pixt, 0, 100); pixDestroy(&pixt); pixt = selaDisplayInPix(sela, 15, 2, 20, 1); pixWrite("/tmp/lept/sel/xsel2.png", pixt, IFF_PNG); pixDisplay(pixt, 500, 100); pixDestroy(&pixt); selaWriteStream(stderr, sela); } pixaDestroy(&pixa); return sela; }