/*! * \brief pixGrayMorphSequence() * * \param[in] pixs * \param[in] sequence string specifying sequence * \param[in] dispsep controls debug display of each result in the sequence: * 0: no output * > 0: gives horizontal separation in pixels between * successive displays * < 0: pdf output; abs(dispsep) is used for naming * \param[in] dispy if dispsep > 0, this gives the y-value of the * UL corner for display; otherwise it is ignored * \return pixd, or NULL on error * * <pre> * Notes: * (1) This works on 8 bpp grayscale images. * (2) This runs a pipeline of operations; no branching is allowed. * (3) This only uses brick SELs. * (4) A new image is always produced; the input image is not changed. * (5) This contains an interpreter, allowing sequences to be * generated and run. * (6) The format of the sequence string is defined below. * (7) In addition to morphological operations, the composite * morph/subtract tophat can be performed. * (8) Sel sizes (width, height) must each be odd numbers. * (9) Intermediate results can optionally be displayed * (10) The sequence string is formatted as follows: * ~ An arbitrary number of operations, each separated * by a '+' character. White space is ignored. * ~ Each operation begins with a case-independent character * specifying the operation: * d or D (dilation) * e or E (erosion) * o or O (opening) * c or C (closing) * t or T (tophat) * ~ The args to the morphological operations are bricks of hits, * and are formatted as a.b, where a and b are horizontal and * vertical dimensions, rsp. (each must be an odd number) * ~ The args to the tophat are w or W (for white tophat) * or b or B (for black tophat), followed by a.b as for * the dilation, erosion, opening and closing. * Example valid sequences are: * "c5.3 + o7.5" * "c9.9 + tw9.9" * </pre> */ PIX * pixGrayMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy) { char *rawop, *op, *fname; char buf[256]; l_int32 nops, i, valid, w, h, x, pdfout; PIX *pixt1, *pixt2; PIXA *pixa; SARRAY *sa; PROCNAME("pixGrayMorphSequence"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!sequence) return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); /* Split sequence into individual operations */ sa = sarrayCreate(0); sarraySplitString(sa, sequence, "+"); nops = sarrayGetCount(sa); pdfout = (dispsep < 0) ? 1 : 0; /* Verify that the operation sequence is valid */ valid = TRUE; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, L_NOCOPY); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': case 'e': case 'E': case 'o': case 'O': case 'c': case 'C': if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { fprintf(stderr, "*** op: %s invalid\n", op); valid = FALSE; break; } if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { fprintf(stderr, "*** op: %s; w = %d, h = %d; must both be odd\n", op, w, h); valid = FALSE; break; } /* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ break; case 't': case 'T': if (op[1] != 'w' && op[1] != 'W' && op[1] != 'b' && op[1] != 'B') { fprintf(stderr, "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]); valid = FALSE; break; } sscanf(&op[2], "%d.%d", &w, &h); if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { fprintf(stderr, "*** op: %s; w = %d, h = %d; must both be odd\n", op, w, h); valid = FALSE; break; } /* fprintf(stderr, "op = %s", op); */ break; default: fprintf(stderr, "*** nonexistent op = %s\n", op); valid = FALSE; } LEPT_FREE(op); } if (!valid) { sarrayDestroy(&sa); return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); } /* Parse and operate */ pixa = NULL; if (pdfout) { pixa = pixaCreate(0); pixaAddPix(pixa, pixs, L_CLONE); snprintf(buf, sizeof(buf), "/tmp/seq_output_%d.pdf", L_ABS(dispsep)); fname = genPathname(buf, NULL); } pixt1 = pixCopy(NULL, pixs); pixt2 = NULL; x = 0; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, L_NOCOPY); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixDilateGray(pixt1, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; case 'e': case 'E': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixErodeGray(pixt1, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; case 'o': case 'O': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixOpenGray(pixt1, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; case 'c': case 'C': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixCloseGray(pixt1, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; case 't': case 'T': sscanf(&op[2], "%d.%d", &w, &h); if (op[1] == 'w' || op[1] == 'W') pixt2 = pixTophat(pixt1, w, h, L_TOPHAT_WHITE); else /* 'b' or 'B' */ pixt2 = pixTophat(pixt1, w, h, L_TOPHAT_BLACK); pixSwapAndDestroy(&pixt1, &pixt2); break; default: /* All invalid ops are caught in the first pass */ break; } LEPT_FREE(op); /* Debug output */ if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } if (pdfout) pixaAddPix(pixa, pixt1, L_COPY); } if (pdfout) { pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); LEPT_FREE(fname); pixaDestroy(&pixa); } sarrayDestroy(&sa); return pixt1; }
int main(int argc, char **argv) { l_int32 i, j; l_int32 w, h, bw, bh, wpls, rval, gval, bval, same; l_uint32 pixel; l_uint32 *lines, *datas; l_float32 sum1, sum2, 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; static char mainName[] = "numa2_reg"; if (argc != 1) return ERROR_INT(" Syntax: numa2_reg", mainName, 1); lept_mkdir("lept"); /* -------------------------------------------------------------------* * Numa-windowed stats * * -------------------------------------------------------------------*/ #if DO_ALL na = numaRead("lyra.5.na"); numaWindowedStats(na, 5, &na1, &na2, &na3, &na4); gplotSimple1(na, GPLOT_PNG, "/tmp/lept/numa_lyra6", "Original"); gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/numa_lyra7", "Mean"); gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/numa_lyra8", "Mean Square"); gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/numa_lyra9", "Variance"); gplotSimple1(na4, GPLOT_PNG, "/tmp/lept/numa_lyra10", "RMS Difference"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixa = pixaCreate(5); pix1 = pixRead("/tmp/lept/numa_lyra6.png"); pix2 = pixRead("/tmp/lept/numa_lyra7.png"); pix3 = pixRead("/tmp/lept/numa_lyra8.png"); pix4 = pixRead("/tmp/lept/numa_lyra9.png"); pix5 = pixRead("/tmp/lept/numa_lyra10.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/lept/numa_window.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/lept/numa_pixg.png", 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/lept/numa_ext1", "Horizontal"); gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/numa_ext2", "Vertical"); gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/numa_ext3", "Slightly more horizontal than vertical"); gplotSimple1(na4, GPLOT_PNG, "/tmp/lept/numa_ext4", "Slightly more vertical than horizontal"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixa = pixaCreate(4); pix1 = pixRead("/tmp/lept/numa_ext1.png"); pix2 = pixRead("/tmp/lept/numa_ext2.png"); pix3 = pixRead("/tmp/lept/numa_ext3.png"); pix4 = pixRead("/tmp/lept/numa_ext4.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/lept/numa_extract.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"); pix = pixConvertTo32(pixs); pixRenderPlotFromNumaGen(&pix, na3, L_HORIZONTAL_LINE, 3, h / 2, 80, 1, 0xff000000); pixRenderPlotFromNuma(&pix, na3, L_PLOT_AT_BOT, 3, 80, 0xff000000); boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); /* 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"); pixRenderPlotFromNumaGen(&pix, na3, L_VERTICAL_LINE, 3, w / 2, 80, 1, 0x00ff0000); pixRenderPlotFromNuma(&pix, na3, L_PLOT_AT_RIGHT, 3, 80, 0x00ff0000); pixDisplay(pix, 500, 200); boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); pixDestroy(&pix); /* 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); pix1 = pixCopy(NULL, pix); pixRenderPlotFromNumaGen(&pix, na1, L_VERTICAL_LINE, 3, 415, 100, 1, 0xff000000); pixRenderPlotFromNumaGen(&pix, na2, L_HORIZONTAL_LINE, 3, bh / 2, 100, 1, 0x00ff0000); pixRenderPlotFromNuma(&pix1, na1, L_PLOT_AT_LEFT, 3, 60, 0x00ff0000); pixRenderPlotFromNuma(&pix1, na1, L_PLOT_AT_MID_VERT, 3, 60, 0x0000ff00); pixRenderPlotFromNuma(&pix1, na1, L_PLOT_AT_RIGHT, 3, 60, 0xff000000); pixRenderPlotFromNuma(&pix1, na2, L_PLOT_AT_TOP, 3, 60, 0x0000ff00); pixRenderPlotFromNuma(&pix1, na2, L_PLOT_AT_MID_HORIZ, 3, 60, 0xff000000); pixRenderPlotFromNuma(&pix1, na2, L_PLOT_AT_BOT, 3, 60, 0x00ff0000); pixDisplay(pix, 500, 900); pixDisplay(pix1, 500, 1000); boxDestroy(&box1); numaDestroy(&na1); numaDestroy(&na2); pixDestroy(&pix); pixDestroy(&pix1); pixDestroy(&pixs); /* Again on a different image */ pix1 = pixRead("boxedpage.jpg"); pix2 = pixConvertTo8(pix1, 0); pixGetDimensions(pix2, &w, &h, NULL); na1 = pixVarianceByRow(pix2, NULL); pix3 = pixConvertTo32(pix1); pixRenderPlotFromNumaGen(&pix3, na1, L_VERTICAL_LINE, 3, 0, 70, 1, 0xff000000); na2 = pixVarianceByColumn(pix2, NULL); pixRenderPlotFromNumaGen(&pix3, na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1, 0x00ff0000); pixDisplay(pix3, 1000, 0); numaDestroy(&na1); numaDestroy(&na2); pixDestroy(&pix3); /* Again, with an erosion */ pix3 = pixErodeGray(pix2, 3, 21); pixDisplay(pix3, 1400, 0); na1 = pixVarianceByRow(pix3, NULL); pix4 = pixConvertTo32(pix1); pixRenderPlotFromNumaGen(&pix4, na1, L_VERTICAL_LINE, 3, 30, 70, 1, 0xff000000); na2 = pixVarianceByColumn(pix3, NULL); pixRenderPlotFromNumaGen(&pix4, na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1, 0x00ff0000); pixDisplay(pix4, 1000, 550); numaDestroy(&na1); numaDestroy(&na2); 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); pix3 = pixCopy(NULL, pix1); /* Plot along horizontal line */ pixWindowedVarianceOnLine(pix2, L_HORIZONTAL_LINE, h / 2 - 30, 0, w, 5, &na1); pixRenderPlotFromNumaGen(&pix1, na1, L_HORIZONTAL_LINE, 3, h / 2 - 30, 80, 1, 0xff000000); pixRenderPlotFromNuma(&pix3, na1, L_PLOT_AT_TOP, 3, 60, 0x00ff0000); pixRenderPlotFromNuma(&pix3, na1, L_PLOT_AT_BOT, 3, 60, 0x0000ff00); /* Plot along vertical line */ pixWindowedVarianceOnLine(pix2, L_VERTICAL_LINE, 0.78 * w, 0, h, 5, &na2); pixRenderPlotFromNumaGen(&pix1, na2, L_VERTICAL_LINE, 3, 0.78 * w, 60, 1, 0x00ff0000); pixRenderPlotFromNuma(&pix3, na2, L_PLOT_AT_LEFT, 3, 60, 0xff000000); pixRenderPlotFromNuma(&pix3, na2, L_PLOT_AT_RIGHT, 3, 60, 0x00ff0000); pixDisplay(pix1, 1000, 1000); pixDisplay(pix3, 1500, 1000); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); numaDestroy(&na1); numaDestroy(&na2); #endif return 0; }
/*! * pixRankFilterGray() * * Input: pixs (8 bpp; no colormap) * wf, hf (width and height of filter; each is >= 1) * rank (in [0.0 ... 1.0]) * Return: pixd (of rank values), or null on error * * Notes: * (1) This defines, for each pixel in pixs, a neighborhood of * pixels given by a rectangle "centered" on the pixel. * This set of wf*hf pixels has a distribution of values, * and if they are sorted in increasing order, we choose * the pixel such that rank*(wf*hf-1) pixels have a lower * or equal value and (1-rank)*(wf*hf-1) pixels have an equal * or greater value. * (2) By this definition, the rank = 0.0 pixel has the lowest * value, and the rank = 1.0 pixel has the highest value. * (3) We add mirrored boundary pixels to avoid boundary effects, * and put the filter center at (0, 0). * (4) This dispatches to grayscale erosion or dilation if the * filter dimensions are odd and the rank is 0.0 or 1.0, rsp. * (5) Returns a copy if both wf and hf are 1. * (6) Uses row-major or column-major incremental updates to the * histograms depending on whether hf > wf or hv <= wf, rsp. */ PIX * pixRankFilterGray(PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank) { l_int32 w, h, d, i, j, k, m, n, rankloc, wplt, wpld, val, sum; l_int32 *histo, *histo16; l_uint32 *datat, *linet, *datad, *lined; PIX *pixt, *pixd; PROCNAME("pixRankFilterGray"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetColormap(pixs) != NULL) return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 8) return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); if (wf < 1 || hf < 1) return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); if (rank < 0.0 || rank > 1.0) return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); if (wf == 1 && hf == 1) /* no-op */ return pixCopy(NULL, pixs); /* For rank = 0.0, this is a grayscale erosion, and for rank = 1.0, * a dilation. Grayscale morphology operations are implemented * for filters of odd dimension, so we dispatch to grayscale * morphology if both wf and hf are odd. Otherwise, we * slightly adjust the rank (to get the correct behavior) and * use the slower rank filter here. */ if (wf % 2 && hf % 2) { if (rank == 0.0) return pixErodeGray(pixs, wf, hf); else if (rank == 1.0) return pixDilateGray(pixs, wf, hf); } if (rank == 0.0) rank = 0.0001; if (rank == 1.0) rank = 0.9999; /* Add wf/2 to each side, and hf/2 to top and bottom of the * image, mirroring for accuracy and to avoid special-casing * the boundary. */ if ((pixt = pixAddMirroredBorder(pixs, wf / 2, wf / 2, hf / 2, hf / 2)) == NULL) return (PIX *)ERROR_PTR("pixt not made", procName, NULL); /* Set up the two histogram arrays. */ histo = (l_int32 *)CALLOC(256, sizeof(l_int32)); histo16 = (l_int32 *)CALLOC(16, sizeof(l_int32)); rankloc = (l_int32)(rank * wf * hf); /* Place the filter center at (0, 0). This is just a * convenient location, because it allows us to perform * the rank filter over x:(0 ... w - 1) and y:(0 ... h - 1). */ pixd = pixCreateTemplate(pixs); datat = pixGetData(pixt); wplt = pixGetWpl(pixt); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); /* If hf > wf, it's more efficient to use row-major scanning. * Otherwise, traverse the image in use column-major order. */ if (hf > wf) { for (j = 0; j < w; j++) { /* row-major */ /* Start each column with clean histogram arrays. */ for (n = 0; n < 256; n++) histo[n] = 0; for (n = 0; n < 16; n++) histo16[n] = 0; for (i = 0; i < h; i++) { /* fast scan on columns */ /* Update the histos for the new location */ lined = datad + i * wpld; if (i == 0) { /* do full histo */ for (k = 0; k < hf; k++) { linet = datat + (i + k) * wplt; for (m = 0; m < wf; m++) { val = GET_DATA_BYTE(linet, j + m); histo[val]++; histo16[val >> 4]++; } } } else { /* incremental update */ linet = datat + (i - 1) * wplt; for (m = 0; m < wf; m++) { /* remove top line */ val = GET_DATA_BYTE(linet, j + m); histo[val]--; histo16[val >> 4]--; } linet = datat + (i + hf - 1) * wplt; for (m = 0; m < wf; m++) { /* add bottom line */ val = GET_DATA_BYTE(linet, j + m); histo[val]++; histo16[val >> 4]++; } } /* Find the rank value */ sum = 0; for (n = 0; n < 16; n++) { /* search over coarse histo */ sum += histo16[n]; if (sum > rankloc) { sum -= histo16[n]; break; } } k = 16 * n; /* starting value in fine histo */ for (m = 0; m < 16; m++) { sum += histo[k]; if (sum > rankloc) { SET_DATA_BYTE(lined, j, k); break; } k++; } } } } else { /* wf >= hf */
int main(int argc, char **argv) { char *filein; l_float32 angle, conf, deg2rad; PIX *pixs, *pix1, *pix2, *pix3, *pix4, *pix5; PIX *pix6, *pix7, *pix8, *pix9; static char mainName[] = "lineremoval"; if (argc != 2) return ERROR_INT(" Syntax: lineremoval filein", mainName, 1); filein = argv[1]; deg2rad = 3.14159 / 180.; if ((pixs = pixRead(filein)) == NULL) return ERROR_INT("pix not made", mainName, 1); /* threshold to binary, extracting much of the lines */ pix1 = pixThresholdToBinary(pixs, 170); pixWrite("/tmp/dave-proc1.png", pix1, IFF_PNG); pixDisplayWrite(pix1, 1); /* find the skew angle and deskew using an interpolated * rotator for anti-aliasing (to avoid jaggies) */ pixFindSkew(pix1, &angle, &conf); pix2 = pixRotateAMGray(pixs, deg2rad * angle, 255); pixWrite("/tmp/dave-proc2.png", pix2, IFF_PNG); pixDisplayWrite(pix2, 1); /* extract the lines to be removed */ pix3 = pixCloseGray(pix2, 51, 1); pixWrite("/tmp/dave-proc3.png", pix3, IFF_PNG); pixDisplayWrite(pix3, 1); /* solidify the lines to be removed */ pix4 = pixErodeGray(pix3, 1, 5); pixWrite("/tmp/dave-proc4.png", pix4, IFF_PNG); pixDisplayWrite(pix4, 1); /* clean the background of those lines */ pix5 = pixThresholdToValue(NULL, pix4, 210, 255); pixWrite("/tmp/dave-proc5.png", pix5, IFF_PNG); pixDisplayWrite(pix5, 1); pix6 = pixThresholdToValue(NULL, pix5, 200, 0); pixWrite("/tmp/dave-proc6.png", pix6, IFF_PNG); pixDisplayWrite(pix6, 1); /* get paint-through mask for changed pixels */ pix7 = pixThresholdToBinary(pix6, 210); pixWrite("/tmp/dave-proc7.png", pix7, IFF_PNG); pixDisplayWrite(pix7, 1); /* add the inverted, cleaned lines to orig. Because * the background was cleaned, the inversion is 0, * so when you add, it doesn't lighten those pixels. * It only lightens (to white) the pixels in the lines! */ pixInvert(pix6, pix6); pix8 = pixAddGray(NULL, pix2, pix6); pixWrite("/tmp/dave-proc8.png", pix8, IFF_PNG); pixDisplayWrite(pix8, 1); pix9 = pixOpenGray(pix8, 1, 9); pixWrite("/tmp/dave-proc9.png", pix9, IFF_PNG); pixDisplayWrite(pix9, 1); pixCombineMasked(pix8, pix9, pix7); pixWrite("/tmp/dave-result.png", pix8, IFF_PNG); pixDisplayWrite(pix8, 1); pixDisplayMultiple("/tmp/display/file*"); return 0; }
main(int argc, char **argv) { PIX *pixs, *pixt1, *pixt2, *pixd; PIXA *pixa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixs = pixRead("test8.jpg"); /* Dilation */ pixa = pixaCreate(0); pixSaveTiled(pixs, pixa, 1, 1, 20, 8); pixt1 = pixDilateGray3(pixs, 3, 1); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixDilateGray(pixs, 3, 1); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 0 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixDilateGray3(pixs, 1, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixDilateGray(pixs, 1, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 1 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixDilateGray3(pixs, 3, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixDilateGray(pixs, 3, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 2 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixd = pixaDisplay(pixa, 0, 0); pixDisplayWithTitle(pixd, 0, 100, "Dilation", rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); /* Erosion */ pixa = pixaCreate(0); pixSaveTiled(pixs, pixa, 1, 1, 20, 8); pixt1 = pixErodeGray3(pixs, 3, 1); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixErodeGray(pixs, 3, 1); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 3 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixErodeGray3(pixs, 1, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixErodeGray(pixs, 1, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 4 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixErodeGray3(pixs, 3, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixErodeGray(pixs, 3, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 5 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixd = pixaDisplay(pixa, 0, 0); pixDisplayWithTitle(pixd, 250, 100, "Erosion", rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); /* Opening */ pixa = pixaCreate(0); pixSaveTiled(pixs, pixa, 1, 1, 20, 8); pixt1 = pixOpenGray3(pixs, 3, 1); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixOpenGray(pixs, 3, 1); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 6 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixOpenGray3(pixs, 1, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixOpenGray(pixs, 1, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 7 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixOpenGray3(pixs, 3, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixOpenGray(pixs, 3, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 8 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixd = pixaDisplay(pixa, 0, 0); pixDisplayWithTitle(pixd, 500, 100, "Opening", rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); /* Closing */ pixa = pixaCreate(0); pixSaveTiled(pixs, pixa, 1, 1, 20, 8); pixt1 = pixCloseGray3(pixs, 3, 1); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixCloseGray(pixs, 3, 1); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 9 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixCloseGray3(pixs, 1, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixCloseGray(pixs, 1, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 10 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixCloseGray3(pixs, 3, 3); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixCloseGray(pixs, 3, 3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); regTestComparePix(rp, pixt1, pixt2); /* 11 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixd = pixaDisplay(pixa, 0, 0); pixDisplayWithTitle(pixd, 750, 100, "Closing", rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); pixDestroy(&pixs); return regTestCleanup(rp); }
// Degrade the pix as if by a print/copy/scan cycle with exposure > 0 // corresponding to darkening on the copier and <0 lighter and 0 not copied. // Exposures in [-2,2] are most useful, with -3 and 3 being extreme. // If rotation is NULL, rotation is skipped. If *rotation is non-zero, the pix // is rotated by *rotation else it is randomly rotated and *rotation is // modified. // // HOW IT WORKS: // Most of the process is really dictated by the fact that the minimum // available convolution is 3X3, which is too big really to simulate a // good quality print/scan process. (2X2 would be better.) // 1 pixel wide inputs are heavily smeared by the 3X3 convolution, making the // images generally biased to being too light, so most of the work is to make // them darker. 3 levels of thickening/darkening are achieved with 2 dilations, // (using a greyscale erosion) one heavy (by being before convolution) and one // light (after convolution). // With no dilation, after covolution, the images are so light that a heavy // constant offset is required to make the 0 image look reasonable. A simple // constant offset multiple of exposure to undo this value is enough to achieve // all the required lightening. This gives the advantage that exposure level 1 // with a single dilation gives a good impression of the broken-yet-too-dark // problem that is often seen in scans. // A small random rotation gives some varying greyscale values on the edges, // and some random salt and pepper noise on top helps to realistically jaggy-up // the edges. // Finally a greyscale ramp provides a continuum of effects between exposure // levels. Pix* DegradeImage(Pix* input, int exposure, TRand* randomizer, float* rotation) { Pix* pix = pixConvertTo8(input, false); pixDestroy(&input); input = pix; int width = pixGetWidth(input); int height = pixGetHeight(input); if (exposure >= 2) { // An erosion simulates the spreading darkening of a dark copy. // This is backwards to binary morphology, // see http://www.leptonica.com/grayscale-morphology.html pix = input; input = pixErodeGray(pix, 3, 3); pixDestroy(&pix); } // A convolution is essential to any mode as no scanner produces an // image as sharp as the electronic image. pix = pixBlockconv(input, 1, 1); pixDestroy(&input); // A small random rotation helps to make the edges jaggy in a realistic way. if (rotation != NULL) { float radians_clockwise = 0.0f; if (*rotation) { radians_clockwise = *rotation; } else if (randomizer != NULL) { radians_clockwise = randomizer->SignedRand(kRotationRange); } input = pixRotate(pix, radians_clockwise, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, 0, 0); // Rotate the boxes to match. *rotation = radians_clockwise; pixDestroy(&pix); } else { input = pix; } if (exposure >= 3 || exposure == 1) { // Erosion after the convolution is not as heavy as before, so it is // good for level 1 and in addition as a level 3. // This is backwards to binary morphology, // see http://www.leptonica.com/grayscale-morphology.html pix = input; input = pixErodeGray(pix, 3, 3); pixDestroy(&pix); } // The convolution really needed to be 2x2 to be realistic enough, but // we only have 3x3, so we have to bias the image darker or lose thin // strokes. int erosion_offset = 0; // For light and 0 exposure, there is no dilation, so compensate for the // convolution with a big darkening bias which is undone for lighter // exposures. if (exposure <= 0) erosion_offset = -3 * kExposureFactor; // Add in a general offset of the greyscales for the exposure level so // a threshold of 128 gives a reasonable binary result. erosion_offset -= exposure * kExposureFactor; // Add a gradual fade over the page and a small amount of salt and pepper // noise to simulate noise in the sensor/paper fibres and varying // illumination. l_uint32* data = pixGetData(input); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { int pixel = GET_DATA_BYTE(data, x); if (randomizer != NULL) pixel += randomizer->IntRand() % (kSaltnPepper*2 + 1) - kSaltnPepper; if (height + width > kMinRampSize) pixel -= (2*x + y) * 32 / (height + width); pixel += erosion_offset; if (pixel < 0) pixel = 0; if (pixel > 255) pixel = 255; SET_DATA_BYTE(data, x, pixel); } data += input->wpl; } return input; }
int main(int argc, char **argv) { l_int32 i, j; l_int32 w, h, bw, bh, wpls, rval, gval, bval, same; l_uint32 pixel; l_uint32 *lines, *datas; l_float32 sum1, sum2, 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; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; lept_mkdir("lept/numa2"); /* -------------------------------------------------------------------* * Numa-windowed stats * * -------------------------------------------------------------------*/ na = numaRead("lyra.5.na"); numaWindowedStats(na, 5, &na1, &na2, &na3, &na4); gplotSimple1(na, GPLOT_PNG, "/tmp/lept/numa2/lyra1", "Original"); gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/numa2/lyra2", "Mean"); gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/numa2/lyra3", "Mean Square"); gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/numa2/lyra4", "Variance"); gplotSimple1(na4, GPLOT_PNG, "/tmp/lept/numa2/lyra5", "RMS Difference"); pix1 = pixRead("/tmp/lept/numa2/lyra1.png"); pix2 = pixRead("/tmp/lept/numa2/lyra2.png"); pix3 = pixRead("/tmp/lept/numa2/lyra3.png"); pix4 = pixRead("/tmp/lept/numa2/lyra4.png"); pix5 = pixRead("/tmp/lept/numa2/lyra5.png"); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 0 */ regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 1 */ regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 2 */ regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 3 */ regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 4 */ pixa = pixaCreate(5); pixaAddPix(pixa, pix1, L_INSERT); pixaAddPix(pixa, pix2, L_INSERT); pixaAddPix(pixa, pix3, L_INSERT); pixaAddPix(pixa, pix4, L_INSERT); pixaAddPix(pixa, pix5, L_INSERT); if (rp->display) { pixd = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 20, 2); pixDisplayWithTitle(pixd, 0, 0, NULL, 1); pixDestroy(&pixd); } pixaDestroy(&pixa); numaDestroy(&na); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); numaDestroy(&na4); /* -------------------------------------------------------------------* * Extraction on a line * * -------------------------------------------------------------------*/ /* 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 */ regTestWritePixAndCheck(rp, pixg, IFF_PNG); /* 5 */ pixDisplayWithTitle(pixg, 0, 300, NULL, rp->display); 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/lept/numa2/ext1", "Horizontal"); gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/numa2/ext2", "Vertical"); gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/numa2/ext3", "Slightly more horizontal than vertical"); gplotSimple1(na4, GPLOT_PNG, "/tmp/lept/numa2/ext4", "Slightly more vertical than horizontal"); pix1 = pixRead("/tmp/lept/numa2/ext1.png"); pix2 = pixRead("/tmp/lept/numa2/ext2.png"); pix3 = pixRead("/tmp/lept/numa2/ext3.png"); pix4 = pixRead("/tmp/lept/numa2/ext4.png"); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 6 */ regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 7 */ regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 8 */ regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 9 */ pixa = pixaCreate(4); pixaAddPix(pixa, pix1, L_INSERT); pixaAddPix(pixa, pix2, L_INSERT); pixaAddPix(pixa, pix3, L_INSERT); pixaAddPix(pixa, pix4, L_INSERT); if (rp->display) { pixd = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 20, 2); pixDisplayWithTitle(pixd, 300, 0, NULL, 1); pixDestroy(&pixd); } pixaDestroy(&pixa); pixDestroy(&pixg); pixDestroy(&pixs); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); numaDestroy(&na4); /* -------------------------------------------------------------------* * Row and column pixel sums * * -------------------------------------------------------------------*/ /* 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); /* for columns */ regTestCompareValues(rp, 1, same, 0); /* 10 */ pix1 = pixConvertTo32(pixs); pixRenderPlotFromNumaGen(&pix1, na3, L_HORIZONTAL_LINE, 3, h / 2, 80, 1, 0xff000000); pixRenderPlotFromNuma(&pix1, na3, L_PLOT_AT_BOT, 3, 80, 0xff000000); boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); /* 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); /* for rows */ regTestCompareValues(rp, 1, same, 0); /* 11 */ pixRenderPlotFromNumaGen(&pix1, na3, L_VERTICAL_LINE, 3, w / 2, 80, 1, 0x00ff0000); pixRenderPlotFromNuma(&pix1, na3, L_PLOT_AT_RIGHT, 3, 80, 0x00ff0000); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 12 */ pixDisplayWithTitle(pix1, 0, 600, NULL, rp->display); pixDestroy(&pix1); boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); numaDestroy(&na3); /* 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 */ regTestCompareValues(rp, 189.59, ave1, 0.01); /* 13 */ regTestCompareValues(rp, 207.89, ave2, 0.01); /* 14 */ if (rp->display) { 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); regTestCompareValues(rp, 0.0, diff1, 0.001); /* 15 */ regTestCompareValues(rp, 10.0, diff2, 10.0); /* 16 */ /* 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 */ regTestCompareValues(rp, 82.06, 0.5 * (var1 + var2), 0.01); /* 17 */ regTestCompareValues(rp, 82.66, var3, 0.01); /* 18 */ boxDestroy(&box1); boxDestroy(&box2); numaDestroy(&na1); numaDestroy(&na2); /* -------------------------------------------------------------------* * Row and column variances * * -------------------------------------------------------------------*/ /* 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); pix1 = pixConvertTo32(pixs); pix2 = pixCopy(NULL, pix1); pixRenderPlotFromNumaGen(&pix1, na1, L_VERTICAL_LINE, 3, 415, 100, 1, 0xff000000); pixRenderPlotFromNumaGen(&pix1, na2, L_HORIZONTAL_LINE, 3, bh / 2, 100, 1, 0x00ff0000); pixRenderPlotFromNuma(&pix2, na1, L_PLOT_AT_LEFT, 3, 60, 0x00ff0000); pixRenderPlotFromNuma(&pix2, na1, L_PLOT_AT_MID_VERT, 3, 60, 0x0000ff00); pixRenderPlotFromNuma(&pix2, na1, L_PLOT_AT_RIGHT, 3, 60, 0xff000000); pixRenderPlotFromNuma(&pix2, na2, L_PLOT_AT_TOP, 3, 60, 0x0000ff00); pixRenderPlotFromNuma(&pix2, na2, L_PLOT_AT_MID_HORIZ, 3, 60, 0xff000000); pixRenderPlotFromNuma(&pix2, na2, L_PLOT_AT_BOT, 3, 60, 0x00ff0000); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 19 */ regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 20 */ pixa = pixaCreate(2); pixaAddPix(pixa, pix1, L_INSERT); pixaAddPix(pixa, pix2, L_INSERT); if (rp->display) { pixd = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 20, 2); pixDisplayWithTitle(pixd, 400, 600, NULL, 1); pixDestroy(&pixd); } pixaDestroy(&pixa); boxDestroy(&box1); numaDestroy(&na1); numaDestroy(&na2); pixDestroy(&pixs); /* Again on a different image */ pix1 = pixRead("boxedpage.jpg"); pix2 = pixConvertTo8(pix1, 0); pixGetDimensions(pix2, &w, &h, NULL); na1 = pixVarianceByRow(pix2, NULL); pix3 = pixConvertTo32(pix1); pixRenderPlotFromNumaGen(&pix3, na1, L_VERTICAL_LINE, 3, 0, 70, 1, 0xff000000); na2 = pixVarianceByColumn(pix2, NULL); pixRenderPlotFromNumaGen(&pix3, na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1, 0x00ff0000); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 21 */ numaDestroy(&na1); numaDestroy(&na2); /* Again, with an erosion */ pix4 = pixErodeGray(pix2, 3, 21); na1 = pixVarianceByRow(pix4, NULL); pix5 = pixConvertTo32(pix1); pixRenderPlotFromNumaGen(&pix5, na1, L_VERTICAL_LINE, 3, 30, 70, 1, 0xff000000); na2 = pixVarianceByColumn(pix4, NULL); pixRenderPlotFromNumaGen(&pix5, na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1, 0x00ff0000); regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 22 */ pixa = pixaCreate(2); pixaAddPix(pixa, pix3, L_INSERT); pixaAddPix(pixa, pix5, L_INSERT); if (rp->display) { pixd = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 20, 2); pixDisplayWithTitle(pixd, 800, 600, NULL, 1); pixDestroy(&pixd); } pixaDestroy(&pixa); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix4); numaDestroy(&na1); numaDestroy(&na2); /* -------------------------------------------------------------------* * Windowed variance along a line * * -------------------------------------------------------------------*/ pix1 = pixRead("boxedpage.jpg"); pix2 = pixConvertTo8(pix1, 0); pixGetDimensions(pix2, &w, &h, NULL); pix3 = pixCopy(NULL, pix1); /* Plot along horizontal line */ pixWindowedVarianceOnLine(pix2, L_HORIZONTAL_LINE, h / 2 - 30, 0, w, 5, &na1); pixRenderPlotFromNumaGen(&pix1, na1, L_HORIZONTAL_LINE, 3, h / 2 - 30, 80, 1, 0xff000000); pixRenderPlotFromNuma(&pix3, na1, L_PLOT_AT_TOP, 3, 60, 0x00ff0000); pixRenderPlotFromNuma(&pix3, na1, L_PLOT_AT_BOT, 3, 60, 0x0000ff00); /* Plot along vertical line */ pixWindowedVarianceOnLine(pix2, L_VERTICAL_LINE, 0.78 * w, 0, h, 5, &na2); pixRenderPlotFromNumaGen(&pix1, na2, L_VERTICAL_LINE, 3, 0.78 * w, 60, 1, 0x00ff0000); pixRenderPlotFromNuma(&pix3, na2, L_PLOT_AT_LEFT, 3, 60, 0xff000000); pixRenderPlotFromNuma(&pix3, na2, L_PLOT_AT_RIGHT, 3, 60, 0x00ff0000); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 23 */ regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 24 */ pixa = pixaCreate(2); pixaAddPix(pixa, pix1, L_INSERT); pixaAddPix(pixa, pix3, L_INSERT); if (rp->display) { pixd = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 20, 2); pixDisplayWithTitle(pixd, 1200, 600, NULL, 1); pixDestroy(&pixd); } pixaDestroy(&pixa); pixDestroy(&pix2); numaDestroy(&na1); numaDestroy(&na2); return regTestCleanup(rp);; }
int main_line_removal() { PIX* pixs_source = pixRead("dave-start.png"); if (!pixs_source) { printf("Error opening file"); return 1; } double deg2rad = 3.1415926535 / 180.; l_float32 angle, conf, score; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; pix1 = pixThresholdToBinary(pixs_source, 160); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-1.tif", pix1, IFF_TIFF_G4); pixFindSkew(pix1, &angle, &conf); pix2 = pixRotateAMGray(pixs_source, deg2rad * angle, 160); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-2.tif", pix2, IFF_TIFF_G4); l_int32 HORIZ = 1; l_int32 VERT = 3; pix3 = pixCloseGray(pix2, 51, HORIZ); //k?p?c 51? printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-3.tif", pix3, IFF_TIFF_G4); pix4 = pixErodeGray(pix3, 5, VERT); //k?p?c 5? printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-4.tif", pix4, IFF_TIFF_G4); pix5 = pix4; pix5 = pixThresholdToValue(pix5, pix4, 230, 255); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-5.tif", pix5, IFF_TIFF_G4); pix6 = pix5; pix6 = pixThresholdToValue(pix5, pix5, 210, 0); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-6.tif", pix6, IFF_TIFF_G4); pix7 = pixThresholdToBinary(pix6, 230); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-7.tif", pix7, IFF_TIFF_G4); pixInvert(pix6, pix6); pix8 = pixAddGray(NULL, pix2, pix6); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-8.tif", pix8, IFF_TIFF_G4); VERT = 7; pix9 = pixOpenGray(pix8, 3, VERT); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-9.tif", pix9, IFF_TIFF_G4); if (pixCombineMasked(pix8, pix9, pix7)) { printf("!!!Error while combining pixs!!!\n"); } printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-final.tif", pix8, IFF_TIFF_G4); printf("\n---\nEnd\n"); getchar(); return 0; }