Leptonica 1.68
C Image Processing Library

adaptmap.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  *  adaptmap.c
00018  *                     
00019  *  ===================================================================
00020  *  Image binarization algorithms are found in:
00021  *     grayquant.c:   standard, simple, general grayscale quantization
00022  *     adaptmap.c:    local adaptive; mostly gray-to-gray in preparation
00023  *                    for binarization
00024  *     binarize.c:    special binarization methods, locally adaptive.
00025  *  ===================================================================
00026  *
00027  *      Adaptive background normalization (top-level functions)
00028  *          PIX       *pixBackgroundNormSimple()     8 and 32 bpp
00029  *          PIX       *pixBackgroundNorm()           8 and 32 bpp
00030  *          PIX       *pixBackgroundNormMorph()      8 and 32 bpp
00031  *
00032  *      Arrays of inverted background values for normalization (16 bpp)
00033  *          l_int32    pixBackgroundNormGrayArray()   8 bpp input
00034  *          l_int32    pixBackgroundNormRGBArrays()   32 bpp input
00035  *          l_int32    pixBackgroundNormGrayArrayMorph()   8 bpp input
00036  *          l_int32    pixBackgroundNormRGBArraysMorph()   32 bpp input
00037  *
00038  *      Measurement of local background
00039  *          l_int32    pixGetBackgroundGrayMap()        8 bpp
00040  *          l_int32    pixGetBackgroundRGBMap()         32 bpp
00041  *          l_int32    pixGetBackgroundGrayMapMorph()   8 bpp
00042  *          l_int32    pixGetBackgroundRGBMapMorph()    32 bpp
00043  *          l_int32    pixFillMapHoles()
00044  *          PIX       *pixExtendByReplication()         8 bpp
00045  *          l_int32    pixSmoothConnectedRegions()      8 bpp
00046  *
00047  *      Measurement of local foreground
00048  *          l_int32    pixGetForegroundGrayMap()        8 bpp
00049  *
00050  *      Generate inverted background map for each component
00051  *          PIX       *pixGetInvBackgroundMap()   16 bpp
00052  *
00053  *      Apply inverse background map to image
00054  *          PIX       *pixApplyInvBackgroundGrayMap()   8 bpp
00055  *          PIX       *pixApplyInvBackgroundRGBMap()    32 bpp
00056  *
00057  *      Apply variable map
00058  *          PIX       *pixApplyVariableGrayMap()        8 bpp
00059  *
00060  *      Non-adaptive (global) mapping
00061  *          PIX       *pixGlobalNormRGB()               32 bpp or cmapped
00062  *          PIX       *pixGlobalNormNoSatRGB()          32 bpp
00063  *
00064  *      Adaptive threshold spread normalization
00065  *          l_int32    pixThresholdSpreadNorm()         8 bpp
00066  *
00067  *      Adaptive background normalization (flexible adaptaption)
00068  *          PIX       *pixBackgroundNormFlex()          8 bpp
00069  *
00070  *      Adaptive contrast normalization
00071  *          PIX             *pixContrastNorm()          8 bpp
00072  *          l_int32          pixMinMaxTiles()
00073  *          l_int32          pixSetLowContrast()
00074  *          PIX             *pixLinearTRCTiled()
00075  *          static l_int32  *iaaGetLinearTRC()
00076  *
00077  *  Background normalization is done by generating a reduced map (or set
00078  *  of maps) representing the estimated background value of the
00079  *  input image, and using this to shift the pixel values so that
00080  *  this background value is set to some constant value.
00081  *
00082  *  Specifically, normalization has 3 steps:
00083  *    (1) Generate a background map at a reduced scale.
00084  *    (2) Make the array of inverted background values by inverting
00085  *        the map.  The result is an array of local multiplicative factors.
00086  *    (3) Apply this inverse background map to the image
00087  *
00088  *  The inverse background arrays can be generated in two different ways here:
00089  *    (1) Remove the 'foreground' pixels and average over the remaining
00090  *        pixels in each tile.  Propagate values into tiles where
00091  *        values have not been assigned, either because there was not
00092  *        enough background in the tile or because the tile is covered
00093  *        by a foreground region described by an image mask.
00094  *        After the background map is made, the inverse map is generated by
00095  *        smoothing over some number of adjacent tiles
00096  *        (block convolution) and then inverting.
00097  *    (2) Remove the foreground pixels using a morphological closing
00098  *        on a subsampled version of the image.  Propagate values
00099  *        into pixels covered by an optional image mask.  Invert the
00100  *        background map without preconditioning by convolutional smoothing.
00101  *
00102  *  Note: Several of these functions make an implicit assumption about RGB
00103  *        component ordering.
00104  *
00105  *  Other methods for adaptively normalizing the image are also given here.
00106  *
00107  *  (1) pixThresholdSpreadNorm() computes a local threshold over the image
00108  *      and normalizes the input pixel values so that this computed threshold
00109  *      is a constant across the entire image.
00110  *
00111  *  (2) pixContrastNorm() computes and applies a local TRC so that the
00112  *      local dynamic range is expanded to the full 8 bits, where the
00113  *      darkest pixels are mapped to 0 and the lightest to 255.  This is
00114  *      useful for improving the appearance of pages with very light
00115  *      foreground or very dark background, and where the local TRC
00116  *      function doesn't change rapidly with position.
00117  */
00118 
00119 #include <stdio.h>
00120 #include <stdlib.h>
00121 #include "allheaders.h"
00122 
00123 
00124     /* Default input parameters for pixBackgroundNormSimple()
00125      * Note:
00126      *    (1) mincount must never exceed the tile area (width * height)
00127      *    (2) bgval must be sufficiently below 255 to avoid accidental
00128      *        saturation; otherwise it should be large to avoid
00129      *        shrinking the dynamic range
00130      *    (3) results should otherwise not be sensitive to these values
00131      */
00132 static const l_int32  DEFAULT_TILE_WIDTH = 10;
00133 static const l_int32  DEFAULT_TILE_HEIGHT = 15;
00134 static const l_int32  DEFAULT_FG_THRESHOLD = 60;
00135 static const l_int32  DEFAULT_MIN_COUNT = 40;
00136 static const l_int32  DEFAULT_BG_VAL = 200;
00137 static const l_int32  DEFAULT_X_SMOOTH_SIZE = 2;
00138 static const l_int32  DEFAULT_Y_SMOOTH_SIZE = 1;
00139 
00140 static l_int32 *iaaGetLinearTRC(l_int32 **iaa, l_int32 diff);
00141 
00142 #ifndef  NO_CONSOLE_IO
00143 #define  DEBUG_GLOBAL    0
00144 #endif  /* ~NO_CONSOLE_IO */
00145 
00146 
00147 
00148 /*------------------------------------------------------------------*
00149  *                Adaptive background normalization                 *
00150  *------------------------------------------------------------------*/
00151 /*!
00152  *  pixBackgroundNormSimple()
00153  *
00154  *      Input:  pixs (8 bpp grayscale or 32 bpp rgb)
00155  *              pixim (<optional> 1 bpp 'image' mask; can be null)
00156  *              pixg (<optional> 8 bpp grayscale version; can be null)
00157  *      Return: pixd (8 bpp or 32 bpp rgb), or null on error
00158  *
00159  *  Notes:
00160  *    (1) This is a simplified interface to pixBackgroundNorm(),
00161  *        where seven parameters are defaulted.
00162  *    (2) The input image is either grayscale or rgb.
00163  *    (3) See pixBackgroundNorm() for usage and function.
00164  */
00165 PIX *
00166 pixBackgroundNormSimple(PIX  *pixs,
00167                         PIX  *pixim,
00168                         PIX  *pixg)
00169 {
00170     return pixBackgroundNorm(pixs, pixim, pixg,
00171                              DEFAULT_TILE_WIDTH, DEFAULT_TILE_HEIGHT,
00172                              DEFAULT_FG_THRESHOLD, DEFAULT_MIN_COUNT,
00173                              DEFAULT_BG_VAL, DEFAULT_X_SMOOTH_SIZE,
00174                              DEFAULT_Y_SMOOTH_SIZE);
00175 }
00176 
00177 
00178 /*!
00179  *  pixBackgroundNorm()
00180  *
00181  *      Input:  pixs (8 bpp grayscale or 32 bpp rgb)
00182  *              pixim (<optional> 1 bpp 'image' mask; can be null)
00183  *              pixg (<optional> 8 bpp grayscale version; can be null)
00184  *              sx, sy (tile size in pixels)
00185  *              thresh (threshold for determining foreground)
00186  *              mincount (min threshold on counts in a tile)
00187  *              bgval (target bg val; typ. > 128)
00188  *              smoothx (half-width of block convolution kernel width)
00189  *              smoothy (half-width of block convolution kernel height)
00190  *      Return: pixd (8 bpp or 32 bpp rgb), or null on error
00191  *
00192  *  Notes:
00193  *    (1) This is a top-level interface for normalizing the image intensity
00194  *        by mapping the image so that the background is near the input
00195  *        value 'bgval'.
00196  *    (2) The input image is either grayscale or rgb.
00197  *    (3) For each component in the input image, the background value
00198  *        in each tile is estimated using the values in the tile that
00199  *        are not part of the foreground, where the foreground is
00200  *        determined by the input 'thresh' argument.
00201  *    (4) An optional binary mask can be specified, with the foreground
00202  *        pixels typically over image regions.  The resulting background
00203  *        map values will be determined by surrounding pixels that are
00204  *        not under the mask foreground.  The origin (0,0) of this mask
00205  *        is assumed to be aligned with the origin of the input image.
00206  *        This binary mask must not fully cover pixs, because then there
00207  *        will be no pixels in the input image available to compute
00208  *        the background.
00209  *    (5) An optional grayscale version of the input pixs can be supplied.
00210  *        The only reason to do this is if the input is RGB and this
00211  *        grayscale version can be used elsewhere.  If the input is RGB
00212  *        and this is not supplied, it is made internally using only
00213  *        the green component, and destroyed after use.
00214  *    (6) The dimensions of the pixel tile (sx, sy) give the amount by
00215  *        by which the map is reduced in size from the input image.
00216  *    (7) The threshold is used to binarize the input image, in order to
00217  *        locate the foreground components.  If this is set too low,
00218  *        some actual foreground may be used to determine the maps;
00219  *        if set too high, there may not be enough background
00220  *        to determine the map values accurately.  Typically, it's
00221  *        better to err by setting the threshold too high.
00222  *    (8) A 'mincount' threshold is a minimum count of pixels in a
00223  *        tile for which a background reading is made, in order for that
00224  *        pixel in the map to be valid.  This number should perhaps be
00225  *        at least 1/3 the size of the tile.
00226  *    (9) A 'bgval' target background value for the normalized image.  This
00227  *        should be at least 128.  If set too close to 255, some
00228  *        clipping will occur in the result.
00229  *    (10) Two factors, 'smoothx' and 'smoothy', are input for smoothing
00230  *        the map.  Each low-pass filter kernel dimension is
00231  *        is 2 * (smoothing factor) + 1, so a
00232  *        value of 0 means no smoothing. A value of 1 or 2 is recommended.
00233  */
00234 PIX *
00235 pixBackgroundNorm(PIX     *pixs,
00236                   PIX     *pixim,
00237                   PIX     *pixg,
00238                   l_int32  sx,
00239                   l_int32  sy,
00240                   l_int32  thresh,
00241                   l_int32  mincount,
00242                   l_int32  bgval,
00243                   l_int32  smoothx,
00244                   l_int32  smoothy)
00245 {
00246 l_int32  d, allfg;
00247 PIX     *pixm, *pixmi, *pixd;
00248 PIX     *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi;
00249 
00250     PROCNAME("pixBackgroundNorm");
00251 
00252     if (!pixs)
00253         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00254     d = pixGetDepth(pixs);
00255     if (d != 8 && d != 32)
00256         return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL);
00257     if (sx < 4 || sy < 4)
00258         return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL);
00259     if (mincount > sx * sy) {
00260         L_WARNING("mincount too large for tile size", procName);
00261         mincount = (sx * sy) / 3;
00262     }
00263 
00264         /* If pixim exists, verify that it is not all foreground. */
00265     if (pixim) {
00266         pixInvert(pixim, pixim);
00267         pixZero(pixim, &allfg);
00268         pixInvert(pixim, pixim);
00269         if (allfg)
00270             return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL);
00271     }
00272 
00273     pixd = NULL;
00274     if (d == 8) {
00275         pixm = NULL;
00276         pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm);
00277         if (!pixm) {
00278             L_WARNING("map not made; returning a copy of the source", procName);
00279             return pixCopy(NULL, pixs);
00280         }
00281 
00282         pixmi = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy);
00283         if (!pixmi)
00284             ERROR_PTR("pixmi not made", procName, NULL);
00285         else
00286             pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, sx, sy);
00287 
00288         pixDestroy(&pixm);
00289         pixDestroy(&pixmi);
00290     }
00291     else {
00292         pixmr = pixmg = pixmb = NULL;
00293         pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh,
00294                                mincount, &pixmr, &pixmg, &pixmb);
00295         if (!pixmr || !pixmg || !pixmb) {
00296             pixDestroy(&pixmr);
00297             pixDestroy(&pixmg);
00298             pixDestroy(&pixmb);
00299             L_WARNING("map not made; returning a copy of the source", procName);
00300             return pixCopy(NULL, pixs);
00301         }
00302 
00303         pixmri = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy);
00304         pixmgi = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy);
00305         pixmbi = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy);
00306         if (!pixmri || !pixmgi || !pixmbi)
00307             ERROR_PTR("not all pixm*i are made", procName, NULL);
00308         else
00309             pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi,
00310                                                sx, sy);
00311 
00312         pixDestroy(&pixmr);
00313         pixDestroy(&pixmg);
00314         pixDestroy(&pixmb);
00315         pixDestroy(&pixmri);
00316         pixDestroy(&pixmgi);
00317         pixDestroy(&pixmbi);
00318     }
00319 
00320     if (!pixd)
00321         ERROR_PTR("pixd not made", procName, NULL);
00322     return pixd;
00323 }
00324 
00325 
00326 /*!
00327  *  pixBackgroundNormMorph()
00328  *
00329  *      Input:  pixs (8 bpp grayscale or 32 bpp rgb)
00330  *              pixim (<optional> 1 bpp 'image' mask; can be null)
00331  *              reduction (at which morph closings are done; between 2 and 16)
00332  *              size (of square Sel for the closing; use an odd number)
00333  *              bgval (target bg val; typ. > 128)
00334  *      Return: pixd (8 bpp), or null on error
00335  *
00336  *  Notes:
00337  *    (1) This is a top-level interface for normalizing the image intensity
00338  *        by mapping the image so that the background is near the input
00339  *        value 'bgval'.
00340  *    (2) The input image is either grayscale or rgb.
00341  *    (3) For each component in the input image, the background value
00342  *        is estimated using a grayscale closing; hence the 'Morph'
00343  *        in the function name.
00344  *    (4) An optional binary mask can be specified, with the foreground
00345  *        pixels typically over image regions.  The resulting background
00346  *        map values will be determined by surrounding pixels that are
00347  *        not under the mask foreground.  The origin (0,0) of this mask
00348  *        is assumed to be aligned with the origin of the input image.
00349  *        This binary mask must not fully cover pixs, because then there
00350  *        will be no pixels in the input image available to compute
00351  *        the background.
00352  *    (5) The map is computed at reduced size (given by 'reduction')
00353  *        from the input pixs and optional pixim.  At this scale,
00354  *        pixs is closed to remove the background, using a square Sel
00355  *        of odd dimension.  The product of reduction * size should be
00356  *        large enough to remove most of the text foreground.
00357  *    (6) No convolutional smoothing needs to be done on the map before
00358  *        inverting it.
00359  *    (7) A 'bgval' target background value for the normalized image.  This
00360  *        should be at least 128.  If set too close to 255, some
00361  *        clipping will occur in the result.
00362  */
00363 PIX *
00364 pixBackgroundNormMorph(PIX     *pixs,
00365                        PIX     *pixim,
00366                        l_int32  reduction,
00367                        l_int32  size,
00368                        l_int32  bgval)
00369 {
00370 l_int32    d, allfg;
00371 PIX       *pixm, *pixmi, *pixd;
00372 PIX       *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi;
00373 
00374     PROCNAME("pixBackgroundNormMorph");
00375 
00376     if (!pixs)
00377         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00378     d = pixGetDepth(pixs);
00379     if (d != 8 && d != 32)
00380         return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL);
00381     if (reduction < 2 || reduction > 16)
00382         return (PIX *)ERROR_PTR("reduction must be between 2 and 16",
00383                                 procName, NULL);
00384 
00385         /* If pixim exists, verify that it is not all foreground. */
00386     if (pixim) {
00387         pixInvert(pixim, pixim);
00388         pixZero(pixim, &allfg);
00389         pixInvert(pixim, pixim);
00390         if (allfg)
00391             return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL);
00392     }
00393 
00394     pixd = NULL;
00395     if (d == 8) {
00396         pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm);
00397         if (!pixm)
00398             return (PIX *)ERROR_PTR("pixm not made", procName, NULL);
00399         pixmi = pixGetInvBackgroundMap(pixm, bgval, 0, 0);
00400         if (!pixmi)
00401             ERROR_PTR("pixmi not made", procName, NULL);
00402         else
00403             pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi,
00404                                                 reduction, reduction);
00405         pixDestroy(&pixm);
00406         pixDestroy(&pixmi);
00407     }
00408     else {  /* d == 32 */
00409         pixmr = pixmg = pixmb = NULL;
00410         pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size,
00411                                     &pixmr, &pixmg, &pixmb);
00412         if (!pixmr || !pixmg || !pixmb) {
00413             pixDestroy(&pixmr);
00414             pixDestroy(&pixmg);
00415             pixDestroy(&pixmb);
00416             return (PIX *)ERROR_PTR("not all pixm*", procName, NULL);
00417         }
00418 
00419         pixmri = pixGetInvBackgroundMap(pixmr, bgval, 0, 0);
00420         pixmgi = pixGetInvBackgroundMap(pixmg, bgval, 0, 0);
00421         pixmbi = pixGetInvBackgroundMap(pixmb, bgval, 0, 0);
00422         if (!pixmri || !pixmgi || !pixmbi)
00423             ERROR_PTR("not all pixm*i are made", procName, NULL);
00424         else
00425             pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi,
00426                                                reduction, reduction);
00427 
00428         pixDestroy(&pixmr);
00429         pixDestroy(&pixmg);
00430         pixDestroy(&pixmb);
00431         pixDestroy(&pixmri);
00432         pixDestroy(&pixmgi);
00433         pixDestroy(&pixmbi);
00434     }
00435 
00436     if (!pixd)
00437         ERROR_PTR("pixd not made", procName, NULL);
00438     return pixd;
00439 }
00440 
00441 
00442 /*-------------------------------------------------------------------------*
00443  *      Arrays of inverted background values for normalization             *
00444  *-------------------------------------------------------------------------*
00445  *  Notes for these four functions:                                        *
00446  *      (1) They are useful if you need to save the actual mapping array.  *
00447  *      (2) They could be used in the top-level functions but are          *
00448  *          not because their use makes those functions less clear.        *
00449  *      (3) Each component in the input pixs generates a 16 bpp pix array. *
00450  *-------------------------------------------------------------------------*/
00451 /*!
00452  *  pixBackgroundNormGrayArray()
00453  *
00454  *      Input:  pixs (8 bpp grayscale)
00455  *              pixim (<optional> 1 bpp 'image' mask; can be null)
00456  *              sx, sy (tile size in pixels)
00457  *              thresh (threshold for determining foreground)
00458  *              mincount (min threshold on counts in a tile)
00459  *              bgval (target bg val; typ. > 128)
00460  *              smoothx (half-width of block convolution kernel width)
00461  *              smoothy (half-width of block convolution kernel height)
00462  *              &pixd (<return> 16 bpp array of inverted background value)
00463  *      Return: 0 if OK, 1 on error
00464  *
00465  *  Notes:
00466  *    (1) See notes in pixBackgroundNorm().
00467  *    (2) This returns a 16 bpp pix that can be used by 
00468  *        pixApplyInvBackgroundGrayMap() to generate a normalized version
00469  *        of the input pixs.
00470  */
00471 l_int32
00472 pixBackgroundNormGrayArray(PIX     *pixs,
00473                            PIX     *pixim,
00474                            l_int32  sx,
00475                            l_int32  sy,
00476                            l_int32  thresh,
00477                            l_int32  mincount,
00478                            l_int32  bgval,
00479                            l_int32  smoothx,
00480                            l_int32  smoothy,
00481                            PIX    **ppixd)
00482 {
00483 l_int32  allfg;
00484 PIX     *pixm;
00485 
00486     PROCNAME("pixBackgroundNormGrayArray");
00487 
00488     if (!ppixd)
00489         return ERROR_INT("&pixd not defined", procName, 1);
00490     *ppixd = NULL;
00491     if (!pixs || pixGetDepth(pixs) != 8)
00492         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
00493     if (pixGetColormap(pixs))
00494         return ERROR_INT("pixs is colormapped", procName, 1);
00495     if (pixim && pixGetDepth(pixim) != 1)
00496         return ERROR_INT("pixim not 1 bpp", procName, 1);
00497     if (sx < 4 || sy < 4)
00498         return ERROR_INT("sx and sy must be >= 4", procName, 1);
00499     if (mincount > sx * sy) {
00500         L_WARNING("mincount too large for tile size", procName);
00501         mincount = (sx * sy) / 3;
00502     }
00503 
00504         /* If pixim exists, verify that it is not all foreground. */
00505     if (pixim) {
00506         pixInvert(pixim, pixim);
00507         pixZero(pixim, &allfg);
00508         pixInvert(pixim, pixim);
00509         if (allfg)
00510             return ERROR_INT("pixim all foreground", procName, 1);
00511     }
00512 
00513     pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm);
00514     if (!pixm)
00515         return ERROR_INT("pixm not made", procName, 1);
00516     *ppixd = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy);
00517     pixDestroy(&pixm);
00518     return 0;
00519 }
00520 
00521 
00522 /*!
00523  *  pixBackgroundNormRGBArrays()
00524  *
00525  *      Input:  pixs (32 bpp rgb)
00526  *              pixim (<optional> 1 bpp 'image' mask; can be null)
00527  *              pixg (<optional> 8 bpp grayscale version; can be null)
00528  *              sx, sy (tile size in pixels)
00529  *              thresh (threshold for determining foreground)
00530  *              mincount (min threshold on counts in a tile)
00531  *              bgval (target bg val; typ. > 128)
00532  *              smoothx (half-width of block convolution kernel width)
00533  *              smoothy (half-width of block convolution kernel height)
00534  *              &pixr (<return> 16 bpp array of inverted R background value)
00535  *              &pixg (<return> 16 bpp array of inverted G background value)
00536  *              &pixb (<return> 16 bpp array of inverted B background value)
00537  *      Return: 0 if OK, 1 on error
00538  *
00539  *  Notes:
00540  *    (1) See notes in pixBackgroundNorm().
00541  *    (2) This returns a set of three 16 bpp pix that can be used by 
00542  *        pixApplyInvBackgroundGrayMap() to generate a normalized version
00543  *        of each component of the input pixs.
00544  */
00545 l_int32
00546 pixBackgroundNormRGBArrays(PIX     *pixs,
00547                            PIX     *pixim,
00548                            PIX     *pixg,
00549                            l_int32  sx,
00550                            l_int32  sy,
00551                            l_int32  thresh,
00552                            l_int32  mincount,
00553                            l_int32  bgval,
00554                            l_int32  smoothx,
00555                            l_int32  smoothy,
00556                            PIX    **ppixr,
00557                            PIX    **ppixg,
00558                            PIX    **ppixb)
00559 {
00560 l_int32  allfg;
00561 PIX     *pixmr, *pixmg, *pixmb;
00562 
00563     PROCNAME("pixBackgroundNormRGBArrays");
00564 
00565     if (!ppixr || !ppixg || !ppixb)
00566         return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1);
00567     *ppixr = *ppixg = *ppixb = NULL;
00568     if (!pixs)
00569         return ERROR_INT("pixs not defined", procName, 1);
00570     if (pixGetDepth(pixs) != 32)
00571         return ERROR_INT("pixs not 32 bpp", procName, 1);
00572     if (pixim && pixGetDepth(pixim) != 1)
00573         return ERROR_INT("pixim not 1 bpp", procName, 1);
00574     if (sx < 4 || sy < 4)
00575         return ERROR_INT("sx and sy must be >= 4", procName, 1);
00576     if (mincount > sx * sy) {
00577         L_WARNING("mincount too large for tile size", procName);
00578         mincount = (sx * sy) / 3;
00579     }
00580 
00581         /* If pixim exists, verify that it is not all foreground. */
00582     if (pixim) {
00583         pixInvert(pixim, pixim);
00584         pixZero(pixim, &allfg);
00585         pixInvert(pixim, pixim);
00586         if (allfg)
00587             return ERROR_INT("pixim all foreground", procName, 1);
00588     }
00589 
00590     pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, mincount,
00591                            &pixmr, &pixmg, &pixmb);
00592     if (!pixmr || !pixmg || !pixmb) {
00593         pixDestroy(&pixmr);
00594         pixDestroy(&pixmg);
00595         pixDestroy(&pixmb);
00596         return ERROR_INT("not all pixm* made", procName, 1);
00597     }
00598 
00599     *ppixr = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy);
00600     *ppixg = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy);
00601     *ppixb = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy);
00602     pixDestroy(&pixmr);
00603     pixDestroy(&pixmg);
00604     pixDestroy(&pixmb);
00605     return 0;
00606 }
00607 
00608 
00609 /*!
00610  *  pixBackgroundNormGrayArrayMorph()
00611  *
00612  *      Input:  pixs (8 bpp grayscale)
00613  *              pixim (<optional> 1 bpp 'image' mask; can be null)
00614  *              reduction (at which morph closings are done; between 2 and 16)
00615  *              size (of square Sel for the closing; use an odd number)
00616  *              bgval (target bg val; typ. > 128)
00617  *              &pixd (<return> 16 bpp array of inverted background value)
00618  *      Return: 0 if OK, 1 on error
00619  *
00620  *  Notes:
00621  *    (1) See notes in pixBackgroundNormMorph().
00622  *    (2) This returns a 16 bpp pix that can be used by 
00623  *        pixApplyInvBackgroundGrayMap() to generate a normalized version
00624  *        of the input pixs.
00625  */
00626 l_int32
00627 pixBackgroundNormGrayArrayMorph(PIX     *pixs,
00628                                 PIX     *pixim,
00629                                 l_int32  reduction,
00630                                 l_int32  size,
00631                                 l_int32  bgval,
00632                                 PIX    **ppixd)
00633 {
00634 l_int32  allfg;
00635 PIX     *pixm;
00636 
00637     PROCNAME("pixBackgroundNormGrayArrayMorph");
00638 
00639     if (!ppixd)
00640         return ERROR_INT("&pixd not defined", procName, 1);
00641     *ppixd = NULL;
00642     if (!pixs)
00643         return ERROR_INT("pixs not defined", procName, 1);
00644     if (pixGetDepth(pixs) != 8)
00645         return ERROR_INT("pixs not 8 bpp", procName, 1);
00646     if (pixim && pixGetDepth(pixim) != 1)
00647         return ERROR_INT("pixim not 1 bpp", procName, 1);
00648     if (reduction < 2 || reduction > 16)
00649         return ERROR_INT("reduction must be between 2 and 16", procName, 1);
00650 
00651         /* If pixim exists, verify that it is not all foreground. */
00652     if (pixim) {
00653         pixInvert(pixim, pixim);
00654         pixZero(pixim, &allfg);
00655         pixInvert(pixim, pixim);
00656         if (allfg)
00657             return ERROR_INT("pixim all foreground", procName, 1);
00658     }
00659 
00660     pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm);
00661     if (!pixm)
00662         return ERROR_INT("pixm not made", procName, 1);
00663     *ppixd = pixGetInvBackgroundMap(pixm, bgval, 0, 0);
00664     pixDestroy(&pixm);
00665     return 0;
00666 }
00667 
00668 
00669 /*!
00670  *  pixBackgroundNormRGBArraysMorph()
00671  *
00672  *      Input:  pixs (32 bpp rgb)
00673  *              pixim (<optional> 1 bpp 'image' mask; can be null)
00674  *              reduction (at which morph closings are done; between 2 and 16)
00675  *              size (of square Sel for the closing; use an odd number)
00676  *              bgval (target bg val; typ. > 128)
00677  *              &pixr (<return> 16 bpp array of inverted R background value)
00678  *              &pixg (<return> 16 bpp array of inverted G background value)
00679  *              &pixb (<return> 16 bpp array of inverted B background value)
00680  *      Return: 0 if OK, 1 on error
00681  *
00682  *  Notes:
00683  *    (1) See notes in pixBackgroundNormMorph().
00684  *    (2) This returns a set of three 16 bpp pix that can be used by 
00685  *        pixApplyInvBackgroundGrayMap() to generate a normalized version
00686  *        of each component of the input pixs.
00687  */
00688 l_int32
00689 pixBackgroundNormRGBArraysMorph(PIX     *pixs,
00690                                 PIX     *pixim,
00691                                 l_int32  reduction,
00692                                 l_int32  size,
00693                                 l_int32  bgval,
00694                                 PIX    **ppixr,
00695                                 PIX    **ppixg,
00696                                 PIX    **ppixb)
00697 {
00698 l_int32  allfg;
00699 PIX     *pixmr, *pixmg, *pixmb;
00700 
00701     PROCNAME("pixBackgroundNormRGBArraysMorph");
00702 
00703     if (!ppixr || !ppixg || !ppixb)
00704         return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1);
00705     *ppixr = *ppixg = *ppixb = NULL;
00706     if (!pixs)
00707         return ERROR_INT("pixs not defined", procName, 1);
00708     if (pixGetDepth(pixs) != 32)
00709         return ERROR_INT("pixs not 32 bpp", procName, 1);
00710     if (pixim && pixGetDepth(pixim) != 1)
00711         return ERROR_INT("pixim not 1 bpp", procName, 1);
00712     if (reduction < 2 || reduction > 16)
00713         return ERROR_INT("reduction must be between 2 and 16", procName, 1);
00714 
00715         /* If pixim exists, verify that it is not all foreground. */
00716     if (pixim) {
00717         pixInvert(pixim, pixim);
00718         pixZero(pixim, &allfg);
00719         pixInvert(pixim, pixim);
00720         if (allfg)
00721             return ERROR_INT("pixim all foreground", procName, 1);
00722     }
00723 
00724     pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size,
00725                                 &pixmr, &pixmg, &pixmb);
00726     if (!pixmr || !pixmg || !pixmb) {
00727         pixDestroy(&pixmr);
00728         pixDestroy(&pixmg);
00729         pixDestroy(&pixmb);
00730         return ERROR_INT("not all pixm* made", procName, 1);
00731     }
00732 
00733     *ppixr = pixGetInvBackgroundMap(pixmr, bgval, 0, 0);
00734     *ppixg = pixGetInvBackgroundMap(pixmg, bgval, 0, 0);
00735     *ppixb = pixGetInvBackgroundMap(pixmb, bgval, 0, 0);
00736     pixDestroy(&pixmr);
00737     pixDestroy(&pixmg);
00738     pixDestroy(&pixmb);
00739     return 0;
00740 }
00741 
00742 
00743 /*------------------------------------------------------------------*
00744  *                 Measurement of local background                  *
00745  *------------------------------------------------------------------*/
00746 /*!
00747  *  pixGetBackgroundGrayMap()
00748  *
00749  *      Input:  pixs (8 bpp grayscale; not cmapped)
00750  *              pixim (<optional> 1 bpp 'image' mask; can be null; it
00751  *                     should not have all foreground pixels)
00752  *              sx, sy (tile size in pixels)
00753  *              thresh (threshold for determining foreground)
00754  *              mincount (min threshold on counts in a tile)
00755  *              &pixd (<return> 8 bpp grayscale map)
00756  *      Return: 0 if OK, 1 on error
00757  *
00758  *  Notes:
00759  *      (1) The background is measured in regions that don't have
00760  *          images.  It is then propagated into the image regions,
00761  *          and finally smoothed in each image region.
00762  */
00763 l_int32
00764 pixGetBackgroundGrayMap(PIX     *pixs,
00765                         PIX     *pixim,
00766                         l_int32  sx,
00767                         l_int32  sy,
00768                         l_int32  thresh,
00769                         l_int32  mincount,
00770                         PIX    **ppixd)
00771 {
00772 l_int32    w, h, wd, hd, wim, him, wpls, wplim, wpld, wplf;
00773 l_int32    xim, yim, delx, nx, ny, i, j, k, m;
00774 l_int32    count, sum, val8;
00775 l_int32    empty, fgpixels;
00776 l_uint32  *datas, *dataim, *datad, *dataf, *lines, *lineim, *lined, *linef;
00777 l_float32  scalex, scaley;
00778 PIX       *pixd, *piximi, *pixb, *pixf, *pixims;
00779 
00780     PROCNAME("pixGetBackgroundGrayMap");
00781 
00782     if (!ppixd)
00783         return ERROR_INT("&pixd not defined", procName, 1);
00784     *ppixd = NULL;
00785     if (!pixs || pixGetDepth(pixs) != 8)
00786         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
00787     if (pixGetColormap(pixs))
00788         return ERROR_INT("pixs is colormapped", procName, 1);
00789     if (pixim && pixGetDepth(pixim) != 1)
00790         return ERROR_INT("pixim not 1 bpp", procName, 1);
00791     if (sx < 4 || sy < 4)
00792         return ERROR_INT("sx and sy must be >= 4", procName, 1);
00793     if (mincount > sx * sy) {
00794         L_WARNING("mincount too large for tile size", procName);
00795         mincount = (sx * sy) / 3;
00796     }
00797 
00798         /* Evaluate the 'image' mask, pixim, and make sure
00799          * it is not all fg. */
00800     fgpixels = 0;  /* boolean for existence of fg pixels in the image mask. */
00801     if (pixim) {
00802         piximi = pixInvert(NULL, pixim);  /* set non-'image' pixels to 1 */
00803         pixZero(piximi, &empty);
00804         pixDestroy(&piximi);
00805         if (empty)
00806             return ERROR_INT("pixim all fg; no background", procName, 1);
00807         pixZero(pixim, &empty);
00808         if (!empty)  /* there are fg pixels in pixim */
00809             fgpixels = 1;
00810     }
00811 
00812         /* Generate the foreground mask, pixf, which is at
00813          * full resolution.  These pixels will be ignored when
00814          * computing the background values. */
00815     pixb = pixThresholdToBinary(pixs, thresh);
00816     pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0);
00817     pixDestroy(&pixb);
00818 
00819 
00820     /* ------------- Set up the output map pixd --------------- */
00821         /* Generate pixd, which is reduced by the factors (sx, sy). */
00822     w = pixGetWidth(pixs);
00823     h = pixGetHeight(pixs);
00824     wd = (w + sx - 1) / sx;
00825     hd = (h + sy - 1) / sy;
00826     pixd = pixCreate(wd, hd, 8);
00827 
00828         /* Note: we only compute map values in tiles that are complete.
00829          * In general, tiles at right and bottom edges will not be
00830          * complete, and we must fill them in later. */
00831     nx = w / sx;
00832     ny = h / sy;
00833     wpls = pixGetWpl(pixs);
00834     datas = pixGetData(pixs);
00835     wpld = pixGetWpl(pixd);
00836     datad = pixGetData(pixd);
00837     wplf = pixGetWpl(pixf);
00838     dataf = pixGetData(pixf);
00839     for (i = 0; i < ny; i++) {
00840         lines = datas + sy * i * wpls;
00841         linef = dataf + sy * i * wplf;
00842         lined = datad + i * wpld;
00843         for (j = 0; j < nx; j++) {
00844             delx = j * sx;
00845             sum = 0;
00846             count = 0;
00847             for (k = 0; k < sy; k++) {
00848                 for (m = 0; m < sx; m++) {
00849                     if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) {
00850                         sum += GET_DATA_BYTE(lines + k * wpls, delx + m);
00851                         count++;
00852                     }
00853                 }
00854             }
00855             if (count >= mincount) {
00856                 val8 = sum / count;
00857                 SET_DATA_BYTE(lined, j, val8);
00858             }
00859         }
00860     }
00861     pixDestroy(&pixf);
00862 
00863         /* If there is an optional mask with fg pixels, erase the previous
00864          * calculation for the corresponding map pixels, setting the
00865          * map values to 0.   Then, when all the map holes are filled,
00866          * these erased pixels will be set by the surrounding map values.
00867          *
00868          * The calculation here is relatively efficient: for each pixel
00869          * in pixd (which corresponds to a tile of mask pixels in pixim)
00870          * we look only at the pixel in pixim that is at the center
00871          * of the tile.  If the mask pixel is ON, we reset the map
00872          * pixel in pixd to 0, so that it can later be filled in. */
00873     pixims = NULL;
00874     if (pixim && fgpixels) {
00875         wim = pixGetWidth(pixim);
00876         him = pixGetHeight(pixim);
00877         dataim = pixGetData(pixim);
00878         wplim = pixGetWpl(pixim);
00879         for (i = 0; i < ny; i++) {
00880             yim = i * sy + sy / 2;
00881             if (yim >= him)
00882                 break;
00883             lineim = dataim + yim * wplim;
00884             for (j = 0; j < nx; j++) {
00885                 xim = j * sx + sx / 2;
00886                 if (xim >= wim)
00887                     break;
00888                 if (GET_DATA_BIT(lineim, xim))
00889                     pixSetPixel(pixd, j, i, 0);
00890             }
00891         }
00892     }
00893 
00894         /* Fill all the holes in the map. */
00895     if (pixFillMapHoles(pixd, nx, ny, L_FILL_BLACK)) {
00896         pixDestroy(&pixd);
00897         L_WARNING("can't make the map", procName);
00898         return 1;
00899     }
00900 
00901         /* Finally, for each connected region corresponding to the
00902          * 'image' mask, reset all pixels to their average value.
00903          * Each of these components represents an image (or part of one)
00904          * in the input, and this smooths the background values
00905          * in each of these regions. */
00906     if (pixim && fgpixels) {
00907         scalex = 1. / (l_float32)sx;
00908         scaley = 1. / (l_float32)sy;
00909         pixims = pixScaleBySampling(pixim, scalex, scaley);
00910         pixSmoothConnectedRegions(pixd, pixims, 2);
00911         pixDestroy(&pixims);
00912     }
00913 
00914     *ppixd = pixd;
00915     return 0;
00916 }
00917 
00918 
00919 /*!
00920  *  pixGetBackgroundRGBMap()
00921  *
00922  *      Input:  pixs (32 bpp rgb)
00923  *              pixim (<optional> 1 bpp 'image' mask; can be null; it
00924  *                     should not have all foreground pixels)
00925  *              pixg (<optional> 8 bpp grayscale version; can be null)
00926  *              sx, sy (tile size in pixels)
00927  *              thresh (threshold for determining foreground)
00928  *              mincount (min threshold on counts in a tile)
00929  *              &pixmr, &pixmg, &pixmb (<return> rgb maps)
00930  *      Return: 0 if OK, 1 on error
00931  *
00932  *  Notes:
00933  *      (1) If pixg, which is a grayscale version of pixs, is provided,
00934  *          use this internally to generate the foreground mask.
00935  *          Otherwise, a grayscale version of pixs will be generated
00936  *          from the green component only, used, and destroyed.
00937  */
00938 l_int32
00939 pixGetBackgroundRGBMap(PIX     *pixs,
00940                        PIX     *pixim,
00941                        PIX     *pixg,
00942                        l_int32  sx,
00943                        l_int32  sy,
00944                        l_int32  thresh,
00945                        l_int32  mincount,
00946                        PIX    **ppixmr,
00947                        PIX    **ppixmg,
00948                        PIX    **ppixmb)
00949 {
00950 l_int32    w, h, wm, hm, wim, him, wpls, wplim, wplf;
00951 l_int32    xim, yim, delx, nx, ny, i, j, k, m;
00952 l_int32    count, rsum, gsum, bsum, rval, gval, bval;
00953 l_int32    empty, fgpixels;
00954 l_uint32   pixel;
00955 l_uint32  *datas, *dataim, *dataf, *lines, *lineim, *linef;
00956 l_float32  scalex, scaley;
00957 PIX       *piximi, *pixgc, *pixb, *pixf, *pixims;
00958 PIX       *pixmr, *pixmg, *pixmb;
00959 
00960     PROCNAME("pixGetBackgroundRGBMap");
00961 
00962     if (!ppixmr || !ppixmg || !ppixmb)
00963         return ERROR_INT("&pixm* not all defined", procName, 1);
00964     *ppixmr = *ppixmg = *ppixmb = NULL;
00965     if (!pixs)
00966         return ERROR_INT("pixs not defined", procName, 1);
00967     if (pixGetDepth(pixs) != 32)
00968         return ERROR_INT("pixs not 32 bpp", procName, 1);
00969     if (pixim && pixGetDepth(pixim) != 1)
00970         return ERROR_INT("pixim not 1 bpp", procName, 1);
00971     if (sx < 4 || sy < 4)
00972         return ERROR_INT("sx and sy must be >= 4", procName, 1);
00973     if (mincount > sx * sy) {
00974         L_WARNING("mincount too large for tile size", procName);
00975         mincount = (sx * sy) / 3;
00976     }
00977 
00978         /* Evaluate the mask pixim and make sure it is not all foreground */
00979     fgpixels = 0;  /* boolean for existence of fg mask pixels */
00980     if (pixim) {
00981         piximi = pixInvert(NULL, pixim);  /* set non-'image' pixels to 1 */
00982         pixZero(piximi, &empty);
00983         pixDestroy(&piximi);
00984         if (empty)
00985             return ERROR_INT("pixim all fg; no background", procName, 1);
00986         pixZero(pixim, &empty);
00987         if (!empty)  /* there are fg pixels in pixim */
00988             fgpixels = 1;
00989     }
00990 
00991         /* Generate the foreground mask.  These pixels will be
00992          * ignored when computing the background values. */
00993     if (pixg)  /* use the input grayscale version if it is provided */
00994         pixgc = pixClone(pixg);
00995     else
00996         pixgc = pixConvertRGBToGrayFast(pixs);
00997     pixb = pixThresholdToBinary(pixgc, thresh);
00998     pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0);
00999     pixDestroy(&pixgc);
01000     pixDestroy(&pixb);
01001 
01002         /* Generate the output mask images */
01003     w = pixGetWidth(pixs);
01004     h = pixGetHeight(pixs);
01005     wm = (w + sx - 1) / sx;
01006     hm = (h + sy - 1) / sy;
01007     pixmr = pixCreate(wm, hm, 8);
01008     pixmg = pixCreate(wm, hm, 8);
01009     pixmb = pixCreate(wm, hm, 8);
01010 
01011     /* ------------- Set up the mapping images --------------- */
01012         /* Note: we only compute map values in tiles that are complete.
01013          * In general, tiles at right and bottom edges will not be
01014          * complete, and we must fill them in later. */
01015     nx = w / sx;
01016     ny = h / sy;
01017     wpls = pixGetWpl(pixs);
01018     datas = pixGetData(pixs);
01019     wplf = pixGetWpl(pixf);
01020     dataf = pixGetData(pixf);
01021     for (i = 0; i < ny; i++) {
01022         lines = datas + sy * i * wpls;
01023         linef = dataf + sy * i * wplf;
01024         for (j = 0; j < nx; j++) {
01025             delx = j * sx;
01026             rsum = gsum = bsum = 0;
01027             count = 0;
01028             for (k = 0; k < sy; k++) {
01029                 for (m = 0; m < sx; m++) {
01030                     if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) {
01031                         pixel = *(lines + k * wpls + delx + m);
01032                         rsum += (pixel >> 24);
01033                         gsum += ((pixel >> 16) & 0xff);
01034                         bsum += ((pixel >> 8) & 0xff);
01035                         count++;
01036                     }
01037                 }
01038             }
01039             if (count >= mincount) {
01040                 rval = rsum / count;
01041                 gval = gsum / count;
01042                 bval = bsum / count;
01043                 pixSetPixel(pixmr, j, i, rval);
01044                 pixSetPixel(pixmg, j, i, gval);
01045                 pixSetPixel(pixmb, j, i, bval);
01046             }
01047         }
01048     }
01049     pixDestroy(&pixf);
01050 
01051         /* If there is an optional mask with fg pixels, erase the previous
01052          * calculation for the corresponding map pixels, setting the
01053          * map values in each of the 3 color maps to 0.   Then, when
01054          * all the map holes are filled, these erased pixels will
01055          * be set by the surrounding map values. */
01056     if (pixim) {
01057         wim = pixGetWidth(pixim);
01058         him = pixGetHeight(pixim);
01059         dataim = pixGetData(pixim);
01060         wplim = pixGetWpl(pixim);
01061         for (i = 0; i < ny; i++) {
01062             yim = i * sy + sy / 2;
01063             if (yim >= him)
01064                 break;
01065             lineim = dataim + yim * wplim;
01066             for (j = 0; j < nx; j++) {
01067                 xim = j * sx + sx / 2;
01068                 if (xim >= wim)
01069                     break;
01070                 if (GET_DATA_BIT(lineim, xim)) {
01071                     pixSetPixel(pixmr, j, i, 0);
01072                     pixSetPixel(pixmg, j, i, 0);
01073                     pixSetPixel(pixmb, j, i, 0);
01074                 }
01075             }
01076         }
01077     }
01078 
01079     /* ----------------- Now fill in the holes ----------------------- */
01080     if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) ||
01081         pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) ||
01082         pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) {
01083         pixDestroy(&pixmr);
01084         pixDestroy(&pixmg);
01085         pixDestroy(&pixmb);
01086         L_WARNING("can't make the maps", procName);
01087         return 1;
01088     }
01089 
01090         /* Finally, for each connected region corresponding to the
01091          * fg mask, reset all pixels to their average value. */
01092     if (pixim && fgpixels) {
01093         scalex = 1. / (l_float32)sx;
01094         scaley = 1. / (l_float32)sy;
01095         pixims = pixScaleBySampling(pixim, scalex, scaley);
01096         pixSmoothConnectedRegions(pixmr, pixims, 2);
01097         pixSmoothConnectedRegions(pixmg, pixims, 2);
01098         pixSmoothConnectedRegions(pixmb, pixims, 2);
01099         pixDestroy(&pixims);
01100     }
01101 
01102     *ppixmr = pixmr;
01103     *ppixmg = pixmg;
01104     *ppixmb = pixmb;
01105     return 0;
01106 }
01107 
01108 
01109 /*!
01110  *  pixGetBackgroundGrayMapMorph()
01111  *
01112  *      Input:  pixs (8 bpp grayscale; not cmapped)
01113  *              pixim (<optional> 1 bpp 'image' mask; can be null; it
01114  *                     should not have all foreground pixels)
01115  *              reduction (factor at which closing is performed)
01116  *              size (of square Sel for the closing; use an odd number)
01117  *              &pixm (<return> grayscale map)
01118  *      Return: 0 if OK, 1 on error
01119  */
01120 l_int32
01121 pixGetBackgroundGrayMapMorph(PIX     *pixs,
01122                              PIX     *pixim,
01123                              l_int32  reduction,
01124                              l_int32  size,
01125                              PIX    **ppixm)
01126 {
01127 l_int32    nx, ny, empty, fgpixels;
01128 l_float32  scale;
01129 PIX       *pixm, *pixt1, *pixt2, *pixt3, *pixims;
01130 
01131     PROCNAME("pixGetBackgroundGrayMapMorph");
01132 
01133     if (!ppixm)
01134         return ERROR_INT("&pixm not defined", procName, 1);
01135     *ppixm = NULL;
01136     if (!pixs || pixGetDepth(pixs) != 8)
01137         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
01138     if (pixGetColormap(pixs))
01139         return ERROR_INT("pixs is colormapped", procName, 1);
01140     if (pixim && pixGetDepth(pixim) != 1)
01141         return ERROR_INT("pixim not 1 bpp", procName, 1);
01142 
01143         /* Evaluate the mask pixim and make sure it is not all foreground. */
01144     fgpixels = 0;  /* boolean for existence of fg mask pixels */
01145     if (pixim) {
01146         pixInvert(pixim, pixim);  /* set background pixels to 1 */
01147         pixZero(pixim, &empty);
01148         if (empty)
01149             return ERROR_INT("pixim all fg; no background", procName, 1);
01150         pixInvert(pixim, pixim);  /* revert to original mask */
01151         pixZero(pixim, &empty);
01152         if (!empty)  /* there are fg pixels in pixim */
01153             fgpixels = 1;
01154     }
01155 
01156         /* Downscale as requested and do the closing to get the background. */
01157     scale = 1. / (l_float32)reduction;
01158     pixt1 = pixScaleBySampling(pixs, scale, scale);
01159     pixt2 = pixCloseGray(pixt1, size, size);
01160     pixt3 = pixExtendByReplication(pixt2, 1, 1);
01161 
01162         /* Downscale the image mask, if any, and remove it from the
01163          * background.  These pixels will be filled in (twice). */
01164     pixims = NULL;
01165     if (pixim) {
01166         pixims = pixScale(pixim, scale, scale);
01167         pixm = pixConvertTo8(pixims, FALSE);
01168         pixAnd(pixm, pixm, pixt3);
01169     } 
01170     else
01171         pixm = pixClone(pixt3);
01172     pixDestroy(&pixt1);
01173     pixDestroy(&pixt2);
01174     pixDestroy(&pixt3);
01175 
01176         /* Fill all the holes in the map. */
01177     nx = pixGetWidth(pixs) / reduction;
01178     ny = pixGetHeight(pixs) / reduction;
01179     if (pixFillMapHoles(pixm, nx, ny, L_FILL_BLACK)) {
01180         pixDestroy(&pixm);
01181         L_WARNING("can't make the map", procName);
01182         return 1;
01183     }
01184 
01185         /* Finally, for each connected region corresponding to the
01186          * fg mask, reset all pixels to their average value. */
01187     if (pixim && fgpixels) {
01188         pixSmoothConnectedRegions(pixm, pixims, 2);
01189         pixDestroy(&pixims);
01190     }
01191 
01192     *ppixm = pixm;
01193     return 0;
01194 }
01195 
01196 
01197 /*!
01198  *  pixGetBackgroundRGBMapMorph()
01199  *
01200  *      Input:  pixs (32 bpp rgb)
01201  *              pixim (<optional> 1 bpp 'image' mask; can be null; it
01202  *                     should not have all foreground pixels)
01203  *              reduction (factor at which closing is performed)
01204  *              size (of square Sel for the closing; use an odd number)
01205  *              &pixmr (<return> red component map)
01206  *              &pixmg (<return> green component map)
01207  *              &pixmb (<return> blue component map)
01208  *      Return: 0 if OK, 1 on error
01209  */
01210 l_int32
01211 pixGetBackgroundRGBMapMorph(PIX     *pixs,
01212                             PIX     *pixim,
01213                             l_int32  reduction,
01214                             l_int32  size,
01215                             PIX    **ppixmr,
01216                             PIX    **ppixmg,
01217                             PIX    **ppixmb)
01218 {
01219 l_int32    nx, ny, empty, fgpixels;
01220 l_float32  scale;
01221 PIX       *pixm, *pixmr, *pixmg, *pixmb, *pixt1, *pixt2, *pixt3, *pixims;
01222 
01223     PROCNAME("pixGetBackgroundRGBMapMorph");
01224 
01225     if (!ppixmr || !ppixmg || !ppixmb)
01226         return ERROR_INT("&pixm* not all defined", procName, 1);
01227     *ppixmr = *ppixmg = *ppixmb = NULL;
01228     if (!pixs)
01229         return ERROR_INT("pixs not defined", procName, 1);
01230     if (pixGetDepth(pixs) != 32)
01231         return ERROR_INT("pixs not 32 bpp", procName, 1);
01232     if (pixim && pixGetDepth(pixim) != 1)
01233         return ERROR_INT("pixim not 1 bpp", procName, 1);
01234 
01235         /* Generate an 8 bpp version of the image mask, if it exists */
01236     scale = 1. / (l_float32)reduction;
01237     pixm = NULL;
01238     if (pixim) {
01239         pixims = pixScale(pixim, scale, scale);
01240         pixm = pixConvertTo8(pixims, FALSE);
01241     }
01242 
01243         /* Evaluate the mask pixim and make sure it is not all foreground. */
01244     fgpixels = 0;  /* boolean for existence of fg mask pixels */
01245     if (pixim) {
01246         pixInvert(pixim, pixim);  /* set background pixels to 1 */
01247         pixZero(pixim, &empty);
01248         if (empty)
01249             return ERROR_INT("pixim all fg; no background", procName, 1);
01250         pixInvert(pixim, pixim);  /* revert to original mask */
01251         pixZero(pixim, &empty);
01252         if (!empty)  /* there are fg pixels in pixim */
01253             fgpixels = 1;
01254     }
01255 
01256         /* Downscale as requested and do the closing to get the background.
01257          * Then remove the image mask pixels from the background.  They
01258          * will be filled in (twice) later.  Do this for all 3 components. */
01259     pixt1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_RED);
01260     pixt2 = pixCloseGray(pixt1, size, size);
01261     pixt3 = pixExtendByReplication(pixt2, 1, 1);
01262     if (pixim)
01263         pixmr = pixAnd(NULL, pixm, pixt3);
01264     else
01265         pixmr = pixClone(pixt3);
01266     pixDestroy(&pixt1);
01267     pixDestroy(&pixt2);
01268     pixDestroy(&pixt3);
01269 
01270     pixt1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_GREEN);
01271     pixt2 = pixCloseGray(pixt1, size, size);
01272     pixt3 = pixExtendByReplication(pixt2, 1, 1);
01273     if (pixim)
01274         pixmg = pixAnd(NULL, pixm, pixt3);
01275     else
01276         pixmg = pixClone(pixt3);
01277     pixDestroy(&pixt1);
01278     pixDestroy(&pixt2);
01279     pixDestroy(&pixt3);
01280 
01281     pixt1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_BLUE);
01282     pixt2 = pixCloseGray(pixt1, size, size);
01283     pixt3 = pixExtendByReplication(pixt2, 1, 1);
01284     if (pixim)
01285         pixmb = pixAnd(NULL, pixm, pixt3);
01286     else
01287         pixmb = pixClone(pixt3);
01288     pixDestroy(&pixm);
01289     pixDestroy(&pixt1);
01290     pixDestroy(&pixt2);
01291     pixDestroy(&pixt3);
01292 
01293         /* Fill all the holes in the three maps. */
01294     nx = pixGetWidth(pixs) / reduction;
01295     ny = pixGetHeight(pixs) / reduction;
01296     if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) ||
01297         pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) ||
01298         pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) {
01299         pixDestroy(&pixmr);
01300         pixDestroy(&pixmg);
01301         pixDestroy(&pixmb);
01302         L_WARNING("can't make the maps", procName);
01303         return 1;
01304     }
01305 
01306         /* Finally, for each connected region corresponding to the
01307          * fg mask in each component, reset all pixels to their
01308          * average value. */
01309     if (pixim && fgpixels) {
01310         pixSmoothConnectedRegions(pixmr, pixims, 2);
01311         pixSmoothConnectedRegions(pixmg, pixims, 2);
01312         pixSmoothConnectedRegions(pixmb, pixims, 2);
01313         pixDestroy(&pixims);
01314     }
01315 
01316     *ppixmr = pixmr;
01317     *ppixmg = pixmg;
01318     *ppixmb = pixmb;
01319     return 0;
01320 }
01321 
01322 
01323 /*!
01324  *  pixFillMapHoles()
01325  *
01326  *      Input:  pix (8 bpp; a map, with one pixel for each tile in
01327  *              a larger image)
01328  *              nx (number of horizontal pixel tiles that are entirely
01329  *                  covered with pixels in the original source image)
01330  *              ny (ditto for the number of vertical pixel tiles)
01331  *              filltype (L_FILL_WHITE or L_FILL_BLACK)
01332  *      Return: 0 if OK, 1 on error
01333  *
01334  *  Notes:
01335  *      (1) This is an in-place operation on pix (the map).  pix is 
01336  *          typically a low-resolution version of some other image
01337  *          from which it was derived, where each pixel in pix
01338  *          corresponds to a rectangular tile (say, m x n) of pixels
01339  *          in the larger image.  All we need to know about the larger
01340  *          image is whether or not the rightmost column and bottommost
01341  *          row of pixels in pix correspond to tiles that are
01342  *          only partially covered by pixels in the larger image.
01343  *      (2) Typically, some number of pixels in the input map are
01344  *          not known, and their values must be determined by near
01345  *          pixels that are known.  These unknown pixels are the 'holes'.
01346  *          They can take on only two values, 0 and 255, and the
01347  *          instruction about which to fill is given by the filltype flag.
01348  *      (3) The "holes" can come from two sources.  The first is when there
01349  *          are not enough foreground or background pixels in a tile;
01350  *          the second is when a tile is at least partially covered
01351  *          by an image mask.  If we're filling holes in a fg mask,
01352  *          the holes are initialized to black (0) and use L_FILL_BLACK.
01353  *          For filling holes in a bg mask, initialize the holes to
01354  *          white (255) and use L_FILL_WHITE.
01355  *      (4) If w is the map width, nx = w or nx = w - 1; ditto for h and ny.
01356  */
01357 l_int32
01358 pixFillMapHoles(PIX     *pix,
01359                 l_int32  nx,
01360                 l_int32  ny,
01361                 l_int32  filltype)
01362 {
01363 l_int32   w, h, y, nmiss, goodcol, i, j, found, ival, valtest;
01364 l_uint32  val, lastval;
01365 NUMA     *na;  /* indicates if there is any data in the column */
01366 PIX      *pixt;
01367 
01368     PROCNAME("pixFillMapHoles");
01369 
01370     if (!pix || pixGetDepth(pix) != 8)
01371         return ERROR_INT("pix not defined or not 8 bpp", procName, 1);
01372     if (pixGetColormap(pix))
01373         return ERROR_INT("pix is colormapped", procName, 1);
01374 
01375     /* ------------- Fill holes in the mapping image columns ----------- */
01376     pixGetDimensions(pix, &w, &h, NULL);
01377     na = numaCreate(0);  /* holds flag for which columns have data */
01378     nmiss = 0;
01379     valtest = (filltype == L_FILL_WHITE) ? 255 : 0;
01380     for (j = 0; j < nx; j++) {  /* do it by columns */
01381         found = FALSE;
01382         for (i = 0; i < ny; i++) {
01383             pixGetPixel(pix, j, i, &val);
01384             if (val != valtest) {
01385                 y = i;
01386                 found = TRUE;
01387                 break;
01388             }
01389         }
01390         if (found == FALSE) {
01391             numaAddNumber(na, 0);  /* no data in the column */
01392             nmiss++;
01393         }
01394         else {
01395             numaAddNumber(na, 1);  /* data in the column */
01396             for (i = y - 1; i >= 0; i--)  /* replicate upwards to top */
01397                 pixSetPixel(pix, j, i, val);
01398             pixGetPixel(pix, j, 0, &lastval);
01399             for (i = 1; i < h; i++) {  /* set going down to bottom */
01400                 pixGetPixel(pix, j, i, &val);
01401                 if (val == valtest)
01402                     pixSetPixel(pix, j, i, lastval);
01403                 else
01404                     lastval = val;
01405             }
01406         }
01407     }
01408     numaAddNumber(na, 0);  /* last column */
01409     
01410     if (nmiss == nx) {  /* no data in any column! */
01411         numaDestroy(&na);
01412         L_WARNING("no bg found; no data in any column", procName);
01413         return 1;
01414     }
01415 
01416     /* ---------- Fill in missing columns by replication ----------- */
01417     if (nmiss > 0) {  /* replicate columns */
01418         pixt = pixCopy(NULL, pix);
01419             /* Find the first good column */
01420         goodcol = 0;
01421         for (j = 0; j < w; j++) {
01422             numaGetIValue(na, j, &ival);
01423             if (ival == 1) {
01424                 goodcol = j;
01425                 break;
01426             }
01427         }
01428         if (goodcol > 0) {  /* copy cols backward */
01429             for (j = goodcol - 1; j >= 0; j--) {
01430                 pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j + 1, 0);
01431                 pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0);
01432             }
01433         }
01434         for (j = goodcol + 1; j < w; j++) {   /* copy cols forward */
01435             numaGetIValue(na, j, &ival);
01436             if (ival == 0) {
01437                     /* Copy the column to the left of j */
01438                 pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j - 1, 0);
01439                 pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0);
01440             }
01441         }
01442         pixDestroy(&pixt);
01443     }
01444     if (w > nx) {  /* replicate the last column */
01445         for (i = 0; i < h; i++) {
01446             pixGetPixel(pix, w - 2, i, &val);
01447             pixSetPixel(pix, w - 1, i, val);
01448         }
01449     }
01450     
01451     numaDestroy(&na);
01452     return 0;
01453 }
01454 
01455 
01456 /*!
01457  *  pixExtendByReplication()
01458  *
01459  *      Input:  pixs (8 bpp)
01460  *              addw (number of extra pixels horizontally to add)
01461  *              addh (number of extra pixels vertically to add)
01462  *      Return: pixd (extended with replicated pixel values), or null on error
01463  *
01464  *  Notes:
01465  *      (1) The pixel values are extended to the left and down, as required.
01466  */
01467 PIX *
01468 pixExtendByReplication(PIX     *pixs,
01469                        l_int32  addw,
01470                        l_int32  addh)
01471 {
01472 l_int32   w, h, i, j;
01473 l_uint32  val;
01474 PIX      *pixd;
01475 
01476     PROCNAME("pixExtendByReplication");
01477 
01478     if (!pixs || pixGetDepth(pixs) != 8)
01479         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
01480 
01481     if (addw == 0 && addh == 0)
01482         return pixCopy(NULL, pixs);
01483 
01484     pixGetDimensions(pixs, &w, &h, NULL);
01485     if ((pixd = pixCreate(w + addw, h + addh, 8)) == NULL)
01486         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01487     pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0);
01488 
01489     if (addw > 0) {
01490         for (i = 0; i < h; i++) {
01491             pixGetPixel(pixd, w - 1, i, &val);
01492             for (j = 0; j < addw; j++)
01493                 pixSetPixel(pixd, w + j, i, val);
01494         }
01495     }
01496 
01497     if (addh > 0) {
01498         for (j = 0; j < w + addw; j++) {
01499             pixGetPixel(pixd, j, h - 1, &val);
01500             for (i = 0; i < addh; i++)
01501                 pixSetPixel(pixd, j, h + i, val);
01502         }
01503     }
01504 
01505     return pixd;
01506 }
01507 
01508 
01509 /*!
01510  *  pixSmoothConnectedRegions()
01511  *
01512  *      Input:  pixs (8 bpp grayscale; no colormap)
01513  *              pixm (<optional> 1 bpp; if null, this is a no-op)
01514  *              factor (subsampling factor for getting average; >= 1)
01515  *      Return: 0 if OK, 1 on error
01516  *
01517  *  Notes:
01518  *      (1) The pixels in pixs corresponding to those in each
01519  *          8-connected region in the mask are set to the average value.
01520  *      (2) This is required for adaptive mapping to avoid the
01521  *          generation of stripes in the background map, due to
01522  *          variations in the pixel values near the edges of mask regions.
01523  *      (3) This function is optimized for background smoothing, where
01524  *          there are a relatively small number of components.  It will
01525  *          be inefficient if used where there are many small components.
01526  */
01527 l_int32
01528 pixSmoothConnectedRegions(PIX     *pixs,
01529                           PIX     *pixm,
01530                           l_int32  factor)
01531 {
01532 l_int32    empty, i, n, x, y;
01533 l_float32  aveval;
01534 BOXA      *boxa;
01535 PIX       *pixmc;
01536 PIXA      *pixa;
01537 
01538     PROCNAME("pixSmoothConnectedRegions");
01539 
01540     if (!pixs || pixGetDepth(pixs) != 8)
01541         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
01542     if (pixGetColormap(pixs))
01543         return ERROR_INT("pixs has colormap", procName, 1);
01544     if (!pixm) {
01545         L_INFO("pixm not defined", procName);
01546         return 0;
01547     }
01548     if (pixGetDepth(pixm) != 1)
01549         return ERROR_INT("pixm not 1 bpp", procName, 1);
01550     pixZero(pixm, &empty);
01551     if (empty) {
01552         L_INFO("pixm has no fg pixels; nothing to do", procName);
01553         return 0;
01554     }
01555 
01556     boxa = pixConnComp(pixm, &pixa, 8);
01557     n = boxaGetCount(boxa);
01558     for (i = 0; i < n; i++) {
01559         if ((pixmc = pixaGetPix(pixa, i, L_CLONE)) == NULL) {
01560             L_WARNING("missing pixmc!", procName);
01561             continue;
01562         }
01563         boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL);
01564         pixGetAverageMasked(pixs, pixmc, x, y, factor, L_MEAN_ABSVAL, &aveval);
01565         pixPaintThroughMask(pixs, pixmc, x, y, (l_int32)aveval);
01566         pixDestroy(&pixmc);
01567     }
01568 
01569     boxaDestroy(&boxa);
01570     pixaDestroy(&pixa);
01571     return 0;
01572 }
01573 
01574 
01575 /*------------------------------------------------------------------*
01576  *                 Measurement of local foreground                  *
01577  *------------------------------------------------------------------*/
01578 #if 0    /* Not working properly: do not use */
01579 
01580 /*!
01581  *  pixGetForegroundGrayMap()
01582  *
01583  *      Input:  pixs (8 bpp)
01584  *              pixim (<optional> 1 bpp 'image' mask; can be null)
01585  *              sx, sy (src tile size, in pixels)
01586  *              thresh (threshold for determining foreground)
01587  *              &pixd (<return> 8 bpp grayscale map)
01588  *      Return: 0 if OK, 1 on error
01589  *
01590  *  Notes:
01591  *      (1) Each (sx, sy) tile of pixs gets mapped to one pixel in pixd.
01592  *      (2) pixd is the estimate of the fg (darkest) value within each tile.
01593  *      (3) All pixels in pixd that are in 'image' regions, as specified
01594  *          by pixim, are given the background value 0.
01595  *      (4) For pixels in pixd that can't directly be given a fg value,
01596  *          the value is inferred by propagating from neighboring pixels.
01597  *      (5) In practice, pixd can be used to normalize the fg, and
01598  *          it can be done after background normalization.
01599  *      (6) The overall procedure is:
01600  *            - reduce 2x by sampling
01601  *            - paint all 'image' pixels white, so that they don't
01602  *              participate in the Min reduction
01603  *            - do a further (sx, sy) Min reduction -- think of
01604  *              it as a large opening followed by subsampling by the
01605  *              reduction factors
01606  *            - threshold the result to identify fg, and set the
01607  *              bg pixels to 255 (these are 'holes')
01608  *            - fill holes by propagation from fg values
01609  *            - replicatively expand by 2x, arriving at the final
01610  *              resolution of pixd
01611  *            - smooth with a 17x17 kernel
01612  *            - paint the 'image' regions black
01613  */
01614 l_int32
01615 pixGetForegroundGrayMap(PIX     *pixs,
01616                         PIX     *pixim,
01617                         l_int32  sx,
01618                         l_int32  sy,
01619                         l_int32  thresh,
01620                         PIX    **ppixd)
01621 {
01622 l_int32  w, h, d, wd, hd;
01623 l_int32  empty, fgpixels;
01624 PIX     *pixd, *piximi, *pixim2, *pixims, *pixs2, *pixb, *pixt1, *pixt2, *pixt3;
01625 
01626     PROCNAME("pixGetForegroundGrayMap");
01627 
01628     if (!ppixd)
01629         return ERROR_INT("&pixd not defined", procName, 1);
01630     *ppixd = NULL;
01631     if (!pixs)
01632         return ERROR_INT("pixs not defined", procName, 1);
01633     pixGetDimensions(pixs, &w, &h, &d);
01634     if (d != 8)
01635         return ERROR_INT("pixs not 8 bpp", procName, 1);
01636     if (pixim && pixGetDepth(pixim) != 1)
01637         return ERROR_INT("pixim not 1 bpp", procName, 1);
01638     if (sx < 2 || sy < 2)
01639         return ERROR_INT("sx and sy must be >= 2", procName, 1);
01640 
01641         /* Generate pixd, which is reduced by the factors (sx, sy). */
01642     wd = (w + sx - 1) / sx;
01643     hd = (h + sy - 1) / sy;
01644     pixd = pixCreate(wd, hd, 8);
01645     *ppixd = pixd;
01646 
01647         /* Evaluate the 'image' mask, pixim.  If it is all fg,
01648          * the output pixd has all pixels with value 0. */
01649     fgpixels = 0;  /* boolean for existence of fg pixels in the image mask. */
01650     if (pixim) {
01651         piximi = pixInvert(NULL, pixim);  /* set non-image pixels to 1 */
01652         pixZero(piximi, &empty);
01653         pixDestroy(&piximi);
01654         if (empty)  /* all 'image'; return with all pixels set to 0 */
01655             return 0;
01656         pixZero(pixim, &empty);
01657         if (!empty)  /* there are fg pixels in pixim */
01658             fgpixels = 1;
01659     }
01660 
01661         /* 2x subsampling; paint white through 'image' mask. */
01662     pixs2 = pixScaleBySampling(pixs, 0.5, 0.5);
01663     if (pixim && fgpixels) {
01664         pixim2 = pixReduceBinary2(pixim, NULL);
01665         pixPaintThroughMask(pixs2, pixim2, 0, 0, 255);
01666         pixDestroy(&pixim2);
01667     }
01668 
01669         /* Min (erosion) downscaling; total reduction (4 sx, 4 sy). */
01670     pixt1 = pixScaleGrayMinMax(pixs2, sx, sy, L_CHOOSE_MIN);
01671 
01672 /*    pixDisplay(pixt1, 300, 200); */
01673 
01674         /* Threshold to identify fg; paint bg pixels to white. */
01675     pixb = pixThresholdToBinary(pixt1, thresh);  /* fg pixels */
01676     pixInvert(pixb, pixb);
01677     pixPaintThroughMask(pixt1, pixb, 0, 0, 255);
01678     pixDestroy(&pixb);
01679 
01680         /* Replicative expansion by 2x to (sx, sy). */
01681     pixt2 = pixExpandReplicate(pixt1, 2);
01682 
01683 /*    pixDisplay(pixt2, 500, 200); */
01684 
01685         /* Fill holes in the fg by propagation */
01686     pixFillMapHoles(pixt2, w / sx, h / sy, L_FILL_WHITE);
01687 
01688 /*    pixDisplay(pixt2, 700, 200); */
01689 
01690         /* Smooth with 17x17 kernel. */
01691     pixt3 = pixBlockconv(pixt2, 8, 8);
01692     pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixt3, 0, 0);
01693 
01694         /* Paint the image parts black. */
01695     pixims = pixScaleBySampling(pixim, 1. / sx, 1. / sy);
01696     pixPaintThroughMask(pixd, pixims, 0, 0, 0);
01697 
01698     pixDestroy(&pixs2);
01699     pixDestroy(&pixt1);
01700     pixDestroy(&pixt2);
01701     pixDestroy(&pixt3);
01702     return 0;
01703 }
01704 #endif   /* Not working properly: do not use */
01705 
01706 
01707 /*------------------------------------------------------------------*
01708  *                  Generate inverted background map                *
01709  *------------------------------------------------------------------*/
01710 /*!
01711  *  pixGetInvBackgroundMap()
01712  *
01713  *      Input:  pixs (8 bpp grayscale; no colormap)
01714  *              bgval (target bg val; typ. > 128)
01715  *              smoothx (half-width of block convolution kernel width)
01716  *              smoothy (half-width of block convolution kernel height)
01717  *      Return: pixd (16 bpp), or null on error
01718  *
01719  *  Note:
01720  *     - bgval should typically be > 120 and < 240
01721  *     - pixd is a normalization image; the original image is
01722  *       multiplied by pixd and the result is divided by 256.
01723  */
01724 PIX *
01725 pixGetInvBackgroundMap(PIX     *pixs,
01726                        l_int32  bgval,
01727                        l_int32  smoothx,
01728                        l_int32  smoothy)
01729 {
01730 l_int32    w, h, wplsm, wpld, i, j;
01731 l_int32    val, val16;
01732 l_uint32  *datasm, *datad, *linesm, *lined;
01733 PIX       *pixsm, *pixd;
01734 
01735     PROCNAME("pixGetInvBackgroundMap");
01736 
01737     if (!pixs || pixGetDepth(pixs) != 8)
01738         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
01739     if (pixGetColormap(pixs))
01740         return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL);
01741     pixGetDimensions(pixs, &w, &h, NULL);
01742     if (w < 5 || h < 5)
01743         return (PIX *)ERROR_PTR("w and h must be >= 5", procName, NULL);
01744 
01745         /* smooth the map image */
01746     pixsm = pixBlockconv(pixs, smoothx, smoothy);
01747     datasm = pixGetData(pixsm);
01748     wplsm = pixGetWpl(pixsm);
01749 
01750         /* invert the map image, scaling up to preserve dynamic range */
01751     pixd = pixCreate(w, h, 16);
01752     datad = pixGetData(pixd);
01753     wpld = pixGetWpl(pixd);
01754     for (i = 0; i < h; i++) {
01755         linesm = datasm + i * wplsm;
01756         lined = datad + i * wpld;
01757         for (j = 0; j < w; j++) {
01758             val = GET_DATA_BYTE(linesm, j);
01759             if (val > 0)
01760                 val16 = (256 * bgval) / val;
01761             else {  /* shouldn't happen */
01762                 L_WARNING("smoothed bg has 0 pixel!", procName);
01763                 val16 = bgval / 2;
01764             }
01765             SET_DATA_TWO_BYTES(lined, j, val16);
01766         }
01767     }
01768 
01769     pixDestroy(&pixsm);
01770     return pixd;
01771 }
01772 
01773 
01774 /*------------------------------------------------------------------*
01775  *                    Apply background map to image                 *
01776  *------------------------------------------------------------------*/
01777 /*!
01778  *  pixApplyInvBackgroundGrayMap()
01779  *
01780  *      Input:  pixs (8 bpp grayscale; no colormap)
01781  *              pixm (16 bpp, inverse background map)
01782  *              sx (tile width in pixels)
01783  *              sy (tile height in pixels)
01784  *      Return: pixd (8 bpp), or null on error
01785  */
01786 PIX *
01787 pixApplyInvBackgroundGrayMap(PIX     *pixs,
01788                              PIX     *pixm,
01789                              l_int32  sx,
01790                              l_int32  sy)
01791 {
01792 l_int32    w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff;
01793 l_int32    vals, vald;
01794 l_uint32   val16;
01795 l_uint32  *datas, *datad, *lines, *lined, *flines, *flined;
01796 PIX       *pixd;
01797 
01798     PROCNAME("pixApplyInvBackgroundGrayMap");
01799 
01800     if (!pixs || pixGetDepth(pixs) != 8)
01801         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
01802     if (pixGetColormap(pixs))
01803         return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL);
01804     if (!pixm || pixGetDepth(pixm) != 16)
01805         return (PIX *)ERROR_PTR("pixm undefined or not 16 bpp", procName, NULL);
01806     if (sx == 0 || sy == 0)
01807         return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL);
01808 
01809     datas = pixGetData(pixs);
01810     wpls = pixGetWpl(pixs);
01811     pixGetDimensions(pixs, &w, &h, NULL);
01812     pixGetDimensions(pixm, &wm, &hm, NULL);
01813     pixd = pixCreateTemplate(pixs);
01814     datad = pixGetData(pixd);
01815     wpld = pixGetWpl(pixd);
01816     for (i = 0; i < hm; i++) {
01817         lines = datas + sy * i * wpls;
01818         lined = datad + sy * i * wpld;
01819         yoff = sy * i;
01820         for (j = 0; j < wm; j++) {
01821             pixGetPixel(pixm, j, i, &val16);
01822             xoff = sx * j;
01823             for (k = 0; k < sy && yoff + k < h; k++) {
01824                 flines = lines + k * wpls;
01825                 flined = lined + k * wpld;
01826                 for (m = 0; m < sx && xoff + m < w; m++) {
01827                     vals = GET_DATA_BYTE(flines, xoff + m);
01828                     vald = (vals * val16) / 256;
01829                     vald = L_MIN(vald, 255);
01830                     SET_DATA_BYTE(flined, xoff + m, vald);
01831                 }
01832             }
01833         }
01834     }
01835 
01836     return pixd;
01837 }
01838 
01839 
01840 /*!
01841  *  pixApplyInvBackgroundRGBMap()
01842  *
01843  *      Input:  pixs (32 bpp rbg)
01844  *              pixmr (16 bpp, red inverse background map)
01845  *              pixmg (16 bpp, green inverse background map)
01846  *              pixmb (16 bpp, blue inverse background map)
01847  *              sx (tile width in pixels)
01848  *              sy (tile height in pixels)
01849  *      Return: pixd (32 bpp rbg), or null on error
01850  */
01851 PIX *
01852 pixApplyInvBackgroundRGBMap(PIX     *pixs,
01853                             PIX     *pixmr,
01854                             PIX     *pixmg,
01855                             PIX     *pixmb,
01856                             l_int32  sx,
01857                             l_int32  sy)
01858 {
01859 l_int32    w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff;
01860 l_int32    rvald, gvald, bvald;
01861 l_uint32   vals;
01862 l_uint32   rval16, gval16, bval16;
01863 l_uint32  *datas, *datad, *lines, *lined, *flines, *flined;
01864 PIX       *pixd;
01865 
01866     PROCNAME("pixApplyInvBackgroundRGBMap");
01867 
01868     if (!pixs)
01869         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01870     if (pixGetDepth(pixs) != 32)
01871         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
01872     if (!pixmr || !pixmg || !pixmb)
01873         return (PIX *)ERROR_PTR("pix maps not all defined", procName, NULL);
01874     if (pixGetDepth(pixmr) != 16 || pixGetDepth(pixmg) != 16 ||
01875         pixGetDepth(pixmb) != 16)
01876         return (PIX *)ERROR_PTR("pix maps not all 16 bpp", procName, NULL);
01877     if (sx == 0 || sy == 0)
01878         return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL);
01879 
01880     datas = pixGetData(pixs);
01881     wpls = pixGetWpl(pixs);
01882     w = pixGetWidth(pixs);
01883     h = pixGetHeight(pixs);
01884     wm = pixGetWidth(pixmr);
01885     hm = pixGetHeight(pixmr);
01886     pixd = pixCreateTemplate(pixs);
01887     datad = pixGetData(pixd);
01888     wpld = pixGetWpl(pixd);
01889     for (i = 0; i < hm; i++) {
01890         lines = datas + sy * i * wpls;
01891         lined = datad + sy * i * wpld;
01892         yoff = sy * i;
01893         for (j = 0; j < wm; j++) {
01894             pixGetPixel(pixmr, j, i, &rval16);
01895             pixGetPixel(pixmg, j, i, &gval16);
01896             pixGetPixel(pixmb, j, i, &bval16);
01897             xoff = sx * j;
01898             for (k = 0; k < sy && yoff + k < h; k++) {
01899                 flines = lines + k * wpls;
01900                 flined = lined + k * wpld;
01901                 for (m = 0; m < sx && xoff + m < w; m++) {
01902                     vals = *(flines + xoff + m);
01903                     rvald = ((vals >> 24) * rval16) / 256;
01904                     rvald = L_MIN(rvald, 255);
01905                     gvald = (((vals >> 16) & 0xff) * gval16) / 256;
01906                     gvald = L_MIN(gvald, 255);
01907                     bvald = (((vals >> 8) & 0xff) * bval16) / 256;
01908                     bvald = L_MIN(bvald, 255);
01909                     composeRGBPixel(rvald, gvald, bvald, flined + xoff + m);
01910                 }
01911             }
01912         }
01913     }
01914 
01915     return pixd;
01916 }
01917 
01918 
01919 /*------------------------------------------------------------------*
01920  *                         Apply variable map                       *
01921  *------------------------------------------------------------------*/
01922 /*!
01923  *  pixApplyVariableGrayMap()
01924  *
01925  *      Input:  pixs (8 bpp)
01926  *              pixg (8 bpp, variable map)
01927  *              target (typ. 128 for threshold)
01928  *      Return: pixd (8 bpp), or null on error
01929  *
01930  *  Notes:
01931  *      (1) Suppose you have an image that you want to transform based
01932  *          on some photometric measurement at each point, such as the
01933  *          threshold value for binarization.  Representing the photometric
01934  *          measurement as an image pixg, you can threshold in input image
01935  *          using pixVarThresholdToBinary().  Alternatively, you can map
01936  *          the input image pointwise so that the threshold over the
01937  *          entire image becomes a constant, such as 128.  For example,
01938  *          if a pixel in pixg is 150 and the target is 128, the
01939  *          corresponding pixel in pixs is mapped linearly to a value
01940  *          (128/150) of the input value.  If the resulting mapped image
01941  *          pixd were then thresholded at 128, you would obtain the
01942  *          same result as a direct binarization using pixg with
01943  *          pixVarThresholdToBinary().
01944  *      (2) The sizes of pixs and pixg must be equal.
01945  */
01946 PIX *
01947 pixApplyVariableGrayMap(PIX     *pixs,
01948                         PIX     *pixg,
01949                         l_int32  target)
01950 {
01951 l_int32    i, j, w, h, d, wpls, wplg, wpld, vals, valg, vald;
01952 l_uint8   *lut;
01953 l_uint32  *datas, *datag, *datad, *lines, *lineg, *lined;
01954 l_float32  fval;
01955 PIX       *pixd;
01956 
01957     PROCNAME("pixApplyVariableGrayMap");
01958 
01959     if (!pixs)
01960         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01961     if (!pixg)
01962         return (PIX *)ERROR_PTR("pixg not defined", procName, NULL);
01963     if (!pixSizesEqual(pixs, pixg))
01964         return (PIX *)ERROR_PTR("pix sizes not equal", procName, NULL);
01965     pixGetDimensions(pixs, &w, &h, &d);
01966     if (d != 8)
01967         return (PIX *)ERROR_PTR("depth not 8 bpp", procName, NULL);
01968 
01969         /* Generate a LUT for the mapping if the image is large enough
01970          * to warrant the overhead.  The LUT is of size 2^16.  For the
01971          * index to the table, get the MSB from pixs and the LSB from pixg.
01972          * Note: this LUT is bigger than the typical 32K L1 cache, so
01973          * we expect cache misses.  L2 latencies are about 5ns.  But 
01974          * division is slooooow.  For large images, this function is about
01975          * 4x faster when using the LUT.  C'est la vie.  */
01976     lut = NULL;
01977     if (w * h > 100000) {  /* more pixels than 2^16 */
01978         if ((lut = (l_uint8 *)CALLOC(0x10000, sizeof(l_uint8))) == NULL)
01979             return (PIX *)ERROR_PTR("lut not made", procName, NULL);
01980         for (i = 0; i < 256; i++) {
01981             for (j = 0; j < 256; j++) {
01982                 fval = (l_float32)(i * target) / (j + 0.5);
01983                 lut[(i << 8) + j] = L_MIN(255, (l_int32)(fval + 0.5));
01984             }
01985         }
01986     }
01987 
01988     pixd = pixCreateNoInit(w, h, 8);
01989     datad = pixGetData(pixd);
01990     wpld = pixGetWpl(pixd);
01991     datas = pixGetData(pixs);
01992     wpls = pixGetWpl(pixs);
01993     datag = pixGetData(pixg);
01994     wplg = pixGetWpl(pixg);
01995     for (i = 0; i < h; i++) {
01996         lines = datas + i * wpls;
01997         lineg = datag + i * wplg;
01998         lined = datad + i * wpld;
01999         if (lut) {
02000             for (j = 0; j < w; j++) {
02001                 vals = GET_DATA_BYTE(lines, j);
02002                 valg = GET_DATA_BYTE(lineg, j);
02003                 vald = lut[(vals << 8) + valg];
02004                 SET_DATA_BYTE(lined, j, vald);
02005             }
02006         }
02007         else {
02008             for (j = 0; j < w; j++) {
02009                 vals = GET_DATA_BYTE(lines, j);
02010                 valg = GET_DATA_BYTE(lineg, j);
02011                 fval = (l_float32)(vals * target) / (valg + 0.5);
02012                 vald = L_MIN(255, (l_int32)(fval + 0.5));
02013                 SET_DATA_BYTE(lined, j, vald);
02014             }
02015         }
02016     }
02017 
02018     if (lut) FREE(lut);
02019     return pixd;
02020 }
02021 
02022 
02023 /*------------------------------------------------------------------*
02024  *                  Non-adaptive (global) mapping                   *
02025  *------------------------------------------------------------------*/
02026 /*!
02027  *  pixGlobalNormRGB()
02028  *
02029  *      Input:  pixd (<optional> null, existing or equal to pixs)
02030  *              pixs (32 bpp rgb, or colormapped)
02031  *              rval, gval, bval (pixel values in pixs that are 
02032  *                                linearly mapped to mapval)
02033  *              mapval (use 255 for mapping to white)
02034  *      Return: pixd (32 bpp rgb or colormapped), or null on error
02035  *
02036  *  Notes:
02037  *    (1) The value of pixd determines if the results are written to a
02038  *        new pix (use NULL), in-place to pixs (use pixs), or to some
02039  *        other existing pix.
02040  *    (2) This does a global normalization of an image where the
02041  *        r,g,b color components are not balanced.  Thus, white in pixs is
02042  *        represented by a set of r,g,b values that are not all 255.
02043  *    (3) The input values (rval, gval, bval) should be chosen to
02044  *        represent the gray color (mapval, mapval, mapval) in src.
02045  *        Thus, this function will map (rval, gval, bval) to that gray color.
02046  *    (4) Typically, mapval = 255, so that (rval, gval, bval)
02047  *        corresponds to the white point of src.  In that case, these
02048  *        parameters should be chosen so that few pixels have higher values.
02049  *    (5) In all cases, we do a linear TRC separately on each of the
02050  *        components, saturating at 255.
02051  *    (6) If the input pix is 8 bpp without a colormap, you can get
02052  *        this functionality with mapval = 255 by calling:
02053  *            pixGammaTRC(pixd, pixs, 1.0, 0, bgval);
02054  *        where bgval is the value you want to be mapped to 255.
02055  *        Or more generally, if you want bgval to be mapped to mapval:
02056  *            pixGammaTRC(pixd, pixs, 1.0, 0, 255 * bgval / mapval);
02057  */
02058 PIX *
02059 pixGlobalNormRGB(PIX     *pixd,
02060                  PIX     *pixs,
02061                  l_int32  rval,
02062                  l_int32  gval,
02063                  l_int32  bval,
02064                  l_int32  mapval)
02065 {
02066 l_int32    w, h, d, i, j, ncolors, rv, gv, bv, wpl;
02067 l_int32   *rarray, *garray, *barray;
02068 l_uint32  *data, *line;
02069 NUMA      *nar, *nag, *nab;
02070 PIXCMAP   *cmap;
02071 
02072     PROCNAME("pixGlobalNormRGB");
02073 
02074     if (!pixs)
02075         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
02076     cmap = pixGetColormap(pixs);
02077     pixGetDimensions(pixs, &w, &h, &d);
02078     if (!cmap && d != 32)
02079         return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
02080     if (mapval <= 0) {
02081         L_WARNING("mapval must be > 0; setting to 255", procName);
02082         mapval = 255;
02083     }
02084 
02085         /* Prepare pixd to be a copy of pixs */
02086     if ((pixd = pixCopy(pixd, pixs)) == NULL)
02087         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
02088 
02089         /* Generate the TRC maps for each component.  Make sure the
02090          * upper range for each color is greater than zero. */
02091     nar = numaGammaTRC(1.0, 0, L_MAX(1, 255 * rval / mapval));
02092     nag = numaGammaTRC(1.0, 0, L_MAX(1, 255 * gval / mapval));
02093     nab = numaGammaTRC(1.0, 0, L_MAX(1, 255 * bval / mapval));
02094     if (!nar || !nag || !nab)
02095         return (PIX *)ERROR_PTR("trc maps not all made", procName, pixd);
02096 
02097         /* Extract copies of the internal arrays */
02098     rarray = numaGetIArray(nar);
02099     garray = numaGetIArray(nag);
02100     barray = numaGetIArray(nab);
02101     if (!rarray || !garray || !barray)
02102         return (PIX *)ERROR_PTR("*arrays not all made", procName, pixd);
02103 
02104     if (cmap) {
02105         ncolors = pixcmapGetCount(cmap);
02106         for (i = 0; i < ncolors; i++) {
02107             pixcmapGetColor(cmap, i, &rv, &gv, &bv);
02108             pixcmapResetColor(cmap, i, rarray[rv], garray[gv], barray[bv]);
02109         }
02110     }
02111     else {
02112         data = pixGetData(pixd);
02113         wpl = pixGetWpl(pixd);
02114         for (i = 0; i < h; i++) {
02115             line = data + i * wpl;
02116             for (j = 0; j < w; j++) {
02117                 extractRGBValues(line[j], &rv, &gv, &bv);
02118                 composeRGBPixel(rarray[rv], garray[gv], barray[bv], line + j);
02119             }
02120         }
02121     }
02122 
02123     numaDestroy(&nar);
02124     numaDestroy(&nag);
02125     numaDestroy(&nab);
02126     FREE(rarray);
02127     FREE(garray);
02128     FREE(barray);
02129     return pixd;
02130 }
02131 
02132 
02133 /*!
02134  *  pixGlobalNormNoSatRGB()
02135  *
02136  *      Input:  pixd (<optional> null, existing or equal to pixs)
02137  *              pixs (32 bpp rgb)
02138  *              rval, gval, bval (pixel values in pixs that are 
02139  *                                linearly mapped to mapval; but see below)
02140  *              factor (subsampling factor; integer >= 1)
02141  *              rank (between 0.0 and 1.0; typ. use a value near 1.0)
02142  *      Return: pixd (32 bpp rgb), or null on error
02143  *
02144  *  Notes:
02145  *    (1) This is a version of pixGlobalNormRGB(), where the output
02146  *        intensity is scaled back so that a controlled fraction of
02147  *        pixel components is allowed to saturate.  See comments in
02148  *        pixGlobalNormRGB().
02149  *    (2) The value of pixd determines if the results are written to a
02150  *        new pix (use NULL), in-place to pixs (use pixs), or to some
02151  *        other existing pix.
02152  *    (3) This does a global normalization of an image where the
02153  *        r,g,b color components are not balanced.  Thus, white in pixs is
02154  *        represented by a set of r,g,b values that are not all 255.
02155  *    (4) The input values (rval, gval, bval) can be chosen to be the
02156  *        color that, after normalization, becomes white background.
02157  *        For images that are mostly background, the closer these values
02158  *        are to the median component values, the closer the resulting
02159  *        background will be to gray, becoming white at the brightest places.
02160  *    (5) The mapval used in pixGlobalNormRGB() is computed here to
02161  *        avoid saturation of any component in the image (save for a
02162  *        fraction of the pixels given by the input rank value).
02163  */
02164 PIX *
02165 pixGlobalNormNoSatRGB(PIX       *pixd,
02166                       PIX       *pixs,
02167                       l_int32    rval,
02168                       l_int32    gval,
02169                       l_int32    bval,
02170                       l_int32    factor,
02171                       l_float32  rank)
02172 {
02173 l_int32    mapval;
02174 l_float32  rankrval, rankgval, rankbval;
02175 l_float32  rfract, gfract, bfract, maxfract;
02176 
02177     PROCNAME("pixGlobalNormNoSatRGB");
02178 
02179     if (!pixs)
02180         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
02181     if (pixGetDepth(pixs) != 32)
02182         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
02183     if (factor < 1)
02184         return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL);
02185     if (rank < 0.0 || rank > 1.0)
02186         return (PIX *)ERROR_PTR("rank not in [0.0 ... 1.0]", procName, NULL);
02187     if (rval <= 0 || gval <= 0 || bval <= 0)
02188         return (PIX *)ERROR_PTR("invalid estim. color values", procName, NULL);
02189 
02190         /* The max value for each component may be larger than the
02191          * input estimated background value.  In that case, mapping
02192          * for those pixels would saturate.  To prevent saturation,
02193          * we compute the fraction for each component by which we
02194          * would oversaturate.  Then take the max of these, and
02195          * reduce, uniformly over all components, the output intensity
02196          * by this value.  Then no component will saturate.
02197          * In practice, if rank < 1.0, a fraction of pixels
02198          * may have a component saturate.  By keeping rank close to 1.0,
02199          * that fraction can be made arbitrarily small. */
02200     pixGetRankValueMaskedRGB(pixs, NULL, 0, 0, factor, rank, &rankrval,
02201                              &rankgval, &rankbval);
02202     rfract = rankrval / (l_float32)rval;
02203     gfract = rankgval / (l_float32)gval;
02204     bfract = rankbval / (l_float32)bval;
02205     maxfract = L_MAX(rfract, gfract);
02206     maxfract = L_MAX(maxfract, bfract);
02207 #if  DEBUG_GLOBAL
02208     fprintf(stderr, "rankrval = %7.2f, rankgval = %7.2f, rankbval = %7.2f\n",
02209             rankrval, rankgval, rankbval);
02210     fprintf(stderr, "rfract = %7.4f, gfract = %7.4f, bfract = %7.4f\n",
02211             rfract, gfract, bfract);
02212 #endif  /* DEBUG_GLOBAL */
02213 
02214     mapval = (l_int32)(255. / maxfract);
02215     pixd = pixGlobalNormRGB(pixd, pixs, rval, gval, bval, mapval);
02216     return pixd;
02217 }
02218 
02219 
02220 /*------------------------------------------------------------------*
02221  *              Adaptive threshold spread normalization             *
02222  *------------------------------------------------------------------*/
02223 /*!
02224  *  pixThresholdSpreadNorm()
02225  *
02226  *      Input:  pixs (8 bpp grayscale; not colormapped)
02227  *              filtertype (L_SOBEL_EDGE or L_TWO_SIDED_EDGE);
02228  *              edgethresh (threshold on magnitude of edge filter; typ 10-20)
02229  *              smoothx, smoothy (half-width of convolution kernel applied to
02230  *                                spread threshold: use 0 for no smoothing)
02231  *              gamma (gamma correction; typ. about 0.7)
02232  *              minval  (input value that gives 0 for output; typ. -25)
02233  *              maxval  (input value that gives 255 for output; typ. 255)
02234  *              targetthresh (target threshold for normalization)
02235  *              &pixth (<optional return> computed local threshold value)
02236  *              &pixb (<optional return> thresholded normalized image)
02237  *              &pixd (<optional return> normalized image)
02238  *      Return: 0 if OK, 1 on error
02239  *
02240  *  Notes:
02241  *      (1) The basis of this approach is the use of seed spreading
02242  *          on a (possibly) sparse set of estimates for the local threshold.
02243  *          The resulting dense estimates are smoothed by convolution
02244  *          and used to either threshold the input image or normalize it
02245  *          with a local transformation that linearly maps the pixels so
02246  *          that the local threshold estimate becomes constant over the
02247  *          resulting image.  This approach is one of several that
02248  *          have been suggested (and implemented) by Ray Smith.
02249  *      (2) You can use either the Sobel or TwoSided edge filters.
02250  *          The results appear to be similar, using typical values
02251  *          of edgethresh in the rang 10-20.
02252  *      (3) To skip the trc enhancement, use gamma = 1.0, minval = 0
02253  *          and maxval = 255.
02254  *      (4) For the normalized image pixd, each pixel is linearly mapped
02255  *          in such a way that the local threshold is equal to targetthresh.
02256  *      (5) The full width and height of the convolution kernel
02257  *          are (2 * smoothx + 1) and (2 * smoothy + 1).
02258  *      (6) This function can be used with the pixtiling utility if the
02259  *          images are too large.  See pixOtsuAdaptiveThreshold() for
02260  *          an example of this.
02261  */
02262 l_int32
02263 pixThresholdSpreadNorm(PIX       *pixs,
02264                        l_int32    filtertype,
02265                        l_int32    edgethresh,
02266                        l_int32    smoothx,
02267                        l_int32    smoothy,
02268                        l_float32  gamma,
02269                        l_int32    minval,
02270                        l_int32    maxval,
02271                        l_int32    targetthresh,
02272                        PIX      **ppixth,
02273                        PIX      **ppixb,
02274                        PIX      **ppixd)
02275 {
02276 PIX     *pixe, *pixet, *pixsd, *pixg1, *pixg2, *pixth;
02277 
02278     PROCNAME("pixThresholdSpreadNorm");
02279 
02280     if (ppixth) *ppixth = NULL;
02281     if (ppixb) *ppixb = NULL;
02282     if (ppixd) *ppixd = NULL;
02283     if (!pixs || pixGetDepth(pixs) != 8)
02284         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
02285     if (pixGetColormap(pixs))
02286         return ERROR_INT("pixs is colormapped", procName, 1);
02287     if (!ppixth && !ppixb && !ppixd)
02288         return ERROR_INT("no output requested", procName, 1);
02289     if (filtertype != L_SOBEL_EDGE && filtertype != L_TWO_SIDED_EDGE)
02290         return ERROR_INT("invalid filter type", procName, 1);
02291 
02292         /* Get the thresholded edge pixels.  These are the ones
02293          * that have values in pixs near the local optimal fg/bg threshold. */
02294     if (filtertype == L_SOBEL_EDGE)
02295         pixe = pixSobelEdgeFilter(pixs, L_VERTICAL_EDGES);
02296     else  /* L_TWO_SIDED_EDGE */
02297         pixe = pixTwoSidedEdgeFilter(pixs, L_VERTICAL_EDGES);
02298     pixet = pixThresholdToBinary(pixe, edgethresh);
02299     pixInvert(pixet, pixet);
02300 
02301         /* Build a seed image whose only nonzero values are those
02302          * values of pixs corresponding to pixels in the fg of pixet. */
02303     pixsd = pixCreateTemplate(pixs);
02304     pixCombineMasked(pixsd, pixs, pixet);
02305 
02306         /* Spread the seed and optionally smooth to reduce noise */
02307     pixg1 = pixSeedspread(pixsd, 4);
02308     pixg2 = pixBlockconv(pixg1, smoothx, smoothy);
02309 
02310         /* Optionally do a gamma enhancement */
02311     pixth = pixGammaTRC(NULL, pixg2, gamma, minval, maxval);
02312 
02313         /* Do the mapping and thresholding */
02314     if (ppixd) {
02315         *ppixd = pixApplyVariableGrayMap(pixs, pixth, targetthresh); 
02316         if (ppixb)
02317             *ppixb = pixThresholdToBinary(*ppixd, targetthresh);
02318     }
02319     else if (ppixb)
02320         *ppixb = pixVarThresholdToBinary(pixs, pixth);
02321 
02322     if (ppixth)
02323         *ppixth = pixth;
02324     else
02325         pixDestroy(&pixth);
02326 
02327     pixDestroy(&pixe);
02328     pixDestroy(&pixet);
02329     pixDestroy(&pixsd);
02330     pixDestroy(&pixg1);
02331     pixDestroy(&pixg2);
02332     return 0;
02333 }
02334 
02335 
02336 /*------------------------------------------------------------------*
02337  *      Adaptive background normalization (flexible adaptaption)    *
02338  *------------------------------------------------------------------*/
02339 /*!
02340  *  pixBackgroundNormFlex()
02341  *
02342  *      Input:  pixs (8 bpp grayscale; not colormapped)
02343  *              sx, sy (desired tile dimensions; actual size may vary; use
02344  *                      values between 3 and 10)
02345  *              smoothx, smoothy (half-width of convolution kernel applied to
02346  *                                threshold array: use values between 1 and 3)
02347  *              delta (difference parameter in basin filling; use 0
02348  *                     to skip)
02349  *      Return: pixd (8 bpp, background-normalized), or null on error)
02350  *
02351  *  Notes:
02352  *      (1) This does adaptation flexibly to a quickly varying background.
02353  *          For that reason, all input parameters should be small.
02354  *      (2) sx and sy give the tile size; they should be in [5 - 7].
02355  *      (3) The full width and height of the convolution kernel
02356  *          are (2 * smoothx + 1) and (2 * smoothy + 1).  They
02357  *          should be in [1 - 2].
02358  *      (4) Basin filling is used to fill the large fg regions.  The
02359  *          parameter @delta measures the height that the black
02360  *          background is raised from the local minima.  By raising
02361  *          the background, it is possible to threshold the large
02362  *          fg regions to foreground.  If @delta is too large,
02363  *          bg regions will be lifted, causing thickening of
02364  *          the fg regions.  Use 0 to skip.
02365  */
02366 PIX *
02367 pixBackgroundNormFlex(PIX     *pixs,
02368                       l_int32  sx,
02369                       l_int32  sy,
02370                       l_int32  smoothx,
02371                       l_int32  smoothy,
02372                       l_int32  delta)
02373 {
02374 l_float32  scalex, scaley;
02375 PIX       *pixt, *pixsd, *pixmin, *pixbg, *pixbgi, *pixd;
02376 
02377     PROCNAME("pixBackgroundNormFlex");
02378 
02379     if (!pixs || pixGetDepth(pixs) != 8)
02380         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
02381     if (pixGetColormap(pixs))
02382         return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
02383     if (sx < 3 || sy < 3)
02384         return (PIX *)ERROR_PTR("sx and/or sy less than 3", procName, NULL);
02385     if (sx > 10 || sy > 10)
02386         return (PIX *)ERROR_PTR("sx and/or sy exceed 10", procName, NULL);
02387     if (smoothx < 1 || smoothy < 1)
02388         return (PIX *)ERROR_PTR("smooth params less than 1", procName, NULL);
02389     if (smoothx > 3 || smoothy > 3)
02390         return (PIX *)ERROR_PTR("smooth params exceed 3", procName, NULL);
02391 
02392         /* Generate the bg estimate using smoothed average with subsampling */
02393     scalex = 1. / (l_float32)sx;
02394     scaley = 1. / (l_float32)sy;
02395     pixt = pixScaleSmooth(pixs, scalex, scaley);
02396 
02397         /* Do basin filling on the bg estimate if requested */
02398     if (delta <= 0)
02399         pixsd = pixClone(pixt);
02400     else {
02401         pixLocalExtrema(pixt, 0, 0, &pixmin, NULL);
02402         pixsd = pixSeedfillGrayBasin(pixmin, pixt, delta, 4);
02403         pixDestroy(&pixmin);
02404     }
02405     pixbg = pixExtendByReplication(pixsd, 1, 1);
02406 
02407         /* Map the bg to 200 */
02408     pixbgi = pixGetInvBackgroundMap(pixbg, 200, smoothx, smoothy);
02409     pixd = pixApplyInvBackgroundGrayMap(pixs, pixbgi, sx, sy);
02410     
02411     pixDestroy(&pixt);
02412     pixDestroy(&pixsd);
02413     pixDestroy(&pixbg);
02414     pixDestroy(&pixbgi);
02415     return pixd;
02416 }
02417 
02418 
02419 /*------------------------------------------------------------------*
02420  *                    Adaptive contrast normalization               *
02421  *------------------------------------------------------------------*/
02422 /*!
02423  *  pixContrastNorm()
02424  *
02425  *      Input:  pixd (<optional> 8 bpp; null or equal to pixs)
02426  *              pixs (8 bpp grayscale; not colormapped)
02427  *              sx, sy (tile dimensions)
02428  *              mindiff (minimum difference to accept as valid)
02429  *              smoothx, smoothy (half-width of convolution kernel applied to
02430  *                                min and max arrays: use 0 for no smoothing)
02431  *      Return: pixd always
02432  *
02433  *  Notes:
02434  *      (1) This function adaptively attempts to expand the contrast
02435  *          to the full dynamic range in each tile.  If the contrast in
02436  *          a tile is smaller than @mindiff, it uses the min and max
02437  *          pixel values from neighboring tiles.  It also can use
02438  *          convolution to smooth the min and max values from
02439  *          neighboring tiles.  After all that processing, it is
02440  *          possible that the actual pixel values in the tile are outside
02441  *          the computed [min ... max] range for local contrast
02442  *          normalization.  Such pixels are taken to be at either 0
02443  *          (if below the min) or 255 (if above the max).
02444  *      (2) pixd can be equal to pixs (in-place operation) or
02445  *          null (makes a new pixd).
02446  *      (3) sx and sy give the tile size; they are typically at least 20.
02447  *      (4) mindiff is used to eliminate results for tiles where it is
02448  *          likely that either fg or bg is missing.  A value around 50
02449  *          or more is reasonable.
02450  *      (5) The full width and height of the convolution kernel
02451  *          are (2 * smoothx + 1) and (2 * smoothy + 1).  Some smoothing
02452  *          is typically useful, and we limit the smoothing half-widths
02453  *          to the range from 0 to 8.
02454  *      (6) A linear TRC (gamma = 1.0) is applied to increase the contrast
02455  *          in each tile.  The result can subsequently be globally corrected,
02456  *          by applying pixGammaTRC() with arbitrary values of gamma
02457  *          and the 0 and 255 points of the mapping.
02458  */
02459 PIX *
02460 pixContrastNorm(PIX       *pixd,
02461                 PIX       *pixs,
02462                 l_int32    sx,
02463                 l_int32    sy,
02464                 l_int32    mindiff,
02465                 l_int32    smoothx,
02466                 l_int32    smoothy)
02467 {
02468 PIX  *pixmin, *pixmax;
02469 
02470     PROCNAME("pixContrastNorm");
02471 
02472     if (!pixs || pixGetDepth(pixs) != 8)
02473         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd);
02474     if (pixd && pixd != pixs)
02475         return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd);
02476     if (pixGetColormap(pixs))
02477         return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd);
02478     if (sx < 5 || sy < 5)
02479         return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd);
02480     if (smoothx < 0 || smoothy < 0)
02481         return (PIX *)ERROR_PTR("smooth params less than 0", procName, pixd);
02482     if (smoothx > 8 || smoothy > 8)
02483         return (PIX *)ERROR_PTR("smooth params exceed 8", procName, pixd);
02484 
02485         /* Get the min and max pixel values in each tile, and represent
02486          * each value as a pixel in pixmin and pixmax, respectively. */
02487     pixMinMaxTiles(pixs, sx, sy, mindiff, smoothx, smoothy, &pixmin, &pixmax);
02488 
02489         /* For each tile, do a linear expansion of the dynamic range
02490          * of pixels so that the min value is mapped to 0 and the
02491          * max value is mapped to 255.  */
02492     pixd = pixLinearTRCTiled(pixd, pixs, sx, sy, pixmin, pixmax);
02493 
02494     pixDestroy(&pixmin);
02495     pixDestroy(&pixmax);
02496     return pixd;
02497 }
02498 
02499 
02500 /*!
02501  *  pixMinMaxTiles()
02502  *
02503  *      Input:  pixs (8 bpp grayscale; not colormapped)
02504  *              sx, sy (tile dimensions)
02505  *              mindiff (minimum difference to accept as valid)
02506  *              smoothx, smoothy (half-width of convolution kernel applied to
02507  *                                min and max arrays: use 0 for no smoothing)
02508  *              &pixmin (<return> tiled minima)
02509  *              &pixmax (<return> tiled maxima)
02510  *      Return: 0 if OK, 1 on error
02511  *
02512  *  Notes:
02513  *      (1) This computes filtered and smoothed values for the min and
02514  *          max pixel values in each tile of the image.
02515  *      (2) See pixContrastNorm() for usage.
02516  */
02517 l_int32
02518 pixMinMaxTiles(PIX     *pixs,
02519                l_int32  sx,
02520                l_int32  sy,
02521                l_int32  mindiff,
02522                l_int32  smoothx,
02523                l_int32  smoothy,
02524                PIX    **ppixmin,
02525                PIX    **ppixmax)
02526 {
02527 l_int32  w, h;
02528 PIX     *pixmin1, *pixmax1, *pixmin2, *pixmax2;
02529 
02530     PROCNAME("pixMinMaxTiles");
02531 
02532     if (!ppixmin || !ppixmax)
02533         return ERROR_INT("&pixmin or &pixmax undefined", procName, 1);
02534     *ppixmin = *ppixmax = NULL;
02535     if (!pixs || pixGetDepth(pixs) != 8)
02536         return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
02537     if (pixGetColormap(pixs))
02538         return ERROR_INT("pixs is colormapped", procName, 1);
02539     if (sx < 5 || sy < 5)
02540         return ERROR_INT("sx and/or sy less than 3", procName, 1);
02541     if (smoothx < 0 || smoothy < 0)
02542         return ERROR_INT("smooth params less than 0", procName, 1);
02543     if (smoothx > 5 || smoothy > 5)
02544         return ERROR_INT("smooth params exceed 5", procName, 1);
02545 
02546         /* Get the min and max values in each tile */
02547     pixmin1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MIN);
02548     pixmax1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MAX);
02549 
02550     pixmin2 = pixExtendByReplication(pixmin1, 1, 1);
02551     pixmax2 = pixExtendByReplication(pixmax1, 1, 1);
02552     pixDestroy(&pixmin1);
02553     pixDestroy(&pixmax1);
02554 
02555         /* Make sure no value is 0 */
02556     pixAddConstantGray(pixmin2, 1);
02557     pixAddConstantGray(pixmax2, 1);
02558 
02559         /* Generate holes where the contrast is too small */
02560     pixSetLowContrast(pixmin2, pixmax2, mindiff);
02561 
02562         /* Fill the holes (0 values) */
02563     pixGetDimensions(pixmin2, &w, &h, NULL);
02564     pixFillMapHoles(pixmin2, w, h, L_FILL_BLACK);
02565     pixFillMapHoles(pixmax2, w, h, L_FILL_BLACK);
02566 
02567         /* Smooth if requested */
02568     if (smoothx > 0 || smoothy > 0) {
02569         smoothx = L_MIN(smoothx, (w - 1) / 2);
02570         smoothy = L_MIN(smoothy, (h - 1) / 2);
02571         *ppixmin = pixBlockconv(pixmin2, smoothx, smoothy);
02572         *ppixmax = pixBlockconv(pixmax2, smoothx, smoothy);
02573     }
02574     else {
02575         *ppixmin = pixClone(pixmin2);
02576         *ppixmax = pixClone(pixmax2);
02577     }
02578     pixDestroy(&pixmin2);
02579     pixDestroy(&pixmax2);
02580 
02581     return 0;
02582 }
02583 
02584 
02585 /*!
02586  *  pixSetLowContrast()
02587  *
02588  *      Input:  pixs1 (8 bpp)
02589  *              pixs2 (8 bpp)
02590  *              mindiff (minimum difference to accept as valid)
02591  *      Return: 0 if OK; 1 if no pixel diffs are large enough, or on error
02592  *
02593  *  Notes:
02594  *      (1) This compares corresponding pixels in pixs1 and pixs2.
02595  *          When they differ by less than @mindiff, set the pixel
02596  *          values to 0 in each.  Each pixel typically represents a tile
02597  *          in a larger image, and a very small difference between
02598  *          the min and max in the tile indicates that the min and max
02599  *          values are not to be trusted.
02600  *      (2) If contrast (pixel difference) detection is expected to fail, 
02601  *          caller should check return value.
02602  */
02603 l_int32
02604 pixSetLowContrast(PIX     *pixs1,
02605                   PIX     *pixs2,
02606                   l_int32  mindiff)
02607 {
02608 l_int32    i, j, w, h, d, wpl, val1, val2, found;
02609 l_uint32  *data1, *data2, *line1, *line2;
02610 
02611     PROCNAME("pixSetLowContrast");
02612 
02613     if (!pixs1 || !pixs2)
02614         return ERROR_INT("pixs1 and pixs2 not both defined", procName, 1);
02615     if (pixSizesEqual(pixs1, pixs2) == 0)
02616         return ERROR_INT("pixs1 and pixs2 not equal size", procName, 1);
02617     pixGetDimensions(pixs1, &w, &h, &d);
02618     if (d != 8)
02619         return ERROR_INT("depth not 8 bpp", procName, 1);
02620     if (mindiff > 254) return 0;
02621 
02622     data1 = pixGetData(pixs1);
02623     data2 = pixGetData(pixs2);
02624     wpl = pixGetWpl(pixs1);
02625     found = 0;  /* init to not finding any diffs >= mindiff */
02626     for (i = 0; i < h; i++) {
02627         line1 = data1 + i * wpl;
02628         line2 = data2 + i * wpl;
02629         for (j = 0; j < w; j++) {
02630             val1 = GET_DATA_BYTE(line1, j);
02631             val2 = GET_DATA_BYTE(line2, j);
02632             if (L_ABS(val1 - val2) >= mindiff) {
02633                 found = 1;
02634                 break;
02635             }
02636         }
02637         if (found) break;
02638     }
02639     if (!found) {
02640         L_WARNING("no pixel pair diffs as large as mindiff", procName);
02641         pixClearAll(pixs1);
02642         pixClearAll(pixs2);
02643         return 1;
02644     }
02645 
02646     for (i = 0; i < h; i++) {
02647         line1 = data1 + i * wpl;
02648         line2 = data2 + i * wpl;
02649         for (j = 0; j < w; j++) {
02650             val1 = GET_DATA_BYTE(line1, j);
02651             val2 = GET_DATA_BYTE(line2, j);
02652             if (L_ABS(val1 - val2) < mindiff) {
02653                 SET_DATA_BYTE(line1, j, 0);
02654                 SET_DATA_BYTE(line2, j, 0);
02655             }
02656         }
02657     }
02658 
02659     return 0;
02660 }
02661 
02662 
02663 /*!
02664  *  pixLinearTRCTiled()
02665  *
02666  *      Input:  pixd (<optional> 8 bpp)
02667  *              pixs (8 bpp, not colormapped)
02668  *              sx, sy (tile dimensions)
02669  *              pixmin (pix of min values in tiles)
02670  *              pixmax (pix of max values in tiles)
02671  *      Return: pixd always
02672  *
02673  *  Notes:
02674  *      (1) pixd can be equal to pixs (in-place operation) or
02675  *          null (makes a new pixd).
02676  *      (2) sx and sy give the tile size; they are typically at least 20.
02677  *      (3) pixmin and pixmax are generated by pixMinMaxTiles()
02678  *      (4) For each tile, this does a linear expansion of the dynamic
02679  *          range so that the min value in the tile becomes 0 and the
02680  *          max value in the tile becomes 255.
02681  *      (5) The LUTs that do the mapping are generated as needed
02682  *          and stored for reuse in an integer array within the ptr array iaa[].
02683  */
02684 PIX *
02685 pixLinearTRCTiled(PIX       *pixd,
02686                   PIX       *pixs,
02687                   l_int32    sx,
02688                   l_int32    sy,
02689                   PIX       *pixmin,
02690                   PIX       *pixmax)
02691 {
02692 l_int32    i, j, k, m, w, h, wt, ht, wpl, wplt, xoff, yoff;
02693 l_int32    minval, maxval, val, sval;
02694 l_int32   *ia;
02695 l_int32  **iaa;
02696 l_uint32  *data, *datamin, *datamax, *line, *tline, *linemin, *linemax;
02697 
02698     PROCNAME("pixLinearTRCTiled");
02699 
02700     if (!pixs || pixGetDepth(pixs) != 8)
02701         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd);
02702     if (pixd && pixd != pixs)
02703         return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd);
02704     if (pixGetColormap(pixs))
02705         return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd);
02706     if (!pixmin || !pixmax)
02707         return (PIX *)ERROR_PTR("pixmin & pixmax not defined", procName, pixd);
02708     if (sx < 5 || sy < 5)
02709         return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd);
02710 
02711     pixd = pixCopy(pixd, pixs);
02712     iaa = (l_int32 **)CALLOC(256, sizeof(l_int32 *));
02713     pixGetDimensions(pixd, &w, &h, NULL);
02714 
02715     data = pixGetData(pixd);
02716     wpl = pixGetWpl(pixd);
02717     datamin = pixGetData(pixmin);
02718     datamax = pixGetData(pixmax);
02719     wplt = pixGetWpl(pixmin);
02720     pixGetDimensions(pixmin, &wt, &ht, NULL);
02721     for (i = 0; i < ht; i++) {
02722         line = data + sy * i * wpl;
02723         linemin = datamin + i * wplt;
02724         linemax = datamax + i * wplt;
02725         yoff = sy * i;
02726         for (j = 0; j < wt; j++) {
02727             xoff = sx * j;
02728             minval = GET_DATA_BYTE(linemin, j);
02729             maxval = GET_DATA_BYTE(linemax, j);
02730             if (maxval == minval) {  /* this is bad */
02731 /*                fprintf(stderr, "should't happen! i,j = %d,%d, minval = %d\n",
02732                         i, j, minval); */
02733                 continue;
02734             } 
02735             ia = iaaGetLinearTRC(iaa, maxval - minval);
02736             for (k = 0; k < sy && yoff + k < h; k++) {
02737                 tline = line + k * wpl;
02738                 for (m = 0; m < sx && xoff + m < w; m++) {
02739                     val = GET_DATA_BYTE(tline, xoff + m);
02740                     sval = val - minval;
02741                     sval = L_MAX(0, sval);
02742                     SET_DATA_BYTE(tline, xoff + m, ia[sval]);
02743                 }
02744             }
02745         }
02746     }
02747 
02748     for (i = 0; i < 256; i++)
02749         if (iaa[i]) FREE(iaa[i]);
02750     FREE(iaa);
02751     return pixd;
02752 }
02753 
02754 
02755 /*!
02756  *  iaaGetLinearTRC()
02757  *
02758  *      Input:  iaa (bare array of ptrs to l_int32)
02759  *              diff (between min and max pixel values that are
02760  *                    to be mapped to 0 and 255)
02761  *      Return: ia (LUT with input (val - minval) and output a
02762  *                  value between 0 and 255)
02763  */
02764 static l_int32 *
02765 iaaGetLinearTRC(l_int32  **iaa,
02766                 l_int32    diff)
02767 {
02768 l_int32    i;
02769 l_int32   *ia;
02770 l_float32  factor;
02771 
02772     PROCNAME("iaaGetLinearTRC");
02773 
02774     if (!iaa)
02775         return (l_int32 *)ERROR_PTR("iaa not defined", procName, NULL);
02776 
02777     if (iaa[diff] != NULL)  /* already have it */
02778        return iaa[diff];
02779                 
02780     if ((ia = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
02781         return (l_int32 *)ERROR_PTR("ia not made", procName, NULL);
02782     iaa[diff] = ia;
02783     if (diff == 0) {  /* shouldn't happen */
02784         for (i = 0; i < 256; i++)
02785             ia[i] = 128;
02786     }
02787     else {
02788         factor = 255. / (l_float32)diff;
02789         for (i = 0; i < diff + 1; i++)
02790             ia[i] = (l_int32)(factor * i + 0.5);
02791         for (i = diff + 1; i < 256; i++)
02792             ia[i] = 255;
02793     }
02794 
02795     return ia;
02796 }
02797 
02798 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines