/*! * pixBilateral() * * Input: pixs (8 bpp gray or 32 bpp rgb, 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: pixd (bilateral filtered image), or null on error * * Notes: * (1) This performs a relatively fast, separable bilateral * filtering operation. The time is proportional to ncomps * and varies inversely approximately as the cube of the * reduction factor. See bilateral.h for algorithm details. * (2) We impose minimum values for range_stdev and ncomps to * avoid nasty artifacts when either are too small. We also * impose a constraint on their product: * ncomps * range_stdev >= 100. * So for values of range_stdev >= 25, ncomps can be as small as 4. * Here is a qualitative, intuitive explanation for this constraint. * Call the difference in k values between the J(k) == 'delta', where * 'delta' ~ 200 / ncomps * Then this constraint is roughly equivalent to the condition: * 'delta' < 2 * range_stdev * Note that at an intensity difference of (2 * range_stdev), the * range part of the kernel reduces the effect by the factor 0.14. * This constraint requires that we have a sufficient number of * PCBs (i.e, a small enough 'delta'), so that for any value of * image intensity I, there exists a k (and a PCB, J(k), such that * |I - k| < range_stdev * Any fewer PCBs and we don't have enough to support this condition. * (3) The upper limit of 30 on ncomps is imposed because the * gain in accuracy is not worth the extra computation. * (4) The size of the gaussian kernel is twice the spatial_stdev * on each side of the origin. The minimum value of * spatial_stdev, 0.5, is required to have a finite sized * spatial kernel. In practice, a much larger value is used. * (5) Computation of the intermediate images goes inversely * as the cube of the reduction factor. If you can use a * reduction of 2 or 4, it is well-advised. * (6) The range kernel is defined over the absolute value of pixel * grayscale differences, and hence must have size 256 x 1. * Values in the array represent the multiplying weight * depending on the absolute gray value difference between * the source pixel and the neighboring pixel, and should * be monotonically decreasing. * (7) Interesting observation. Run this on prog/fish24.jpg, with * range_stdev = 60, ncomps = 6, and spatial_dev = {10, 30, 50}. * As spatial_dev gets larger, we get the counter-intuitive * result that the body of the red fish becomes less blurry. */ PIX * pixBilateral(PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev, l_int32 ncomps, l_int32 reduction) { l_int32 d; l_float32 sstdev; /* scaled spatial stdev */ PIX *pixt, *pixr, *pixg, *pixb, *pixd; PROCNAME("pixBilateral"); if (!pixs || pixGetColormap(pixs)) return (PIX *)ERROR_PTR("pixs not defined or cmapped", procName, NULL); d = pixGetDepth(pixs); if (d != 8 && d != 32) return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); if (reduction != 1 && reduction != 2 && reduction != 4) return (PIX *)ERROR_PTR("reduction invalid", procName, NULL); sstdev = spatial_stdev / (l_float32)reduction; /* reduced spat. stdev */ if (sstdev < 0.5) return (PIX *)ERROR_PTR("sstdev < 0.5", procName, NULL); if (range_stdev <= 5.0) return (PIX *)ERROR_PTR("range_stdev <= 5.0", procName, NULL); if (ncomps < 4 || ncomps > 30) return (PIX *)ERROR_PTR("ncomps not in [4 ... 30]", procName, NULL); if (ncomps * range_stdev < 100.0) return (PIX *)ERROR_PTR("ncomps * range_stdev < 100.0", procName, NULL); if (d == 8) return pixBilateralGray(pixs, spatial_stdev, range_stdev, ncomps, reduction); pixt = pixGetRGBComponent(pixs, COLOR_RED); pixr = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, reduction); pixDestroy(&pixt); pixt = pixGetRGBComponent(pixs, COLOR_GREEN); pixg = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, reduction); pixDestroy(&pixt); pixt = pixGetRGBComponent(pixs, COLOR_BLUE); pixb = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, reduction); pixDestroy(&pixt); pixd = pixCreateRGBImage(pixr, pixg, pixb); pixDestroy(&pixr); pixDestroy(&pixg); pixDestroy(&pixb); return pixd; }
/*! * pixApplyFilter() * * Input: pix (8 or 32 bpp; or 2, 4 or 8 bpp with colormap) * dpix (filter) * outflag (L_CLIP_TO_ZERO, L_TAKE_ABSVAL, * L_THRESH_NEG_TO_BLACK or L_THRESH_NEG_TO_WHITE) * Return: pixd, or null on error * * Notes: * (1) Apply the input filter to pix by multiplying it for the * Fourier transform of pix. The inverse Fourier transform * is then used on the result to return the filtered image. * (2) If pix is 32 bpp RGB, the filter is applied to each color * channel separately. * (3) If colormapped, remove to grayscale. */ PIX * pixApplyFilter(PIX *pixs, DPIX *dpix, l_int32 outflag) { l_int32 w, h, d; PIX *pixt, *pixd, *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc; PROCNAME("pixApplyFilter"); if (!pixs && !dpix) return (PIX *)ERROR_PTR("pixs or dpix not defined", procName, NULL); /* Remove colormap if necessary */ pixGetDimensions(pixs, &w, &h, &d); if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pixs)) { L_WARNING("pix has colormap; removing", procName); pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); d = pixGetDepth(pixt); } else pixt = pixClone(pixs); if (d != 8 && d != 32) { pixDestroy(&pixt); return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); } if (d == 8) { pixd = pixApplyFilterGray(pixt, dpix, outflag); } else { /* d == 32 */ pixr = pixGetRGBComponent(pixt, COLOR_RED); pixrc = pixApplyFilterGray(pixr, dpix, outflag); pixDestroy(&pixr); pixg = pixGetRGBComponent(pixt, COLOR_GREEN); pixgc = pixApplyFilterGray(pixg, dpix, outflag); pixDestroy(&pixg); pixb = pixGetRGBComponent(pixt, COLOR_BLUE); pixbc = pixApplyFilterGray(pixb, dpix, outflag); pixDestroy(&pixb); pixd = pixCreateRGBImage(pixrc, pixgc, pixbc); pixDestroy(&pixrc); pixDestroy(&pixgc); pixDestroy(&pixbc); } pixDestroy(&pixt); return pixd; }
/*! * pixBilateralExact() * * Input: pixs (8 bpp gray or 32 bpp rgb) * spatial_kel (gaussian kernel) * range_kel (<optional> 256 x 1, monotonically decreasing) * Return: pixd (8 bpp bilateral filtered image) * * Notes: * (1) The spatial_kel is a conventional smoothing kernel, typically a * 2-d Gaussian kernel or other block kernel. It can be either * normalized or not, but must be everywhere positive. * (2) The range_kel is defined over the absolute value of pixel * grayscale differences, and hence must have size 256 x 1. * Values in the array represent the multiplying weight for each * gray value difference between the target pixel and center of the * kernel, and should be monotonically decreasing. * (3) If range_kel == NULL, a constant weight is applied regardless * of the range value difference. This degenerates to a regular * pixConvolve() with a normalized kernel. */ PIX * pixBilateralExact(PIX *pixs, L_KERNEL *spatial_kel, L_KERNEL *range_kel) { l_int32 d; PIX *pixt, *pixr, *pixg, *pixb, *pixd; PROCNAME("pixBilateralExact"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetColormap(pixs) != NULL) return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); d = pixGetDepth(pixs); if (d != 8 && d != 32) return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); if (!spatial_kel) return (PIX *)ERROR_PTR("spatial_ke not defined", procName, NULL); if (d == 8) { return pixBilateralGrayExact(pixs, spatial_kel, range_kel); } else { /* d == 32 */ pixt = pixGetRGBComponent(pixs, COLOR_RED); pixr = pixBilateralGrayExact(pixt, spatial_kel, range_kel); pixDestroy(&pixt); pixt = pixGetRGBComponent(pixs, COLOR_GREEN); pixg = pixBilateralGrayExact(pixt, spatial_kel, range_kel); pixDestroy(&pixt); pixt = pixGetRGBComponent(pixs, COLOR_BLUE); pixb = pixBilateralGrayExact(pixt, spatial_kel, range_kel); pixDestroy(&pixt); pixd = pixCreateRGBImage(pixr, pixg, pixb); pixDestroy(&pixr); pixDestroy(&pixg); pixDestroy(&pixb); return pixd; } }
/*! * pixRankFilterRGB() * * Input: pixs (32 bpp) * 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. * For each component, if the values are sorted in increasing * order, we choose the component 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) Apply gray rank filtering to each component independently. * (3) See notes in pixRankFilterGray() for further details. */ PIX * pixRankFilterRGB(PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank) { PIX *pixr, *pixg, *pixb, *pixrf, *pixgf, *pixbf, *pixd; PROCNAME("pixRankFilterRGB"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("pixs not 32 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); pixr = pixGetRGBComponent(pixs, COLOR_RED); pixg = pixGetRGBComponent(pixs, COLOR_GREEN); pixb = pixGetRGBComponent(pixs, COLOR_BLUE); pixrf = pixRankFilterGray(pixr, wf, hf, rank); pixgf = pixRankFilterGray(pixg, wf, hf, rank); pixbf = pixRankFilterGray(pixb, wf, hf, rank); pixd = pixCreateRGBImage(pixrf, pixgf, pixbf); pixDestroy(&pixr); pixDestroy(&pixg); pixDestroy(&pixb); pixDestroy(&pixrf); pixDestroy(&pixgf); pixDestroy(&pixbf); return pixd; }
/*! * pixColorMorph() * * Input: pixs * type (L_MORPH_DILATE, L_MORPH_ERODE, L_MORPH_OPEN, * or L_MORPH_CLOSE) * hsize (of Sel; must be odd; origin implicitly in center) * vsize (ditto) * Return: pixd * * Notes: * (1) This does the morph operation on each component separately, * and recombines the result. * (2) Sel is a brick with all elements being hits. * (3) If hsize = vsize = 1, just returns a copy. */ PIX * pixColorMorph(PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize) { PIX *pixr, *pixg, *pixb, *pixrm, *pixgm, *pixbm, *pixd; PROCNAME("pixColorMorph"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && type != L_MORPH_OPEN && type != L_MORPH_CLOSE) return (PIX *)ERROR_PTR("invalid morph type", procName, NULL); if (hsize < 1 || vsize < 1) return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); if ((hsize & 1) == 0 ) { L_WARNING("horiz sel size must be odd; increasing by 1", procName); hsize++; } if ((vsize & 1) == 0 ) { L_WARNING("vert sel size must be odd; increasing by 1", procName); vsize++; } if (hsize == 1 && vsize == 1) return pixCopy(NULL, pixs); pixr = pixGetRGBComponent(pixs, COLOR_RED); pixg = pixGetRGBComponent(pixs, COLOR_GREEN); pixb = pixGetRGBComponent(pixs, COLOR_BLUE); if (type == L_MORPH_DILATE) { pixrm = pixDilateGray(pixr, hsize, vsize); pixgm = pixDilateGray(pixg, hsize, vsize); pixbm = pixDilateGray(pixb, hsize, vsize); } else if (type == L_MORPH_ERODE) { pixrm = pixErodeGray(pixr, hsize, vsize); pixgm = pixErodeGray(pixg, hsize, vsize); pixbm = pixErodeGray(pixb, hsize, vsize); } else if (type == L_MORPH_OPEN) { pixrm = pixOpenGray(pixr, hsize, vsize); pixgm = pixOpenGray(pixg, hsize, vsize); pixbm = pixOpenGray(pixb, hsize, vsize); } else { /* type == L_MORPH_CLOSE */ pixrm = pixCloseGray(pixr, hsize, vsize); pixgm = pixCloseGray(pixg, hsize, vsize); pixbm = pixCloseGray(pixb, hsize, vsize); } pixd = pixCreateRGBImage(pixrm, pixgm, pixbm); pixDestroy(&pixr); pixDestroy(&pixrm); pixDestroy(&pixg); pixDestroy(&pixgm); pixDestroy(&pixb); pixDestroy(&pixbm); return pixd; }
/*! * pixLocToColorTransform() * * Input: pixs (1 bpp) * Return: pixd (32 bpp rgb), or null on error * * Notes: * (1) This generates an RGB image where each component value * is coded depending on the (x.y) location and the size * of the fg connected component that the pixel in pixs belongs to. * It is independent of the 4-fold orthogonal orientation, and * only weakly depends on translations and small angle rotations. * Background pixels are black. * (2) Such encodings can be compared between two 1 bpp images * by performing this transform and calculating the * "earth-mover" distance on the resulting R,G,B histograms. */ PIX * pixLocToColorTransform(PIX *pixs) { l_int32 w, h, w2, h2, wpls, wplr, wplg, wplb, wplcc, i, j, rval, gval, bval; l_float32 invw2, invh2; l_uint32 *datas, *datar, *datag, *datab, *datacc; l_uint32 *lines, *liner, *lineg, *lineb, *linecc; PIX *pix1, *pixcc, *pixr, *pixg, *pixb, *pixd; PROCNAME("pixLocToColorTransform"); if (!pixs || pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); /* Label each pixel with the area of the c.c. to which it belongs. * Clip the result to 255 in an 8 bpp pix. This is used for * the blue component of pixd. */ pixGetDimensions(pixs, &w, &h, NULL); w2 = w / 2; h2 = h / 2; invw2 = 255.0 / (l_float32)w2; invh2 = 255.0 / (l_float32)h2; pix1 = pixConnCompAreaTransform(pixs, 8); pixcc = pixConvert16To8(pix1, L_CLIP_TO_255); pixDestroy(&pix1); /* Label the red and green components depending on the location * of the fg pixels, in a way that is 4-fold rotationally invariant. */ pixr = pixCreate(w, h, 8); pixg = pixCreate(w, h, 8); pixb = pixCreate(w, h, 8); wpls = pixGetWpl(pixs); wplr = pixGetWpl(pixr); wplg = pixGetWpl(pixg); wplb = pixGetWpl(pixb); wplcc = pixGetWpl(pixcc); datas = pixGetData(pixs); datar = pixGetData(pixr); datag = pixGetData(pixg); datab = pixGetData(pixb); datacc = pixGetData(pixcc); for (i = 0; i < h; i++) { lines = datas + i * wpls; liner = datar + i * wplr; lineg = datag + i * wplg; lineb = datab + i * wplb; linecc = datacc+ i * wplcc; for (j = 0; j < w; j++) { if (GET_DATA_BIT(lines, j) == 0) continue; if (w < h) { rval = invh2 * L_ABS((l_float32)(i - h2)); gval = invw2 * L_ABS((l_float32)(j - w2)); } else { rval = invw2 * L_ABS((l_float32)(j - w2)); gval = invh2 * L_ABS((l_float32)(i - h2)); } bval = GET_DATA_BYTE(linecc, j); SET_DATA_BYTE(liner, j, rval); SET_DATA_BYTE(lineg, j, gval); SET_DATA_BYTE(lineb, j, bval); } } pixd = pixCreateRGBImage(pixr, pixg, pixb); pixDestroy(&pixcc); pixDestroy(&pixr); pixDestroy(&pixg); pixDestroy(&pixb); return pixd; }