/*! * pixcmapGetRankIntensity() * * Input: cmap * rankval (0.0 for darkest, 1.0 for lightest color) * &index (<return> the index into the colormap that * corresponds to the rank intensity color) * Return: 0 if OK, 1 on error */ l_int32 pixcmapGetRankIntensity(PIXCMAP *cmap, l_float32 rankval, l_int32 *pindex) { l_int32 n, i, rval, gval, bval, rankindex; NUMA *na, *nasort; PROCNAME("pixcmapGetRankIntensity"); if (!pindex) return ERROR_INT("&index not defined", procName, 1); *pindex = 0; if (!cmap) return ERROR_INT("cmap not defined", procName, 1); if (rankval < 0.0 || rankval > 1.0) return ERROR_INT("rankval not in [0.0 ... 1.0]", procName, 1); n = pixcmapGetCount(cmap); na = numaCreate(n); for (i = 0; i < n; i++) { pixcmapGetColor(cmap, i, &rval, &gval, &bval); numaAddNumber(na, rval + gval + bval); } nasort = numaGetSortIndex(na, L_SORT_INCREASING); rankindex = (l_int32)(rankval * (n - 1) + 0.5); numaGetIValue(nasort, rankindex, pindex); numaDestroy(&na); numaDestroy(&nasort); return 0; }
/*! * ptaaRemoveShortLines() * * Input: pixs (1 bpp) * ptaas (input lines) * fract (minimum fraction of longest line to keep) * debugflag * Return: ptaad (containing only lines of sufficient length), * or null on error */ PTAA * ptaaRemoveShortLines(PIX *pixs, PTAA *ptaas, l_float32 fract, l_int32 debugflag) { l_int32 w, n, i, index, maxlen, len; l_float32 minx, maxx; NUMA *na, *naindex; PIX *pixt1, *pixt2; PTA *pta; PTAA *ptaad; PROCNAME("ptaaRemoveShortLines"); if (!pixs || pixGetDepth(pixs) != 1) return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (!ptaas) return (PTAA *)ERROR_PTR("ptaas undefined", procName, NULL); pixGetDimensions(pixs, &w, NULL, NULL); n = ptaaGetCount(ptaas); ptaad = ptaaCreate(n); na = numaCreate(n); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaas, i, L_CLONE); ptaGetRange(pta, &minx, &maxx, NULL, NULL); numaAddNumber(na, maxx - minx + 1); ptaDestroy(&pta); } /* Sort by length and find all that are long enough */ naindex = numaGetSortIndex(na, L_SORT_DECREASING); numaGetIValue(naindex, 0, &index); numaGetIValue(na, index, &maxlen); if (maxlen < 0.5 * w) L_WARNING("lines are relatively short", procName); pta = ptaaGetPta(ptaas, index, L_CLONE); ptaaAddPta(ptaad, pta, L_INSERT); for (i = 1; i < n; i++) { numaGetIValue(naindex, i, &index); numaGetIValue(na, index, &len); if (len < fract * maxlen) break; pta = ptaaGetPta(ptaas, index, L_CLONE); ptaaAddPta(ptaad, pta, L_INSERT); } if (debugflag) { pixt1 = pixCopy(NULL, pixs); pixt2 = pixDisplayPtaa(pixt1, ptaad); pixDisplayWithTitle(pixt2, 0, 200, "pix4", 1); pixDestroy(&pixt1); pixDestroy(&pixt2); } numaDestroy(&na); numaDestroy(&naindex); return ptaad; }
/*! * \brief pixColorSegmentClean() * * \param[in] pixs 8 bpp, colormapped * \param[in] selsize for closing * \param[in] countarray ptr to array containing the number of pixels * found in each color in the colormap * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This operation is in-place. * (2) This is phase 3 of color segmentation. It is the first * part of a two-step noise removal process. Colors with a * large population are closed first; this operation absorbs * small sets of intercolated pixels of a different color. * </pre> */ l_ok pixColorSegmentClean(PIX *pixs, l_int32 selsize, l_int32 *countarray) { l_int32 i, ncolors, val; l_uint32 val32; NUMA *na, *nasi; PIX *pixt1, *pixt2; PIXCMAP *cmap; PROCNAME("pixColorSegmentClean"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (pixGetDepth(pixs) != 8) return ERROR_INT("pixs not 8 bpp", procName, 1); if ((cmap = pixGetColormap(pixs)) == NULL) return ERROR_INT("cmap not found", procName, 1); if (!countarray) return ERROR_INT("countarray not defined", procName, 1); if (selsize <= 1) return 0; /* nothing to do */ /* Sort colormap indices in decreasing order of pixel population */ ncolors = pixcmapGetCount(cmap); na = numaCreate(ncolors); for (i = 0; i < ncolors; i++) numaAddNumber(na, countarray[i]); nasi = numaGetSortIndex(na, L_SORT_DECREASING); numaDestroy(&na); if (!nasi) return ERROR_INT("nasi not made", procName, 1); /* For each color, in order of decreasing population, * do a closing and absorb the added pixels. Note that * if the closing removes pixels at the border, they'll * still appear in the xor and will be properly (re)set. */ for (i = 0; i < ncolors; i++) { numaGetIValue(nasi, i, &val); pixt1 = pixGenerateMaskByValue(pixs, val, 1); pixt2 = pixCloseSafeCompBrick(NULL, pixt1, selsize, selsize); pixXor(pixt2, pixt2, pixt1); /* pixels to be added to type 'val' */ pixcmapGetColor32(cmap, val, &val32); pixSetMasked(pixs, pixt2, val32); /* add them */ pixDestroy(&pixt1); pixDestroy(&pixt2); } numaDestroy(&nasi); return 0; }
/*! * ptaGetSortIndex() * * Input: ptas * sorttype (L_SORT_BY_X, L_SORT_BY_Y) * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) * &naindex (<return> index of sorted order into * original array) * Return: 0 if OK, 1 on error */ l_int32 ptaGetSortIndex(PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex) { l_int32 i, n; l_float32 x, y; NUMA *na; PROCNAME("ptaGetSortIndex"); if (!pnaindex) return ERROR_INT("&naindex not defined", procName, 1); *pnaindex = NULL; if (!ptas) return ERROR_INT("ptas not defined", procName, 1); if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) return ERROR_INT("invalid sort type", procName, 1); if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) return ERROR_INT("invalid sort order", procName, 1); /* Build up numa of specific data */ n = ptaGetCount(ptas); if ((na = numaCreate(n)) == NULL) return ERROR_INT("na not made", procName, 1); for (i = 0; i < n; i++) { ptaGetPt(ptas, i, &x, &y); if (sorttype == L_SORT_BY_X) numaAddNumber(na, x); else numaAddNumber(na, y); } /* Get the sort index for data array */ *pnaindex = numaGetSortIndex(na, sortorder); numaDestroy(&na); if (!*pnaindex) return ERROR_INT("naindex not made", procName, 1); return 0; }
/*! * pixaSort() * * Input: pixas * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO) * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) * &naindex (<optional return> index of sorted order into * original array) * copyflag (L_COPY, L_CLONE) * Return: pixad (sorted version of pixas), or null on error * * Notes: * (1) This sorts based on the data in the boxa. If the boxa * count is not the same as the pixa count, this returns an error. * (2) The copyflag refers to the pix and box copies that are * inserted into the sorted pixa. These are either L_COPY * or L_CLONE. */ PIXA * pixaSort(PIXA *pixas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex, l_int32 copyflag) { l_int32 i, n, x, y, w, h; BOXA *boxa; NUMA *na, *naindex; PIXA *pixad; PROCNAME("pixaSort"); if (pnaindex) *pnaindex = NULL; if (!pixas) return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && sorttype != L_SORT_BY_MIN_DIMENSION && sorttype != L_SORT_BY_MAX_DIMENSION && sorttype != L_SORT_BY_PERIMETER && sorttype != L_SORT_BY_AREA && sorttype != L_SORT_BY_ASPECT_RATIO) return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); if (copyflag != L_COPY && copyflag != L_CLONE) return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ return (PIXA *)ERROR_PTR("boxa not found", procName, NULL); n = pixaGetCount(pixas); if (boxaGetCount(boxa) != n) return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); /* Use O(n) binsort if possible */ if (n > MIN_COMPS_FOR_BIN_SORT && ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || (sorttype == L_SORT_BY_PERIMETER))) return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag); /* Build up numa of specific data */ if ((na = numaCreate(n)) == NULL) return (PIXA *)ERROR_PTR("na not made", procName, NULL); for (i = 0; i < n; i++) { boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); switch (sorttype) { case L_SORT_BY_X: numaAddNumber(na, x); break; case L_SORT_BY_Y: numaAddNumber(na, y); break; case L_SORT_BY_WIDTH: numaAddNumber(na, w); break; case L_SORT_BY_HEIGHT: numaAddNumber(na, h); break; case L_SORT_BY_MIN_DIMENSION: numaAddNumber(na, L_MIN(w, h)); break; case L_SORT_BY_MAX_DIMENSION: numaAddNumber(na, L_MAX(w, h)); break; case L_SORT_BY_PERIMETER: numaAddNumber(na, w + h); break; case L_SORT_BY_AREA: numaAddNumber(na, w * h); break; case L_SORT_BY_ASPECT_RATIO: numaAddNumber(na, (l_float32)w / (l_float32)h); break; default: L_WARNING("invalid sort type", procName); } } /* Get the sort index for data array */ if ((naindex = numaGetSortIndex(na, sortorder)) == NULL) return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); /* Build up sorted pixa using sort index */ if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); if (pnaindex) *pnaindex = naindex; else numaDestroy(&naindex); numaDestroy(&na); return pixad; }
/*! * 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; }
/*! * \brief pixColorSegmentRemoveColors() * * \param[in] pixd 8 bpp, colormapped * \param[in] pixs 32 bpp rgb, with initial pixel values * \param[in] finalcolors max number of colors to retain * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This operation is in-place. * (2) This is phase 4 of color segmentation, and the second part * of the 2-step noise removal. Only 'finalcolors' different * colors are retained, with colors with smaller populations * being replaced by the nearest color of the remaining colors. * For highest accuracy, for pixels that are being replaced, * we find the nearest colormap color to the original rgb color. * </pre> */ l_ok pixColorSegmentRemoveColors(PIX *pixd, PIX *pixs, l_int32 finalcolors) { l_int32 i, ncolors, index, tempindex; l_int32 *tab; l_uint32 tempcolor; NUMA *na, *nasi; PIX *pixm; PIXCMAP *cmap; PROCNAME("pixColorSegmentRemoveColors"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixGetDepth(pixd) != 8) return ERROR_INT("pixd not 8 bpp", procName, 1); if ((cmap = pixGetColormap(pixd)) == NULL) return ERROR_INT("cmap not found", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); ncolors = pixcmapGetCount(cmap); if (finalcolors >= ncolors) /* few enough colors already; nothing to do */ return 0; /* Generate a mask over all pixels that are not in the * 'finalcolors' most populated colors. Save the colormap * index of any one of the retained colors in 'tempindex'. * The LUT has values 0 for the 'finalcolors' most populated colors, * which will be retained; and 1 for the rest, which are marked * by fg pixels in pixm and will be removed. */ na = pixGetCmapHistogram(pixd, 1); if ((nasi = numaGetSortIndex(na, L_SORT_DECREASING)) == NULL) { numaDestroy(&na); return ERROR_INT("nasi not made", procName, 1); } numaGetIValue(nasi, finalcolors - 1, &tempindex); /* retain down to this */ pixcmapGetColor32(cmap, tempindex, &tempcolor); /* use this color */ tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); for (i = finalcolors; i < ncolors; i++) { numaGetIValue(nasi, i, &index); tab[index] = 1; } pixm = pixMakeMaskFromLUT(pixd, tab); LEPT_FREE(tab); /* Reassign the masked pixels temporarily to the saved index * (tempindex). This guarantees that no pixels are labeled by * a colormap index of any colors that will be removed. * The actual value doesn't matter, as long as it's one * of the retained colors, because these pixels will later * be reassigned based on the full set of colors retained * in the colormap. */ pixSetMasked(pixd, pixm, tempcolor); /* Now remove unused colors from the colormap. This reassigns * image pixels as required. */ pixRemoveUnusedColors(pixd); /* Finally, reassign the pixels under the mask (those that were * given a 'tempindex' value) to the nearest color in the colormap. * This is the function used in phase 2 on all image pixels; here * it is only used on the masked pixels given by pixm. */ pixAssignToNearestColor(pixd, pixs, pixm, LEVEL_IN_OCTCUBE, NULL); pixDestroy(&pixm); numaDestroy(&na); numaDestroy(&nasi); return 0; }
/*! * boxaSort() * * Input: boxa * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO) * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) * &naindex (<optional return> index of sorted order into * original array) * Return: boxad (sorted version of boxas), or null on error */ BOXA * boxaSort(BOXA *boxas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex) { l_int32 i, n, x, y, w, h, size; BOXA *boxad; NUMA *na, *naindex; PROCNAME("boxaSort"); if (pnaindex) *pnaindex = NULL; if (!boxas) return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && sorttype != L_SORT_BY_MIN_DIMENSION && sorttype != L_SORT_BY_MAX_DIMENSION && sorttype != L_SORT_BY_PERIMETER && sorttype != L_SORT_BY_AREA && sorttype != L_SORT_BY_ASPECT_RATIO) return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL); if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL); /* Use O(n) binsort if possible */ n = boxaGetCount(boxas); if (n > MIN_COMPS_FOR_BIN_SORT && ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || (sorttype == L_SORT_BY_PERIMETER))) return boxaBinSort(boxas, sorttype, sortorder, pnaindex); /* Build up numa of specific data */ if ((na = numaCreate(n)) == NULL) return (BOXA *)ERROR_PTR("na not made", procName, NULL); for (i = 0; i < n; i++) { boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h); switch (sorttype) { case L_SORT_BY_X: numaAddNumber(na, x); break; case L_SORT_BY_Y: numaAddNumber(na, y); break; case L_SORT_BY_WIDTH: numaAddNumber(na, w); break; case L_SORT_BY_HEIGHT: numaAddNumber(na, h); break; case L_SORT_BY_MIN_DIMENSION: size = L_MIN(w, h); numaAddNumber(na, size); break; case L_SORT_BY_MAX_DIMENSION: size = L_MAX(w, h); numaAddNumber(na, size); break; case L_SORT_BY_PERIMETER: size = w + h; numaAddNumber(na, size); break; case L_SORT_BY_AREA: size = w * h; numaAddNumber(na, size); break; case L_SORT_BY_ASPECT_RATIO: numaAddNumber(na, (l_float32)w / (l_float32)h); break; default: L_WARNING("invalid sort type", procName); } } /* Get the sort index for data array */ if ((naindex = numaGetSortIndex(na, sortorder)) == NULL) return (BOXA *)ERROR_PTR("naindex not made", procName, NULL); /* Build up sorted boxa using sort index */ boxad = boxaSortByIndex(boxas, naindex); if (pnaindex) *pnaindex = naindex; else numaDestroy(&naindex); numaDestroy(&na); return boxad; }