/*! * pixBilateralGrayExact() * * Input: pixs (8 bpp gray) * spatial_kel (gaussian kernel) * range_kel (<optional> 256 x 1, monotonically decreasing) * Return: pixd (8 bpp bilateral filtered image) * * Notes: * (1) See pixBilateralExact(). */ PIX * pixBilateralGrayExact(PIX *pixs, L_KERNEL *spatial_kel, L_KERNEL *range_kel) { l_int32 i, j, id, jd, k, m, w, h, d, sx, sy, cx, cy, wplt, wpld; l_int32 val, center_val; l_uint32 *datat, *datad, *linet, *lined; l_float32 sum, weight_sum, weight; L_KERNEL *keli; PIX *pixt, *pixd; PROCNAME("pixBilateralGrayExact"); if (!pixs) return (PIX *) ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 8) return (PIX *) ERROR_PTR("pixs must be gray", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (!spatial_kel) return (PIX *) ERROR_PTR("spatial kel not defined", procName, NULL); if (!range_kel) return pixConvolve(pixs, spatial_kel, 8, 1); if (range_kel->sx != 256 || range_kel->sy != 1) return (PIX *) ERROR_PTR("range kel not {256 x 1", procName, NULL); keli = kernelInvert(spatial_kel); kernelGetParameters(keli, &sy, &sx, &cy, &cx); if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL) return (PIX *) ERROR_PTR("pixt not made", procName, NULL); pixd = pixCreate(w, h, 8); datat = pixGetData(pixt); datad = pixGetData(pixd); wplt = pixGetWpl(pixt); wpld = pixGetWpl(pixd); for (i = 0, id = 0; id < h; i++, id++) { lined = datad + id * wpld; for (j = 0, jd = 0; jd < w; j++, jd++) { center_val = GET_DATA_BYTE(datat + (i + cy) * wplt, j + cx); weight_sum = 0.0; sum = 0.0; for (k = 0; k < sy; k++) { linet = datat + (i + k) * wplt; for (m = 0; m < sx; m++) { val = GET_DATA_BYTE(linet, j + m); weight = keli->data[k][m] * range_kel->data[0][L_ABS(center_val - val)]; weight_sum += weight; sum += val * weight; } } SET_DATA_BYTE(lined, jd, (l_int32)(sum / weight_sum + 0.5)); } } kernelDestroy(&keli); pixDestroy(&pixt); return pixd; }
/*! * pixAdaptiveMeanFilter() * * Input: pixs (8 bpp grayscale) * wc, hc (half width/height of convolution kernel) * varn (value of overall noise variance) * Return: pixd (8 bpp, filtered image) * * Notes: * (1) The filter reduces gaussian noise, achieving results similar * to the arithmetic and geometric mean filters but avoiding the * considerable image blurring effect introduced by those filters. * (2) The filter can be expressed mathematically by: * f'(x, y) = g(x, y) - varN / varL * [ g(x, y) - meanL ] * where: * -- g(x, y) is the pixel at the center of local region S of * width (2 * wc + 1) and height (2 * wh + 1) * -- varN and varL are the overall noise variance (given in input) * and local variance of S, respectively * -- meanL is the local mean of S * (3) Typically @varn is estimated by studying the PDFs produced by * the camera or equipment sensors. */ PIX * pixAdaptiveMeanFilter(PIX *pixs, l_int32 wc, l_int32 hc, l_float32 varn) { l_int32 i, j, w, h, d, wplt, wpld, wincr, hincr; l_uint32 val; l_uint32 *datat, *datad, *linet, *lined; l_float32 norm, meanl, varl, ratio; PIX *pixt, *pixd; PROCNAME("pixAdaptiveMeanFilter"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 8) return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); if (wc < 1 || hc < 1) return (PIX *)ERROR_PTR("wc and hc not >= 1", procName, NULL); /* Add wc to each side, and hc to top and bottom of the image, * mirroring for accuracy and to avoid special-casing the boundary. */ if ((pixt = pixAddMirroredBorder(pixs, wc, wc, hc, hc)) == NULL) return (PIX *)ERROR_PTR("pixt not made", procName, NULL); /* Place the filter center at (0, 0). This is just a * convenient location, because it allows us to perform * the filtering over x:(0 ... w - 1) and y:(0 ... h - 1). */ pixd = pixCreateTemplate(pixs); wplt = pixGetWpl(pixt); wpld = pixGetWpl(pixd); datat = pixGetData(pixt); datad = pixGetData(pixd); wincr = 2 * wc + 1; hincr = 2 * hc + 1; norm = 1.0 / (wincr * hincr); for (i = 0; i < h; i++) { linet = datat + (i + hc) * wplt; lined = datad + i * wpld; for (j = 0; j < w; j++) { /* Calculate mean intensity value */ meanl = calculateLocalMeanLow(datat, wplt, wincr, hincr, i, j); /* Calculate local variance */ varl = calculateLocalVarianceLow(datat, wplt, wincr, hincr, i, j, meanl); /* Account for special case in which varN is more than varL */ ratio = (varn > varl) ? 1 : varn / varl; val = GET_DATA_BYTE(linet, j + wc); SET_DATA_BYTE(lined, j, (l_uint8) (val - ratio * (val - meanl))); } } pixDestroy(&pixt); return pixd; }
main(int argc, char **argv) { l_int32 i, j, equal; PIX *pixs, *pixt, *pixd; static char mainName[] = "rasteropip_reg"; pixs = pixRead("test8.jpg"); pixt = pixCopy(NULL, pixs); /* Copy, in-place and one COLUMN at a time, from the right side to the left side. */ for (j = 0; j < 200; j++) pixRasterop(pixs, 20 + j, 20, 1, 250, PIX_SRC, pixs, 250 + j, 20); pixDisplay(pixs, 50, 50); /* Copy, in-place and one ROW at a time, from the right side to the left side. */ for (i = 0; i < 250; i++) pixRasterop(pixt, 20, 20 + i, 200, 1, PIX_SRC, pixt, 250, 20 + i); pixDisplay(pixt, 620, 50); /* Test */ pixEqual(pixs, pixt, &equal); if (equal) fprintf(stderr, "OK: images are the same\n"); else fprintf(stderr, "Error: images are different\n"); pixWrite("/tmp/junkpix.png", pixs, IFF_PNG); pixDestroy(&pixs); pixDestroy(&pixt); /* Show the mirrored border, which uses the general pixRasterop() on an image in-place. */ pixs = pixRead("test8.jpg"); pixt = pixRemoveBorder(pixs, 40); pixd = pixAddMirroredBorder(pixt, 25, 25, 25, 25); pixDisplay(pixd, 50, 550); pixDestroy(&pixs); pixDestroy(&pixt); pixDestroy(&pixd); return 0; }
main(int argc, char **argv) { l_int32 i, j; PIX *pixs, *pixt, *pixd; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixs = pixRead("test8.jpg"); pixt = pixCopy(NULL, pixs); /* Copy, in-place and one COLUMN at a time, from the right side to the left side. */ for (j = 0; j < 200; j++) pixRasterop(pixs, 20 + j, 20, 1, 250, PIX_SRC, pixs, 250 + j, 20); pixDisplayWithTitle(pixs, 50, 50, "in-place copy", rp->display); /* Copy, in-place and one ROW at a time, from the right side to the left side. */ for (i = 0; i < 250; i++) pixRasterop(pixt, 20, 20 + i, 200, 1, PIX_SRC, pixt, 250, 20 + i); /* Test */ regTestComparePix(rp, pixs, pixt); /* 0 */ pixDestroy(&pixs); pixDestroy(&pixt); /* Show the mirrored border, which uses the general pixRasterop() on an image in-place. */ pixs = pixRead("test8.jpg"); pixt = pixRemoveBorder(pixs, 40); pixd = pixAddMirroredBorder(pixt, 40, 40, 40, 40); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 1 */ pixDisplayWithTitle(pixd, 650, 50, "mirrored border", rp->display); pixDestroy(&pixs); pixDestroy(&pixt); pixDestroy(&pixd); return regTestCleanup(rp); }
/*! * pixSauvolaBinarize() * * Input: pixs (8 bpp grayscale; not colormapped) * whsize (window half-width for measuring local statistics) * factor (factor for reducing threshold due to variance; >= 0) * addborder (1 to add border of width (@whsize + 1) on all sides) * &pixm (<optional return> local mean values) * &pixsd (<optional return> local standard deviation values) * &pixth (<optional return> threshold values) * &pixd (<optional return> thresholded image) * Return: 0 if OK, 1 on error * * Notes: * (1) The window width and height are 2 * @whsize + 1. The minimum * value for @whsize is 2; typically it is >= 7.. * (2) The local statistics, measured over the window, are the * average and standard deviation. * (3) The measurements of the mean and standard deviation are * performed inside a border of (@whsize + 1) pixels. If pixs does * not have these added border pixels, use @addborder = 1 to add * it here; otherwise use @addborder = 0. * (4) The Sauvola threshold is determined from the formula: * t = m * (1 - k * (1 - s / 128)) * where: * t = local threshold * m = local mean * k = @factor (>= 0) [ typ. 0.35 ] * s = local standard deviation, which is maximized at * 127.5 when half the samples are 0 and half are 255. * (5) The basic idea of Niblack and Sauvola binarization is that * the local threshold should be less than the median value, * and the larger the variance, the closer to the median * it should be chosen. Typical values for k are between * 0.2 and 0.5. */ l_int32 pixSauvolaBinarize(PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 addborder, PIX **ppixm, PIX **ppixsd, PIX **ppixth, PIX **ppixd) { l_int32 w, h; PIX *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd; PROCNAME("pixSauvolaBinarize"); if (!ppixm && !ppixsd && !ppixth && !ppixd) return ERROR_INT("no outputs", procName, 1); if (ppixm) *ppixm = NULL; if (ppixsd) *ppixsd = NULL; if (ppixth) *ppixth = NULL; if (ppixd) *ppixd = NULL; if (!pixs || pixGetDepth(pixs) != 8) return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); if (pixGetColormap(pixs)) return ERROR_INT("pixs is cmapped", procName, 1); pixGetDimensions(pixs, &w, &h, NULL); if (whsize < 2) return ERROR_INT("whsize must be >= 2", procName, 1); if (w < 2 * whsize + 3 || h < 2 * whsize + 3) return ERROR_INT("whsize too large for image", procName, 1); if (factor < 0.0) return ERROR_INT("factor must be >= 0", procName, 1); if (addborder) { pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1, whsize + 1, whsize + 1); pixsc = pixClone(pixs); } else { pixg = pixClone(pixs); pixsc = pixRemoveBorder(pixs, whsize + 1); } if (!pixg || !pixsc) return ERROR_INT("pixg and pixsc not made", procName, 1); /* All these functions strip off the border pixels. */ if (ppixm || ppixth || ppixd) pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1); if (ppixsd || ppixth || ppixd) pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1); if (ppixth || ppixd) pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd); if (ppixd) pixd = pixApplyLocalThreshold(pixsc, pixth, 1); if (ppixm) *ppixm = pixm; else pixDestroy(&pixm); pixDestroy(&pixms); if (ppixth) *ppixth = pixth; else pixDestroy(&pixth); if (ppixd) *ppixd = pixd; else pixDestroy(&pixd); pixDestroy(&pixg); pixDestroy(&pixsc); return 0; }
/*! * bilateralCreate() * * Input: pixs (8 bpp gray, no colormap) * spatial_stdev (of gaussian kernel; in pixels, > 0.5) * range_stdev (of gaussian range kernel; > 5.0; typ. 50.0) * ncomps (number of intermediate sums J(k,x); in [4 ... 30]) * reduction (1, 2 or 4) * Return: bil, or null on error * * Notes: * (1) This initializes a bilateral filtering operation, generating all * the data required. It takes most of the time in the bilateral * filtering operation. * (2) See bilateral.h for details of the algorithm. * (3) See pixBilateral() for constraints on input parameters, which * are not checked here. */ static L_BILATERAL * bilateralCreate(PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev, l_int32 ncomps, l_int32 reduction) { l_int32 w, ws, wd, h, hs, hd, i, j, k, index; l_int32 border, minval, maxval, spatial_size; l_int32 halfwidth, wpls, wplt, wpld, kval, nval, dval; l_float32 sstdev, fval1, fval2, denom, sum, norm, kern; l_int32 *nc, *kindex; l_float32 *kfract, *range, *spatial; l_uint32 *datas, *datat, *datad, *lines, *linet, *lined; L_BILATERAL *bil; PIX *pixt, *pixt2, *pixsc, *pixd; PIXA *pixac; PROCNAME("bilateralCreate"); sstdev = spatial_stdev / (l_float32) reduction; /* reduced spat. stdev */ if ((bil = (L_BILATERAL *) CALLOC(1, sizeof(L_BILATERAL))) == NULL) return (L_BILATERAL *) ERROR_PTR("bil not made", procName, NULL); bil->spatial_stdev = sstdev; bil->range_stdev = range_stdev; bil->reduction = reduction; bil->ncomps = ncomps; if (reduction == 1) { pixt = pixClone(pixs); } else if (reduction == 2) { pixt = pixScaleAreaMap2(pixs); } else { /* reduction == 4) */ pixt2 = pixScaleAreaMap2(pixs); pixt = pixScaleAreaMap2(pixt2); pixDestroy(&pixt2); } pixGetExtremeValue(pixt, 1, L_SELECT_MIN, NULL, NULL, NULL, &minval); pixGetExtremeValue(pixt, 1, L_SELECT_MAX, NULL, NULL, NULL, &maxval); bil->minval = minval; bil->maxval = maxval; border = (l_int32)(2 * sstdev + 1); pixsc = pixAddMirroredBorder(pixt, border, border, border, border); bil->pixsc = pixsc; pixDestroy(&pixt); bil->pixs = pixClone(pixs); /* -------------------------------------------------------------------- * * Generate arrays for interpolation of J(k,x): * (1.0 - kfract[.]) * J(kindex[.], x) + kfract[.] * J(kindex[.] + 1, x), * where I(x) is the index into kfract[] and kindex[], * and x is an index into the 2D image array. * -------------------------------------------------------------------- */ /* nc is the set of k values to be used in J(k,x) */ nc = (l_int32 *) CALLOC(ncomps, sizeof(l_int32)); for (i = 0; i < ncomps; i++) nc[i] = minval + i * (maxval - minval) / (ncomps - 1); bil->nc = nc; /* kindex maps from intensity I(x) to the lower k index for J(k,x) */ kindex = (l_int32 *) CALLOC(256, sizeof(l_int32)); for (i = minval, k = 0; i <= maxval && k < ncomps - 1; k++) { fval2 = nc[k + 1]; while (i < fval2) { kindex[i] = k; i++; } } kindex[maxval] = ncomps - 2; bil->kindex = kindex; /* kfract maps from intensity I(x) to the fraction of J(k+1,x) used */ kfract = (l_float32 *) CALLOC(256, sizeof(l_float32)); /* from lower */ for (i = minval, k = 0; i <= maxval && k < ncomps - 1; k++) { fval1 = nc[k]; fval2 = nc[k + 1]; while (i < fval2) { kfract[i] = (l_float32)(i - fval1) / (l_float32)(fval2 - fval1); i++; } } kfract[maxval] = 1.0; bil->kfract = kfract; #if DEBUG_BILATERAL for (i = minval; i <= maxval; i++) fprintf(stderr, "kindex[%d] = %d; kfract[%d] = %5.3f\n", i, kindex[i], i, kfract[i]); for (i = 0; i < ncomps; i++) fprintf(stderr, "nc[%d] = %d\n", i, nc[i]); #endif /* DEBUG_BILATERAL */ /* -------------------------------------------------------------------- * * Generate 1-D kernel arrays (spatial and range) * * -------------------------------------------------------------------- */ spatial_size = 2 * sstdev + 1; spatial = (l_float32 *) CALLOC(spatial_size, sizeof(l_float32)); denom = 2. * sstdev * sstdev; for (i = 0; i < spatial_size; i++) spatial[i] = expf(-(l_float32)(i * i) / denom); bil->spatial = spatial; range = (l_float32 *) CALLOC(256, sizeof(l_float32)); denom = 2. * range_stdev * range_stdev; for (i = 0; i < 256; i++) range[i] = expf(-(l_float32)(i * i) / denom); bil->range = range; /* -------------------------------------------------------------------- * * Generate principal bilateral component images * * -------------------------------------------------------------------- */ pixac = pixaCreate(ncomps); pixGetDimensions(pixsc, &ws, &hs, NULL); datas = pixGetData(pixsc); wpls = pixGetWpl(pixsc); pixGetDimensions(pixs, &w, &h, NULL); wd = (w + reduction - 1) / reduction; hd = (h + reduction - 1) / reduction; halfwidth = (l_int32)(2.0 * sstdev); for (index = 0; index < ncomps; index++) { pixt = pixCopy(NULL, pixsc); datat = pixGetData(pixt); wplt = pixGetWpl(pixt); kval = nc[index]; /* Separable convolutions: horizontal first */ for (i = 0; i < hd; i++) { lines = datas + (border + i) * wpls; linet = datat + (border + i) * wplt; for (j = 0; j < wd; j++) { sum = 0.0; norm = 0.0; for (k = -halfwidth; k <= halfwidth; k++) { nval = GET_DATA_BYTE(lines, border + j + k); kern = spatial[L_ABS(k)] * range[L_ABS(kval - nval)]; sum += kern * nval; norm += kern; } dval = (l_int32)((sum / norm) + 0.5); SET_DATA_BYTE(linet, border + j, dval); } } /* Vertical convolution */ pixd = pixCreate(wd, hd, 8); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); for (i = 0; i < hd; i++) { linet = datat + (border + i) * wplt; lined = datad + i * wpld; for (j = 0; j < wd; j++) { sum = 0.0; norm = 0.0; for (k = -halfwidth; k <= halfwidth; k++) { nval = GET_DATA_BYTE(linet + k * wplt, border + j); kern = spatial[L_ABS(k)] * range[L_ABS(kval - nval)]; sum += kern * nval; norm += kern; } dval = (l_int32)((sum / norm) + 0.5); SET_DATA_BYTE(lined, j, dval); } } pixDestroy(&pixt); pixaAddPix(pixac, pixd, L_INSERT); } bil->pixac = pixac; bil->lineset = (l_uint32 ***) pixaGetLinePtrs(pixac, NULL); return bil; }
/*! * \brief pixTilingGetTile() * * \param[in] pt pixtiling * \param[in] i tile row index * \param[in] j tile column index * \return pixd tile with appropriate boundary (overlap) pixels added, * or NULL on error */ PIX * pixTilingGetTile(PIXTILING *pt, l_int32 i, l_int32 j) { l_int32 wpix, hpix, wt, ht, nx, ny; l_int32 xoverlap, yoverlap, wtlast, htlast; l_int32 left, top, xtraleft, xtraright, xtratop, xtrabot, width, height; BOX *box; PIX *pixs, *pixt, *pixd; PROCNAME("pixTilingGetTile"); if (!pt) return (PIX *)ERROR_PTR("pt not defined", procName, NULL); if ((pixs = pt->pix) == NULL) return (PIX *)ERROR_PTR("pix not found", procName, NULL); pixTilingGetCount(pt, &nx, &ny); if (i < 0 || i >= ny) return (PIX *)ERROR_PTR("invalid row index i", procName, NULL); if (j < 0 || j >= nx) return (PIX *)ERROR_PTR("invalid column index j", procName, NULL); /* Grab the tile with as much overlap as exists within the * input pix. First, compute the (left, top) coordinates. */ pixGetDimensions(pixs, &wpix, &hpix, NULL); pixTilingGetSize(pt, &wt, &ht); xoverlap = pt->xoverlap; yoverlap = pt->yoverlap; wtlast = wpix - wt * (nx - 1); htlast = hpix - ht * (ny - 1); left = L_MAX(0, j * wt - xoverlap); top = L_MAX(0, i * ht - yoverlap); /* Get the width and height of the tile, including whatever * overlap is available. */ if (nx == 1) width = wpix; else if (j == 0) width = wt + xoverlap; else if (j == nx - 1) width = wtlast + xoverlap; else width = wt + 2 * xoverlap; if (ny == 1) height = hpix; else if (i == 0) height = ht + yoverlap; else if (i == ny - 1) height = htlast + yoverlap; else height = ht + 2 * yoverlap; box = boxCreate(left, top, width, height); pixt = pixClipRectangle(pixs, box, NULL); boxDestroy(&box); /* If no overlap, do not add any special case borders */ if (xoverlap == 0 && yoverlap == 0) return pixt; /* Add overlap as a mirrored border, in the 8 special cases where * the tile touches the border of the input pix. The xtratop (etc) * parameters are required where the tile is either full width * or full height. */ xtratop = xtrabot = xtraleft = xtraright = 0; if (nx == 1) xtraleft = xtraright = xoverlap; if (ny == 1) xtratop = xtrabot = yoverlap; if (i == 0 && j == 0) pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, yoverlap, xtrabot); else if (i == 0 && j == nx - 1) pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, yoverlap, xtrabot); else if (i == ny - 1 && j == 0) pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, xtratop, yoverlap); else if (i == ny - 1 && j == nx - 1) pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, xtratop, yoverlap); else if (i == 0) pixd = pixAddMirroredBorder(pixt, 0, 0, yoverlap, xtrabot); else if (i == ny - 1) pixd = pixAddMirroredBorder(pixt, 0, 0, xtratop, yoverlap); else if (j == 0) pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, 0, 0); else if (j == nx - 1) pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, 0, 0); else pixd = pixClone(pixt); pixDestroy(&pixt); return pixd; }
int main(int argc, char **argv) { FPIX *fpix1, *fpix2, *fpix3, *fpix4; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; /* Test orthogonal rotations */ pix1 = pixRead("marge.jpg"); pix2 = pixConvertTo8(pix1, 0); fpix1 = pixConvertToFPix(pix2, 1); fpix2 = fpixRotateOrth(fpix1, 1); pix3 = fpixConvertToPix(fpix2, 8, L_CLIP_TO_ZERO, 0); pix4 = pixRotateOrth(pix2, 1); regTestComparePix(rp, pix3, pix4); /* 0 */ pixDisplayWithTitle(pix3, 100, 100, NULL, rp->display); fpix3 = fpixRotateOrth(fpix1, 2); pix5 = fpixConvertToPix(fpix3, 8, L_CLIP_TO_ZERO, 0); pix6 = pixRotateOrth(pix2, 2); regTestComparePix(rp, pix5, pix6); /* 1 */ pixDisplayWithTitle(pix5, 560, 100, NULL, rp->display); fpix4 = fpixRotateOrth(fpix1, 3); pix7 = fpixConvertToPix(fpix4, 8, L_CLIP_TO_ZERO, 0); pix8 = pixRotateOrth(pix2, 3); regTestComparePix(rp, pix7, pix8); /* 2 */ pixDisplayWithTitle(pix7, 1170, 100, NULL, rp->display); pixDisplayWithTitle(pix2, 560, 580, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); pixDestroy(&pix7); pixDestroy(&pix8); fpixDestroy(&fpix1); fpixDestroy(&fpix2); fpixDestroy(&fpix3); fpixDestroy(&fpix4); /* Test adding various borders */ pix1 = pixRead("marge.jpg"); pix2 = pixConvertTo8(pix1, 0); fpix1 = pixConvertToFPix(pix2, 1); fpix2 = fpixAddMirroredBorder(fpix1, 21, 21, 25, 25); pix3 = fpixConvertToPix(fpix2, 8, L_CLIP_TO_ZERO, 0); pix4 = pixAddMirroredBorder(pix2, 21, 21, 25, 25); regTestComparePix(rp, pix3, pix4); /* 3 */ pixDisplayWithTitle(pix3, 100, 1000, NULL, rp->display); fpix3 = fpixAddContinuedBorder(fpix1, 21, 21, 25, 25); pix5 = fpixConvertToPix(fpix3, 8, L_CLIP_TO_ZERO, 0); pix6 = pixAddContinuedBorder(pix2, 21, 21, 25, 25); regTestComparePix(rp, pix5, pix6); /* 4 */ pixDisplayWithTitle(pix5, 750, 1000, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); fpixDestroy(&fpix1); fpixDestroy(&fpix2); fpixDestroy(&fpix3); return regTestCleanup(rp); }
/*! * 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 */