Leptonica 1.68
C Image Processing Library

enhance.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  *  enhance.c
00018  *
00019  *      Gamma TRC (tone reproduction curve) mapping
00020  *           PIX     *pixGammaTRC()
00021  *           PIX     *pixGammaTRCMasked()
00022  *           PIX     *pixGammaTRCWithAlpha()
00023  *           NUMA    *numaGammaTRC()
00024  *
00025  *      Contrast enhancement
00026  *           PIX     *pixContrastTRC()
00027  *           PIX     *pixContrastTRCMasked()
00028  *           NUMA    *numaContrastTRC()
00029  *
00030  *      Histogram equalization
00031  *           PIX     *pixEqualizeTRC()
00032  *           NUMA    *numaEqualizeTRC()
00033  *
00034  *      Generic TRC mapper
00035  *           PIX     *pixTRCMap()
00036  *
00037  *      Unsharp-masking
00038  *           PIX     *pixUnsharpMasking()
00039  *           PIX     *pixUnsharpMaskingGray()
00040  *           PIX     *pixUnsharpMaskingFast()
00041  *           PIX     *pixUnsharpMaskingGrayFast()
00042  *           PIX     *pixUnsharpMaskingGray1D()
00043  *           PIX     *pixUnsharpMaskingGray2D()
00044  *
00045  *      Hue and saturation modification
00046  *           PIX     *pixModifyHue()
00047  *           PIX     *pixModifySaturation()
00048  *           l_int32  pixMeasureSaturation()
00049  *
00050  *      General multiplicative constant color transform
00051  *           PIX     *pixMultConstantColor()
00052  *           PIX     *pixMultMatrixColor()
00053  *
00054  *      Edge by bandpass
00055  *           PIX     *pixHalfEdgeByBandpass()
00056  *
00057  *      Gamma correction, contrast enhancement and histogram equalization
00058  *      apply a simple mapping function to each pixel (or, for color
00059  *      images, to each sample (i.e., r,g,b) of the pixel).
00060  *
00061  *       - Gamma correction either lightens the image or darkens
00062  *         it, depending on whether the gamma factor is greater
00063  *         or less than 1.0, respectively.
00064  *
00065  *       - Contrast enhancement darkens the pixels that are already
00066  *         darker than the middle of the dynamic range (128)
00067  *         and lightens pixels that are lighter than 128.
00068  *
00069  *       - Histogram equalization remaps to have the same number
00070  *         of image pixels at each of 256 intensity values.  This is
00071  *         a quick and dirty method of adjusting contrast and brightness
00072  *         to bring out details in both light and dark regions.
00073  *
00074  *      Unsharp masking is a more complicated enhancement.
00075  *      A "high frequency" image, generated by subtracting
00076  *      the smoothed ("low frequency") part of the image from
00077  *      itself, has all the energy at the edges.  This "edge image"
00078  *      has 0 average value.  A fraction of the edge image is
00079  *      then added to the original, enhancing the differences
00080  *      between pixel values at edges.  Because we represent
00081  *      images as l_uint8 arrays, we preserve dynamic range and
00082  *      handle negative values by doing all the arithmetic on
00083  *      shifted l_uint16 arrays; the l_uint8 values are recovered
00084  *      at the end.
00085  *
00086  *      Hue and saturation modification work in HSV space.  Because
00087  *      this is too large for efficient table lookup, each pixel value
00088  *      is transformed to HSV, modified, and transformed back.
00089  *      It's not the fastest way to do this, but the method is
00090  *      easily understood.
00091  *
00092  *      Unsharp masking is never in-place, and returns a clone if no
00093  *      operation is to be performed.
00094  */
00095 
00096 
00097 #include <stdio.h>
00098 #include <stdlib.h>
00099 #include <math.h>
00100 #include "allheaders.h"
00101 
00102     /* Scales contrast enhancement factor to have a useful range
00103      * between 0.0 and 1.0 */
00104 static const l_float32  ENHANCE_SCALE_FACTOR = 5.;
00105 
00106     /* Default number of pixels sampled to determine histogram */
00107 static const l_int32  DEFAULT_HISTO_SAMPLES = 100000;
00108 
00109 
00110 /*-------------------------------------------------------------*
00111  *         Gamma TRC (tone reproduction curve) mapping         *
00112  *-------------------------------------------------------------*/
00113 /*!
00114  *  pixGammaTRC()
00115  *
00116  *      Input:  pixd (<optional> null or equal to pixs)
00117  *              pixs (8 or 32 bpp; or 2, 4 or 8 bpp with colormap)
00118  *              gamma (gamma correction; must be > 0.0)
00119  *              minval  (input value that gives 0 for output; can be < 0)
00120  *              maxval  (input value that gives 255 for output; can be > 255)
00121  *      Return: pixd always
00122  *
00123  *  Notes:
00124  *      (1) pixd must either be null or equal to pixs.
00125  *          For in-place operation, set pixd == pixs:
00126  *             pixGammaTRC(pixs, pixs, ...);
00127  *          To get a new image, set pixd == null:
00128  *             pixd = pixGammaTRC(NULL, pixs, ...);
00129  *      (2) If pixs is colormapped, the colormap is transformed,
00130  *          either in-place or in a copy of pixs.
00131  *      (3) We use a gamma mapping between minval and maxval.
00132  *      (4) If gamma < 1.0, the image will appear darker;
00133  *          if gamma > 1.0, the image will appear lighter;
00134  *      (5) If gamma = 1.0 and minval = 0 and maxval = 255, no
00135  *          enhancement is performed; return a copy unless in-place,
00136  *          in which case this is a no-op.
00137  *      (6) For color images that are not colormapped, the mapping
00138  *          is applied to each component.
00139  *      (7) minval and maxval are not restricted to the interval [0, 255].
00140  *          If minval < 0, an input value of 0 is mapped to a
00141  *          nonzero output.  This will turn black to gray.
00142  *          If maxval > 255, an input value of 255 is mapped to
00143  *          an output value less than 255.  This will turn
00144  *          white (e.g., in the background) to gray.
00145  *      (8) Increasing minval darkens the image.
00146  *      (9) Decreasing maxval bleaches the image.
00147  *      (10) Simultaneously increasing minval and decreasing maxval
00148  *           will darken the image and make the colors more intense;
00149  *           e.g., minval = 50, maxval = 200.
00150  *      (11) See numaGammaTRC() for further examples of use.
00151  */
00152 PIX *
00153 pixGammaTRC(PIX       *pixd,
00154             PIX       *pixs,
00155             l_float32  gamma,
00156             l_int32    minval,
00157             l_int32    maxval)
00158 {
00159 l_int32   d;
00160 NUMA     *nag;
00161 PIXCMAP  *cmap;
00162 
00163     PROCNAME("pixGammaTRC");
00164 
00165     if (!pixs)
00166         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00167     if (pixd && (pixd != pixs))
00168         return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd);
00169     if (gamma <= 0.0) {
00170         L_WARNING("gamma must be > 0.0; setting to 1.0", procName);
00171         gamma = 1.0;
00172     }
00173     if (minval >= maxval)
00174         return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd);
00175     cmap = pixGetColormap(pixs);
00176     d = pixGetDepth(pixs);
00177     if (!cmap && d != 8 && d != 32)
00178         return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd);
00179 
00180     if (gamma == 1.0 && minval == 0 && maxval == 255)  /* no-op */
00181         return pixCopy(pixd, pixs);
00182 
00183     if (!pixd)  /* start with a copy if not in-place */
00184         pixd = pixCopy(NULL, pixs);
00185 
00186     if (cmap) {
00187         pixcmapGammaTRC(pixGetColormap(pixd), gamma, minval, maxval);
00188         return pixd;
00189     }
00190 
00191         /* pixd is 8 or 32 bpp */
00192     if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL)
00193         return (PIX *)ERROR_PTR("nag not made", procName, pixd);
00194     pixTRCMap(pixd, NULL, nag);
00195     numaDestroy(&nag);
00196 
00197     return pixd;
00198 }
00199 
00200 
00201 /*!
00202  *  pixGammaTRCMasked()
00203  *
00204  *      Input:  pixd (<optional> null or equal to pixs)
00205  *              pixs (8 or 32 bpp; not colormapped)
00206  *              pixm (<optional> null or 1 bpp)
00207  *              gamma (gamma correction; must be > 0.0)
00208  *              minval  (input value that gives 0 for output; can be < 0)
00209  *              maxval  (input value that gives 255 for output; can be > 255)
00210  *      Return: pixd always
00211  *
00212  *  Notes:
00213  *      (1) Same as pixGammaTRC() except mapping is optionally over
00214  *          a subset of pixels described by pixm.
00215  *      (2) Masking does not work for colormapped images.
00216  *      (3) See pixGammaTRC() for details on how to use the parameters.
00217  */
00218 PIX *
00219 pixGammaTRCMasked(PIX       *pixd,
00220                   PIX       *pixs,
00221                   PIX       *pixm,
00222                   l_float32  gamma,
00223                   l_int32    minval,
00224                   l_int32    maxval)
00225 {
00226 l_int32  d;
00227 NUMA    *nag;
00228 
00229     PROCNAME("pixGammaTRCMasked");
00230 
00231     if (!pixm)
00232         return pixGammaTRC(pixd, pixs, gamma, minval, maxval);
00233 
00234     if (!pixs)
00235         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00236     if (pixGetColormap(pixs))
00237         return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd);
00238     if (pixd && (pixd != pixs))
00239         return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd);
00240     d = pixGetDepth(pixs);
00241     if (d != 8 && d != 32)
00242         return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd);
00243     if (minval >= maxval)
00244         return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd);
00245     if (gamma <= 0.0) {
00246         L_WARNING("gamma must be > 0.0; setting to 1.0", procName);
00247         gamma = 1.0;
00248     }
00249 
00250     if (gamma == 1.0 && minval == 0 && maxval == 255)
00251         return pixCopy(pixd, pixs);
00252 
00253     if (!pixd)  /* start with a copy if not in-place */
00254         pixd = pixCopy(NULL, pixs);
00255 
00256     if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL)
00257         return (PIX *)ERROR_PTR("nag not made", procName, pixd);
00258     pixTRCMap(pixd, pixm, nag);
00259     numaDestroy(&nag);
00260 
00261     return pixd;
00262 }
00263 
00264 
00265 /*!
00266  *  pixGammaTRCWithAlpha()
00267  *
00268  *      Input:  pixd (<optional> null or equal to pixs)
00269  *              pixs (32 bpp)
00270  *              gamma (gamma correction; must be > 0.0)
00271  *              minval  (input value that gives 0 for output; can be < 0)
00272  *              maxval  (input value that gives 255 for output; can be > 255)
00273  *      Return: pixd always
00274  *
00275  *  Notes:
00276  *      (1) See usage notes in pixGammaTRC().
00277  *      (2) This version saves the alpha channel.  It is only valid
00278  *          for 32 bpp (no colormap), and is a bit slower.
00279  */
00280 PIX *
00281 pixGammaTRCWithAlpha(PIX       *pixd,
00282                      PIX       *pixs,
00283                      l_float32  gamma,
00284                      l_int32    minval,
00285                      l_int32    maxval)
00286 {
00287 NUMA  *nag;
00288 PIX   *pixalpha;
00289 
00290     PROCNAME("pixGammaTRCWithAlpha");
00291 
00292     if (!pixs || pixGetDepth(pixs) != 32)
00293         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, pixd);
00294     if (pixd && (pixd != pixs))
00295         return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd);
00296     if (gamma <= 0.0) {
00297         L_WARNING("gamma must be > 0.0; setting to 1.0", procName);
00298         gamma = 1.0;
00299     }
00300     if (minval >= maxval)
00301         return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd);
00302 
00303     if (gamma == 1.0 && minval == 0 && maxval == 255)
00304         return pixCopy(pixd, pixs);
00305 
00306     pixalpha = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);  /* save */
00307     if (!pixd)  /* start with a copy if not in-place */
00308         pixd = pixCopy(NULL, pixs);
00309 
00310     if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL)
00311         return (PIX *)ERROR_PTR("nag not made", procName, pixd);
00312     pixTRCMap(pixd, NULL, nag);
00313     pixSetRGBComponent(pixd, pixalpha, L_ALPHA_CHANNEL);  /* restore */
00314 
00315     numaDestroy(&nag);
00316     pixDestroy(&pixalpha);
00317     return pixd;
00318 }
00319 
00320 
00321 /*!
00322  *  numaGammaTRC()
00323  *
00324  *      Input:  gamma   (gamma factor; must be > 0.0)
00325  *              minval  (input value that gives 0 for output)
00326  *              maxval  (input value that gives 255 for output)
00327  *      Return: na, or null on error
00328  *
00329  *  Notes:
00330  *      (1) The map is returned as a numa; values are clipped to [0, 255].
00331  *      (2) To force all intensities into a range within fraction delta
00332  *          of white, use: minval = -256 * (1 - delta) / delta
00333  *                         maxval = 255
00334  *      (3) To force all intensities into a range within fraction delta
00335  *          of black, use: minval = 0
00336  *                         maxval = 256 * (1 - delta) / delta
00337  */
00338 NUMA *
00339 numaGammaTRC(l_float32  gamma,
00340              l_int32    minval,
00341              l_int32    maxval)
00342 {
00343 l_int32    i, val;
00344 l_float32  x, invgamma;
00345 NUMA      *na;
00346 
00347     PROCNAME("numaGammaTRC");
00348 
00349     if (minval >= maxval)
00350         return (NUMA *)ERROR_PTR("minval not < maxval", procName, NULL);
00351     if (gamma <= 0.0) {
00352         L_WARNING("gamma must be > 0.0; setting to 1.0", procName);
00353         gamma = 1.0;
00354     }
00355 
00356     invgamma = 1. / gamma;
00357     na = numaCreate(256);
00358     for (i = 0; i < minval; i++)
00359         numaAddNumber(na, 0);
00360     for (i = minval; i <= maxval; i++) {
00361         if (i < 0) continue;
00362         if (i > 255) continue;
00363         x = (l_float32)(i - minval) / (l_float32)(maxval - minval);
00364         val = (l_int32)(255. * powf(x, invgamma) + 0.5);
00365         val = L_MAX(val, 0);
00366         val = L_MIN(val, 255);
00367         numaAddNumber(na, val);
00368     }
00369     for (i = maxval + 1; i < 256; i++)
00370         numaAddNumber(na, 255);
00371 
00372     return na;
00373 }
00374 
00375 
00376 /*-------------------------------------------------------------*
00377  *                      Contrast enhancement                   *
00378  *-------------------------------------------------------------*/
00379 /*!
00380  *  pixContrastTRC()
00381  *
00382  *      Input:  pixd (<optional> null or equal to pixs)
00383  *              pixs (8 or 32 bpp; or 2, 4 or 8 bpp with colormap)
00384  *              factor  (0.0 is no enhancement)
00385  *      Return: pixd always
00386  *
00387  *  Notes:
00388  *      (1) pixd must either be null or equal to pixs.
00389  *          For in-place operation, set pixd == pixs:
00390  *             pixContrastTRC(pixs, pixs, ...);
00391  *          To get a new image, set pixd == null:
00392  *             pixd = pixContrastTRC(NULL, pixs, ...);
00393  *      (2) If pixs is colormapped, the colormap is transformed,
00394  *          either in-place or in a copy of pixs.
00395  *      (3) Contrast is enhanced by mapping each color component
00396  *          using an atan function with maximum slope at 127.
00397  *          Pixels below 127 are lowered in intensity and pixels
00398  *          above 127 are increased.
00399  *      (4) The useful range for the contrast factor is scaled to
00400  *          be in (0.0 to 1.0), but larger values can also be used.
00401  *      (5) If factor == 0.0, no enhancement is performed; return a copy
00402  *          unless in-place, in which case this is a no-op.
00403  *      (6) For color images that are not colormapped, the mapping
00404  *          is applied to each component.
00405  */
00406 PIX *
00407 pixContrastTRC(PIX       *pixd,
00408                PIX       *pixs,
00409                l_float32  factor)
00410 {
00411 l_int32   d;
00412 NUMA     *nac;
00413 PIXCMAP  *cmap;
00414 
00415     PROCNAME("pixContrastTRC");
00416 
00417     if (!pixs)
00418         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00419     if (pixd && (pixd != pixs))
00420         return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd);
00421     if (factor < 0.0) {
00422         L_WARNING("factor must be >= 0.0; using 0.0", procName);
00423         factor = 0.0;
00424     }
00425     if (factor == 0.0)
00426         return pixCopy(pixd, pixs);
00427 
00428     cmap = pixGetColormap(pixs);
00429     d = pixGetDepth(pixs);
00430     if (!cmap && d != 8 && d != 32)
00431         return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd);
00432 
00433     if (!pixd)  /* start with a copy if not in-place */
00434         pixd = pixCopy(NULL, pixs);
00435 
00436     if (cmap) {
00437         pixcmapContrastTRC(pixGetColormap(pixd), factor);
00438         return pixd;
00439     }
00440 
00441         /* pixd is 8 or 32 bpp */
00442     if ((nac = numaContrastTRC(factor)) == NULL)
00443         return (PIX *)ERROR_PTR("nac not made", procName, pixd);
00444     pixTRCMap(pixd, NULL, nac);
00445     numaDestroy(&nac);
00446 
00447     return pixd;
00448 }
00449 
00450 
00451 /*!
00452  *  pixContrastTRCMasked()
00453  *
00454  *      Input:  pixd (<optional> null or equal to pixs)
00455  *              pixs (8 or 32 bpp; or 2, 4 or 8 bpp with colormap)
00456  *              pixm (<optional> null or 1 bpp)
00457  *              factor  (0.0 is no enhancement)
00458  *      Return: pixd always
00459  *
00460  *  Notes:
00461  *      (1) Same as pixContrastTRC() except mapping is optionally over
00462  *          a subset of pixels described by pixm.
00463  *      (2) Masking does not work for colormapped images.
00464  *      (3) See pixContrastTRC() for details on how to use the parameters.
00465  */
00466 PIX *
00467 pixContrastTRCMasked(PIX       *pixd,
00468                      PIX       *pixs,
00469                      PIX       *pixm,
00470                      l_float32  factor)
00471 {
00472 l_int32  d;
00473 NUMA    *nac;
00474 
00475     PROCNAME("pixContrastTRCMasked");
00476 
00477     if (!pixm)
00478         return pixContrastTRC(pixd, pixs, factor);
00479 
00480     if (!pixs)
00481         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00482     if (pixGetColormap(pixs))
00483         return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd);
00484     if (pixd && (pixd != pixs))
00485         return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd);
00486     d = pixGetDepth(pixs);
00487     if (d != 8 && d != 32)
00488         return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd);
00489 
00490     if (factor < 0.0) {
00491         L_WARNING("factor must be >= 0.0; using 0.0", procName);
00492         factor = 0.0;
00493     }
00494     if (factor == 0.0)
00495         return pixCopy(pixd, pixs);
00496 
00497     if (!pixd)  /* start with a copy if not in-place */
00498         pixd = pixCopy(NULL, pixs);
00499 
00500     if ((nac = numaContrastTRC(factor)) == NULL)
00501         return (PIX *)ERROR_PTR("nac not made", procName, pixd);
00502     pixTRCMap(pixd, pixm, nac);
00503     numaDestroy(&nac);
00504 
00505     return pixd;
00506 }
00507 
00508 
00509 /*!
00510  *  numaContrastTRC()
00511  *
00512  *      Input:  factor (generally between 0.0 (no enhancement)
00513  *              and 1.0, but can be larger than 1.0)
00514  *      Return: na, or null on error
00515  *
00516  *  Notes:
00517  *      (1) The mapping is monotonic increasing, where 0 is mapped
00518  *          to 0 and 255 is mapped to 255. 
00519  *      (2) As 'factor' is increased from 0.0 (where the mapping is linear),
00520  *          the map gets closer to its limit as a step function that
00521  *          jumps from 0 to 255 at the center (input value = 127).
00522  */
00523 NUMA *
00524 numaContrastTRC(l_float32  factor)
00525 {
00526 l_int32    i, val;
00527 l_float64  x, ymax, ymin, dely, scale;
00528 NUMA      *na;
00529 
00530     PROCNAME("numaContrastTRC");
00531 
00532     if (factor < 0.0) {
00533         L_WARNING("factor must be >= 0.0; using 0.0; no enhancement", procName);
00534         factor = 0.0;
00535     }
00536     if (factor == 0.0)
00537         return numaMakeSequence(0, 1, 256);  /* linear map */
00538 
00539     scale = ENHANCE_SCALE_FACTOR;
00540     ymax = atan((l_float64)(1.0 * factor * scale));
00541     ymin = atan((l_float64)(-127. * factor * scale / 128.));
00542     dely = ymax - ymin;
00543     na = numaCreate(256);
00544     for (i = 0; i < 256; i++) {
00545         x = (l_float64)i;
00546         val = (l_int32)((255. / dely) *
00547              (-ymin + atan((l_float64)(factor * scale * (x - 127.) / 128.))) +
00548                  0.5);
00549         numaAddNumber(na, val);
00550     }
00551 
00552     return na;
00553 }
00554 
00555 
00556 /*-------------------------------------------------------------*
00557  *                     Histogram equalization                  *
00558  *-------------------------------------------------------------*/
00559 /*!
00560  *  pixEqualizeTRC()
00561  *
00562  *      Input:  pixd (<optional> null or equal to pixs)
00563  *              pixs (8 bpp gray, 32 bpp rgb, or colormapped)
00564  *              fract (fraction of equalization movement of pixel values)
00565  *              factor (subsampling factor; integer >= 1)
00566  *      Return: pixd, or null on error
00567  *
00568  *  Notes:
00569  *      (1) pixd must either be null or equal to pixs.
00570  *          For in-place operation, set pixd == pixs:
00571  *             pixEqualizeTRC(pixs, pixs, ...);
00572  *          To get a new image, set pixd == null:
00573  *             pixd = pixEqualizeTRC(NULL, pixs, ...);
00574  *      (2) In histogram equalization, a tone reproduction curve
00575  *          mapping is used to make the number of pixels at each
00576  *          intensity equal.
00577  *      (3) If fract == 0.0, no equalization is performed; return a copy
00578  *          unless in-place, in which case this is a no-op.
00579  *          If fract == 1.0, equalization is complete.
00580  *      (4) Set the subsampling factor > 1 to reduce the amount of computation.
00581  *      (5) If pixs is colormapped, the colormap is removed and
00582  *          converted to rgb or grayscale.
00583  *      (6) If pixs has color, equalization is done in each channel
00584  *          separately.
00585  *      (7) Note that even if there is a colormap, we can get an
00586  *          in-place operation because the intermediate image pixt
00587  *          is copied back to pixs (which for in-place is the same
00588  *          as pixd).
00589  */
00590 PIX *
00591 pixEqualizeTRC(PIX       *pixd,
00592                PIX       *pixs,
00593                l_float32  fract,
00594                l_int32    factor)
00595 {
00596 l_int32   d;
00597 NUMA     *na;
00598 PIX      *pixt, *pix8;
00599 PIXCMAP  *cmap;
00600 
00601     PROCNAME("pixEqualizeTRC");
00602 
00603     if (!pixs)
00604         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00605     if (pixd && (pixd != pixs))
00606         return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd);
00607     cmap = pixGetColormap(pixs);
00608     d = pixGetDepth(pixs);
00609     if (d != 8 && d != 32 && !cmap)
00610         return (PIX *)ERROR_PTR("pixs not 8/32 bpp or cmapped", procName, NULL);
00611     if (fract < 0.0 || fract > 1.0)
00612         return (PIX *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL);
00613     if (factor < 1)
00614         return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL);
00615 
00616     if (fract == 0.0)
00617         return pixCopy(pixd, pixs);
00618  
00619         /* If there is a colormap, remove it. */
00620     if (cmap)
00621         pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
00622     else
00623         pixt = pixClone(pixs);
00624 
00625         /* Make a copy if necessary */
00626     pixd = pixCopy(pixd, pixt);
00627     pixDestroy(&pixt);
00628 
00629     d = pixGetDepth(pixd);
00630     if (d == 8) {
00631         na = numaEqualizeTRC(pixd, fract, factor);
00632         pixTRCMap(pixd, NULL, na);
00633         numaDestroy(&na);
00634     }
00635     else {  /* 32 bpp */
00636         pix8 = pixGetRGBComponent(pixd, COLOR_RED);
00637         na = numaEqualizeTRC(pix8, fract, factor);
00638         pixTRCMap(pix8, NULL, na);
00639         pixSetRGBComponent(pixd, pix8, COLOR_RED);
00640         numaDestroy(&na);
00641         pixDestroy(&pix8);
00642         pix8 = pixGetRGBComponent(pixd, COLOR_GREEN);
00643         na = numaEqualizeTRC(pix8, fract, factor);
00644         pixTRCMap(pix8, NULL, na);
00645         pixSetRGBComponent(pixd, pix8, COLOR_GREEN);
00646         numaDestroy(&na);
00647         pixDestroy(&pix8);
00648         pix8 = pixGetRGBComponent(pixd, COLOR_BLUE);
00649         na = numaEqualizeTRC(pix8, fract, factor);
00650         pixTRCMap(pix8, NULL, na);
00651         pixSetRGBComponent(pixd, pix8, COLOR_BLUE);
00652         numaDestroy(&na);
00653         pixDestroy(&pix8);
00654     }
00655 
00656     return pixd;
00657 }
00658 
00659 
00660 /*!
00661  *  numaEqualizeTRC()
00662  *
00663  *      Input:  pix (8 bpp, no colormap)
00664  *              fract (fraction of equalization movement of pixel values)
00665  *              factor (subsampling factor; integer >= 1)
00666  *      Return: nad, or null on error
00667  *
00668  *  Notes:
00669  *      (1) If fract == 0.0, no equalization will be performed.
00670  *          If fract == 1.0, equalization is complete.
00671  *      (2) Set the subsampling factor > 1 to reduce the amount of computation.
00672  *      (3) The map is returned as a numa with 256 values, specifying
00673  *          the equalized value (array value) for every input value
00674  *          (the array index).
00675  */
00676 NUMA *
00677 numaEqualizeTRC(PIX       *pix,
00678                 l_float32  fract,
00679                 l_int32    factor)
00680 {
00681 l_int32    iin, iout, itarg;
00682 l_float32  val, sum;
00683 NUMA      *nah, *nasum, *nad;
00684 
00685     PROCNAME("numaEqualizeTRC");
00686 
00687     if (!pix)
00688         return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
00689     if (pixGetDepth(pix) != 8)
00690         return (NUMA *)ERROR_PTR("pix not 8 bpp", procName, NULL);
00691     if (fract < 0.0 || fract > 1.0)
00692         return (NUMA *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL);
00693     if (factor < 1)
00694         return (NUMA *)ERROR_PTR("sampling factor < 1", procName, NULL);
00695 
00696     if (fract == 0.0)
00697         L_WARNING("fract = 0.0; no equalization requested", procName);
00698 
00699     if ((nah = pixGetGrayHistogram(pix, factor)) == NULL)
00700         return (NUMA *)ERROR_PTR("histogram not made", procName, NULL);
00701     numaGetSum(nah, &sum);
00702     nasum = numaGetPartialSums(nah);
00703 
00704     nad = numaCreate(256);
00705     for (iin = 0; iin < 256; iin++) {
00706         numaGetFValue(nasum, iin, &val);
00707         itarg = (l_int32)(255. * val / sum + 0.5);
00708         iout = iin + (l_int32)(fract * (itarg - iin));
00709         iout = L_MIN(iout, 255);  /* to be safe */
00710         numaAddNumber(nad, iout);
00711     }
00712 
00713     numaDestroy(&nah);
00714     numaDestroy(&nasum);
00715     return nad;
00716 }
00717 
00718 
00719 /*-------------------------------------------------------------*
00720  *                       Generic TRC mapping                   *
00721  *-------------------------------------------------------------*/
00722 /*!
00723  *  pixTRCMap()
00724  *
00725  *      Input:  pixs (8 grayscale or 32 bpp rgb; not colormapped)
00726  *              pixm (<optional> 1 bpp mask)
00727  *              na (mapping array)
00728  *      Return: pixd, or null on error
00729  *
00730  *  Notes:
00731  *      (1) This operation is in-place on pixs.
00732  *      (2) For 32 bpp, this applies the same map to each of the r,g,b
00733  *          components.
00734  *      (3) The mapping array is of size 256, and it maps the input
00735  *          index into values in the range [0, 255].
00736  *      (4) If defined, the optional 1 bpp mask pixm has its origin
00737  *          aligned with pixs, and the map function is applied only
00738  *          to pixels in pixs under the fg of pixm.
00739  *      (5) For 32 bpp, this does not save the alpha channel.
00740  */
00741 l_int32
00742 pixTRCMap(PIX   *pixs,
00743           PIX   *pixm,
00744           NUMA  *na)
00745 {
00746 l_int32    w, h, d, wm, hm, wpl, wplm, i, j, sval8, dval8;
00747 l_int32   *tab;
00748 l_uint32   sval32, dval32;
00749 l_uint32  *data, *datam, *line, *linem;
00750 
00751     PROCNAME("pixTRCMap");
00752 
00753     if (!pixs)
00754         return ERROR_INT("pixs not defined", procName, 1);
00755     if (pixGetColormap(pixs))
00756         return ERROR_INT("pixs is colormapped", procName, 1);
00757     if (!na)
00758         return ERROR_INT("na not defined", procName, 1);
00759     if (numaGetCount(na) != 256)
00760         return ERROR_INT("na not of size 256", procName, 1);
00761     pixGetDimensions(pixs, &w, &h, &d);
00762     if (d != 8 && d != 32)
00763         return ERROR_INT("pixs not 8 or 32 bpp", procName, 1);
00764     if (pixm) {
00765         if (pixGetDepth(pixm) != 1)
00766             return ERROR_INT("pixm not 1 bpp", procName, 1);
00767     }
00768 
00769     tab = numaGetIArray(na);  /* get the array for efficiency */
00770     wpl = pixGetWpl(pixs);
00771     data = pixGetData(pixs);
00772     if (!pixm) {
00773         if (d == 8) {
00774             for (i = 0; i < h; i++) {
00775                 line = data + i * wpl;
00776                 for (j = 0; j < w; j++) {
00777                     sval8 = GET_DATA_BYTE(line, j);
00778                     dval8 = tab[sval8];
00779                     SET_DATA_BYTE(line, j, dval8);
00780                 }
00781             }
00782         }
00783         else {  /* d == 32 */
00784             for (i = 0; i < h; i++) {
00785                 line = data + i * wpl;
00786                 for (j = 0; j < w; j++) {
00787                     sval32 = *(line + j);
00788                     dval32 =
00789                         tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT |
00790                         tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT |
00791                         tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT;
00792                     *(line + j) = dval32;
00793                 }
00794             }
00795         }
00796     }
00797     else {
00798         datam = pixGetData(pixm);
00799         wplm = pixGetWpl(pixm);
00800         pixGetDimensions(pixm, &wm, &hm, NULL);
00801         if (d == 8) {
00802             for (i = 0; i < h; i++) {
00803                 if (i >= hm)
00804                     break;
00805                 line = data + i * wpl;
00806                 linem = datam + i * wplm;
00807                 for (j = 0; j < w; j++) {
00808                     if (j >= wm)
00809                         break;
00810                     if (GET_DATA_BIT(linem, j) == 0)
00811                         continue;
00812                     sval8 = GET_DATA_BYTE(line, j);
00813                     dval8 = tab[sval8];
00814                     SET_DATA_BYTE(line, j, dval8);
00815                 }
00816             }
00817         }
00818         else {  /* d == 32 */
00819             for (i = 0; i < h; i++) {
00820                 if (i >= hm)
00821                     break;
00822                 line = data + i * wpl;
00823                 linem = datam + i * wplm;
00824                 for (j = 0; j < w; j++) {
00825                     if (j >= wm)
00826                         break;
00827                     if (GET_DATA_BIT(linem, j) == 0)
00828                         continue;
00829                     sval32 = *(line + j);
00830                     dval32 =
00831                         tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT |
00832                         tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT |
00833                         tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT;
00834                     *(line + j) = dval32;
00835                 }
00836             }
00837         }
00838     }
00839 
00840     FREE(tab);
00841     return 0;
00842 }
00843 
00844 
00845 
00846 /*-----------------------------------------------------------------------*
00847  *                             Unsharp masking                           *
00848  *-----------------------------------------------------------------------*/
00849 /*!
00850  *  pixUnsharpMasking()
00851  *
00852  *      Input:  pixs (all depths except 1 bpp; with or without colormaps)
00853  *              halfwidth  ("half-width" of smoothing filter)
00854  *              fract  (fraction of edge added back into image)
00855  *      Return: pixd, or null on error
00856  *
00857  *  Notes:
00858  *      (1) We use symmetric smoothing filters of odd dimension,
00859  *          typically use sizes of 3, 5, 7, etc.  The @halfwidth parameter
00860  *          for these is (size - 1)/2; i.e., 1, 2, 3, etc.
00861  *      (2) The fract parameter is typically taken in the
00862  *          range:  0.2 < fract < 0.7
00863  *      (3) Returns a clone if no sharpening is requested.
00864  */
00865 PIX *
00866 pixUnsharpMasking(PIX       *pixs,
00867                   l_int32    halfwidth,
00868                   l_float32  fract)
00869 {
00870 l_int32  d;
00871 PIX     *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs;
00872 
00873     PROCNAME("pixUnsharpMasking");
00874 
00875     if (!pixs || (pixGetDepth(pixs) == 1))
00876         return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL);
00877     if (fract <= 0.0 || halfwidth <= 0) {
00878         L_WARNING("no sharpening requested; clone returned", procName);
00879         return pixClone(pixs);
00880     }
00881 
00882     if (halfwidth == 1 || halfwidth == 2)
00883         return pixUnsharpMaskingFast(pixs, halfwidth, fract, L_BOTH_DIRECTIONS);
00884 
00885         /* Remove colormap; clone if possible; result is either 8 or 32 bpp */
00886     if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL)
00887         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
00888 
00889         /* Sharpen */
00890     d = pixGetDepth(pixt);
00891     if (d == 8)
00892         pixd = pixUnsharpMaskingGray(pixt, halfwidth, fract);
00893     else {  /* d == 32 */
00894         pixr = pixGetRGBComponent(pixs, COLOR_RED);
00895         pixrs = pixUnsharpMaskingGray(pixr, halfwidth, fract);
00896         pixDestroy(&pixr);
00897         pixg = pixGetRGBComponent(pixs, COLOR_GREEN);
00898         pixgs = pixUnsharpMaskingGray(pixg, halfwidth, fract);
00899         pixDestroy(&pixg);
00900         pixb = pixGetRGBComponent(pixs, COLOR_BLUE);
00901         pixbs = pixUnsharpMaskingGray(pixb, halfwidth, fract);
00902         pixDestroy(&pixb);
00903         pixd = pixCreateRGBImage(pixrs, pixgs, pixbs);
00904         pixDestroy(&pixrs);
00905         pixDestroy(&pixgs);
00906         pixDestroy(&pixbs);
00907     }
00908 
00909     pixDestroy(&pixt);
00910     return pixd;
00911 }
00912 
00913 
00914 /*!
00915  *  pixUnsharpMaskingGray()
00916  *
00917  *      Input:  pixs (8 bpp; no colormap)
00918  *              halfwidth  ("half-width" of smoothing filter)
00919  *              fract  (fraction of edge added back into image)
00920  *      Return: pixd, or null on error
00921  *
00922  *  Notes:
00923  *      (1) We use symmetric smoothing filters of odd dimension,
00924  *          typically use sizes of 3, 5, 7, etc.  The @halfwidth parameter
00925  *          for these is (size - 1)/2; i.e., 1, 2, 3, etc.
00926  *      (2) The fract parameter is typically taken in the range:
00927  *          0.2 < fract < 0.7
00928  *      (3) Returns a clone if no sharpening is requested.
00929  */
00930 PIX *
00931 pixUnsharpMaskingGray(PIX       *pixs,
00932                       l_int32    halfwidth,
00933                       l_float32  fract)
00934 {
00935 l_int32  w, h, d;
00936 PIX     *pixc, *pixd;
00937 PIXACC  *pixacc;
00938 
00939     PROCNAME("pixUnsharpMaskingGray");
00940 
00941     if (!pixs)
00942         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00943     pixGetDimensions(pixs, &w, &h, &d);
00944     if (d != 8 || pixGetColormap(pixs) != NULL)
00945         return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL);
00946     if (fract <= 0.0 || halfwidth <= 0) {
00947         L_WARNING("no sharpening requested; clone returned", procName);
00948         return pixClone(pixs);
00949     }
00950     if (halfwidth == 1 || halfwidth == 2)
00951         return pixUnsharpMaskingGrayFast(pixs, halfwidth, fract,
00952                                          L_BOTH_DIRECTIONS);
00953 
00954     if ((pixc = pixBlockconvGray(pixs, NULL, halfwidth, halfwidth)) == NULL)
00955         return (PIX *)ERROR_PTR("pixc not made", procName, NULL);
00956 
00957         /* Steps:
00958          *    (1) edge image is pixs - pixc  (this is highpass part)
00959          *    (2) multiply edge image by fract
00960          *    (3) add fraction of edge to pixs
00961          *
00962          * To show how this is done with both interfaces to arithmetic
00963          * on integer Pix, here is the implementation in the lower-level
00964          * function calls:
00965          *    pixt = pixInitAccumulate(w, h, 0x10000000)) == NULL)
00966          *    pixAccumulate(pixt, pixs, L_ARITH_ADD);
00967          *    pixAccumulate(pixt, pixc, L_ARITH_SUBTRACT);
00968          *    pixMultConstAccumulate(pixt, fract, 0x10000000);
00969          *    pixAccumulate(pixt, pixs, L_ARITH_ADD);
00970          *    pixd = pixFinalAccumulate(pixt, 0x10000000, 8)) == NULL)
00971          *    pixDestroy(&pixt);
00972          *
00973          * The code below does the same thing using the Pixacc accumulator,
00974          * hiding the details of the offset that is needed for subtraction.
00975          */
00976     pixacc = pixaccCreate(w, h, 1);
00977     pixaccAdd(pixacc, pixs);
00978     pixaccSubtract(pixacc, pixc);
00979     pixaccMultConst(pixacc, fract);
00980     pixaccAdd(pixacc, pixs);
00981     pixd = pixaccFinal(pixacc, 8);
00982     pixaccDestroy(&pixacc);
00983 
00984     pixDestroy(&pixc);
00985     return pixd;
00986 }
00987 
00988 
00989 /*!
00990  *  pixUnsharpMaskingFast()
00991  *
00992  *      Input:  pixs (all depths except 1 bpp; with or without colormaps)
00993  *              halfwidth  ("half-width" of smoothing filter; 1 and 2 only)
00994  *              fract  (fraction of high frequency added to image)
00995  *              direction (L_HORIZ, L_VERT, L_BOTH_DIRECTIONS)
00996  *      Return: pixd, or null on error
00997  *
00998  *  Notes:
00999  *      (1) The fast version uses separable 1-D filters directly on
01000  *          the input image.  The halfwidth is either 1 (full width = 3)
01001  *          or 2 (full width = 5).
01002  *      (2) The fract parameter is typically taken in the
01003  *            range:  0.2 < fract < 0.7
01004  *      (3) To skip horizontal sharpening, use @fracth = 0.0; ditto for @fractv
01005  *      (4) For one dimensional filtering (as an example):
01006  *          For @halfwidth = 1, the low-pass filter is
01007  *              L:    1/3    1/3   1/3
01008  *          and the high-pass filter is
01009  *              H = I - L:   -1/3   2/3   -1/3
01010  *          For @halfwidth = 2, the low-pass filter is
01011  *              L:    1/5    1/5   1/5    1/5    1/5
01012  *          and the high-pass filter is
01013  *              H = I - L:   -1/5  -1/5   4/5  -1/5   -1/5
01014  *          The new sharpened pixel value is found by adding some fraction
01015  *          of the high-pass filter value (which sums to 0) to the
01016  *          initial pixel value:
01017  *              N = I + fract * H
01018  *      (5) For 2D, the sharpening filter is not separable, because the
01019  *          vertical filter depends on the horizontal location relative
01020  *          to the filter origin, and v.v.   So we either do the full
01021  *          2D filter (for @halfwidth == 1) or do the low-pass
01022  *          convolution separably and then compose with the original pix.
01023  *      (6) Returns a clone if no sharpening is requested.
01024  */
01025 PIX *
01026 pixUnsharpMaskingFast(PIX       *pixs,
01027                       l_int32    halfwidth,
01028                       l_float32  fract,
01029                       l_int32    direction)
01030 {
01031 l_int32  d;
01032 PIX     *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs;
01033 
01034     PROCNAME("pixUnsharpMaskingFast");
01035 
01036     if (!pixs || (pixGetDepth(pixs) == 1))
01037         return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL);
01038     if (fract <= 0.0 || halfwidth <= 0) {
01039         L_WARNING("no sharpening requested; clone returned", procName);
01040         return pixClone(pixs);
01041     }
01042     if (halfwidth != 1 && halfwidth != 2)
01043         return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL);
01044     if (direction != L_HORIZ && direction != L_VERT &&
01045         direction != L_BOTH_DIRECTIONS)
01046         return (PIX *)ERROR_PTR("invalid direction", procName, NULL);
01047 
01048         /* Remove colormap; clone if possible; result is either 8 or 32 bpp */
01049     if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL)
01050         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
01051 
01052         /* Sharpen */
01053     d = pixGetDepth(pixt);
01054     if (d == 8)
01055         pixd = pixUnsharpMaskingGrayFast(pixt, halfwidth, fract, direction);
01056     else {  /* d == 32 */
01057         pixr = pixGetRGBComponent(pixs, COLOR_RED);
01058         pixrs = pixUnsharpMaskingGrayFast(pixr, halfwidth, fract, direction);
01059         pixDestroy(&pixr);
01060         pixg = pixGetRGBComponent(pixs, COLOR_GREEN);
01061         pixgs = pixUnsharpMaskingGrayFast(pixg, halfwidth, fract, direction);
01062         pixDestroy(&pixg);
01063         pixb = pixGetRGBComponent(pixs, COLOR_BLUE);
01064         pixbs = pixUnsharpMaskingGrayFast(pixb, halfwidth, fract, direction);
01065         pixDestroy(&pixb);
01066         pixd = pixCreateRGBImage(pixrs, pixgs, pixbs);
01067         pixDestroy(&pixrs);
01068         pixDestroy(&pixgs);
01069         pixDestroy(&pixbs);
01070     }
01071 
01072     pixDestroy(&pixt);
01073     return pixd;
01074 }
01075 
01076 
01077 
01078 /*!
01079  *  pixUnsharpMaskingGrayFast()
01080  *
01081  *      Input:  pixs (8 bpp; no colormap)
01082  *              halfwidth  ("half-width" of smoothing filter: 1 or 2)
01083  *              fract  (fraction of high frequency added to image)
01084  *              direction (L_HORIZ, L_VERT, L_BOTH_DIRECTIONS)
01085  *      Return: pixd, or null on error
01086  *
01087  *  Notes:
01088  *      (1) For usage and explanation of the algorithm, see notes
01089  *          in pixUnsharpMaskingFast().
01090  *      (2) Returns a clone if no sharpening is requested.
01091  */
01092 PIX *
01093 pixUnsharpMaskingGrayFast(PIX       *pixs,
01094                           l_int32    halfwidth,
01095                           l_float32  fract,
01096                           l_int32    direction)
01097 {
01098 PIX  *pixd;
01099 
01100     PROCNAME("pixUnsharpMaskingGrayFast");
01101 
01102     if (!pixs)
01103         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01104     if (pixGetDepth(pixs) != 8 || pixGetColormap(pixs) != NULL)
01105         return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL);
01106     if (fract <= 0.0 || halfwidth <= 0) {
01107         L_WARNING("no sharpening requested; clone returned", procName);
01108         return pixClone(pixs);
01109     }
01110     if (halfwidth != 1 && halfwidth != 2)
01111         return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL);
01112     if (direction != L_HORIZ && direction != L_VERT &&
01113         direction != L_BOTH_DIRECTIONS)
01114         return (PIX *)ERROR_PTR("invalid direction", procName, NULL);
01115 
01116     if (direction != L_BOTH_DIRECTIONS)
01117         pixd = pixUnsharpMaskingGray1D(pixs, halfwidth, fract, direction);
01118     else  /* 2D sharpening */
01119         pixd = pixUnsharpMaskingGray2D(pixs, halfwidth, fract);
01120 
01121     return pixd;
01122 }
01123 
01124 
01125 /*!
01126  *  pixUnsharpMaskingGray1D()
01127  *
01128  *      Input:  pixs (8 bpp; no colormap)
01129  *              halfwidth  ("half-width" of smoothing filter: 1 or 2)
01130  *              fract  (fraction of high frequency added to image)
01131  *              direction (of filtering; use L_HORIZ or L_VERT)
01132  *      Return: pixd, or null on error
01133  *
01134  *  Notes:
01135  *      (1) For usage and explanation of the algorithm, see notes
01136  *          in pixUnsharpMaskingFast().
01137  *      (2) Returns a clone if no sharpening is requested.
01138  */
01139 PIX *
01140 pixUnsharpMaskingGray1D(PIX       *pixs,
01141                         l_int32    halfwidth,
01142                         l_float32  fract,
01143                         l_int32    direction)
01144 {
01145 l_int32    w, h, d, wpls, wpld, i, j, ival;
01146 l_uint32  *datas, *datad;
01147 l_uint32  *lines, *lines0, *lines1, *lines2, *lines3, *lines4, *lined;
01148 l_float32  val, a[5];
01149 PIX       *pixd;
01150 
01151     PROCNAME("pixUnsharpMaskingGray1D");
01152 
01153     if (!pixs)
01154         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01155     pixGetDimensions(pixs, &w, &h, &d);
01156     if (d != 8 || pixGetColormap(pixs) != NULL)
01157         return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL);
01158     if (fract <= 0.0 || halfwidth <= 0) {
01159         L_WARNING("no sharpening requested; clone returned", procName);
01160         return pixClone(pixs);
01161     }
01162     if (halfwidth != 1 && halfwidth != 2)
01163         return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL);
01164 
01165         /* Initialize pixd with pixels from pixs that will not be
01166          * set when computing the sharpened values. */
01167     pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth,
01168                          halfwidth, halfwidth);
01169     datas = pixGetData(pixs);
01170     datad = pixGetData(pixd);
01171     wpls = pixGetWpl(pixs);
01172     wpld = pixGetWpl(pixd);
01173 
01174     if (halfwidth == 1) {
01175         a[0] = -fract / 3.0;
01176         a[1] = 1.0 + fract * 2.0 / 3.0;
01177         a[2] = a[0];
01178     }
01179     else {  /* halfwidth == 2 */
01180         a[0] = -fract / 5.0;
01181         a[1] = a[0];
01182         a[2] = 1.0 + fract * 4.0 / 5.0;
01183         a[3] = a[0];
01184         a[4] = a[0];
01185     }
01186 
01187     if (direction == L_HORIZ) {
01188         for (i = 0; i < h; i++) {
01189             lines = datas + i * wpls;
01190             lined = datad + i * wpld;
01191             if (halfwidth == 1) {
01192                 for (j = 1; j < w - 1; j++) {
01193                     val = a[0] * GET_DATA_BYTE(lines, j - 1) +
01194                           a[1] * GET_DATA_BYTE(lines, j) +
01195                           a[2] * GET_DATA_BYTE(lines, j + 1);
01196                     ival = (l_int32)val;
01197                     ival = L_MAX(0, ival);
01198                     ival = L_MIN(255, ival);
01199                     SET_DATA_BYTE(lined, j, ival);
01200                 }
01201             }
01202             else {  /* halfwidth == 2 */
01203                 for (j = 2; j < w - 2; j++) {
01204                     val = a[0] * GET_DATA_BYTE(lines, j - 2) +
01205                           a[1] * GET_DATA_BYTE(lines, j - 1) +
01206                           a[2] * GET_DATA_BYTE(lines, j) +
01207                           a[3] * GET_DATA_BYTE(lines, j + 1) +
01208                           a[4] * GET_DATA_BYTE(lines, j + 2);
01209                     ival = (l_int32)val;
01210                     ival = L_MAX(0, ival);
01211                     ival = L_MIN(255, ival);
01212                     SET_DATA_BYTE(lined, j, ival);
01213                 }
01214             }
01215         }
01216     }
01217     else {  /* direction == L_VERT */
01218         if (halfwidth == 1) {
01219             for (i = 1; i < h - 1; i++) {
01220                 lines0 = datas + (i - 1) * wpls;
01221                 lines1 = datas + i * wpls;
01222                 lines2 = datas + (i + 1) * wpls;
01223                 lined = datad + i * wpld;
01224                 for (j = 0; j < w; j++) {
01225                     val = a[0] * GET_DATA_BYTE(lines0, j) +
01226                           a[1] * GET_DATA_BYTE(lines1, j) +
01227                           a[2] * GET_DATA_BYTE(lines2, j);
01228                     ival = (l_int32)val;
01229                     ival = L_MAX(0, ival);
01230                     ival = L_MIN(255, ival);
01231                     SET_DATA_BYTE(lined, j, ival);
01232                 }
01233             }
01234         }
01235         else {  /* halfwidth == 2 */
01236             for (i = 2; i < h - 2; i++) {
01237                 lines0 = datas + (i - 2) * wpls;
01238                 lines1 = datas + (i - 1) * wpls;
01239                 lines2 = datas + i * wpls;
01240                 lines3 = datas + (i + 1) * wpls;
01241                 lines4 = datas + (i + 2) * wpls;
01242                 lined = datad + i * wpld;
01243                 for (j = 0; j < w; j++) {
01244                     val = a[0] * GET_DATA_BYTE(lines0, j) +
01245                           a[1] * GET_DATA_BYTE(lines1, j) +
01246                           a[2] * GET_DATA_BYTE(lines2, j) +
01247                           a[3] * GET_DATA_BYTE(lines3, j) +
01248                           a[4] * GET_DATA_BYTE(lines4, j);
01249                     ival = (l_int32)val;
01250                     ival = L_MAX(0, ival);
01251                     ival = L_MIN(255, ival);
01252                     SET_DATA_BYTE(lined, j, ival);
01253                 }
01254             }
01255         }
01256     }
01257 
01258     return pixd;
01259 }
01260 
01261 
01262 /*!
01263  *  pixUnsharpMaskingGray2D()
01264  *
01265  *      Input:  pixs (8 bpp; no colormap)
01266  *              halfwidth  ("half-width" of smoothing filter: 1 or 2)
01267  *              fract  (fraction of high frequency added to image)
01268  *      Return: pixd, or null on error
01269  *
01270  *  Notes:
01271  *      (1) For halfwidth == 1, we implement the full sharpening filter
01272  *          directly.  For halfwidth == 2, we implement the the lowpass
01273  *          filter separably and then compute the sharpening result locally.
01274  *      (2) Returns a clone if no sharpening is requested.
01275  */
01276 PIX *
01277 pixUnsharpMaskingGray2D(PIX       *pixs,
01278                         l_int32    halfwidth,
01279                         l_float32  fract)
01280 {
01281 l_int32     w, h, d, wpls, wpld, wplf, i, j, ival, sval;
01282 l_uint32   *datas, *datad, *lines, *lines0, *lines1, *lines2, *lined;
01283 l_float32   val, a[9];
01284 l_float32  *dataf, *linef, *linef0, *linef1, *linef2, *linef3, *linef4;
01285 PIX        *pixd;
01286 FPIX       *fpix;
01287 
01288     PROCNAME("pixUnsharpMaskingGray2D");
01289 
01290     if (!pixs)
01291         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01292     pixGetDimensions(pixs, &w, &h, &d);
01293     if (d != 8 || pixGetColormap(pixs) != NULL)
01294         return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL);
01295     if (fract <= 0.0 || halfwidth <= 0) {
01296         L_WARNING("no sharpening requested; clone returned", procName);
01297         return pixClone(pixs);
01298     }
01299     if (halfwidth != 1 && halfwidth != 2)
01300         return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL);
01301 
01302     pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth,
01303                          halfwidth, halfwidth);
01304     datad = pixGetData(pixd);
01305     wpld = pixGetWpl(pixd);
01306     datas = pixGetData(pixs);
01307     wpls = pixGetWpl(pixs);
01308 
01309     if (halfwidth == 1) {
01310         for (i = 0; i < 9; i++)
01311             a[i] = -fract / 9.0;
01312         a[4] = 1.0 + fract * 8.0 / 9.0;
01313         for (i = 1; i < h - 1; i++) {
01314             lines0 = datas + (i - 1) * wpls;
01315             lines1 = datas + i * wpls;
01316             lines2 = datas + (i + 1) * wpls;
01317             lined = datad + i * wpld;
01318             for (j = 1; j < w - 1; j++) {
01319                 val = a[0] * GET_DATA_BYTE(lines0, j - 1) +
01320                       a[1] * GET_DATA_BYTE(lines0, j) +
01321                       a[2] * GET_DATA_BYTE(lines0, j + 1) +
01322                       a[3] * GET_DATA_BYTE(lines1, j - 1) +
01323                       a[4] * GET_DATA_BYTE(lines1, j) +
01324                       a[5] * GET_DATA_BYTE(lines1, j + 1) +
01325                       a[6] * GET_DATA_BYTE(lines2, j - 1) +
01326                       a[7] * GET_DATA_BYTE(lines2, j) +
01327                       a[8] * GET_DATA_BYTE(lines2, j + 1);
01328                 ival = (l_int32)(val + 0.5);
01329                 ival = L_MAX(0, ival);
01330                 ival = L_MIN(255, ival);
01331                 SET_DATA_BYTE(lined, j, ival);
01332             }
01333         }
01334 
01335         return pixd;
01336     }
01337 
01338         /* For halfwidth == 2, do the low pass separably.  Store
01339          * the result of horizontal smoothing in an intermediate fpix. */
01340     fpix = fpixCreate(w, h);
01341     dataf = fpixGetData(fpix);
01342     wplf = fpixGetWpl(fpix);
01343     for (i = 2; i < h - 2; i++) {
01344         lines = datas + i * wpls;
01345         linef = dataf + i * wplf;
01346         for (j = 2; j < w - 2; j++) {
01347             val = GET_DATA_BYTE(lines, j - 2) +
01348                   GET_DATA_BYTE(lines, j - 1) +
01349                   GET_DATA_BYTE(lines, j) +
01350                   GET_DATA_BYTE(lines, j + 1) +
01351                   GET_DATA_BYTE(lines, j + 2);
01352             linef[j] = val;
01353         }
01354     }
01355 
01356         /* Do vertical smoothing to finish the low-pass filter.
01357          * At each pixel, if L is the lowpass value, I is the
01358          * src pixel value and f is the fraction of highpass to
01359          * be added to I, then the highpass filter value is
01360          *     H = I - L
01361          * and the new sharpened value is
01362          *     N = I + f * H.
01363          */
01364     for (i = 2; i < h - 2; i++) {
01365         linef0 = dataf + (i - 2) * wplf;
01366         linef1 = dataf + (i - 1) * wplf;
01367         linef2 = dataf + i * wplf;
01368         linef3 = dataf + (i + 1) * wplf;
01369         linef4 = dataf + (i + 2) * wplf;
01370         lined = datad + i * wpld;
01371         lines = datas + i * wpls;
01372         for (j = 2; j < w - 2; j++) {
01373             val = 0.04 * (linef0[j] + linef1[j] + linef2[j] +
01374                           linef3[j] + linef4[j]);  /* L: lowpass filter value */
01375             sval = GET_DATA_BYTE(lines, j);   /* I: source pixel */
01376             ival = (l_int32)(sval + fract * (sval - val) + 0.5);
01377             ival = L_MAX(0, ival);
01378             ival = L_MIN(255, ival);
01379             SET_DATA_BYTE(lined, j, ival);
01380         }
01381     }
01382 
01383     fpixDestroy(&fpix);
01384     return pixd;
01385 }
01386 
01387 
01388 
01389 /*-----------------------------------------------------------------------*
01390  *                    Hue and saturation modification                    *
01391  *-----------------------------------------------------------------------*/
01392 /*!
01393  *  pixModifyHue()
01394  *
01395  *      Input:  pixd (<optional> can be null or equal to pixs)
01396  *              pixs (32 bpp rgb)
01397  *              fract (between -1.0 and 1.0)
01398  *      Return: pixd, or null on error
01399  *
01400  *  Notes:
01401  *      (1) pixd must either be null or equal to pixs.
01402  *          For in-place operation, set pixd == pixs:
01403  *             pixEqualizeTRC(pixs, pixs, ...);
01404  *          To get a new image, set pixd == null:
01405  *             pixd = pixEqualizeTRC(NULL, pixs, ...);
01406  *      (1) Use fract > 0.0 to increase hue value; < 0.0 to decrease it.
01407  *          1.0 (or -1.0) represents a 360 degree rotation; i.e., no change.
01408  *      (2) If no modification is requested (fract = -1.0 or 0 or 1.0),
01409  *          return a copy unless in-place, in which case this is a no-op.
01410  */
01411 PIX  *
01412 pixModifyHue(PIX       *pixd,
01413              PIX       *pixs,
01414              l_float32  fract)
01415 {
01416 l_int32    w, h, d, i, j, wpl, delhue;
01417 l_int32    rval, gval, bval, hval, sval, vval;
01418 l_uint32  *data, *line;
01419 
01420     PROCNAME("pixModifyHue");
01421 
01422     if (!pixs)
01423         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01424     if (pixGetColormap(pixs) != NULL)
01425         return (PIX *)ERROR_PTR("pixs colormapped", procName, NULL);
01426     if (pixd && (pixd != pixs))
01427         return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd);
01428     pixGetDimensions(pixs, &w, &h, &d);
01429     if (d != 32) 
01430         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
01431     if (L_ABS(fract) > 1.0)
01432         return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL);
01433 
01434     pixd = pixCopy(pixd, pixs);
01435 
01436     delhue = (l_int32)(240 * fract);
01437     if (delhue == 0 || delhue == 240 || delhue == -240) {
01438         L_WARNING("no change requested in hue", procName);
01439         return pixd;
01440     }
01441     if (delhue < 0)
01442         delhue += 240;
01443 
01444     data = pixGetData(pixd);
01445     wpl = pixGetWpl(pixd);
01446     for (i = 0; i < h; i++) {
01447         line = data + i * wpl;
01448         for (j = 0; j < w; j++) {
01449             extractRGBValues(line[j], &rval, &gval, &bval);
01450             convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval);
01451             hval = (hval + delhue) % 240;
01452             convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval);
01453             composeRGBPixel(rval, gval, bval, line + j);
01454         }
01455     }
01456 
01457     return pixd;
01458 }
01459 
01460 
01461 /*!
01462  *  pixModifySaturation()
01463  *
01464  *      Input:  pixd (<optional> can be null, existing or equal to pixs)
01465  *              pixs (32 bpp rgb)
01466  *              fract (between -1.0 and 1.0)
01467  *      Return: pixd, or null on error
01468  *
01469  *  Notes:
01470  *      (1) If fract > 0.0, it gives the fraction that the pixel
01471  *          saturation is moved from its initial value toward 255.
01472  *          If fract < 0.0, it gives the fraction that the pixel
01473  *          saturation is moved from its initial value toward 0.
01474  *          The limiting values for fract = -1.0 (1.0) thus set the
01475  *          saturation to 0 (255).
01476  *      (2) If fract = 0, no modification is requested; return a copy
01477  *          unless in-place, in which case this is a no-op.
01478  */
01479 PIX  *
01480 pixModifySaturation(PIX       *pixd,
01481                     PIX       *pixs,
01482                     l_float32  fract)
01483 {
01484 l_int32    w, h, d, i, j, wpl;
01485 l_int32    rval, gval, bval, hval, sval, vval;
01486 l_uint32  *data, *line;
01487 
01488     PROCNAME("pixModifySaturation");
01489 
01490     if (!pixs)
01491         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01492     pixGetDimensions(pixs, &w, &h, &d);
01493     if (d != 32) 
01494         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
01495     if (L_ABS(fract) > 1.0)
01496         return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL);
01497 
01498     pixd = pixCopy(pixd, pixs);
01499     if (fract == 0.0) {
01500         L_WARNING("no change requested in saturation", procName);
01501         return pixd;
01502     }
01503 
01504     data = pixGetData(pixd);
01505     wpl = pixGetWpl(pixd);
01506     for (i = 0; i < h; i++) {
01507         line = data + i * wpl;
01508         for (j = 0; j < w; j++) {
01509             extractRGBValues(line[j], &rval, &gval, &bval);
01510             convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval);
01511             if (fract < 0.0)
01512                 sval = (l_int32)(sval * (1.0 + fract));
01513             else
01514                 sval = (l_int32)(sval + fract * (255 - sval));
01515             convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval);
01516             composeRGBPixel(rval, gval, bval, line + j);
01517         }
01518     }
01519 
01520     return pixd;
01521 }
01522 
01523 
01524 /*!
01525  *  pixMeasureSaturation()
01526  *
01527  *      Input:  pixs (32 bpp rgb)
01528  *              factor (subsampling factor; integer >= 1)
01529  *              &sat (<return> average saturation)
01530  *      Return: pixd, or null on error
01531  */
01532 l_int32
01533 pixMeasureSaturation(PIX        *pixs,
01534                      l_int32     factor,
01535                      l_float32  *psat)
01536 {
01537 l_int32    w, h, d, i, j, wpl, sum, count;
01538 l_int32    rval, gval, bval, hval, sval, vval;
01539 l_uint32  *data, *line;
01540 
01541     PROCNAME("pixMeasureSaturation");
01542 
01543     if (!psat)
01544         return ERROR_INT("pixs not defined", procName, 1);
01545     *psat = 0.0;
01546     if (!pixs)
01547         return ERROR_INT("pixs not defined", procName, 1);
01548     pixGetDimensions(pixs, &w, &h, &d);
01549     if (d != 32) 
01550         return ERROR_INT("pixs not 32 bpp", procName, 1);
01551     if (factor < 1)
01552         return ERROR_INT("subsampling factor < 1", procName, 1);
01553 
01554     data = pixGetData(pixs);
01555     wpl = pixGetWpl(pixs);
01556     for (i = 0, sum = 0, count = 0; i < h; i += factor) {
01557         line = data + i * wpl;
01558         for (j = 0; j < w; j += factor) {
01559             extractRGBValues(line[j], &rval, &gval, &bval);
01560             convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval);
01561             sum += sval;
01562             count++;
01563         }
01564     }
01565 
01566     if (count > 0)
01567         *psat = (l_float32)sum / (l_float32)count;
01568     return 0;
01569 }
01570 
01571 
01572 /*-----------------------------------------------------------------------*
01573  *            General multiplicative constant color transform            *
01574  *-----------------------------------------------------------------------*/
01575 /*
01576  *  pixMultConstantColor()
01577  *
01578  *      Input:  pixs (colormapped or rgb)
01579  *              rfact, gfact, bfact (multiplicative factors on each component)
01580  *      Return: pixd (colormapped or rgb, with colors scaled), or null on error
01581  *
01582  *  Notes:
01583  *      (1) rfact, gfact and bfact can only have non-negative values.
01584  *          They can be greater than 1.0.  All transformed component
01585  *          values are clipped to the interval [0, 255].
01586  *      (2) For multiplication with a general 3x3 matrix of constants,
01587  *          use pixMultMatrixColor().
01588  */
01589 PIX *
01590 pixMultConstantColor(PIX       *pixs,
01591                      l_float32  rfact,
01592                      l_float32  gfact,
01593                      l_float32  bfact)
01594 {
01595 l_int32    i, j, w, h, d, wpls, wpld;
01596 l_int32    ncolors, rval, gval, bval, nrval, ngval, nbval;
01597 l_uint32   nval;
01598 l_uint32  *datas, *datad, *lines, *lined;
01599 PIX       *pixd;
01600 PIXCMAP   *cmap;
01601 
01602     PROCNAME("pixMultConstantColor");
01603 
01604     if (!pixs)
01605         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01606     pixGetDimensions(pixs, &w, &h, &d);
01607     cmap = pixGetColormap(pixs);
01608     if (!cmap && d != 32)
01609         return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
01610     rfact = L_MAX(0.0, rfact);
01611     gfact = L_MAX(0.0, gfact);
01612     bfact = L_MAX(0.0, bfact);
01613 
01614     if (cmap) {
01615         if ((pixd = pixCopy(NULL, pixs)) == NULL)
01616             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01617         cmap = pixGetColormap(pixd);
01618         ncolors = pixcmapGetCount(cmap);
01619         for (i = 0; i < ncolors; i++) {
01620             pixcmapGetColor(cmap, i, &rval, &gval, &bval);
01621             nrval = (l_int32)(rfact * rval);
01622             ngval = (l_int32)(gfact * gval);
01623             nbval = (l_int32)(bfact * bval);
01624             nrval = L_MIN(255, nrval);
01625             ngval = L_MIN(255, ngval);
01626             nbval = L_MIN(255, nbval);
01627             pixcmapResetColor(cmap, i, nrval, ngval, nbval);
01628         }
01629         return pixd;
01630     }
01631 
01632     if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL)
01633         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01634     datas = pixGetData(pixs);
01635     datad = pixGetData(pixd);
01636     wpls = pixGetWpl(pixs);
01637     wpld = pixGetWpl(pixd);
01638     for (i = 0; i < h; i++) {
01639         lines = datas + i * wpls;
01640         lined = datad + i * wpld;
01641         for (j = 0; j < w; j++) {
01642             extractRGBValues(lines[j], &rval, &gval, &bval);
01643             nrval = (l_int32)(rfact * rval);
01644             ngval = (l_int32)(gfact * gval);
01645             nbval = (l_int32)(bfact * bval);
01646             nrval = L_MIN(255, nrval);
01647             ngval = L_MIN(255, ngval);
01648             nbval = L_MIN(255, nbval);
01649             composeRGBPixel(nrval, ngval, nbval, &nval);
01650             *(lined + j) = nval;
01651         }
01652     }
01653 
01654     return pixd;
01655 }
01656 
01657 
01658 /*
01659  *  pixMultMatrixColor()
01660  *
01661  *      Input:  pixs (colormapped or rgb)
01662  *              kernel (3x3 matrix of floats)
01663  *      Return: pixd (colormapped or rgb), or null on error
01664  *
01665  *  Notes:
01666  *      (1) The kernel is a data structure used mostly for floating point
01667  *          convolution.  Here it is a 3x3 matrix of floats that are used
01668  *          to transform the pixel values by matrix multiplication:
01669  *            nrval = a[0,0] * rval + a[0,1] * gval + a[0,2] * bval
01670  *            ngval = a[1,0] * rval + a[1,1] * gval + a[1,2] * bval
01671  *            nbval = a[2,0] * rval + a[2,1] * gval + a[2,2] * bval
01672  *      (2) The matrix can be generated in several ways.
01673  *          See kernel.c for details.  Here are two of them:
01674  *            (a) kel = kernelCreate(3, 3);
01675  *                kernelSetElement(kel, 0, 0, val00);
01676  *                kernelSetElement(kel, 0, 1, val01);
01677  *                ...
01678  *            (b) from a static string; e.g.,:
01679  *                const char *kdata = " 0.6  0.3 -0.2 "
01680  *                                    " 0.1  1.2  0.4 "
01681  *                                    " -0.4 0.2  0.9 ";
01682  *                kel = kernelCreateFromString(3, 3, 0, 0, kdata);
01683  *      (3) For the special case where the matrix is diagonal, it is easier
01684  *          to use pixMultConstantColor().
01685  *      (4) Matrix entries can have positive and negative values, and can
01686  *          be larger than 1.0.  All transformed component values
01687  *          are clipped to [0, 255].
01688  */
01689 PIX *
01690 pixMultMatrixColor(PIX       *pixs,
01691                    L_KERNEL  *kel)
01692 {
01693 l_int32    i, j, index, kw, kh, w, h, d, wpls, wpld;
01694 l_int32    ncolors, rval, gval, bval, nrval, ngval, nbval;
01695 l_uint32   nval;
01696 l_uint32  *datas, *datad, *lines, *lined;
01697 l_float32  v[9];  /* use linear array for convenience */
01698 PIX       *pixd;
01699 PIXCMAP   *cmap;
01700 
01701     PROCNAME("pixMultMatrixColor");
01702 
01703     if (!pixs)
01704         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01705     if (!kel)
01706         return (PIX *)ERROR_PTR("kel not defined", procName, NULL);
01707     kernelGetParameters(kel, &kw, &kh, NULL, NULL);
01708     if (kw != 3 || kh != 3)
01709         return (PIX *)ERROR_PTR("matrix not 3x3", procName, NULL);
01710     pixGetDimensions(pixs, &w, &h, &d);
01711     cmap = pixGetColormap(pixs);
01712     if (!cmap && d != 32)
01713         return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
01714 
01715     for (i = 0, index = 0; i < 3; i++)
01716         for (j = 0; j < 3; j++, index++)
01717             kernelGetElement(kel, i, j, v + index);
01718 
01719     if (cmap) {
01720         if ((pixd = pixCopy(NULL, pixs)) == NULL)
01721             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01722         cmap = pixGetColormap(pixd);
01723         ncolors = pixcmapGetCount(cmap);
01724         for (i = 0; i < ncolors; i++) {
01725             pixcmapGetColor(cmap, i, &rval, &gval, &bval);
01726             nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval);
01727             ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval);
01728             nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval);
01729             nrval = L_MAX(0, L_MIN(255, nrval));
01730             ngval = L_MAX(0, L_MIN(255, ngval));
01731             nbval = L_MAX(0, L_MIN(255, nbval));
01732             pixcmapResetColor(cmap, i, nrval, ngval, nbval);
01733         }
01734         return pixd;
01735     }
01736 
01737     if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL)
01738         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01739     datas = pixGetData(pixs);
01740     datad = pixGetData(pixd);
01741     wpls = pixGetWpl(pixs);
01742     wpld = pixGetWpl(pixd);
01743     for (i = 0; i < h; i++) {
01744         lines = datas + i * wpls;
01745         lined = datad + i * wpld;
01746         for (j = 0; j < w; j++) {
01747             extractRGBValues(lines[j], &rval, &gval, &bval);
01748             nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval);
01749             ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval);
01750             nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval);
01751             nrval = L_MAX(0, L_MIN(255, nrval));
01752             ngval = L_MAX(0, L_MIN(255, ngval));
01753             nbval = L_MAX(0, L_MIN(255, nbval));
01754             composeRGBPixel(nrval, ngval, nbval, &nval);
01755             *(lined + j) = nval;
01756         }
01757     }
01758 
01759     return pixd;
01760 }
01761 
01762 
01763 /*-------------------------------------------------------------*
01764  *                    Half-edge by bandpass                    *
01765  *-------------------------------------------------------------*/
01766 /*!
01767  *  pixHalfEdgeByBandpass()
01768  *
01769  *      Input:  pixs (8 bpp gray or 32 bpp rgb)
01770  *              sm1h, sm1v ("half-widths" of smoothing filter sm1)
01771  *              sm2h, sm2v ("half-widths" of smoothing filter sm2)
01772  *                      (require sm2 != sm1)
01773  *      Return: pixd, or null on error
01774  *
01775  *  Notes:
01776  *      (1) We use symmetric smoothing filters of odd dimension,
01777  *          typically use 3, 5, 7, etc.  The smoothing parameters
01778  *          for these are 1, 2, 3, etc.  The filter size is related
01779  *          to the smoothing parameter by
01780  *               size = 2 * smoothing + 1
01781  *      (2) Because we take the difference of two lowpass filters,
01782  *          this is actually a bandpass filter.
01783  *      (3) We allow both filters to be anisotropic.
01784  *      (4) Consider either the h or v component of the 2 filters.
01785  *          Depending on whether sm1 > sm2 or sm2 > sm1, we get
01786  *          different halves of the smoothed gradients (or "edges").
01787  *          This difference of smoothed signals looks more like
01788  *          a second derivative of a transition, which we rectify
01789  *          by not allowing the signal to go below zero.  If sm1 < sm2,
01790  *          the sm2 transition is broader, so the difference between
01791  *          sm1 and sm2 signals is positive on the upper half of
01792  *          the transition.  Likewise, if sm1 > sm2, the sm1 - sm2
01793  *          signal difference is positive on the lower half of
01794  *          the transition.
01795  */
01796 PIX *
01797 pixHalfEdgeByBandpass(PIX     *pixs,
01798                       l_int32  sm1h,
01799                       l_int32  sm1v,
01800                       l_int32  sm2h,
01801                       l_int32  sm2v)
01802 {
01803 l_int32  d;
01804 PIX     *pixg, *pixacc, *pixc1, *pixc2;
01805 
01806     PROCNAME("pixHalfEdgeByBandpass");
01807 
01808     if (!pixs)
01809         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01810     if (sm1h == sm2h && sm1v == sm2v)
01811         return (PIX *)ERROR_PTR("sm2 = sm1", procName, NULL);
01812     d = pixGetDepth(pixs);
01813     if (d != 8 && d != 32)
01814         return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL);
01815     if (d == 32)
01816         pixg = pixConvertRGBToLuminance(pixs);
01817     else   /* d == 8 */
01818         pixg = pixClone(pixs);
01819 
01820         /* Make a convolution accumulator and use it twice */
01821     if ((pixacc = pixBlockconvAccum(pixg)) == NULL)
01822         return (PIX *)ERROR_PTR("pixacc not made", procName, NULL);
01823     if ((pixc1 = pixBlockconvGray(pixg, pixacc, sm1h, sm1v)) == NULL)
01824         return (PIX *)ERROR_PTR("pixc1 not made", procName, NULL);
01825     if ((pixc2 = pixBlockconvGray(pixg, pixacc, sm2h, sm2v)) == NULL)
01826         return (PIX *)ERROR_PTR("pixc2 not made", procName, NULL);
01827     pixDestroy(&pixacc);
01828 
01829         /* Compute the half-edge using pixc1 - pixc2.  */
01830     pixSubtractGray(pixc1, pixc1, pixc2);
01831 
01832     pixDestroy(&pixg);
01833     pixDestroy(&pixc2);
01834     return pixc1;
01835 }
01836 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines