Leptonica 1.68
C Image Processing Library

convolve.c

Go to the documentation of this file.
00001 /*====================================================================*
00002  -  Copyright (C) 2001 Leptonica.  All rights reserved.
00003  -  This software is distributed in the hope that it will be
00004  -  useful, but with NO WARRANTY OF ANY KIND.
00005  -  No author or distributor accepts responsibility to anyone for the
00006  -  consequences of using this software, or for whether it serves any
00007  -  particular purpose or works at all, unless he or she says so in
00008  -  writing.  Everyone is granted permission to copy, modify and
00009  -  redistribute this source code, for commercial or non-commercial
00010  -  purposes, with the following restrictions: (1) the origin of this
00011  -  source code must not be misrepresented; (2) modified versions must
00012  -  be plainly marked as such; and (3) this notice may not be removed
00013  -  or altered from any source or modified source distribution.
00014  *====================================================================*/
00015 
00016 /*
00017  *  convolve.c
00018  *
00019  *      Top level grayscale or color block convolution
00020  *          PIX      *pixBlockconv()
00021  *
00022  *      Grayscale block convolution
00023  *          PIX      *pixBlockconvGray()
00024  *
00025  *      Accumulator for 1, 8 and 32 bpp convolution
00026  *          PIX      *pixBlockconvAccum()
00027  *
00028  *      Un-normalized grayscale block convolution
00029  *          PIX      *pixBlockconvGrayUnnormalized()
00030  *
00031  *      Tiled grayscale or color block convolution
00032  *          PIX      *pixBlockconvTiled()
00033  *          PIX      *pixBlockconvGrayTile()
00034  *
00035  *      Convolution for mean, mean square, variance and rms deviation
00036  *      in specified window
00037  *          l_int32   pixWindowedStats()
00038  *          PIX      *pixWindowedMean()
00039  *          PIX      *pixWindowedMeanSquare()
00040  *          l_int32   pixWindowedVariance()
00041  *          DPIX     *pixMeanSquareAccum()
00042  *
00043  *      Binary block sum and rank filter
00044  *          PIX      *pixBlockrank()
00045  *          PIX      *pixBlocksum()
00046  *
00047  *      Census transform
00048  *          PIX      *pixCensusTransform()
00049  *
00050  *      Generic convolution (with Pix)
00051  *          PIX      *pixConvolve()
00052  *          PIX      *pixConvolveSep()
00053  *          PIX      *pixConvolveRGB()
00054  *          PIX      *pixConvolveRGBSep()
00055  *
00056  *      Generic convolution (with float arrays)
00057  *          FPIX     *fpixConvolve()
00058  *          FPIX     *fpixConvolveSep()
00059  *
00060  *      Set parameter for convolution subsampling
00061  *          void      l_setConvolveSampling()
00062  */
00063 
00064 #include <math.h>
00065 #include "allheaders.h"
00066 
00067     /* These globals determine the subsampling factors for
00068      * generic convolution of pix and fpix.  Declare extern to use.
00069      * To change the values, use l_setConvolveSampling(). */
00070 LEPT_DLL l_int32  ConvolveSamplingFactX = 1;
00071 LEPT_DLL l_int32  ConvolveSamplingFactY = 1;
00072 
00073 /*----------------------------------------------------------------------*
00074  *             Top-level grayscale or color block convolution           *
00075  *----------------------------------------------------------------------*/
00076 /*!
00077  *  pixBlockconv()
00078  *
00079  *      Input:  pix (8 or 32 bpp; or 2, 4 or 8 bpp with colormap)
00080  *              wc, hc   (half width/height of convolution kernel)
00081  *      Return: pixd, or null on error
00082  *
00083  *  Notes:
00084  *      (1) The full width and height of the convolution kernel
00085  *          are (2 * wc + 1) and (2 * hc + 1)
00086  *      (2) Returns a copy if both wc and hc are 0
00087  *      (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
00088  *          where (w,h) are the dimensions of pixs.
00089  */
00090 PIX  *
00091 pixBlockconv(PIX     *pix,
00092              l_int32  wc,
00093              l_int32  hc)
00094 {
00095 l_int32  w, h, d;
00096 PIX     *pixs, *pixd, *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc;
00097 
00098     PROCNAME("pixBlockconv");
00099 
00100     if (!pix)
00101         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
00102     if (wc < 0) wc = 0;
00103     if (hc < 0) hc = 0;
00104     pixGetDimensions(pix, &w, &h, &d);
00105     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
00106         wc = L_MIN(wc, (w - 1) / 2);
00107         hc = L_MIN(hc, (h - 1) / 2);
00108         L_WARNING("kernel too large; reducing!", procName);
00109         L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc);
00110     }
00111     if (wc == 0 && hc == 0)   /* no-op */
00112         return pixCopy(NULL, pix);
00113 
00114         /* Remove colormap if necessary */ 
00115     if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
00116         L_WARNING("pix has colormap; removing", procName);
00117         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
00118         d = pixGetDepth(pixs);
00119     }
00120     else
00121         pixs = pixClone(pix);
00122 
00123     if (d != 8 && d != 32) {
00124         pixDestroy(&pixs);
00125         return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL);
00126     }
00127 
00128     if (d == 8)
00129         pixd = pixBlockconvGray(pixs, NULL, wc, hc);
00130     else { /* d == 32 */
00131         pixr = pixGetRGBComponent(pixs, COLOR_RED);
00132         pixrc = pixBlockconvGray(pixr, NULL, wc, hc);
00133         pixDestroy(&pixr);
00134         pixg = pixGetRGBComponent(pixs, COLOR_GREEN);
00135         pixgc = pixBlockconvGray(pixg, NULL, wc, hc);
00136         pixDestroy(&pixg);
00137         pixb = pixGetRGBComponent(pixs, COLOR_BLUE);
00138         pixbc = pixBlockconvGray(pixb, NULL, wc, hc);
00139         pixDestroy(&pixb);
00140         pixd = pixCreateRGBImage(pixrc, pixgc, pixbc);
00141         pixDestroy(&pixrc);
00142         pixDestroy(&pixgc);
00143         pixDestroy(&pixbc);
00144     }
00145 
00146     pixDestroy(&pixs);
00147     return pixd;
00148 }
00149 
00150 
00151 /*----------------------------------------------------------------------*
00152  *                     Grayscale block convolution                      *
00153  *----------------------------------------------------------------------*/
00154 /*!
00155  *  pixBlockconvGray()
00156  *
00157  *      Input:  pix (8 bpp)
00158  *              accum pix (32 bpp; can be null)
00159  *              wc, hc   (half width/height of convolution kernel)
00160  *      Return: pix (8 bpp), or null on error
00161  *
00162  *  Notes:
00163  *      (1) If accum pix is null, make one and destroy it before
00164  *          returning; otherwise, just use the input accum pix.
00165  *      (2) The full width and height of the convolution kernel
00166  *          are (2 * wc + 1) and (2 * hc + 1).
00167  *      (3) Returns a copy if both wc and hc are 0.
00168  *      (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
00169  *          where (w,h) are the dimensions of pixs.
00170  */
00171 PIX *
00172 pixBlockconvGray(PIX     *pixs,
00173                  PIX     *pixacc,
00174                  l_int32  wc,
00175                  l_int32  hc)
00176 {
00177 l_int32    w, h, d, wpl, wpla;
00178 l_uint32  *datad, *dataa;
00179 PIX       *pixd, *pixt;
00180 
00181     PROCNAME("pixBlockconvGray");
00182 
00183     if (!pixs)
00184         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00185     pixGetDimensions(pixs, &w, &h, &d);
00186     if (d != 8)
00187         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
00188     if (wc < 0) wc = 0;
00189     if (hc < 0) hc = 0;
00190     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
00191         wc = L_MIN(wc, (w - 1) / 2);
00192         hc = L_MIN(hc, (h - 1) / 2);
00193         L_WARNING("kernel too large; reducing!", procName);
00194         L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc);
00195     }
00196     if (wc == 0 && hc == 0)   /* no-op */
00197         return pixCopy(NULL, pixs);
00198 
00199     if (pixacc) {
00200         if (pixGetDepth(pixacc) == 32)
00201             pixt = pixClone(pixacc);
00202         else {
00203             L_WARNING("pixacc not 32 bpp; making new one", procName);
00204             if ((pixt = pixBlockconvAccum(pixs)) == NULL)
00205                 return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
00206         }
00207     }
00208     else {
00209         if ((pixt = pixBlockconvAccum(pixs)) == NULL)
00210             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
00211     }
00212         
00213     if ((pixd = pixCreateTemplate(pixs)) == NULL) {
00214         pixDestroy(&pixt);
00215         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00216     }
00217     
00218     wpl = pixGetWpl(pixs);
00219     wpla = pixGetWpl(pixt);
00220     datad = pixGetData(pixd);
00221     dataa = pixGetData(pixt);
00222     blockconvLow(datad, w, h, wpl, dataa, wpla, wc, hc);
00223 
00224     pixDestroy(&pixt);
00225     return pixd;
00226 }
00227 
00228 
00229 /*----------------------------------------------------------------------*
00230  *              Accumulator for 1, 8 and 32 bpp convolution             *
00231  *----------------------------------------------------------------------*/
00232 /*!
00233  *  pixBlockconvAccum()
00234  *
00235  *      Input:  pixs (1, 8 or 32 bpp)
00236  *      Return: accum pix (32 bpp), or null on error.
00237  *
00238  *  Notes:
00239  *      (1) The general recursion relation is
00240  *            a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1)
00241  *          For the first line, this reduces to the special case
00242  *            a(i,j) = v(i,j) + a(i, j-1)
00243  *          For the first column, the special case is
00244  *            a(i,j) = v(i,j) + a(i-1, j)
00245  */
00246 PIX *
00247 pixBlockconvAccum(PIX  *pixs)
00248 {
00249 l_int32    w, h, d, wpls, wpld;
00250 l_uint32  *datas, *datad;
00251 PIX       *pixd;
00252 
00253     PROCNAME("pixBlockconvAccum");
00254 
00255     if (!pixs)
00256         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00257 
00258     pixGetDimensions(pixs, &w, &h, &d);
00259     if (d != 1 && d != 8 && d != 32)
00260         return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL);
00261     if ((pixd = pixCreate(w, h, 32)) == NULL)
00262         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00263 
00264     datas = pixGetData(pixs);
00265     datad = pixGetData(pixd);
00266     wpls = pixGetWpl(pixs);
00267     wpld = pixGetWpl(pixd);
00268     blockconvAccumLow(datad, w, h, wpld, datas, d, wpls);
00269 
00270     return pixd;
00271 }
00272 
00273 
00274 /*----------------------------------------------------------------------*
00275  *               Un-normalized grayscale block convolution              *
00276  *----------------------------------------------------------------------*/
00277 /*!
00278  *  pixBlockconvGrayUnnormalized()
00279  *
00280  *      Input:  pixs (8 bpp)
00281  *              wc, hc   (half width/height of convolution kernel)
00282  *      Return: pix (32 bpp; containing the convolution without normalizing
00283  *                   for the window size), or null on error
00284  *
00285  *  Notes:
00286  *      (1) The full width and height of the convolution kernel
00287  *          are (2 * wc + 1) and (2 * hc + 1).
00288  *      (2) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
00289  *          where (w,h) are the dimensions of pixs.
00290  *      (3) Returns a copy if both wc and hc are 0.
00291  *      (3) Adds mirrored border to avoid treating the boundary pixels
00292  *          specially.  Note that we add wc + 1 pixels to the left
00293  *          and wc to the right.  The added width is 2 * wc + 1 pixels,
00294  *          and the particular choice simplifies the indexing in the loop.
00295  *          Likewise, add hc + 1 pixels to the top and hc to the bottom.
00296  *      (4) To get the normalized result, divide by the area of the
00297  *          convolution kernel: (2 * wc + 1) * (2 * hc + 1)
00298  *          Specifically, do this:
00299  *               pixc = pixBlockconvGrayUnnormalized(pixs, wc, hc);
00300  *               fract = 1. / ((2 * wc + 1) * (2 * hc + 1));
00301  *               pixMultConstantGray(pixc, fract);
00302  *               pixd = pixGetRGBComponent(pixc, L_ALPHA_CHANNEL);
00303  *      (5) Unlike pixBlockconvGray(), this always computes the accumulation
00304  *          pix because its size is tied to wc and hc.
00305  *      (6) Compare this implementation with pixBlockconvGray(), where
00306  *          most of the code in blockconvLow() is special casing for
00307  *          efficiently handling the boundary.  Here, the use of
00308  *          mirrored borders and destination indexing makes the
00309  *          implementation very simple.
00310  */
00311 PIX *
00312 pixBlockconvGrayUnnormalized(PIX     *pixs,
00313                              l_int32  wc,
00314                              l_int32  hc)
00315 {
00316 l_int32    i, j, w, h, d, wpla, wpld, jmax;
00317 l_uint32  *linemina, *linemaxa, *lined, *dataa, *datad;
00318 PIX       *pixsb, *pixacc, *pixd;
00319 
00320     PROCNAME("pixBlockconvGrayUnnormalized");
00321 
00322     if (!pixs)
00323         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00324     pixGetDimensions(pixs, &w, &h, &d);
00325     if (d != 8)
00326         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
00327     if (wc < 0) wc = 0;
00328     if (hc < 0) hc = 0;
00329     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
00330         wc = L_MIN(wc, (w - 1) / 2);
00331         hc = L_MIN(hc, (h - 1) / 2);
00332         L_WARNING("kernel too large; reducing!", procName);
00333         L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc);
00334     }
00335     if (wc == 0 && hc == 0)   /* no-op */
00336         return pixCopy(NULL, pixs);
00337 
00338     if ((pixsb = pixAddMirroredBorder(pixs, wc + 1, wc, hc + 1, hc)) == NULL)
00339         return (PIX *)ERROR_PTR("pixsb not made", procName, NULL);
00340     pixacc = pixBlockconvAccum(pixsb);
00341     pixDestroy(&pixsb);
00342     if (!pixacc)
00343         return (PIX *)ERROR_PTR("pixacc not made", procName, NULL);
00344     if ((pixd = pixCreate(w, h, 32)) == NULL) {
00345         pixDestroy(&pixacc);
00346         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00347     }
00348     
00349     wpla = pixGetWpl(pixacc);
00350     wpld = pixGetWpl(pixd);
00351     datad = pixGetData(pixd);
00352     dataa = pixGetData(pixacc);
00353     for (i = 0; i < h; i++) {
00354         lined = datad + i * wpld;
00355         linemina = dataa + i * wpla;
00356         linemaxa = dataa + (i + 2 * hc + 1) * wpla;
00357         for (j = 0; j < w; j++) {
00358             jmax = j + 2 * wc + 1;
00359             lined[j] = linemaxa[jmax] - linemaxa[j] -
00360                        linemina[jmax] + linemina[j];
00361         }
00362     }
00363 
00364     pixDestroy(&pixacc);
00365     return pixd;
00366 }
00367 
00368 
00369 /*----------------------------------------------------------------------*
00370  *               Tiled grayscale or color block convolution             *
00371  *----------------------------------------------------------------------*/
00372 /*!
00373  *  pixBlockconvTiled()
00374  *
00375  *      Input:  pix (8 or 32 bpp; or 2, 4 or 8 bpp with colormap)
00376  *              wc, hc   (half width/height of convolution kernel)
00377  *              nx, ny  (subdivision into tiles)
00378  *      Return: pixd, or null on error
00379  *
00380  *  Notes:
00381  *      (1) The full width and height of the convolution kernel
00382  *          are (2 * wc + 1) and (2 * hc + 1)
00383  *      (2) Returns a copy if both wc and hc are 0
00384  *      (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
00385  *          where (w,h) are the dimensions of pixs.
00386  *      (4) For nx == ny == 1, this defaults to pixBlockconv(), which
00387  *          is typically about twice as fast, and gives nearly
00388  *          identical results as pixBlockconvGrayTile().
00389  *      (5) If the tiles are too small, nx and/or ny are reduced
00390  *          a minimum amount so that the tiles are expanded to the
00391  *          smallest workable size in the problematic direction(s).
00392  *      (6) Why a tiled version?  Three reasons:
00393  *          (a) Because the accumulator is a uint32, overflow can occur
00394  *              for an image with more than 16M pixels.
00395  *          (b) The accumulator array for 16M pixels is 64 MB; using
00396  *              tiles reduces the size of this array.
00397  *          (c) Each tile can be processed independently, in parallel,
00398  *              on a multicore processor.
00399  */
00400 PIX *
00401 pixBlockconvTiled(PIX     *pix,
00402                   l_int32  wc,
00403                   l_int32  hc,
00404                   l_int32  nx,
00405                   l_int32  ny)
00406 {
00407 l_int32     i, j, w, h, d, xrat, yrat;
00408 PIX        *pixs, *pixd, *pixc, *pixt;
00409 PIX        *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc;
00410 PIXTILING  *pt;
00411 
00412     PROCNAME("pixBlockconvTiled");
00413 
00414     if (!pix)
00415         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
00416     if (wc < 0) wc = 0;
00417     if (hc < 0) hc = 0;
00418     pixGetDimensions(pix, &w, &h, &d);
00419     if (w < 2 * wc + 3 || h < 2 * hc + 3) {
00420         wc = L_MAX(0, L_MIN(wc, (w - 3) / 2));
00421         hc = L_MAX(0, L_MIN(hc, (h - 3) / 2));
00422         L_WARNING("kernel too large; reducing!", procName);
00423         L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc);
00424     }
00425     if (wc == 0 && hc == 0)   /* no-op */
00426         return pixCopy(NULL, pix);
00427     if (nx <= 1 && ny <= 1)
00428         return pixBlockconv(pix, wc, hc);
00429 
00430         /* Test to see if the tiles are too small.  The required
00431          * condition is that the tile dimensions must be at least
00432          * (wc + 2) x (hc + 2). */
00433     xrat = w / nx;
00434     yrat = h / ny;
00435     if (xrat < wc + 2) {
00436         nx = w / (wc + 2);
00437         L_WARNING_INT("tile width too small; nx reduced to %d", procName, nx);
00438     }
00439     if (yrat < hc + 2) {
00440         ny = h / (hc + 2);
00441         L_WARNING_INT("tile height too small; ny reduced to %d", procName, ny);
00442     }
00443  
00444         /* Remove colormap if necessary */ 
00445     if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
00446         L_WARNING("pix has colormap; removing", procName);
00447         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
00448         d = pixGetDepth(pixs);
00449     }
00450     else
00451         pixs = pixClone(pix);
00452 
00453     if (d != 8 && d != 32) {
00454         pixDestroy(&pixs);
00455         return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL);
00456     }
00457 
00458        /* Note that the overlaps in the width and height that
00459         * are added to the tile are (wc + 2) and (hc + 2).
00460         * These overlaps are removed by pixTilingPaintTile().
00461         * They are larger than the extent of the filter because
00462         * although the filter is symmetric with respect to its origin,
00463         * the implementation is asymmetric -- see the implementation in
00464         * pixBlockconvGrayTile(). */
00465     if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) {
00466         pixDestroy(&pixs);
00467         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00468     }
00469     pt = pixTilingCreate(pixs, nx, ny, 0, 0, wc + 2, hc + 2);
00470     for (i = 0; i < ny; i++) {
00471         for (j = 0; j < nx; j++) {
00472             pixt = pixTilingGetTile(pt, i, j);
00473 
00474                 /* Convolve over the tile */
00475             if (d == 8)
00476                 pixc = pixBlockconvGrayTile(pixt, NULL, wc, hc);
00477             else { /* d == 32 */
00478                 pixr = pixGetRGBComponent(pixt, COLOR_RED);
00479                 pixrc = pixBlockconvGrayTile(pixr, NULL, wc, hc);
00480                 pixDestroy(&pixr);
00481                 pixg = pixGetRGBComponent(pixt, COLOR_GREEN);
00482                 pixgc = pixBlockconvGrayTile(pixg, NULL, wc, hc);
00483                 pixDestroy(&pixg);
00484                 pixb = pixGetRGBComponent(pixt, COLOR_BLUE);
00485                 pixbc = pixBlockconvGrayTile(pixb, NULL, wc, hc);
00486                 pixDestroy(&pixb);
00487                 pixc = pixCreateRGBImage(pixrc, pixgc, pixbc);
00488                 pixDestroy(&pixrc);
00489                 pixDestroy(&pixgc);
00490                 pixDestroy(&pixbc);
00491             }
00492 
00493             pixTilingPaintTile(pixd, i, j, pixc, pt);
00494             pixDestroy(&pixt);
00495             pixDestroy(&pixc);
00496         }
00497     }
00498 
00499     pixDestroy(&pixs);
00500     pixTilingDestroy(&pt);
00501     return pixd;
00502 }
00503 
00504 
00505 /*!
00506  *  pixBlockconvGrayTile()
00507  *
00508  *      Input:  pixs (8 bpp gray)
00509  *              pixacc (32 bpp accum pix)
00510  *              wc, hc   (half width/height of convolution kernel)
00511  *      Return: pixd, or null on error
00512  *
00513  *  Notes:
00514  *      (1) The full width and height of the convolution kernel
00515  *          are (2 * wc + 1) and (2 * hc + 1)
00516  *      (2) Assumes that the input pixs is padded with (wc + 1) pixels on
00517  *          left and right, and with (hc + 1) pixels on top and bottom.
00518  *          The returned pix has these stripped off; they are only used
00519  *          for computation.
00520  *      (3) Returns a copy if both wc and hc are 0
00521  *      (4) Require that w > 2 * wc + 1 and h > 2 * hc + 1,
00522  *          where (w,h) are the dimensions of pixs.
00523  */
00524 PIX *
00525 pixBlockconvGrayTile(PIX     *pixs,
00526                      PIX     *pixacc,
00527                      l_int32  wc,
00528                      l_int32  hc)
00529 {
00530 l_int32    w, h, d, wd, hd, i, j, imin, imax, jmin, jmax, wplt, wpld;
00531 l_float32  norm;
00532 l_uint32   val;
00533 l_uint32  *datat, *datad, *lined, *linemint, *linemaxt;
00534 PIX       *pixt, *pixd;
00535 
00536     PROCNAME("pixBlockconvGrayTile");
00537 
00538     if (!pixs)
00539         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
00540     pixGetDimensions(pixs, &w, &h, &d);
00541     if (d != 8)
00542         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
00543     if (wc < 0) wc = 0;
00544     if (hc < 0) hc = 0;
00545     if (w < 2 * wc + 3 || h < 2 * hc + 3) {
00546         wc = L_MAX(0, L_MIN(wc, (w - 3) / 2));
00547         hc = L_MAX(0, L_MIN(hc, (h - 3) / 2));
00548         L_WARNING("kernel too large; reducing!", procName);
00549         L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc);
00550     }
00551     if (wc == 0 && hc == 0)
00552         return pixCopy(NULL, pixs);
00553     wd = w - 2 * wc;
00554     hd = h - 2 * hc;
00555 
00556     if (pixacc) {
00557         if (pixGetDepth(pixacc) == 32)
00558             pixt = pixClone(pixacc);
00559         else {
00560             L_WARNING("pixacc not 32 bpp; making new one", procName);
00561             if ((pixt = pixBlockconvAccum(pixs)) == NULL)
00562                 return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
00563         }
00564     }
00565     else {
00566         if ((pixt = pixBlockconvAccum(pixs)) == NULL)
00567             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
00568     }
00569         
00570     if ((pixd = pixCreateTemplate(pixs)) == NULL) {
00571         pixDestroy(&pixt);
00572         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00573     }
00574     datat = pixGetData(pixt);
00575     wplt = pixGetWpl(pixt);
00576     datad = pixGetData(pixd);
00577     wpld = pixGetWpl(pixd);
00578     norm = 1. / (l_float32)((2 * wc + 1) * (2 * hc + 1));
00579 
00580         /* Do the convolution over the subregion of size (wd - 2, hd - 2),
00581          * which exactly corresponds to the size of the subregion that
00582          * will be extracted by pixTilingPaintTile().  Note that the
00583          * region in which points are computed is not symmetric about
00584          * the center of the images; instead the computation in
00585          * the accumulator image is shifted up and to the left by 1,
00586          * relative to the center, because the 4 accumulator sampling
00587          * points are taken at the LL corner of the filter and at 3 other
00588          * points that are shifted -wc and -hc to the left and above.  */
00589     for (i = hc; i < hc + hd - 2; i++) {
00590         imin = L_MAX(i - hc - 1, 0);
00591         imax = L_MIN(i + hc, h - 1);
00592         lined = datad + i * wpld;
00593         linemint = datat + imin * wplt;
00594         linemaxt = datat + imax * wplt;
00595         for (j = wc; j < wc + wd - 2; j++) {
00596             jmin = L_MAX(j - wc - 1, 0);
00597             jmax = L_MIN(j + wc, w - 1);
00598             val = linemaxt[jmax] - linemaxt[jmin]
00599                   + linemint[jmin] - linemint[jmax];
00600             val = (l_uint8)(norm * val + 0.5);
00601             SET_DATA_BYTE(lined, j, val);
00602         }
00603     }
00604 
00605     pixDestroy(&pixt);
00606     return pixd;
00607 }
00608 
00609 
00610 /*----------------------------------------------------------------------*
00611  *     Convolution for mean, mean square, variance and rms deviation    *
00612  *----------------------------------------------------------------------*/
00613 /*!
00614  *  pixWindowedStats()
00615  *
00616  *      Input:  pixs (8 bpp grayscale)
00617  *              wc, hc   (half width/height of convolution kernel)
00618  *              hasborder (use 1 if it already has (wc + 1) border pixels
00619  *                         on left and right, and (hc + 1) on top and bottom;
00620  *                         use 0 to add kernel-dependent border)
00621  *              &pixm (<optional return> 8 bpp mean value in window)
00622  *              &pixms (<optional return> 32 bpp mean square value in window)
00623  *              &fpixv (<optional return> float variance in window)
00624  *              &fpixrv (<optional return> float rms deviation from the mean)
00625  *      Return: 0 if OK, 1 on error
00626  *
00627  *  Notes:
00628  *      (1) This is a high-level convenience function for calculating
00629  *          any or all of these derived images.
00630  *      (2) If @hasborder = 0, a border is added and the result is
00631  *          computed over all pixels in pixs.  Otherwise, no border is
00632  *          added and the border pixels are removed from the output images.
00633  *      (3) These statistical measures over the pixels in the
00634  *          rectangular window are:
00635  *            - average value: <p>  (pixm)
00636  *            - average squared value: <p*p> (pixms)
00637  *            - variance: <(p - <p>)*(p - <p>)> = <p*p> - <p>*<p>  (pixv)
00638  *            - square-root of variance: (pixrv)
00639  *          where the brackets < .. > indicate that the average value is
00640  *          to be taken over the window.
00641  *      (4) Note that the variance is just the mean square difference from
00642  *          the mean value; and the square root of the variance is the
00643  *          root mean square difference from the mean, sometimes also
00644  *          called the 'standard deviation'.
00645  *      (5) The added border, along with the use of an accumulator array,
00646  *          allows computation without special treatment of pixels near
00647  *          the image boundary, and runs in a time that is independent
00648  *          of the size of the convolution kernel.
00649  */
00650 l_int32
00651 pixWindowedStats(PIX     *pixs,
00652                  l_int32  wc,
00653                  l_int32  hc,
00654                  l_int32  hasborder,
00655                  PIX    **ppixm,
00656                  PIX    **ppixms,
00657                  FPIX   **pfpixv,
00658                  FPIX   **pfpixrv)
00659 {
00660 PIX  *pixb, *pixm, *pixms;
00661 
00662     PROCNAME("pixWindowedStats");
00663 
00664     if (!pixs || pixGetDepth(pixs) != 8)
00665         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
00666     if (wc < 2 || hc < 2)
00667         return ERROR_INT("wc and hc not >= 2", procName, 1);
00668     if (!ppixm && !ppixms && !pfpixv && !pfpixrv)
00669         return ERROR_INT("no output requested", procName, 1);
00670     if (ppixm) *ppixm = NULL;
00671     if (ppixms) *ppixms = NULL;
00672     if (pfpixv) *pfpixv = NULL;
00673     if (pfpixrv) *pfpixrv = NULL;
00674 
00675         /* Add border if requested */
00676     if (!hasborder)
00677         pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0);
00678     else
00679         pixb = pixClone(pixs);
00680 
00681     if (!pfpixv && !pfpixrv) {
00682         if (ppixm) *ppixm = pixWindowedMean(pixb, wc, hc, 1, 1);
00683         if (ppixms) *ppixms = pixWindowedMeanSquare(pixb, wc, hc, 1);
00684         pixDestroy(&pixb);
00685         return 0;
00686     }
00687 
00688     pixm = pixWindowedMean(pixb, wc, hc, 1, 1);
00689     pixms = pixWindowedMeanSquare(pixb, wc, hc, 1);
00690     pixWindowedVariance(pixm, pixms, pfpixv, pfpixrv);
00691     if (ppixm)
00692         *ppixm = pixm;
00693     else
00694         pixDestroy(&pixm);
00695     if (ppixms)
00696         *ppixms = pixms;
00697     else
00698         pixDestroy(&pixms);
00699     pixDestroy(&pixb);
00700     return 0;
00701 }
00702 
00703 
00704 /*!
00705  *  pixWindowedMean()
00706  *
00707  *      Input:  pixs (8 or 32 bpp grayscale)
00708  *              wc, hc   (half width/height of convolution kernel)
00709  *              hasborder (use 1 if it already has (wc + 1) border pixels
00710  *                         on left and right, and (hc + 1) on top and bottom;
00711  *                         use 0 to add kernel-dependent border)
00712  *              normflag (1 for normalization to get average in window;
00713  *                        0 for the sum in the window (un-normalized))
00714  *      Return: pixd (8 or 32 bpp, average over kernel window)
00715  *
00716  *  Notes:
00717  *      (1) The input and output depths are the same.
00718  *      (2) A set of border pixels of width (wc + 1) on left and right,
00719  *          and of height (hc + 1) on top and bottom, must be on the
00720  *          pix before the accumulator is found.  The output pixd
00721  *          (after convolution) has this border removed.
00722  *          If @hasborder = 0, the required border is added.
00723  *      (3) Typically, @normflag == 1.  However, if you want the sum
00724  *          within the window, rather than a normalized convolution,
00725  *          use @normflag == 0.
00726  *      (4) This builds a block accumulator pix, uses it here, and
00727  *          destroys it.
00728  *      (5) The added border, along with the use of an accumulator array,
00729  *          allows computation without special treatment of pixels near
00730  *          the image boundary, and runs in a time that is independent
00731  *          of the size of the convolution kernel.
00732  */
00733 PIX *
00734 pixWindowedMean(PIX     *pixs,
00735                 l_int32  wc,
00736                 l_int32  hc,
00737                 l_int32  hasborder,
00738                 l_int32  normflag)
00739 {
00740 l_int32    i, j, w, h, d, wd, hd, wplc, wpld, wincr, hincr;
00741 l_uint32   val;
00742 l_uint32  *datac, *datad, *linec1, *linec2, *lined;
00743 l_float32  norm;
00744 PIX       *pixb, *pixc, *pixd;
00745 
00746     PROCNAME("pixWindowedMean");
00747     
00748     if (!pixs)
00749         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00750     d = pixGetDepth(pixs);
00751     if (d != 8 && d != 32)
00752         return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL);
00753     if (wc < 2 || hc < 2)
00754         return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL);
00755 
00756         /* Add border if requested */
00757     if (!hasborder)
00758         pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0);
00759     else
00760         pixb = pixClone(pixs);
00761 
00762         /* The output has wc + 1 border pixels stripped from each side
00763          * of pixb, and hc + 1 border pixels stripped from top and bottom. */
00764     pixGetDimensions(pixb, &w, &h, NULL);
00765     wd = w - 2 * (wc + 1);
00766     hd = h - 2 * (hc + 1);
00767     if (wd < 2 || hd < 2)
00768         return (PIX *)ERROR_PTR("w or h too small for kernel", procName, NULL);
00769     if ((pixd = pixCreate(wd, hd, d)) == NULL)
00770         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00771 
00772         /* Make the accumulator pix from pixb */
00773     if ((pixc = pixBlockconvAccum(pixb)) == NULL) {
00774         pixDestroy(&pixb);
00775         pixDestroy(&pixd);
00776         return (PIX *)ERROR_PTR("pixc not made", procName, NULL);
00777     }
00778     wplc = pixGetWpl(pixc);
00779     wpld = pixGetWpl(pixd);
00780     datad = pixGetData(pixd);
00781     datac = pixGetData(pixc);
00782 
00783     wincr = 2 * wc + 1;
00784     hincr = 2 * hc + 1;
00785     norm = 1.0;  /* use this for sum-in-window */
00786     if (normflag)
00787         norm = 1.0 / (wincr * hincr);
00788     for (i = 0; i < hd; i++) {
00789         linec1 = datac + i * wplc;
00790         linec2 = datac + (i + hincr) * wplc;
00791         lined = datad + i * wpld;
00792         for (j = 0; j < wd; j++) {
00793             val = linec2[j + wincr] - linec2[j] - linec1[j + wincr] + linec1[j];
00794             if (d == 8) {
00795                 val = (l_uint8)(norm * val);
00796                 SET_DATA_BYTE(lined, j, val);
00797             } else {  /* d == 32 */
00798                 val = (l_uint32)(norm * val);
00799                 lined[j] = val;
00800             }
00801         } 
00802     }
00803             
00804     pixDestroy(&pixc);
00805     pixDestroy(&pixb);
00806     return pixd;
00807 }
00808 
00809 
00810 /*!
00811  *  pixWindowedMeanSquare()
00812  *
00813  *      Input:  pixs (8 bpp grayscale)
00814  *              wc, hc   (half width/height of convolution kernel)
00815  *              hasborder (use 1 if it already has (wc + 1) border pixels
00816  *                         on left and right, and (hc + 1) on top and bottom;
00817  *                         use 0 to add kernel-dependent border)
00818  *      Return: pixd (32 bpp, average over rectangular window of
00819  *                    width = 2 * wc + 1 and height = 2 * hc + 1)
00820  *
00821  *  Notes:
00822  *      (1) A set of border pixels of width (wc + 1) on left and right,
00823  *          and of height (hc + 1) on top and bottom, must be on the
00824  *          pix before the accumulator is found.  The output pixd
00825  *          (after convolution) has this border removed.
00826  *          If @hasborder = 0, the required border is added.
00827  *      (2) The advantage is that we are unaffected by the boundary, and
00828  *          it is not necessary to treat pixels within @wc and @hc of the
00829  *          border differently.  This is because processing for pixd
00830  *          only takes place for pixels in pixs for which the
00831  *          kernel is entirely contained in pixs.
00832  *      (3) Why do we have an added border of width (@wc + 1) and
00833  *          height (@hc + 1), when we only need @wc and @hc pixels
00834  *          to satisfy this condition?  Answer: the accumulators
00835  *          are asymmetric, requiring an extra row and column of
00836  *          pixels at top and left to work accurately.
00837  *      (4) The added border, along with the use of an accumulator array,
00838  *          allows computation without special treatment of pixels near
00839  *          the image boundary, and runs in a time that is independent
00840  *          of the size of the convolution kernel.
00841  */
00842 PIX *
00843 pixWindowedMeanSquare(PIX     *pixs,
00844                       l_int32  wc,
00845                       l_int32  hc,
00846                       l_int32  hasborder)
00847 {
00848 l_int32     i, j, w, h, wd, hd, wpl, wpld, wincr, hincr;
00849 l_uint32    ival;
00850 l_uint32   *datad, *lined;
00851 l_float64   norm;
00852 l_float64   val;
00853 l_float64  *data, *line1, *line2;
00854 DPIX       *dpix;
00855 PIX        *pixb, *pixd;
00856 
00857     PROCNAME("pixWindowedMeanSquare");
00858     
00859     if (!pixs || (pixGetDepth(pixs) != 8))
00860         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
00861     if (wc < 2 || hc < 2)
00862         return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL);
00863 
00864         /* Add border if requested */
00865     if (!hasborder)
00866         pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0);
00867     else
00868         pixb = pixClone(pixs);
00869 
00870     if ((dpix = pixMeanSquareAccum(pixb)) == NULL)
00871         return (PIX *)ERROR_PTR("dpix not made", procName, NULL);
00872     wpl = dpixGetWpl(dpix);
00873     data = dpixGetData(dpix);
00874 
00875         /* The output has wc + 1 border pixels stripped from each side
00876          * of pixb, and hc + 1 border pixels stripped from top and bottom. */
00877     pixGetDimensions(pixb, &w, &h, NULL);
00878     wd = w - 2 * (wc + 1);
00879     hd = h - 2 * (hc + 1);
00880     if (wd < 2 || hd < 2)
00881         return (PIX *)ERROR_PTR("w or h too small for kernel", procName, NULL);
00882     if ((pixd = pixCreate(wd, hd, 32)) == NULL) {
00883         dpixDestroy(&dpix);
00884         pixDestroy(&pixb);
00885         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00886     }
00887     wpld = pixGetWpl(pixd);
00888     datad = pixGetData(pixd);
00889 
00890     wincr = 2 * wc + 1;
00891     hincr = 2 * hc + 1;
00892     norm = 1.0 / (wincr * hincr);
00893     for (i = 0; i < hd; i++) {
00894         line1 = data + i * wpl;
00895         line2 = data + (i + hincr) * wpl;
00896         lined = datad + i * wpld;
00897         for (j = 0; j < wd; j++) {
00898             val = line2[j + wincr] - line2[j] - line1[j + wincr] + line1[j];
00899             ival = (l_uint32)(norm * val);
00900             lined[j] = ival;
00901         } 
00902     }
00903             
00904     dpixDestroy(&dpix);
00905     pixDestroy(&pixb);
00906     return pixd;
00907 }
00908 
00909 
00910 /*!
00911  *  pixWindowedVariance()
00912  *
00913  *      Input:  pixm (mean over window; 8 or 32 bpp grayscale)
00914  *              pixms (mean square over window; 32 bpp)
00915  *              &fpixv (<optional return> float variance -- the ms deviation
00916  *                      from the mean)
00917  *              &fpixrv (<optional return> float rms deviation from the mean)
00918  *      Return: 0 if OK, 1 on error
00919  *
00920  *  Notes:
00921  *      (1) The mean and mean square values are precomputed, using
00922  *          pixWindowedMean() and pixWindowedMeanSquare().
00923  *      (2) Either or both of the variance and square-root of variance
00924  *          are returned as an fpix, where the variance is the
00925  *          average over the window of the mean square difference of
00926  *          the pixel value from the mean:
00927  *                <(p - <p>)*(p - <p>)> = <p*p> - <p>*<p>
00928  *      (3) To visualize the results:
00929  *            - for both, use fpixDisplayMaxDynamicRange().
00930  *            - for rms deviation, simply convert the output fpix to pix,
00931  */
00932 l_int32
00933 pixWindowedVariance(PIX    *pixm,
00934                     PIX    *pixms,
00935                     FPIX  **pfpixv,
00936                     FPIX  **pfpixrv)
00937 {
00938 l_int32     i, j, w, h, ws, hs, ds, wplm, wplms, wplv, wplrv, valm, valms;
00939 l_float32   var;
00940 l_uint32   *linem, *linems, *datam, *datams;
00941 l_float32  *linev, *linerv, *datav, *datarv;
00942 FPIX       *fpixv, *fpixrv;  /* variance and square root of variance */
00943 
00944     PROCNAME("pixWindowedVariance");
00945 
00946     if (!pfpixv && !pfpixrv)
00947         return ERROR_INT("&fpixv and &fpixrv not defined", procName, 1);
00948     if (pfpixv) *pfpixv = NULL;
00949     if (pfpixrv) *pfpixrv = NULL;
00950     if (!pixm || pixGetDepth(pixm) != 8)
00951         return ERROR_INT("pixm undefined or not 8 bpp", procName, 1);
00952     if (!pixms || pixGetDepth(pixms) != 32)
00953         return ERROR_INT("pixms undefined or not 32 bpp", procName, 1);
00954     pixGetDimensions(pixm, &w, &h, NULL);
00955     pixGetDimensions(pixms, &ws, &hs, &ds);
00956     if (w != ws || h != hs)
00957         return ERROR_INT("pixm and pixms sizes differ", procName, 1);
00958 
00959     if (pfpixv) {
00960         fpixv = fpixCreate(w, h);
00961         *pfpixv = fpixv;
00962         wplv = fpixGetWpl(fpixv);
00963         datav = fpixGetData(fpixv);
00964     }
00965     if (pfpixrv) {
00966         fpixrv = fpixCreate(w, h);
00967         *pfpixrv = fpixrv;
00968         wplrv = fpixGetWpl(fpixrv);
00969         datarv = fpixGetData(fpixrv);
00970     }
00971 
00972     wplm = pixGetWpl(pixm);
00973     wplms = pixGetWpl(pixms);
00974     datam = pixGetData(pixm);
00975     datams = pixGetData(pixms);
00976     for (i = 0; i < h; i++) {
00977         linem = datam + i * wplm;
00978         linems = datams + i * wplms;
00979         if (pfpixv)
00980             linev = datav + i * wplv;
00981         if (pfpixrv)
00982             linerv = datarv + i * wplrv;
00983         for (j = 0; j < w; j++) {
00984             valm = GET_DATA_BYTE(linem, j);
00985             if (ds == 8)
00986                 valms = GET_DATA_BYTE(linems, j);
00987             else  /* ds == 32 */
00988                 valms = (l_int32)linems[j];
00989             var = (l_float32)valms - (l_float32)valm * valm;
00990             if (pfpixv)
00991                 linev[j] = var;
00992             if (pfpixrv)
00993                 linerv[j] = (l_float32)sqrt(var);
00994         }
00995     }
00996 
00997     return 0;
00998 }
00999 
01000 
01001 /*!
01002  *  pixMeanSquareAccum()
01003  *
01004  *      Input:  pixs (8 bpp grayscale)
01005  *      Return: dpix (64 bit array), or null on error
01006  *
01007  *  Notes:
01008  *      (1) Similar to pixBlockconvAccum(), this computes the
01009  *          sum of the squares of the pixel values in such a way
01010  *          that the value at (i,j) is the sum of all squares in
01011  *          the rectangle from the origin to (i,j).
01012  *      (2) The general recursion relation (v are squared pixel values) is
01013  *            a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1)
01014  *          For the first line, this reduces to the special case
01015  *            a(i,j) = v(i,j) + a(i, j-1)
01016  *          For the first column, the special case is
01017  *            a(i,j) = v(i,j) + a(i-1, j)
01018  */
01019 DPIX *
01020 pixMeanSquareAccum(PIX  *pixs)
01021 {
01022 l_int32     i, j, w, h, wpl, wpls, val;
01023 l_uint32   *datas, *lines;
01024 l_float64  *data, *line, *linep;
01025 DPIX       *dpix;
01026 
01027     PROCNAME("pixMeanSquareAccum");
01028 
01029     
01030     if (!pixs || (pixGetDepth(pixs) != 8))
01031         return (DPIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
01032     pixGetDimensions(pixs, &w, &h, NULL);
01033     if ((dpix = dpixCreate(w, h)) ==  NULL)
01034         return (DPIX *)ERROR_PTR("dpix not made", procName, NULL);
01035 
01036     datas = pixGetData(pixs);
01037     wpls = pixGetWpl(pixs);
01038     data = dpixGetData(dpix);
01039     wpl = dpixGetWpl(dpix);
01040 
01041     lines = datas;
01042     line = data;
01043     for (j = 0; j < w; j++) {   /* first line */
01044         val = GET_DATA_BYTE(lines, j);
01045         if (j == 0)
01046             line[0] = val * val;
01047         else
01048             line[j] = line[j - 1] + val * val;
01049     }
01050 
01051         /* Do the other lines */
01052     for (i = 1; i < h; i++) {
01053         lines = datas + i * wpls;
01054         line = data + i * wpl;  /* current dest line */
01055         linep = line - wpl;;  /* prev dest line */
01056         for (j = 0; j < w; j++) {
01057             val = GET_DATA_BYTE(lines, j);
01058             if (j == 0)
01059                 line[0] = linep[0] + val * val;
01060             else
01061                 line[j] = line[j - 1] + linep[j] - linep[j - 1] + val * val;
01062         }
01063     }
01064 
01065     return dpix;
01066 }
01067 
01068 
01069 /*----------------------------------------------------------------------*
01070  *                        Binary block sum/rank                         *
01071  *----------------------------------------------------------------------*/
01072 /*!
01073  *  pixBlockrank()
01074  *
01075  *      Input:  pixs (1 bpp)
01076  *              accum pix (<optional> 32 bpp)
01077  *              wc, hc   (half width/height of block sum/rank kernel)
01078  *              rank   (between 0.0 and 1.0; 0.5 is median filter)
01079  *      Return: pixd (1 bpp)
01080  *
01081  *  Notes:
01082  *      (1) The full width and height of the convolution kernel
01083  *          are (2 * wc + 1) and (2 * hc + 1)
01084  *      (2) This returns a pixd where each pixel is a 1 if the
01085  *          neighborhood (2 * wc + 1) x (2 * hc + 1)) pixels
01086  *          contains the rank fraction of 1 pixels.  Otherwise,
01087  *          the returned pixel is 0.  Note that the special case
01088  *          of rank = 0.0 is always satisfied, so the returned
01089  *          pixd has all pixels with value 1.
01090  *      (3) If accum pix is null, make one, use it, and destroy it
01091  *          before returning; otherwise, just use the input accum pix
01092  *      (4) If both wc and hc are 0, returns a copy unless rank == 0.0,
01093  *          in which case this returns an all-ones image.
01094  *      (5) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
01095  *          where (w,h) are the dimensions of pixs.
01096  */
01097 PIX *
01098 pixBlockrank(PIX       *pixs,
01099              PIX       *pixacc,
01100              l_int32    wc,
01101              l_int32    hc,
01102              l_float32  rank)
01103 {
01104 l_int32  w, h, d, thresh;
01105 PIX     *pixt, *pixd;
01106 
01107     PROCNAME("pixBlockrank");
01108 
01109     if (!pixs)
01110         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01111     pixGetDimensions(pixs, &w, &h, &d);
01112     if (d != 1)
01113         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
01114     if (rank < 0.0 || rank > 1.0)
01115         return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL);
01116 
01117     if (rank == 0.0) {
01118         pixd = pixCreateTemplate(pixs);
01119         pixSetAll(pixd);
01120         return pixd;
01121     }
01122 
01123     if (wc < 0) wc = 0;
01124     if (hc < 0) hc = 0;
01125     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
01126         wc = L_MIN(wc, (w - 1) / 2);
01127         hc = L_MIN(hc, (h - 1) / 2);
01128         L_WARNING("kernel too large; reducing!", procName);
01129         L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc);
01130     }
01131     if (wc == 0 && hc == 0)
01132         return pixCopy(NULL, pixs);
01133 
01134     if ((pixt = pixBlocksum(pixs, pixacc, wc, hc)) == NULL)
01135         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
01136 
01137         /* 1 bpp block rank filter output.
01138          * Must invert because threshold gives 1 for values < thresh,
01139          * but we need a 1 if the value is >= thresh. */
01140     thresh = (l_int32)(255. * rank);
01141     pixd = pixThresholdToBinary(pixt, thresh);
01142     pixInvert(pixd, pixd);
01143     pixDestroy(&pixt);
01144     return pixd;
01145 }
01146 
01147 
01148 /*!
01149  *  pixBlocksum()
01150  *
01151  *      Input:  pixs (1 bpp)
01152  *              accum pix (<optional> 32 bpp)
01153  *              wc, hc   (half width/height of block sum/rank kernel)
01154  *      Return: pixd (8 bpp)
01155  *
01156  *  Notes:
01157  *      (1) If accum pix is null, make one and destroy it before
01158  *          returning; otherwise, just use the input accum pix
01159  *      (2) The full width and height of the convolution kernel
01160  *          are (2 * wc + 1) and (2 * hc + 1)
01161  *      (3) Use of wc = hc = 1, followed by pixInvert() on the
01162  *          8 bpp result, gives a nice anti-aliased, and somewhat
01163  *          darkened, result on text.
01164  *      (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
01165  *          where (w,h) are the dimensions of pixs.
01166  *      (5) Returns in each dest pixel the sum of all src pixels
01167  *          that are within a block of size of the kernel, centered
01168  *          on the dest pixel.  This sum is the number of src ON
01169  *          pixels in the block at each location, normalized to 255
01170  *          for a block containing all ON pixels.  For pixels near
01171  *          the boundary, where the block is not entirely contained
01172  *          within the image, we then multiply by a second normalization
01173  *          factor that is greater than one, so that all results
01174  *          are normalized by the number of participating pixels
01175  *          within the block.
01176  */
01177 PIX *
01178 pixBlocksum(PIX     *pixs,
01179             PIX     *pixacc,
01180             l_int32  wc,
01181             l_int32  hc)
01182 {
01183 l_int32    w, h, d, wplt, wpld;
01184 l_uint32  *datat, *datad;
01185 PIX       *pixt, *pixd;
01186 
01187     PROCNAME("pixBlocksum");
01188 
01189     if (!pixs)
01190         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01191     pixGetDimensions(pixs, &w, &h, &d);
01192     if (d != 1)
01193         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
01194     if (wc < 0) wc = 0;
01195     if (hc < 0) hc = 0;
01196     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
01197         wc = L_MIN(wc, (w - 1) / 2);
01198         hc = L_MIN(hc, (h - 1) / 2);
01199         L_WARNING("kernel too large; reducing!", procName);
01200         L_INFO_INT2("wc = %d, hc = %d", procName, wc, hc);
01201     }
01202     if (wc == 0 && hc == 0)
01203         return pixCopy(NULL, pixs);
01204 
01205     if (pixacc) {
01206         if (pixGetDepth(pixacc) != 32)
01207             return (PIX *)ERROR_PTR("pixacc not 32 bpp", procName, NULL);
01208         pixt = pixClone(pixacc);
01209     }
01210     else {
01211         if ((pixt = pixBlockconvAccum(pixs)) == NULL)
01212             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
01213     }
01214         
01215         /* 8 bpp block sum output */
01216     if ((pixd = pixCreate(w, h, 8)) == NULL) {
01217         pixDestroy(&pixt);
01218         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01219     }
01220     pixCopyResolution(pixd, pixs);
01221 
01222     wpld = pixGetWpl(pixd);
01223     wplt = pixGetWpl(pixt);
01224     datad = pixGetData(pixd);
01225     datat = pixGetData(pixt);
01226     blocksumLow(datad, w, h, wpld, datat, wplt, wc, hc);
01227 
01228     pixDestroy(&pixt);
01229     return pixd;
01230 }
01231 
01232 
01233 /*----------------------------------------------------------------------*
01234  *                          Census transform                            *
01235  *----------------------------------------------------------------------*/
01236 /*!
01237  *  pixCensusTransform()
01238  *
01239  *      Input:  pixs (8 bpp)
01240  *              halfsize (of square over which neighbors are averaged)
01241  *              accum pix (<optional> 32 bpp)
01242  *      Return: pixd (1 bpp)
01243  *
01244  *  Notes:
01245  *      (1) The Census transform was invented by Ramin Zabih and John Woodfill
01246  *          ("Non-parametric local transforms for computing visual
01247  *          correspondence", Third European Conference on Computer Vision,
01248  *          Stockholm, Sweden, May 1994); see publications at
01249  *             http://www.cs.cornell.edu/~rdz/index.htm
01250  *          This compares each pixel against the average of its neighbors,
01251  *          in a square of odd dimension centered on the pixel.
01252  *          If the pixel is greater than the average of its neighbors,
01253  *          the output pixel value is 1; otherwise it is 0.
01254  *      (2) This can be used as an encoding for an image that is
01255  *          fairly robust against slow illumination changes, with
01256  *          applications in image comparison and mosaicing.
01257  *      (3) The size of the convolution kernel is (2 * halfsize + 1)
01258  *          on a side.  The halfsize parameter must be >= 1.
01259  *      (4) If accum pix is null, make one, use it, and destroy it
01260  *          before returning; otherwise, just use the input accum pix
01261  */
01262 PIX *
01263 pixCensusTransform(PIX     *pixs,
01264                    l_int32  halfsize,
01265                    PIX     *pixacc)
01266 {
01267 l_int32    i, j, w, h, wpls, wplv, wpld;
01268 l_int32    vals, valv;
01269 l_uint32  *datas, *datav, *datad, *lines, *linev, *lined;
01270 PIX       *pixav, *pixd;
01271 
01272     PROCNAME("pixCensusTransform");
01273 
01274     if (!pixs)
01275         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01276     if (pixGetDepth(pixs) != 8)
01277         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
01278     if (halfsize < 1)
01279         return (PIX *)ERROR_PTR("halfsize must be >= 1", procName, NULL);
01280 
01281         /* Get the average of each pixel with its neighbors */
01282     if ((pixav = pixBlockconvGray(pixs, pixacc, halfsize, halfsize))
01283           == NULL)
01284         return (PIX *)ERROR_PTR("pixav not made", procName, NULL);
01285 
01286         /* Subtract the pixel from the average, and then compare
01287          * the pixel value with the remaining average */
01288     pixGetDimensions(pixs, &w, &h, NULL);
01289     if ((pixd = pixCreate(w, h, 1)) == NULL) {
01290         pixDestroy(&pixav);
01291         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01292     }
01293     datas = pixGetData(pixs);
01294     datav = pixGetData(pixav);
01295     datad = pixGetData(pixd);
01296     wpls = pixGetWpl(pixs);
01297     wplv = pixGetWpl(pixav);
01298     wpld = pixGetWpl(pixd);
01299     for (i = 0; i < h; i++) {
01300         lines = datas + i * wpls;
01301         linev = datav + i * wplv;
01302         lined = datad + i * wpld;
01303         for (j = 0; j < w; j++) {
01304             vals = GET_DATA_BYTE(lines, j);
01305             valv = GET_DATA_BYTE(linev, j);
01306             if (vals > valv)
01307                 SET_DATA_BIT(lined, j);
01308         }
01309     }
01310 
01311     pixDestroy(&pixav);
01312     return pixd;
01313 }
01314         
01315 
01316 /*----------------------------------------------------------------------*
01317  *                         Generic convolution                          *
01318  *----------------------------------------------------------------------*/
01319 /*!
01320  *  pixConvolve()
01321  *
01322  *      Input:  pixs (8, 16, 32 bpp; no colormap)
01323  *              kernel
01324  *              outdepth (of pixd: 8, 16 or 32)
01325  *              normflag (1 to normalize kernel to unit sum; 0 otherwise)
01326  *      Return: pixd (8, 16 or 32 bpp)
01327  *
01328  *  Notes:
01329  *      (1) This gives a convolution with an arbitrary kernel.
01330  *      (2) The input pixs must have only one sample/pixel.
01331  *          To do a convolution on an RGB image, use pixConvolveRGB().
01332  *      (3) The parameter @outdepth determines the depth of the result.
01333  *          If the kernel is normalized to unit sum, the output values
01334  *          can never exceed 255, so an output depth of 8 bpp is sufficient.
01335  *          If the kernel is not normalized, it may be necessary to use
01336  *          16 or 32 bpp output to avoid overflow.
01337  *      (4) If normflag == 1, the result is normalized by scaling
01338  *          all kernel values for a unit sum.  Do not normalize if
01339  *          the kernel has null sum, such as a DoG.
01340  *      (5) The kernel values can be positive or negative, but the
01341  *          result for the convolution can only be stored as a positive
01342  *          number.  Consequently, if it goes negative, the choices are
01343  *          to clip to 0 or take the absolute value.  We're choosing
01344  *          the former for now.  Another possibility would be to output
01345  *          a second unsigned image for the negative values.
01346  *      (6) This uses a mirrored border to avoid special casing on
01347  *          the boundaries.
01348  *      (7) To get a subsampled output, call l_setConvolveSampling().
01349  *          The time to make a subsampled output is reduced by the
01350  *          product of the sampling factors.
01351  *      (8) The function is slow, running at about 12 machine cycles for
01352  *          each pixel-op in the convolution.  For example, with a 3 GHz
01353  *          cpu, a 1 Mpixel grayscale image, and a kernel with
01354  *          (sx * sy) = 25 elements, the convolution takes about 100 msec.
01355  */
01356 PIX *
01357 pixConvolve(PIX       *pixs,
01358             L_KERNEL  *kel,
01359             l_int32    outdepth,
01360             l_int32    normflag)
01361 {
01362 l_int32    i, j, id, jd, k, m, w, h, d, wd, hd, sx, sy, cx, cy, wplt, wpld;
01363 l_int32    val;
01364 l_uint32  *datat, *datad, *linet, *lined;
01365 l_float32  sum;
01366 L_KERNEL  *keli, *keln;
01367 PIX       *pixt, *pixd;
01368 
01369     PROCNAME("pixConvolve");
01370 
01371     if (!pixs)
01372         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01373     if (pixGetColormap(pixs))
01374         return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL);
01375     pixGetDimensions(pixs, &w, &h, &d);
01376     if (d != 8 && d != 16 && d != 32)
01377         return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL);
01378     if (!kel)
01379         return (PIX *)ERROR_PTR("kel not defined", procName, NULL);
01380 
01381     keli = kernelInvert(kel);
01382     kernelGetParameters(keli, &sy, &sx, &cy, &cx);
01383     if (normflag)
01384         keln = kernelNormalize(keli, 1.0);
01385     else
01386         keln = kernelCopy(keli);
01387 
01388     if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL)
01389         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
01390 
01391     wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX;
01392     hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY;
01393     pixd = pixCreate(wd, hd, outdepth);
01394     datat = pixGetData(pixt);
01395     datad = pixGetData(pixd);
01396     wplt = pixGetWpl(pixt);
01397     wpld = pixGetWpl(pixd);
01398     for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) {
01399         lined = datad + id * wpld;
01400         for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) {
01401             sum = 0.0;
01402             for (k = 0; k < sy; k++) {
01403                 linet = datat + (i + k) * wplt;
01404                 if (d == 8) {
01405                     for (m = 0; m < sx; m++) {
01406                         val = GET_DATA_BYTE(linet, j + m);
01407                         sum += val * keln->data[k][m];
01408                     }
01409                 }
01410                 else if (d == 16) {
01411                     for (m = 0; m < sx; m++) {
01412                         val = GET_DATA_TWO_BYTES(linet, j + m);
01413                         sum += val * keln->data[k][m];
01414                     }
01415                 }
01416                 else {  /* d == 32 */
01417                     for (m = 0; m < sx; m++) {
01418                         val = *(linet + j + m);
01419                         sum += val * keln->data[k][m];
01420                     }
01421                 }
01422             }
01423 #if 1
01424             if (sum < 0.0) sum = -sum;  /* make it non-negative */
01425 #endif
01426             if (outdepth == 8)
01427                 SET_DATA_BYTE(lined, jd, (l_int32)(sum + 0.5));
01428             else if (outdepth == 16)
01429                 SET_DATA_TWO_BYTES(lined, jd, (l_int32)(sum + 0.5));
01430             else  /* outdepth == 32 */
01431                 *(lined + jd) = (l_uint32)(sum + 0.5);
01432         }
01433     }
01434 
01435     kernelDestroy(&keli);
01436     kernelDestroy(&keln);
01437     pixDestroy(&pixt);
01438     return pixd;
01439 }
01440 
01441 
01442 /*!
01443  *  pixConvolveSep()
01444  *
01445  *      Input:  pixs (8, 16, 32 bpp; no colormap)
01446  *              kelx (x-dependent kernel)
01447  *              kely (y-dependent kernel)
01448  *              outdepth (of pixd: 8, 16 or 32)
01449  *              normflag (1 to normalize kernel to unit sum; 0 otherwise)
01450  *      Return: pixd (8, 16 or 32 bpp)
01451  *
01452  *  Notes:
01453  *      (1) This does a convolution with a separable kernel that is
01454  *          is a sequence of convolutions in x and y.  The two
01455  *          one-dimensional kernel components must be input separately;
01456  *          the full kernel is the product of these components.
01457  *          The support for the full kernel is thus a rectangular region.
01458  *      (2) The input pixs must have only one sample/pixel.
01459  *          To do a convolution on an RGB image, use pixConvolveSepRGB().
01460  *      (3) The parameter @outdepth determines the depth of the result.
01461  *          If the kernel is normalized to unit sum, the output values
01462  *          can never exceed 255, so an output depth of 8 bpp is sufficient.
01463  *          If the kernel is not normalized, it may be necessary to use
01464  *          16 or 32 bpp output to avoid overflow.
01465  *      (2) The @normflag parameter is used as in pixConvolve().
01466  *      (4) The kernel values can be positive or negative, but the
01467  *          result for the convolution can only be stored as a positive
01468  *          number.  Consequently, if it goes negative, the choices are
01469  *          to clip to 0 or take the absolute value.  We're choosing
01470  *          the former for now.  Another possibility would be to output
01471  *          a second unsigned image for the negative values.
01472  *      (5) Warning: if you use l_setConvolveSampling() to get a
01473  *          subsampled output, and the sampling factor is larger than
01474  *          the kernel half-width, it is faster to use the non-separable
01475  *          version pixConvolve().  This is because the first convolution
01476  *          here must be done on every raster line, regardless of the
01477  *          vertical sampling factor.  If the sampling factor is smaller
01478  *          than kernel half-width, it's faster to use the separable
01479  *          convolution.
01480  *      (6) This uses mirrored borders to avoid special casing on
01481  *          the boundaries.
01482  */
01483 PIX *
01484 pixConvolveSep(PIX       *pixs,
01485                L_KERNEL  *kelx,
01486                L_KERNEL  *kely,
01487                l_int32    outdepth,
01488                l_int32    normflag)
01489 {
01490 l_int32    d, xfact, yfact;
01491 L_KERNEL  *kelxn, *kelyn;
01492 PIX       *pixt, *pixd;
01493 
01494     PROCNAME("pixConvolveSep");
01495 
01496     if (!pixs)
01497         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01498     d = pixGetDepth(pixs);
01499     if (d != 8 && d != 16 && d != 32)
01500         return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL);
01501     if (!kelx)
01502         return (PIX *)ERROR_PTR("kelx not defined", procName, NULL);
01503     if (!kely)
01504         return (PIX *)ERROR_PTR("kely not defined", procName, NULL);
01505 
01506     xfact = ConvolveSamplingFactX;
01507     yfact = ConvolveSamplingFactY;
01508     if (normflag) {
01509         kelxn = kernelNormalize(kelx, 1000.0);
01510         kelyn = kernelNormalize(kely, 0.001);
01511         l_setConvolveSampling(xfact, 1);
01512         pixt = pixConvolve(pixs, kelxn, 32, 0);
01513         l_setConvolveSampling(1, yfact);
01514         pixd = pixConvolve(pixt, kelyn, outdepth, 0);
01515         l_setConvolveSampling(xfact, yfact);  /* restore */
01516         kernelDestroy(&kelxn);
01517         kernelDestroy(&kelyn);
01518     }
01519     else {  /* don't normalize */
01520         l_setConvolveSampling(xfact, 1);
01521         pixt = pixConvolve(pixs, kelx, 32, 0);
01522         l_setConvolveSampling(1, yfact);
01523         pixd = pixConvolve(pixt, kely, outdepth, 0);
01524         l_setConvolveSampling(xfact, yfact);
01525     }
01526 
01527     pixDestroy(&pixt);
01528     return pixd;
01529 }
01530 
01531 
01532 /*!
01533  *  pixConvolveRGB()
01534  *
01535  *      Input:  pixs (32 bpp rgb)
01536  *              kernel
01537  *      Return: pixd (32 bpp rgb)
01538  *
01539  *  Notes:
01540  *      (1) This gives a convolution on an RGB image using an
01541  *          arbitrary kernel (which we normalize to keep each
01542  *          component within the range [0 ... 255].
01543  *      (2) The input pixs must be RGB.
01544  *      (3) The kernel values can be positive or negative, but the
01545  *          result for the convolution can only be stored as a positive
01546  *          number.  Consequently, if it goes negative, we clip the
01547  *          result to 0.
01548  *      (4) To get a subsampled output, call l_setConvolveSampling().
01549  *          The time to make a subsampled output is reduced by the
01550  *          product of the sampling factors.
01551  *      (5) This uses a mirrored border to avoid special casing on
01552  *          the boundaries.
01553  */
01554 PIX *
01555 pixConvolveRGB(PIX       *pixs,
01556                L_KERNEL  *kel)
01557 {
01558 PIX  *pixt, *pixr, *pixg, *pixb, *pixd;
01559 
01560     PROCNAME("pixConvolveRGB");
01561 
01562     if (!pixs)
01563         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01564     if (pixGetDepth(pixs) != 32)
01565         return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL);
01566     if (!kel)
01567         return (PIX *)ERROR_PTR("kel not defined", procName, NULL);
01568 
01569     pixt = pixGetRGBComponent(pixs, COLOR_RED);
01570     pixr = pixConvolve(pixt, kel, 8, 1);
01571     pixDestroy(&pixt);
01572     pixt = pixGetRGBComponent(pixs, COLOR_GREEN);
01573     pixg = pixConvolve(pixt, kel, 8, 1);
01574     pixDestroy(&pixt);
01575     pixt = pixGetRGBComponent(pixs, COLOR_BLUE);
01576     pixb = pixConvolve(pixt, kel, 8, 1);
01577     pixDestroy(&pixt);
01578     pixd = pixCreateRGBImage(pixr, pixg, pixb);
01579 
01580     pixDestroy(&pixr);
01581     pixDestroy(&pixg);
01582     pixDestroy(&pixb);
01583     return pixd;
01584 }
01585 
01586 
01587 /*!
01588  *  pixConvolveRGBSep()
01589  *
01590  *      Input:  pixs (32 bpp rgb)
01591  *              kelx (x-dependent kernel)
01592  *              kely (y-dependent kernel)
01593  *      Return: pixd (32 bpp rgb)
01594  *
01595  *  Notes:
01596  *      (1) This does a convolution on an RGB image using a separable
01597  *          kernel that is a sequence of convolutions in x and y.  The two
01598  *          one-dimensional kernel components must be input separately;
01599  *          the full kernel is the product of these components.
01600  *          The support for the full kernel is thus a rectangular region.
01601  *      (2) The kernel values can be positive or negative, but the
01602  *          result for the convolution can only be stored as a positive
01603  *          number.  Consequently, if it goes negative, we clip the
01604  *          result to 0.
01605  *      (3) To get a subsampled output, call l_setConvolveSampling().
01606  *          The time to make a subsampled output is reduced by the
01607  *          product of the sampling factors.
01608  *      (4) This uses a mirrored border to avoid special casing on
01609  *          the boundaries.
01610  */
01611 PIX *
01612 pixConvolveRGBSep(PIX       *pixs,
01613                   L_KERNEL  *kelx,
01614                   L_KERNEL  *kely)
01615 {
01616 PIX  *pixt, *pixr, *pixg, *pixb, *pixd;
01617 
01618     PROCNAME("pixConvolveRGBSep");
01619 
01620     if (!pixs)
01621         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01622     if (pixGetDepth(pixs) != 32)
01623         return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL);
01624     if (!kelx || !kely)
01625         return (PIX *)ERROR_PTR("kelx, kely not both defined", procName, NULL);
01626 
01627     pixt = pixGetRGBComponent(pixs, COLOR_RED);
01628     pixr = pixConvolveSep(pixt, kelx, kely, 8, 1);
01629     pixDestroy(&pixt);
01630     pixt = pixGetRGBComponent(pixs, COLOR_GREEN);
01631     pixg = pixConvolveSep(pixt, kelx, kely, 8, 1);
01632     pixDestroy(&pixt);
01633     pixt = pixGetRGBComponent(pixs, COLOR_BLUE);
01634     pixb = pixConvolveSep(pixt, kelx, kely, 8, 1);
01635     pixDestroy(&pixt);
01636     pixd = pixCreateRGBImage(pixr, pixg, pixb);
01637 
01638     pixDestroy(&pixr);
01639     pixDestroy(&pixg);
01640     pixDestroy(&pixb);
01641     return pixd;
01642 }
01643 
01644 
01645 /*----------------------------------------------------------------------*
01646  *                  Generic convolution with float array                *
01647  *----------------------------------------------------------------------*/
01648 /*!
01649  *  fpixConvolve()
01650  *
01651  *      Input:  fpixs (32 bit float array)
01652  *              kernel
01653  *              normflag (1 to normalize kernel to unit sum; 0 otherwise)
01654  *      Return: fpixd (32 bit float array)
01655  *
01656  *  Notes:
01657  *      (1) This gives a float convolution with an arbitrary kernel.
01658  *      (2) If normflag == 1, the result is normalized by scaling
01659  *          all kernel values for a unit sum.  Do not normalize if
01660  *          the kernel has null sum, such as a DoG.
01661  *      (3) With the FPix, there are no issues about negative
01662  *          array or kernel values.  The convolution is performed
01663  *          with single precision arithmetic.
01664  *      (4) To get a subsampled output, call l_setConvolveSampling().
01665  *          The time to make a subsampled output is reduced by the
01666  *          product of the sampling factors.
01667  *      (5) This uses a mirrored border to avoid special casing on
01668  *          the boundaries.
01669  */
01670 FPIX *
01671 fpixConvolve(FPIX      *fpixs,
01672              L_KERNEL  *kel,
01673              l_int32    normflag)
01674 {
01675 l_int32     i, j, id, jd, k, m, w, h, wd, hd, sx, sy, cx, cy, wplt, wpld;
01676 l_float32   val;
01677 l_float32  *datat, *datad, *linet, *lined;
01678 l_float32   sum;
01679 L_KERNEL   *keli, *keln;
01680 FPIX       *fpixt, *fpixd;
01681 
01682     PROCNAME("fpixConvolve");
01683 
01684     if (!fpixs)
01685         return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
01686     if (!kel)
01687         return (FPIX *)ERROR_PTR("kel not defined", procName, NULL);
01688 
01689     keli = kernelInvert(kel);
01690     kernelGetParameters(keli, &sy, &sx, &cy, &cx);
01691     if (normflag)
01692         keln = kernelNormalize(keli, 1.0);
01693     else
01694         keln = kernelCopy(keli);
01695 
01696     fpixGetDimensions(fpixs, &w, &h);
01697     fpixt = fpixAddMirroredBorder(fpixs, cx, sx - cx, cy, sy - cy);
01698     if (!fpixt)
01699         return (FPIX *)ERROR_PTR("fpixt not made", procName, NULL);
01700 
01701     wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX;
01702     hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY;
01703     fpixd = fpixCreate(wd, hd);
01704     datat = fpixGetData(fpixt);
01705     datad = fpixGetData(fpixd);
01706     wplt = fpixGetWpl(fpixt);
01707     wpld = fpixGetWpl(fpixd);
01708     for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) {
01709         lined = datad + id * wpld;
01710         for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) {
01711             sum = 0.0;
01712             for (k = 0; k < sy; k++) {
01713                 linet = datat + (i + k) * wplt;
01714                 for (m = 0; m < sx; m++) {
01715                     val = *(linet + j + m);
01716                     sum += val * keln->data[k][m];
01717                 }
01718             }
01719             *(lined + jd) = sum;
01720         }
01721     }
01722 
01723     kernelDestroy(&keli);
01724     kernelDestroy(&keln);
01725     fpixDestroy(&fpixt);
01726     return fpixd;
01727 }
01728 
01729 
01730 /*!
01731  *  fpixConvolveSep()
01732  *
01733  *      Input:  fpixs (32 bit float array)
01734  *              kelx (x-dependent kernel)
01735  *              kely (y-dependent kernel)
01736  *              normflag (1 to normalize kernel to unit sum; 0 otherwise)
01737  *      Return: fpixd (32 bit float array)
01738  *
01739  *  Notes:
01740  *      (1) This does a convolution with a separable kernel that is
01741  *          is a sequence of convolutions in x and y.  The two
01742  *          one-dimensional kernel components must be input separately;
01743  *          the full kernel is the product of these components.
01744  *          The support for the full kernel is thus a rectangular region.
01745  *      (2) The normflag parameter is used as in fpixConvolve().
01746  *      (3) Warning: if you use l_setConvolveSampling() to get a
01747  *          subsampled output, and the sampling factor is larger than
01748  *          the kernel half-width, it is faster to use the non-separable
01749  *          version pixConvolve().  This is because the first convolution
01750  *          here must be done on every raster line, regardless of the
01751  *          vertical sampling factor.  If the sampling factor is smaller
01752  *          than kernel half-width, it's faster to use the separable
01753  *          convolution.
01754  *      (4) This uses mirrored borders to avoid special casing on
01755  *          the boundaries.
01756  */
01757 FPIX *
01758 fpixConvolveSep(FPIX      *fpixs,
01759                 L_KERNEL  *kelx,
01760                 L_KERNEL  *kely,
01761                 l_int32    normflag)
01762 {
01763 l_int32    xfact, yfact;
01764 L_KERNEL  *kelxn, *kelyn;
01765 FPIX      *fpixt, *fpixd;
01766 
01767     PROCNAME("fpixConvolveSep");
01768 
01769     if (!fpixs)
01770         return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL);
01771     if (!kelx)
01772         return (FPIX *)ERROR_PTR("kelx not defined", procName, NULL);
01773     if (!kely)
01774         return (FPIX *)ERROR_PTR("kely not defined", procName, NULL);
01775 
01776     xfact = ConvolveSamplingFactX;
01777     yfact = ConvolveSamplingFactY;
01778     if (normflag) {
01779         kelxn = kernelNormalize(kelx, 1.0);
01780         kelyn = kernelNormalize(kely, 1.0);
01781         l_setConvolveSampling(xfact, 1);
01782         fpixt = fpixConvolve(fpixs, kelxn, 0);
01783         l_setConvolveSampling(1, yfact);
01784         fpixd = fpixConvolve(fpixt, kelyn, 0);
01785         l_setConvolveSampling(xfact, yfact);  /* restore */
01786         kernelDestroy(&kelxn);
01787         kernelDestroy(&kelyn);
01788     }
01789     else {  /* don't normalize */
01790         l_setConvolveSampling(xfact, 1);
01791         fpixt = fpixConvolve(fpixs, kelx, 0);
01792         l_setConvolveSampling(1, yfact);
01793         fpixd = fpixConvolve(fpixt, kely, 0);
01794         l_setConvolveSampling(xfact, yfact);
01795     }
01796 
01797     fpixDestroy(&fpixt);
01798     return fpixd;
01799 }
01800 
01801 
01802 /*------------------------------------------------------------------------*
01803  *                Set parameter for convolution subsampling               *
01804  *------------------------------------------------------------------------*/
01805 /*!
01806  *  l_setConvolveSampling()
01807  *
01808  *      Input:  xfact, yfact (integer >= 1)
01809  *      Return: void
01810  *
01811  *  Notes:
01812  *      (1) This sets the x and y output subsampling factors for generic pix
01813  *          and fpix convolution.  The default values are 1 (no subsampling).
01814  */
01815 void
01816 l_setConvolveSampling(l_int32  xfact,
01817                       l_int32  yfact)
01818 {
01819     if (xfact < 1) xfact = 1;
01820     if (yfact < 1) yfact = 1;
01821     ConvolveSamplingFactX = xfact;
01822     ConvolveSamplingFactY = yfact;
01823 }
01824 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines