/*! * numaaTruncate() * * Input: naa * Return: 0 if OK, 1 on error * * Notes: * (1) This identifies the largest index containing a numa that * has any numbers within it, destroys all numa beyond that * index, and resets the count. */ l_int32 numaaTruncate(NUMAA *naa) { l_int32 i, n, nn; NUMA *na; PROCNAME("numaaTruncate"); if (!naa) return ERROR_INT("naa not defined", procName, 1); n = numaaGetCount(naa); for (i = n - 1; i >= 0; i--) { na = numaaGetNuma(naa, i, L_CLONE); if (!na) continue; nn = numaGetCount(na); numaDestroy(&na); if (nn == 0) numaDestroy(&naa->numa[i]); else break; } naa->n = i + 1; return 0; }
/*! * gplotSimpleXYN() * * Input: nax (<optional>; can be NULL) * naay (numaa of arrays to plot against @nax) * outformat (GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_X11, * GPLOT_LATEX) * outroot (root of output files) * title (<optional>) * Return: 0 if OK, 1 on error * * Notes: * (1) This gives line plots of each Numa in @naa against nax, * generated in the specified output format. The title is optional. * (2) @nax is optional. If NULL, each Numa array is plotted against * the array index. * (3) When calling these simple plot functions more than once, use * different @outroot to avoid overwriting the output files. */ l_int32 gplotSimpleXYN(NUMA *nax, NUMAA *naay, l_int32 outformat, const char *outroot, const char *title) { l_int32 i, n; GPLOT *gplot; NUMA *nay; PROCNAME("gplotSimpleXYN"); if (!naay) return ERROR_INT("naay not defined", procName, 1); if ((n = numaaGetCount(naay)) == 0) return ERROR_INT("no numa in array", procName, 1); if (outformat != GPLOT_PNG && outformat != GPLOT_PS && outformat != GPLOT_EPS && outformat != GPLOT_X11 && outformat != GPLOT_LATEX) return ERROR_INT("invalid outformat", procName, 1); if (!outroot) return ERROR_INT("outroot not specified", procName, 1); if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0) return ERROR_INT("gplot not made", procName, 1); for (i = 0; i < n; i++) { nay = numaaGetNuma(naay, i, L_CLONE); gplotAddPlot(gplot, nax, nay, GPLOT_LINES, NULL); numaDestroy(&nay); } gplotMakeOutput(gplot); gplotDestroy(&gplot); return 0; }
/*! * numaaWriteStream() * * Input: stream, naa * Return: 0 if OK, 1 on error */ l_int32 numaaWriteStream(FILE *fp, NUMAA *naa) { l_int32 i, n; NUMA *na; PROCNAME("numaaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!naa) return ERROR_INT("naa not defined", procName, 1); n = numaaGetCount(naa); fprintf(fp, "\nNumaa Version %d\n", NUMA_VERSION_NUMBER); fprintf(fp, "Number of numa = %d\n\n", n); for (i = 0; i < n; i++) { if ((na = numaaGetNuma(naa, i, L_CLONE)) == NULL) return ERROR_INT("na not found", procName, 1); fprintf(fp, "Numa[%d]:", i); numaWriteStream(fp, na); numaDestroy(&na); } return 0; }
/*! * pixaSort2dByIndex() * * Input: pixas * naa (numaa that maps from the new pixaa to the input pixas) * copyflag (L_CLONE or L_COPY) * Return: pixaa (sorted), or null on error */ PIXAA * pixaSort2dByIndex(PIXA *pixas, NUMAA *naa, l_int32 copyflag) { l_int32 pixtot, ntot, i, j, n, nn, index; BOX *box; NUMA *na; PIX *pix; PIXA *pixa; PIXAA *pixaa; PROCNAME("pixaSort2dByIndex"); if (!pixas) return (PIXAA *)ERROR_PTR("pixas not defined", procName, NULL); if (!naa) return (PIXAA *)ERROR_PTR("naindex not defined", procName, NULL); /* Check counts */ ntot = numaaGetNumberCount(naa); pixtot = pixaGetCount(pixas); if (ntot != pixtot) return (PIXAA *)ERROR_PTR("element count mismatch", procName, NULL); n = numaaGetCount(naa); pixaa = pixaaCreate(n); for (i = 0; i < n; i++) { na = numaaGetNuma(naa, i, L_CLONE); nn = numaGetCount(na); pixa = pixaCreate(nn); for (j = 0; j < nn; j++) { numaGetIValue(na, j, &index); pix = pixaGetPix(pixas, index, copyflag); box = pixaGetBox(pixas, index, copyflag); pixaAddPix(pixa, pix, L_INSERT); pixaAddBox(pixa, box, L_INSERT); } pixaaAddPixa(pixaa, pixa, L_INSERT); numaDestroy(&na); } return pixaa; }
/*! * numaaGetNumberCount() * * Input: naa * Return: count (total number of numbers in the numaa), * or 0 if no numbers or on error */ l_int32 numaaGetNumberCount(NUMAA *naa) { NUMA *na; l_int32 n, sum, i; PROCNAME("numaaGetNumberCount"); if (!naa) return ERROR_INT("naa not defined", procName, 0); n = numaaGetCount(naa); for (sum = 0, i = 0; i < n; i++) { na = numaaGetNuma(naa, i, L_CLONE); sum += numaGetCount(na); numaDestroy(&na); } return sum; }
/*! * numaaAddNumber() * * Input: naa * index (of numa within numaa) * val (float or int to be added; stored as a float) * Return: 0 if OK, 1 on error * * Notes: * (1) Adds to an existing numa only. */ l_int32 numaaAddNumber(NUMAA *naa, l_int32 index, l_float32 val) { l_int32 n; NUMA *na; PROCNAME("numaaAddNumber"); if (!naa) return ERROR_INT("naa not defined", procName, 1); n = numaaGetCount(naa); if (index < 0 || index >= n) return ERROR_INT("invalid index in naa", procName, 1); na = numaaGetNuma(naa, index, L_CLONE); numaAddNumber(na, val); numaDestroy(&na); return 0; }
/*! * boxaSort2dByIndex() * * Input: boxas * naa (numaa that maps from the new baa to the input boxa) * Return: baa (sorted boxaa), or null on error */ BOXAA * boxaSort2dByIndex(BOXA *boxas, NUMAA *naa) { l_int32 ntot, boxtot, i, j, n, nn, index; BOX *box; BOXA *boxa; BOXAA *baa; NUMA *na; PROCNAME("boxaSort2dByIndex"); if (!boxas) return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); if (!naa) return (BOXAA *)ERROR_PTR("naindex not defined", procName, NULL); /* Check counts */ ntot = numaaGetNumberCount(naa); boxtot = boxaGetCount(boxas); if (ntot != boxtot) return (BOXAA *)ERROR_PTR("element count mismatch", procName, NULL); n = numaaGetCount(naa); baa = boxaaCreate(n); for (i = 0; i < n; i++) { na = numaaGetNuma(naa, i, L_CLONE); nn = numaGetCount(na); boxa = boxaCreate(nn); for (j = 0; j < nn; j++) { numaGetIValue(na, i, &index); box = boxaGetBox(boxas, index, L_COPY); boxaAddBox(boxa, box, L_INSERT); } boxaaAddBoxa(baa, boxa, L_INSERT); numaDestroy(&na); } return baa; }
/*! * \brief numaaCompareImagesByBoxes() * * \param[in] naa1 for image 1, formatted by boxaExtractSortedPattern() * \param[in] naa2 ditto; for image 2 * \param[in] nperline number of box regions to be used in each textline * \param[in] nreq number of complete row matches required * \param[in] maxshiftx max allowed x shift between two patterns, in pixels * \param[in] maxshifty max allowed y shift between two patterns, in pixels * \param[in] delx max allowed difference in x data, after alignment * \param[in] dely max allowed difference in y data, after alignment * \param[out] psame 1 if %nreq row matches are found; 0 otherwise * \param[in] debugflag 1 for debug output * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) Each input numaa describes a set of sorted bounding boxes * (sorted by textline and, within each textline, from * left to right) in the images from which they are derived. * See boxaExtractSortedPattern() for a description of the data * format in each of the input numaa. * (2) This function does an alignment between the input * descriptions of bounding boxes for two images. The * input parameter %nperline specifies the number of boxes * to consider in each line when testing for a match, and * %nreq is the required number of lines that must be well-aligned * to get a match. * (3) Testing by alignment has 3 steps: * (a) Generating the location of word bounding boxes from the * images (prior to calling this function). * (b) Listing all possible pairs of aligned rows, based on * tolerances in horizontal and vertical positions of * the boxes. Specifically, all pairs of rows are enumerated * whose first %nperline boxes can be brought into close * alignment, based on the delx parameter for boxes in the * line and within the overall the %maxshiftx and %maxshifty * constraints. * (c) Each pair, starting with the first, is used to search * for a set of %nreq - 1 other pairs that can all be aligned * with a difference in global translation of not more * than (%delx, %dely). * </pre> */ l_int32 numaaCompareImagesByBoxes(NUMAA *naa1, NUMAA *naa2, l_int32 nperline, l_int32 nreq, l_int32 maxshiftx, l_int32 maxshifty, l_int32 delx, l_int32 dely, l_int32 *psame, l_int32 debugflag) { l_int32 n1, n2, i, j, nbox, y1, y2, xl1, xl2; l_int32 shiftx, shifty, match; l_int32 *line1, *line2; /* indicator for sufficient boxes in a line */ l_int32 *yloc1, *yloc2; /* arrays of y value for first box in a line */ l_int32 *xleft1, *xleft2; /* arrays of x value for left side of first box */ NUMA *na1, *na2, *nai1, *nai2, *nasx, *nasy; PROCNAME("numaaCompareImagesByBoxes"); if (!psame) return ERROR_INT("&same not defined", procName, 1); *psame = 0; if (!naa1) return ERROR_INT("naa1 not defined", procName, 1); if (!naa2) return ERROR_INT("naa2 not defined", procName, 1); if (nperline < 1) return ERROR_INT("nperline < 1", procName, 1); if (nreq < 1) return ERROR_INT("nreq < 1", procName, 1); n1 = numaaGetCount(naa1); n2 = numaaGetCount(naa2); if (n1 < nreq || n2 < nreq) return 0; /* Find the lines in naa1 and naa2 with sufficient boxes. * Also, find the y-values for each of the lines, and the * LH x-values of the first box in each line. */ line1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); line2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); yloc1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); yloc2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); xleft1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); xleft2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); for (i = 0; i < n1; i++) { na1 = numaaGetNuma(naa1, i, L_CLONE); numaGetIValue(na1, 0, yloc1 + i); numaGetIValue(na1, 1, xleft1 + i); nbox = (numaGetCount(na1) - 1) / 2; if (nbox >= nperline) line1[i] = 1; numaDestroy(&na1); } for (i = 0; i < n2; i++) { na2 = numaaGetNuma(naa2, i, L_CLONE); numaGetIValue(na2, 0, yloc2 + i); numaGetIValue(na2, 1, xleft2 + i); nbox = (numaGetCount(na2) - 1) / 2; if (nbox >= nperline) line2[i] = 1; numaDestroy(&na2); } /* Enumerate all possible line matches. A 'possible' line * match is one where the x and y shifts for the first box * in each line are within the maxshiftx and maxshifty * constraints, and the left and right sides of the remaining * (nperline - 1) successive boxes are within delx of each other. * The result is a set of four numas giving parameters of * each set of matching lines. */ nai1 = numaCreate(0); /* line index 1 of match */ nai2 = numaCreate(0); /* line index 2 of match */ nasx = numaCreate(0); /* shiftx for match */ nasy = numaCreate(0); /* shifty for match */ for (i = 0; i < n1; i++) { if (line1[i] == 0) continue; y1 = yloc1[i]; xl1 = xleft1[i]; na1 = numaaGetNuma(naa1, i, L_CLONE); for (j = 0; j < n2; j++) { if (line2[j] == 0) continue; y2 = yloc2[j]; if (L_ABS(y1 - y2) > maxshifty) continue; xl2 = xleft2[j]; if (L_ABS(xl1 - xl2) > maxshiftx) continue; shiftx = xl1 - xl2; /* shift to add to x2 values */ shifty = y1 - y2; /* shift to add to y2 values */ na2 = numaaGetNuma(naa2, j, L_CLONE); /* Now check if 'nperline' boxes in the two lines match */ match = testLineAlignmentX(na1, na2, shiftx, delx, nperline); if (match) { numaAddNumber(nai1, i); numaAddNumber(nai2, j); numaAddNumber(nasx, shiftx); numaAddNumber(nasy, shifty); } numaDestroy(&na2); } numaDestroy(&na1); } /* Determine if there are a sufficient number of mutually * aligned matches. Mutually aligned matches place an additional * constraint on the 'possible' matches, where the relative * shifts must not exceed the (delx, dely) distances. */ countAlignedMatches(nai1, nai2, nasx, nasy, n1, n2, delx, dely, nreq, psame, debugflag); LEPT_FREE(line1); LEPT_FREE(line2); LEPT_FREE(yloc1); LEPT_FREE(yloc2); LEPT_FREE(xleft1); LEPT_FREE(xleft2); numaDestroy(&nai1); numaDestroy(&nai2); numaDestroy(&nasx); numaDestroy(&nasy); return 0; }
int main(int argc, char **argv) { char label[512]; l_int32 rval, gval, bval, w, h, i, j, rwhite, gwhite, bwhite, count; l_uint32 pixel; GPLOT *gplot1, *gplot2; NUMA *naseq, *na; NUMAA *naa1, *naa2; PIX *pixs, *pixt, *pixt0, *pixt1, *pixt2; PIX *pixr, *pixg, *pixb; PIXA *pixa; PIXCMAP *cmap; static char mainName[] = "colorspacetest"; if (argc != 2) return ERROR_INT(" Syntax: colorspacetest filein", mainName, 1); if ((pixs = pixRead(argv[1])) == NULL) return ERROR_INT("pixs not made", mainName, 1); /* Generate colors by sampling hue with max sat and value. * This was used to make the color strip 19-colors.png. */ pixa = pixaCreate(19); for (i = 0; i < 19; i++) { convertHSVToRGB((240 * i / 18), 255, 255, &rval, &gval, &bval); composeRGBPixel(rval, gval, bval, &pixel); pixt1 = pixCreate(50, 100, 32); pixSetAllArbitrary(pixt1, pixel); pixaAddPix(pixa, pixt1, L_INSERT); } pixt2 = pixaDisplayTiledInRows(pixa, 32, 1100, 1.0, 0, 0, 0); pixDisplayWrite(pixt2, 1); pixDestroy(&pixt2); pixaDestroy(&pixa); /* Colorspace conversion in rgb */ pixDisplayWrite(pixs, 1); pixt = pixConvertRGBToHSV(NULL, pixs); pixDisplayWrite(pixt, 1); pixConvertHSVToRGB(pixt, pixt); pixDisplayWrite(pixt, 1); pixDestroy(&pixt); /* Colorspace conversion on a colormap */ pixt = pixOctreeQuantNumColors(pixs, 25, 0); pixDisplayWrite(pixt, 1); cmap = pixGetColormap(pixt); pixcmapWriteStream(stderr, cmap); pixcmapConvertRGBToHSV(cmap); pixcmapWriteStream(stderr, cmap); pixDisplayWrite(pixt, 1); pixcmapConvertHSVToRGB(cmap); pixcmapWriteStream(stderr, cmap); pixDisplayWrite(pixt, 1); pixDestroy(&pixt); /* Color content extraction */ pixColorContent(pixs, 0, 0, 0, 0, &pixr, &pixg, &pixb); pixDisplayWrite(pixr, 1); pixDisplayWrite(pixg, 1); pixDisplayWrite(pixb, 1); pixDestroy(&pixr); pixDestroy(&pixg); pixDestroy(&pixb); /* Color content measurement */ pixa = pixaCreate(20); naseq = numaMakeSequence(100, 5, 20); naa1 = numaaCreate(6); naa2 = numaaCreate(6); for (i = 0; i < 6; i++) { na = numaCreate(20); numaaAddNuma(naa1, na, L_COPY); numaaAddNuma(naa2, na, L_INSERT); } pixGetDimensions(pixs, &w, &h, NULL); for (i = 0; i < 20; i++) { rwhite = 100 + 5 * i; gwhite = 200 - 5 * i; bwhite = 150; pixt0 = pixGlobalNormRGB(NULL, pixs, rwhite, gwhite, bwhite, 255); pixaAddPix(pixa, pixt0, L_INSERT); pixt1 = pixColorMagnitude(pixs, rwhite, gwhite, bwhite, L_MAX_DIFF_FROM_AVERAGE_2); for (j = 0; j < 6; j++) { pixt2 = pixThresholdToBinary(pixt1, 30 + 10 * j); pixInvert(pixt2, pixt2); pixCountPixels(pixt2, &count, NULL); na = numaaGetNuma(naa1, j, L_CLONE); numaAddNumber(na, (l_float32)count / (l_float32)(w * h)); numaDestroy(&na); pixDestroy(&pixt2); } pixDestroy(&pixt1); pixt1 = pixColorMagnitude(pixs, rwhite, gwhite, bwhite, L_MAX_MIN_DIFF_FROM_2); for (j = 0; j < 6; j++) { pixt2 = pixThresholdToBinary(pixt1, 30 + 10 * j); pixInvert(pixt2, pixt2); pixCountPixels(pixt2, &count, NULL); na = numaaGetNuma(naa2, j, L_CLONE); numaAddNumber(na, (l_float32)count / (l_float32)(w * h)); numaDestroy(&na); pixDestroy(&pixt2); } pixDestroy(&pixt1); } gplot1 = gplotCreate("/tmp/junkplot1", GPLOT_X11, "Fraction with given color (diff from average)", "white point space for red", "amount of color"); gplot2 = gplotCreate("/tmp/junkplot2", GPLOT_X11, "Fraction with given color (min diff)", "white point space for red", "amount of color"); for (j = 0; j < 6; j++) { na = numaaGetNuma(naa1, j, L_CLONE); sprintf(label, "thresh %d", 30 + 10 * j); gplotAddPlot(gplot1, naseq, na, GPLOT_LINES, label); numaDestroy(&na); na = numaaGetNuma(naa2, j, L_CLONE); gplotAddPlot(gplot2, naseq, na, GPLOT_LINES, label); numaDestroy(&na); } gplotMakeOutput(gplot1); gplotMakeOutput(gplot2); gplotDestroy(&gplot1); gplotDestroy(&gplot2); pixt1 = pixaDisplayTiledAndScaled(pixa, 32, 250, 4, 0, 10, 2); pixWrite("/tmp/junkcolormag", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 0, 100, "Color magnitude", 1); pixDestroy(&pixt1); pixaDestroy(&pixa); numaDestroy(&naseq); numaaDestroy(&naa1); numaaDestroy(&naa2); pixDisplayMultiple("/tmp/display/file*"); pixDestroy(&pixs); return 0; }
int main(int argc, char **argv) { char label[512]; l_int32 rval, gval, bval, w, h, i, j, rwhite, gwhite, bwhite, count; l_uint32 pixel; GPLOT *gplot1, *gplot2; NUMA *naseq, *na; NUMAA *naa1, *naa2; PIX *pixs, *pixt, *pixt0, *pixt1, *pixt2; PIX *pixr, *pixg, *pixb; /* for color content extraction */ PIXA *pixa, *pixat; PIXCMAP *cmap; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; /* Generate a pdf of results when called with display */ pixa = pixaCreate(0); /* Generate colors by sampling hue with max sat and value. * This image has been saved as 19-colors.png. */ pixat = pixaCreate(19); for (i = 0; i < 19; i++) { convertHSVToRGB((240 * i / 18), 255, 255, &rval, &gval, &bval); composeRGBPixel(rval, gval, bval, &pixel); pixt1 = pixCreate(50, 100, 32); pixSetAllArbitrary(pixt1, pixel); pixaAddPix(pixat, pixt1, L_INSERT); } pixt2 = pixaDisplayTiledInRows(pixat, 32, 1100, 1.0, 0, 0, 0); regTestWritePixAndCheck(rp, pixt2, IFF_PNG); /* 0 */ pixaAddPix(pixa, pixt2, L_INSERT); pixaDestroy(&pixat); /* Colorspace conversion in rgb */ pixs = pixRead("wyom.jpg"); pixaAddPix(pixa, pixs, L_INSERT); pixt = pixConvertRGBToHSV(NULL, pixs); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 1 */ pixaAddPix(pixa, pixt, L_COPY); pixConvertHSVToRGB(pixt, pixt); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 2 */ pixaAddPix(pixa, pixt, L_INSERT); /* Colorspace conversion on a colormap */ pixt = pixOctreeQuantNumColors(pixs, 25, 0); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 3 */ pixaAddPix(pixa, pixt, L_COPY); cmap = pixGetColormap(pixt); if (rp->display) pixcmapWriteStream(stderr, cmap); pixcmapConvertRGBToHSV(cmap); if (rp->display) pixcmapWriteStream(stderr, cmap); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 4 */ pixaAddPix(pixa, pixt, L_COPY); pixcmapConvertHSVToRGB(cmap); if (rp->display) pixcmapWriteStream(stderr, cmap); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 5 */ pixaAddPix(pixa, pixt, L_INSERT); /* Color content extraction */ pixColorContent(pixs, 0, 0, 0, 0, &pixr, &pixg, &pixb); regTestWritePixAndCheck(rp, pixr, IFF_JFIF_JPEG); /* 6 */ pixaAddPix(pixa, pixr, L_INSERT); regTestWritePixAndCheck(rp, pixg, IFF_JFIF_JPEG); /* 7 */ pixaAddPix(pixa, pixg, L_INSERT); regTestWritePixAndCheck(rp, pixb, IFF_JFIF_JPEG); /* 8 */ pixaAddPix(pixa, pixb, L_INSERT); /* Color content measurement. This tests the global * mapping of (r,g,b) --> (white), for 20 different * values of (r,g,b). For each mappings, we compute * the color magnitude and threshold it at six values. * For each of those six thresholds, we plot the * fraction of pixels that exceeds the threshold * color magnitude, where the red value (mapped to * white) goes between 100 and 195. */ pixat = pixaCreate(20); naseq = numaMakeSequence(100, 5, 20); naa1 = numaaCreate(6); naa2 = numaaCreate(6); for (i = 0; i < 6; i++) { na = numaCreate(20); numaaAddNuma(naa1, na, L_COPY); numaaAddNuma(naa2, na, L_INSERT); } pixGetDimensions(pixs, &w, &h, NULL); for (i = 0; i < 20; i++) { rwhite = 100 + 5 * i; gwhite = 200 - 5 * i; bwhite = 150; pixt0 = pixGlobalNormRGB(NULL, pixs, rwhite, gwhite, bwhite, 255); pixaAddPix(pixat, pixt0, L_INSERT); pixt1 = pixColorMagnitude(pixs, rwhite, gwhite, bwhite, L_MAX_DIFF_FROM_AVERAGE_2); for (j = 0; j < 6; j++) { pixt2 = pixThresholdToBinary(pixt1, 30 + 10 * j); pixInvert(pixt2, pixt2); pixCountPixels(pixt2, &count, NULL); na = numaaGetNuma(naa1, j, L_CLONE); numaAddNumber(na, (l_float32)count / (l_float32)(w * h)); numaDestroy(&na); pixDestroy(&pixt2); } pixDestroy(&pixt1); pixt1 = pixColorMagnitude(pixs, rwhite, gwhite, bwhite, L_MAX_MIN_DIFF_FROM_2); for (j = 0; j < 6; j++) { pixt2 = pixThresholdToBinary(pixt1, 30 + 10 * j); pixInvert(pixt2, pixt2); pixCountPixels(pixt2, &count, NULL); na = numaaGetNuma(naa2, j, L_CLONE); numaAddNumber(na, (l_float32)count / (l_float32)(w * h)); numaDestroy(&na); pixDestroy(&pixt2); } pixDestroy(&pixt1); } gplot1 = gplotCreate("/tmp/regout/colorspace.10", GPLOT_PNG, "Fraction with given color (diff from average)", "white point space for red", "amount of color"); gplot2 = gplotCreate("/tmp/regout/colorspace.11", GPLOT_PNG, "Fraction with given color (min diff)", "white point space for red", "amount of color"); for (j = 0; j < 6; j++) { na = numaaGetNuma(naa1, j, L_CLONE); sprintf(label, "thresh %d", 30 + 10 * j); gplotAddPlot(gplot1, naseq, na, GPLOT_LINES, label); numaDestroy(&na); na = numaaGetNuma(naa2, j, L_CLONE); gplotAddPlot(gplot2, naseq, na, GPLOT_LINES, label); numaDestroy(&na); } gplotMakeOutput(gplot1); gplotMakeOutput(gplot2); gplotDestroy(&gplot1); gplotDestroy(&gplot2); pixt1 = pixaDisplayTiledAndScaled(pixat, 32, 250, 4, 0, 10, 2); regTestWritePixAndCheck(rp, pixt1, IFF_JFIF_JPEG); /* 9 */ pixaAddPix(pixa, pixt1, L_INSERT); pixDisplayWithTitle(pixt1, 0, 100, "Color magnitude", rp->display); pixaDestroy(&pixat); numaDestroy(&naseq); numaaDestroy(&naa1); numaaDestroy(&naa2); /* Give gnuplot time to write out the files */ #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ /* Save as golden files, or check against them */ regTestCheckFile(rp, "/tmp/regout/colorspace.10.png"); /* 10 */ regTestCheckFile(rp, "/tmp/regout/colorspace.11.png"); /* 11 */ if (rp->display) { pixt = pixRead("/tmp/regout/colorspace.10.png"); pixaAddPix(pixa, pixt, L_INSERT); pixt = pixRead("/tmp/regout/colorspace.11.png"); pixaAddPix(pixa, pixt, L_INSERT); pixaConvertToPdf(pixa, 0, 1.0, 0, 0, "colorspace tests", "/tmp/regout/colorspace.pdf"); L_INFO("Output pdf: /tmp/regout/colorspace.pdf\n", rp->testname); } pixaDestroy(&pixa); return regTestCleanup(rp); }
/*! * boxaSort2d() * * Input: boxas * &naa (<optional return> numaa with sorted indices * whose values are the indices of the input array) * delta1 (min overlap that permits aggregation of a box * onto a boxa of horizontally-aligned boxes; pass 1) * delta2 (min overlap that permits aggregation of a box * onto a boxa of horizontally-aligned boxes; pass 2) * minh1 (components less than this height either join an * existing boxa or are set aside for pass 2) * Return: boxaa (2d sorted version of boxa), or null on error * * Notes: * (1) The final result is a sort where the 'fast scan' direction is * left to right, and the 'slow scan' direction is from top * to bottom. Each boxa in the boxaa represents a sorted set * of boxes from left to right. * (2) Two passes are used to aggregate the boxas, which can corresond * to characters or words in a line of text. In pass 1, only * taller components, which correspond to xheight or larger, * are permitted to start a new boxa, whereas in pass 2, * the remaining vertically-challenged components are allowed * to join an existing boxa or start a new one. * (3) If delta1 < 0, the first pass allows aggregation when * boxes in the same boxa do not overlap vertically. * The distance by which they can miss and still be aggregated * is the absolute value |delta1|. Similar for delta2 on * the second pass. * (4) On the first pass, any component of height less than minh1 * cannot start a new boxa; it's put aside for later insertion. * (5) On the second pass, any small component that doesn't align * with an existing boxa can start a new one. * (6) This can be used to identify lines of text from * character or word bounding boxes. */ BOXAA * boxaSort2d(BOXA *boxas, NUMAA **pnaad, l_int32 delta1, l_int32 delta2, l_int32 minh1) { l_int32 i, index, h, nt, ne, n, m, ival; BOX *box; BOXA *boxa, *boxae, *boxan, *boxat1, *boxat2, *boxav, *boxavs; BOXAA *baa, *baad; NUMA *naindex, *nae, *nan, *nah, *nav, *nat1, *nat2, *nad; NUMAA *naa, *naad; PROCNAME("boxaSort2d"); if (pnaad) *pnaad = NULL; if (!boxas) return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); /* Sort from left to right */ if ((boxa = boxaSort(boxas, L_SORT_BY_X, L_SORT_INCREASING, &naindex)) == NULL) return (BOXAA *)ERROR_PTR("boxa not made", procName, NULL); /* First pass: assign taller boxes to boxa by row */ nt = boxaGetCount(boxa); baa = boxaaCreate(0); naa = numaaCreate(0); boxae = boxaCreate(0); /* save small height boxes here */ nae = numaCreate(0); /* keep track of small height boxes */ for (i = 0; i < nt; i++) { box = boxaGetBox(boxa, i, L_CLONE); boxGetGeometry(box, NULL, NULL, NULL, &h); if (h < minh1) { /* save for 2nd pass */ boxaAddBox(boxae, box, L_INSERT); numaAddNumber(nae, i); } else { n = boxaaGetCount(baa); boxaaAlignBox(baa, box, delta1, &index); if (index < n) { /* append to an existing boxa */ boxaaAddBox(baa, index, box, L_INSERT); } else { /* doesn't align, need new boxa */ boxan = boxaCreate(0); boxaAddBox(boxan, box, L_INSERT); boxaaAddBoxa(baa, boxan, L_INSERT); nan = numaCreate(0); numaaAddNuma(naa, nan, L_INSERT); } numaGetIValue(naindex, i, &ival); numaaAddNumber(naa, index, ival); } } boxaDestroy(&boxa); numaDestroy(&naindex); /* Second pass: feed in small height boxes; * TODO: this correctly, using local y position! */ ne = boxaGetCount(boxae); for (i = 0; i < ne; i++) { box = boxaGetBox(boxae, i, L_CLONE); n = boxaaGetCount(baa); boxaaAlignBox(baa, box, delta2, &index); if (index < n) { /* append to an existing boxa */ boxaaAddBox(baa, index, box, L_INSERT); } else { /* doesn't align, need new boxa */ boxan = boxaCreate(0); boxaAddBox(boxan, box, L_INSERT); boxaaAddBoxa(baa, boxan, L_INSERT); nan = numaCreate(0); numaaAddNuma(naa, nan, L_INSERT); } numaGetIValue(nae, i, &ival); /* location in original boxas */ numaaAddNumber(naa, index, ival); } /* Sort each boxa in the boxaa */ m = boxaaGetCount(baa); for (i = 0; i < m; i++) { boxat1 = boxaaGetBoxa(baa, i, L_CLONE); boxat2 = boxaSort(boxat1, L_SORT_BY_X, L_SORT_INCREASING, &nah); boxaaReplaceBoxa(baa, i, boxat2); nat1 = numaaGetNuma(naa, i, L_CLONE); nat2 = numaSortByIndex(nat1, nah); numaaReplaceNuma(naa, i, nat2); boxaDestroy(&boxat1); numaDestroy(&nat1); numaDestroy(&nah); } /* Sort boxa vertically within boxaa, using the first box * in each boxa. */ m = boxaaGetCount(baa); boxav = boxaCreate(m); /* holds first box in each boxa in baa */ naad = numaaCreate(m); if (pnaad) *pnaad = naad; baad = boxaaCreate(m); for (i = 0; i < m; i++) { boxat1 = boxaaGetBoxa(baa, i, L_CLONE); box = boxaGetBox(boxat1, 0, L_CLONE); boxaAddBox(boxav, box, L_INSERT); boxaDestroy(&boxat1); } boxavs = boxaSort(boxav, L_SORT_BY_Y, L_SORT_INCREASING, &nav); for (i = 0; i < m; i++) { numaGetIValue(nav, i, &index); boxa = boxaaGetBoxa(baa, index, L_CLONE); boxaaAddBoxa(baad, boxa, L_INSERT); nad = numaaGetNuma(naa, index, L_CLONE); numaaAddNuma(naad, nad, L_INSERT); } /* fprintf(stderr, "box count = %d, numaa count = %d\n", nt, numaaGetNumberCount(naad)); */ boxaaDestroy(&baa); boxaDestroy(&boxav); boxaDestroy(&boxavs); boxaDestroy(&boxae); numaDestroy(&nav); numaDestroy(&nae); numaaDestroy(&naa); if (!pnaad) numaaDestroy(&naad); return baad; }